ctags -t and routines that return structs; bug report 4.3BSD/ucb/239
[unix-history] / usr / src / usr.bin / ctags / C.c
CommitLineData
bcb9ffff
KB
1/*
2 * Copyright (c) 1987 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 */
6
7#ifndef lint
4c5197ec 8static char sccsid[] = "@(#)C.c 5.2 (Berkeley) %G%";
bcb9ffff
KB
9#endif not lint
10
11#include <stdio.h>
12#include <ctags.h>
13
14/*
15 * c_entries --
16 * read .c and .h files and call appropriate routines
17 */
18c_entries()
19{
20 extern int tflag; /* -t: create tags for typedefs */
21 register int c, /* current character */
22 level; /* brace level */
23 register char *sp; /* buffer pointer */
24 int token, /* if reading a token */
25 t_def, /* if reading a typedef */
26 t_level; /* typedef's brace level */
27 char tok[MAXTOKEN]; /* token buffer */
28
29 lineftell = ftell(inf);
30 sp = tok; token = t_def = NO; t_level = -1; level = 0; lineno = 1;
31 while (GETC(!=,EOF)) {
32
33 switch ((char)c) {
34 /*
35 * Here's where it DOESN'T handle:
36 * foo(a)
37 * {
38 * #ifdef notdef
39 * }
40 * #endif
41 * if (a)
42 * puts("hello, world");
43 * }
44 */
45 case '{':
46 ++level;
47 goto endtok;
48 case '}':
49 /*
50 * if level goes below zero, try and fix
51 * it, even though we've already messed up
52 */
53 if (--level < 0)
54 level = 0;
55 goto endtok;
56
57 case '\n':
58 SETLINE;
59 /*
60 * the above 3 cases are similar in that they
61 * are special characters that also end tokens.
62 */
63endtok: if (sp > tok) {
64 *sp = EOS;
65 token = YES;
66 sp = tok;
67 }
68 else
69 token = NO;
70 continue;
71
72 /* we ignore quoted strings and comments in their entirety */
73 case '"':
74 case '\'':
75 (void)skip_key(c);
76 break;
77
78 /*
79 * comments can be fun; note the state is unchanged after
80 * return, in case we found:
81 * "foo() XX comment XX { int bar; }"
82 */
83 case '/':
84 if (GETC(==,'*')) {
85 skip_comment();
86 continue;
87 }
88 (void)ungetc(c,inf);
89 c = '/';
90 goto storec;
91
92 /* hash marks flag #define's. */
93 case '#':
94 if (sp == tok) {
95 hash_entry();
96 break;
97 }
98 goto storec;
99
100 /*
101 * if we have a current token, parenthesis on
102 * level zero indicates a function.
103 */
104 case '(':
105 if (!level && token) {
106 int curline;
107
108 if (sp != tok)
109 *sp = EOS;
110 /*
111 * grab the line immediately, we may
112 * already be wrong, for example,
113 * foo\n
114 * (arg1,
115 */
116 getline();
117 curline = lineno;
118 if (func_entry()) {
119 ++level;
120 pfnote(tok,curline);
121 }
122 break;
123 }
124 goto storec;
125
126 /*
127 * semi-colons indicate the end of a typedef; if we find a
128 * typedef we search for the next semi-colon of the same
129 * level as the typedef. Ignoring "structs", they are
130 * tricky, since you can find:
131 *
132 * "typedef long time_t;"
133 * "typedef unsigned int u_int;"
134 * "typedef unsigned int u_int [10];"
135 *
136 * If looking at a typedef, we save a copy of the last token
137 * found. Then, when we find the ';' we take the current
138 * token if it starts with a valid token name, else we take
139 * the one we saved. There's probably some reasonable
140 * alternative to this...
141 */
142 case ';':
143 if (t_def && level == t_level) {
144 t_def = NO;
145 getline();
146 if (sp != tok)
147 *sp = EOS;
148 pfnote(tok,lineno);
149 break;
150 }
151 goto storec;
152
153 /*
154 * store characters until one that can't be part of a token
155 * comes along; check the current token against certain
156 * reserved words.
157 */
158 default:
159storec: if (!intoken(c)) {
160 if (sp == tok)
161 break;
162 *sp = EOS;
163 if (tflag) {
164 /* no typedefs inside typedefs */
165 if (!t_def && !bcmp(tok,"typedef",8)) {
166 t_def = YES;
167 t_level = level;
168 break;
169 }
170 /* catch "typedef struct" */
171 if ((!t_def || t_level < level)
172 && (!bcmp(tok,"struct",7)
173 || !bcmp(tok,"union",6)
174 || !bcmp(tok,"enum",5))) {
175 /*
176 * get line immediately;
177 * may change before '{'
178 */
179 getline();
180 if (str_entry(c))
181 ++level;
182 break;
183 }
184 }
185 sp = tok;
186 }
187 else if (sp != tok || begtoken(c)) {
188 *sp++ = c;
189 token = YES;
190 }
191 continue;
192 }
193 sp = tok;
194 token = NO;
195 }
196}
197
198/*
199 * func_entry --
200 * handle a function reference
201 */
202static
203func_entry()
204{
205 register int c; /* current character */
206
207 /*
208 * we assume that the character after a function's right paren
209 * is a token character if it's a function and a non-token
210 * character if it's a declaration. Comments don't count...
211 */
212 (void)skip_key((int)')');
213 for (;;) {
214 while (GETC(!=,EOF) && iswhite(c))
215 if (c == (int)'\n')
216 SETLINE;
217 if (intoken(c) || c == (int)'{')
218 break;
219 if (c == (int)'/' && GETC(==,'*'))
220 skip_comment();
221 else { /* don't ever "read" '/' */
222 (void)ungetc(c,inf);
223 return(NO);
224 }
225 }
226 if (c != (int)'{')
227 (void)skip_key((int)'{');
228 return(YES);
229}
230
231/*
232 * hash_entry --
233 * handle a line starting with a '#'
234 */
235static
236hash_entry()
237{
238 extern int dflag; /* -d: non-macro defines */
239 register int c, /* character read */
240 curline; /* line started on */
241 register char *sp; /* buffer pointer */
242 char tok[MAXTOKEN]; /* storage buffer */
243
244 curline = lineno;
245 for (sp = tok;;) { /* get next token */
246 if (GETC(==,EOF))
247 return;
248 if (iswhite(c))
249 break;
250 *sp++ = c;
251 }
252 *sp = EOS;
253 if (bcmp(tok,"define",6)) /* only interested in #define's */
254 goto skip;
255 for (;;) { /* this doesn't handle "#define \n" */
256 if (GETC(==,EOF))
257 return;
258 if (!iswhite(c))
259 break;
260 }
261 for (sp = tok;;) { /* get next token */
262 *sp++ = c;
263 if (GETC(==,EOF))
264 return;
265 /*
266 * this is where it DOESN'T handle
267 * "#define \n"
268 */
269 if (!intoken(c))
270 break;
271 }
272 *sp = EOS;
273 if (dflag || c == (int)'(') { /* only want macros */
274 getline();
275 pfnote(tok,curline);
276 }
277skip: if (c == (int)'\n') { /* get rid of rest of define */
278 SETLINE
279 if (*(sp - 1) != '\\')
280 return;
281 }
282 (void)skip_key((int)'\n');
283}
284
285/*
286 * str_entry --
287 * handle a struct, union or enum entry
288 */
289static
290str_entry(c)
291 register int c; /* current character */
292{
293 register char *sp; /* buffer pointer */
294 int curline; /* line started on */
295 char tok[BUFSIZ]; /* storage buffer */
296
297 curline = lineno;
298 while (iswhite(c))
299 if (GETC(==,EOF))
300 return(NO);
301 if (c == (int)'{') /* it was "struct {" */
302 return(YES);
303 for (sp = tok;;) { /* get next token */
304 *sp++ = c;
305 if (GETC(==,EOF))
306 return(NO);
307 if (!intoken(c))
308 break;
309 }
310 switch ((char)c) {
311 case '{': /* it was "struct foo{" */
312 --sp;
313 break;
314 case '\n': /* it was "struct foo\n" */
315 SETLINE;
316 /*FALLTHROUGH*/
317 default: /* probably "struct foo " */
318 while (GETC(!=,EOF))
319 if (!iswhite(c))
320 break;
4c5197ec
KB
321 if (c != (int)'{') {
322 (void)ungetc(c, inf);
bcb9ffff 323 return(NO);
4c5197ec 324 }
bcb9ffff
KB
325 }
326 *sp = EOS;
327 pfnote(tok,curline);
328 return(YES);
329}
330
331/*
332 * skip_comment --
333 * skip over comment
334 */
335skip_comment()
336{
337 register int c, /* character read */
338 star; /* '*' flag */
339
340 for (star = 0;GETC(!=,EOF);)
341 switch((char)c) {
342 /* comments don't nest, nor can they be escaped. */
343 case '*':
344 star = YES;
345 break;
346 case '/':
347 if (star)
348 return;
349 break;
350 case '\n':
351 SETLINE;
352 /*FALLTHROUGH*/
353 default:
354 star = NO;
355 }
356}
357
358/*
359 * skip_key --
360 * skip to next char "key"
361 */
362skip_key(key)
363 register int key;
364{
365 register int c,
366 skip,
367 retval;
368
369 for (skip = retval = NO;GETC(!=,EOF);)
370 switch((char)c) {
371 case '\\': /* a backslash escapes anything */
372 skip = !skip; /* we toggle in case it's "\\" */
373 break;
374 case ';': /* special case for yacc; if one */
375 case '|': /* of these chars occurs, we may */
376 retval = YES; /* have moved out of the rule */
377 break; /* not used by C */
378 case '\n':
379 SETLINE;
380 /*FALLTHROUGH*/
381 default:
382 if (c == key && !skip)
383 return(retval);
384 skip = NO;
385 }
386 return(retval);
387}