3 /* This is a reimplementation of the ctags(1) program. It supports ANSI C,
4 * and has heaps o' flags. It is meant to be distributed with elvis.
23 #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
25 /* -------------------------------------------------------------------------- */
26 /* Some global variables */
28 /* The following boolean variables are set according to command line flags */
29 int incl_static
; /* -s include static tags */
30 int incl_types
; /* -t include typedefs and structs */
31 int incl_vars
; /* -v include variables */
32 int make_refs
; /* -r generate a "refs" file */
33 int append_files
; /* -a append to "tags" [and "refs"] files */
35 /* The following are used for outputting to the "tags" and "refs" files */
36 FILE *tags
; /* used for writing to the "tags" file */
37 FILE *refs
; /* used for writing to the "refs" file */
39 /* -------------------------------------------------------------------------- */
40 /* These are used for reading a source file. It keeps track of line numbers */
41 char *file_name
; /* name of the current file */
42 FILE *file_fp
; /* stream used for reading the file */
43 long file_lnum
; /* line number in the current file */
44 long file_seek
; /* fseek() offset to the start of current line */
45 int file_afternl
; /* boolean: was previous character a newline? */
46 int file_prevch
; /* a single character that was ungotten */
47 int file_header
; /* boolean: is the current file a header file? */
49 /* This function opens a file, and resets the line counter. If it fails, it
50 * it will display an error message and leave the file_fp set to NULL.
53 char *name
; /* name of file to be opened */
55 /* if another file was already open, then close it */
61 /* try to open the file for reading. The file must be opened in
62 * "binary" mode because otherwise fseek() would misbehave under DOS.
65 file_fp
= fopen(name
, "rb");
67 file_fp
= fopen(name
, "r");
74 /* reset the name & line number */
80 /* determine whether this is a header file */
82 name
+= strlen(name
) - 2;
83 if (name
>= file_name
&& name
[0] == '.' && (name
[1] == 'h' || name
[1] == 'H'))
89 /* This function reads a single character from the stream. If the *previous*
90 * character was a newline, then it also increments file_lnum and sets
97 /* if there is an ungotten character, then return it. Don't do any
98 * other processing on it, though, because we already did that the
99 * first time it was read.
108 /* if previous character was a newline, then we're starting a line */
111 file_afternl
= FALSE
;
112 file_seek
= ftell(file_fp
);
116 /* Get a character. If no file is open, then return EOF */
117 ch
= (file_fp
? getc(file_fp
) : EOF
);
119 /* if it is a newline, then remember that fact */
125 /* return the character */
129 /* This function ungets a character from the current source file */
131 int ch
; /* character to be ungotten */
136 /* This function copies the current line out some other fp. It has no effect
137 * on the file_getc() function. During copying, any '\' characters are doubled
138 * and a leading '^' or trailing '$' is also quoted. The newline character is
141 * This is meant to be used when generating a tag line.
143 void file_copyline(seek
, fp
)
144 long seek
; /* where the lines starts in the source file */
145 FILE *fp
; /* the output stream to copy it to */
147 long oldseek
;/* where the file's pointer was before we messed it up */
148 char ch
; /* a single character from the file */
149 char next
; /* the next character from this file */
151 /* go to the start of the line */
152 oldseek
= ftell(file_fp
);
153 fseek(file_fp
, seek
, 0);
155 /* if first character is '^', then emit \^ */
164 /* write everything up to, but not including, the newline */
167 /* preread the next character from this file */
168 next
= getc(file_fp
);
170 /* if character is '\', or a terminal '$', then quote it */
171 if (ch
== '\\' || (ch
== '$' && next
== '\n'))
177 /* next character... */
181 /* seek back to the old position */
182 fseek(file_fp
, oldseek
, 0);
185 /* -------------------------------------------------------------------------- */
186 /* This section handles preprocessor directives. It strips out all of the
187 * directives, and may emit a tag for #define directives.
190 int cpp_afternl
; /* boolean: look for '#' character? */
191 int cpp_prevch
; /* an ungotten character, if any */
192 int cpp_refsok
; /* boolean: can we echo characters out to "refs"? */
194 /* This function opens the file & resets variables */
196 char *name
; /* name of source file to be opened */
198 /* use the lower-level file_open function to open the file */
201 /* reset variables */
206 /* This function copies a character from the source file to the "refs" file */
208 int ch
; /* the character to copy */
212 /* echo non-EOF chars, unless not making "ref", or echo turned off */
213 if (ch
!= EOF
&& make_refs
&& cpp_refsok
&& !file_header
)
215 /* try to avoid blank lines */
229 /* add the character */
234 /* This function returns the next character which isn't part of a directive */
238 int ch
; /* the next input character */
241 /* if we have an ungotten character, then return it */
249 /* Get a character from the file. Return it if not special '#' */
257 else if (ch
!= '#' || !cpp_afternl
)
259 /* normal character. Any non-whitespace should turn off afternl */
260 if (ch
!= ' ' && ch
!= '\t')
268 /* Yikes! We found a directive */
270 /* see whether this is a #define line */
276 /* space character matches any whitespace */
280 } while (ch
== ' ' || ch
== '\t');
285 /* other characters should match exactly */
296 /* is this a #define line? and should we generate a tag for it? */
297 if (!*scan
&& (file_header
|| incl_static
))
299 /* if not a header, then this will be a static tag */
302 fputs(file_name
, tags
);
306 /* output the tag name */
307 for (ch
= file_getc(); isalnum(ch
) || ch
== '_'; ch
= file_getc())
312 /* output a tab, the filename, another tab, and the line number */
313 fprintf(tags
, "\t%s\t%ld\n", file_name
, file_lnum
);
316 /* skip to the end of the directive -- a newline that isn't preceded
317 * by a '\' character.
319 while (ch
!= EOF
&& ch
!= '\n')
328 /* return the newline that we found at the end of the directive */
333 /* This puts a character back into the input queue for the source file */
335 int ch
; /* a character to be ungotten */
341 /* -------------------------------------------------------------------------- */
342 /* This is the lexical analyser. It gets characters from the preprocessor,
343 * and gives tokens to the parser. Some special codes are...
344 * (deleted) / *...* / (comments)
345 * (deleted) //...\n (comments)
346 * (deleted) (* (parens used in complex declaration)
347 * (deleted) [...] (array subscript, when ... contains no ])
348 * (deleted) struct (intro to structure declaration)
349 * BODY {...} ('{' can occur anywhere, '}' only at BOW if ... has '{')
350 * ARGS (...{ (args of function, not extern or forward)
351 * ARGS (...); (args of an extern/forward function declaration)
352 * COMMA , (separate declarations that have same scope)
353 * SEMICOLON ; (separate declarations that have different scope)
354 * SEMICOLON =...; (initializer)
355 * TYPEDEF typedef (the "typedef" keyword)
356 * STATIC static (the "static" keyword)
357 * STATIC private (the "static" keyword)
358 * STATIC PRIVATE (the "static" keyword)
359 * NAME [a-z]+ (really any valid name that isn't reserved word)
373 char lex_name
[BLKSIZE
]; /* the name of a "NAME" token */
374 long lex_seek
; /* start of line that contains lex_name */
378 int ch
; /* a character from the preprocessor */
379 int next
; /* the next character */
380 int token
; /* the token that we'll return */
383 /* loop until we get a token that isn't "DELETED" */
386 /* get the next character */
389 /* process the character */
401 /* get the next character */
405 case '*': /* start of C comment */
408 while (next
!= EOF
&& (ch
!= '*' || next
!= '/'))
415 case '/': /* start of a C++ comment */
419 } while (ch
!= '\n' && ch
!= EOF
);
422 default: /* some other slash */
437 while (ch
!= '{' && ch
!= EOF
&& (ch
!= ')' || next
!= ';'))/*}*/
446 else if (next
== ';')
455 /* don't send the next characters to "refs" */
458 /* skip ahead to closing '}', or to embedded '{' */
462 } while (ch
!= '{' && ch
!= '}' && ch
!= EOF
);
464 /* if has embedded '{', then skip to '}' in column 1 */
469 while (ch
!= EOF
&& (ch
!= '\n' || next
!= '}'))/*{*/
476 /* resume "refs" processing */
484 /* skip to matching ']' */
488 } while (ch
!= ']' && ch
!= EOF
);
493 /* skip to next ';' */
498 /* leave array initializers out of "refs" */
503 } while (ch
!= ';' && ch
!= EOF
);
505 /* resume echoing to "refs" */
520 /* is this the start of a name/keyword? */
521 if (isalpha(ch
) || ch
== '_')
523 /* collect the whole word */
525 for (i
= 1, ch
= cpp_getc();
526 i
< BLKSIZE
- 1 && (isalnum(ch
) || ch
== '_');
527 i
++, ch
= cpp_getc())
534 /* is it a reserved word? */
535 if (!strcmp(lex_name
, "typedef"))
540 else if (!strcmp(lex_name
, "static")
541 || !strcmp(lex_name
, "private")
542 || !strcmp(lex_name
, "PRIVATE"))
547 else if (!strcmp(lex_name
, "extern")
548 || !strcmp(lex_name
, "EXTERN")
549 || !strcmp(lex_name
, "FORWARD"))
557 lex_seek
= file_seek
;
560 else /* not part of a name/keyword */
565 } /* end switch(ch) */
567 } while (token
== DELETED
);
572 /* -------------------------------------------------------------------------- */
573 /* This is the parser. It locates tag candidates, and then decides whether to
574 * generate a tag for them.
577 /* This function generates a tag for the object in lex_name, whose tag line is
578 * located at a given seek offset.
580 void maketag(scope
, seek
)
581 int scope
; /* 0 if global, or STATIC if static */
582 long seek
; /* the seek offset of the line */
584 /* output the tagname and filename fields */
587 /* whoa! we should *never* output a tag for "extern" decl */
590 else if (scope
== STATIC
)
592 fprintf(tags
, "%s:%s\t%s\t", file_name
, lex_name
, file_name
);
596 fprintf(tags
, "%s\t%s\t", lex_name
, file_name
);
599 /* output the target line */
602 file_copyline(seek
, tags
);
609 /* This function parses a source file, adding any tags that it finds */
611 char *name
; /* the name of a source file to be checked */
613 int prev
; /* the previous token from the source file */
614 int token
; /* the current token from the source file */
615 int scope
; /* normally 0, but could be a TYPEDEF or STATIC token */
616 int gotname
;/* boolean: does lex_name contain a tag candidate? */
617 long tagseek
;/* start of line that contains lex_name */
627 /* parse until the end of the file */
628 while (prev
= token
, (token
= lex_gettoken()) != EOF
)
631 if (token
== TYPEDEF
|| token
== STATIC
|| token
== EXTERN
)
638 /* name of a possible tag candidate? */
646 /* if NAME BODY, without ARGS, then NAME is a struct tag */
647 if (gotname
&& token
== BODY
&& prev
!= ARGS
)
651 /* ignore if in typedef -- better name is coming soon */
652 if (scope
== TYPEDEF
)
657 /* generate a tag, if -t and maybe -s */
658 if (incl_types
&& (file_header
|| incl_static
))
660 maketag(file_header
? 0 : STATIC
, tagseek
);
664 /* If NAME ARGS BODY, then NAME is a function */
665 if (gotname
&& prev
== ARGS
&& token
== BODY
)
669 /* generate a tag, maybe checking -s */
670 if (scope
!= STATIC
|| incl_static
)
672 maketag(scope
, tagseek
);
676 /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef */
677 if (gotname
&& (token
== SEMICOLON
|| token
== COMMA
))
681 /* generate a tag, if -v/-t and maybe -s */
682 if (scope
== TYPEDEF
&& incl_types
&& (file_header
|| incl_static
)
683 || scope
== STATIC
&& incl_vars
&& incl_static
686 /* a TYPEDEF outside of a header is STATIC */
687 if (scope
== TYPEDEF
&& !file_header
)
689 maketag(STATIC
, tagseek
);
691 else /* use whatever scope was declared */
693 maketag(scope
, tagseek
);
698 /* reset after a semicolon or ARGS BODY pair */
699 if (token
== SEMICOLON
|| (prev
== ARGS
&& token
== BODY
))
706 /* The source file will be automatically closed */
709 /* -------------------------------------------------------------------------- */
713 fprintf(stderr
, "usage: ctags [flags] filenames...\n");
714 fprintf(stderr
, "\t-s include static functions\n");
715 fprintf(stderr
, "\t-t include typedefs\n");
716 fprintf(stderr
, "\t-v include variable declarations\n");
717 fprintf(stderr
, "\t-r generate a \"refs\" file, too\n");
718 fprintf(stderr
, "\t-a append to \"tags\", instead of overwriting\n");
725 # include "amiwild.c"
729 # include "vmswild.c"
740 argv
= wildexpand(&argc
, argv
);
743 /* build the tables used by the ctype macros */
746 /* parse the option flags */
747 for (i
= 1; i
< argc
&& argv
[i
][0] == '-'; i
++)
749 for (j
= 1; argv
[i
][j
]; j
++)
753 case 's': incl_static
= TRUE
; break;
754 case 't': incl_types
= TRUE
; break;
755 case 'v': incl_vars
= TRUE
; break;
756 case 'r': make_refs
= TRUE
; break;
757 case 'a': append_files
= TRUE
; break;
763 /* There should always be at least one source file named in args */
769 /* open the "tags" and maybe "refs" files */
770 tags
= fopen(TAGS
, append_files
? "a" : "w");
778 refs
= fopen(REFS
, append_files
? "a" : "w");
786 /* parse each source file */
787 for (; i
< argc
; i
++)
792 /* close "tags" and maybe "refs" */
800 /* This is a hack which will sort the tags list. It should
801 * on UNIX and OS-9. You may have trouble with csh. Note
802 * that the tags list only has to be sorted if you intend to
803 * use it with the real vi; elvis permits unsorted tags.
806 system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
808 system("sort tags >_tags$$; mv _tags$$ tags");
817 # define WILDCARD_NO_MAIN
818 # include "wildcard.c"