* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)ctags.x 5.1 (Berkeley) 10/22/88";
* ctags: create a tags file
#define MAXTOKEN 250 /* max size of single token */
#define SETLINE {++lineno;lineftell = ftell(inf);}
#define iswhite(arg) (_wht[arg]) /* T if char is white */
#define begtoken(arg) (_btk[arg]) /* T if char can start token */
#define intoken(arg) (_itk[arg]) /* T if char can be in token */
#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
#define isgood(arg) (_gd[arg]) /* T if char can be after ')' */
typedef struct nd_st
{ /* sorting structure */
*right
; /* left and right sons */
char *entry
, /* function or type name */
*pat
; /* search pattern */
int lno
; /* for -x option */
bool been_warned
; /* set if noticed dup */
NODE
*head
; /* head of the sorted binary tree */
/* boolean "func" (see init()) */
bool _wht
[0177],_etk
[0177],_itk
[0177],_btk
[0177],_gd
[0177];
FILE *inf
, /* ioptr for current input file */
*outf
; /* ioptr for tags file */
long lineftell
; /* ftell after getc( inf ) == '\n' */
int lineno
, /* line number of current line */
aflag
, /* -a: append to tags */
dflag
, /* -d: non-macro defines */
tflag
, /* -t: create tags for typedefs */
uflag
, /* -u: update tags */
wflag
, /* -w: suppress warnings */
vflag
, /* -v: vgrind style index output */
xflag
; /* -x: cxref style output */
char *curfile
, /* current input file name */
searchar
= '/', /* use /.../ searches by default */
line
[4*BUFSIZ
], /* current input line */
extern char *optarg
; /* getopt arguments */
static char *outfile
= "tags"; /* output file */
int exit_val
, /* exit value */
step
, /* step through args */
char cmd
[100], /* too ugly to explain */
while ((ch
= getopt(argc
,argv
,"BFadf:tuwvx")) != EOF
)
usage
: puts("Usage: ctags [-BFadtuwvx] [-f tagsfile] file ...");
for (exit_val
= step
= 0;step
< argc
;++step
) {
curfile
= savestr(argv
[step
]);
if (!(inf
= fopen(argv
[step
],"r"))) {
find_entries(argv
[step
]);
for (step
= 0;step
< argc
;step
++) {
(void)sprintf(cmd
,"mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",outfile
,argv
[step
],outfile
);
outf
= fopen(outfile
, aflag
? "a" : "w");
(void)sprintf(cmd
,"sort %s -o %s",outfile
,outfile
);
* this routine sets up the boolean psuedo-functions which work by
* setting boolean flags dependent upon the corresponding character.
* Every char which is NOT in that string is false with respect to
* the pseudo-function. Therefore, all of the array "_wht" is NO
* by default and then the elements subscripted by the chars in
* CWHITE are set to YES. Thus, "_wht" of a char is YES if it is in
* the string CWHITE, else NO.
for (i
= 0; i
< 0177; i
++) {
_wht
[i
] = _etk
[i
] = _itk
[i
] = _btk
[i
] = NO
;
for (sp
= CWHITE
; *sp
; sp
++) /* white space chars */
#define CTOKEN " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?"
for (sp
= CTOKEN
; *sp
; sp
++) /* token ending chars */
#define CINTOK "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789"
for (sp
= CINTOK
; *sp
; sp
++) /* valid in-token chars */
#define CBEGIN "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
for (sp
= CBEGIN
; *sp
; sp
++) /* token starting chars */
for (sp
= CNOTGD
; *sp
; sp
++) /* invalid after-function chars */
* this routine opens the specified file and calls the function
* which searches the file.
lineno
= 0; /* should be 1 ?? KB */
if (cp
= rindex(file
, '.')) {
if (cp
[1] == 'l' && !cp
[2]) {
/* lisp */ if (index(";([",(char)first_char())) {
/* yacc */ else if (cp
[1] == 'y' && !cp
[2])
/* fortran */ else if ((cp
[1] != 'c' && cp
[1] != 'h') && !cp
[2]) {
c_entries(); /* default: try C */
* enter a new node in the tree
if (!(np
= (NODE
*)malloc(sizeof(NODE
)))) {
fputs("ctags: too many entries to sort\n",stderr
);
if (!(np
= (NODE
*)malloc(sizeof(NODE
)))) {
fputs("ctags: out of space.\n",stderr
);
if (!xflag
&& !bcmp(name
,"main",4)) {
if (!(fp
= rindex(curfile
,'/')))
(void)sprintf(nbuf
,"M%s",fp
);
np
->entry
= savestr(name
);
np
->left
= np
->right
= 0;
* read .c and .h files and call appropriate routines
register int c
, /* current character */
register char *sp
; /* buffer pointer */
int token
, /* if reading a token */
t_def
, /* if reading a typedef */
t_level
; /* typedef's brace level */
char tok
[MAXTOKEN
]; /* token buffer */
sp
= tok
; token
= t_def
= NO
; t_level
= -1; level
= 0; lineno
= 1;
while ((c
= getc(inf
)) != EOF
) {
* Here's where it DOESN'T handle:
* if level goes below zero, try and fix
* it, even though we've already messed up
* the above 3 cases are similar in that they
* are special characters that also end tokens.
/* we ignore quoted strings and comments in their entirety */
if ((c
= getc(inf
)) == '*') {
/* hash marks are interesting if they start #define's. */
* if we have a current token, parenthesis on
* level zero indicates a function.
* grab the line immediately, we may
* already be wrong, for example,
* semi-colons are interesting in that they indicate the end
* of a typedef; if we find a typedef we search for the next
* semi-colon of the same level as the typedef. They are
* "typedef unsigned int u_int;"
* "typedef unsigned int u_int [10];"
* If looking at a typedef, we save a copy of the last token
* found. Then, when we find the ';' we take the current
* token if it starts with a valid token name, else we take
* the one we saved. There's probably some reasonable
if (t_def
&& level
== t_level
) {
* store characters until one that can't be part of a token
* comes along; check the current token against certain
storec
: if (!intoken(c
)) {
/* no typedefs inside typedefs */
if (!t_def
&& !bcmp(tok
,"typedef",8)) {
/* catch "typedef struct" */
if ((!t_def
|| t_level
< level
)
&& (!bcmp(tok
,"struct",7)
|| !bcmp(tok
,"enum",5))) {
else if (sp
!= tok
|| begtoken(c
)) {
* handle a function reference
register int c
; /* current character */
while ((c
= getc(inf
)) != EOF
&& iswhite(c
))
if (!intoken(c
) && c
!= (int)'{')
* handle a line starting with a '#'
register int c
, /* character read */
curline
; /* line started on */
register char *sp
; /* buffer pointer */
char tok
[MAXTOKEN
]; /* storage buffer */
for (sp
= tok
;;) { /* get next token */
if ((c
= getc(inf
)) == EOF
)
if (bcmp(tok
,"define",6)) /* only interested in #define's */
for (;;) { /* this doesn't handle "#define \n" */
if ((c
= getc(inf
)) == EOF
)
for (sp
= tok
;;) { /* get next token */
if ((c
= getc(inf
)) == EOF
)
* this is where it DOESN'T handle
if (dflag
|| c
== (int)'(') { /* only want macros */
skip
: if (c
== (int)'\n') { /* get rid of rest of define */
* handle a struct, union or enum entry
register int c
; /* current character */
register char *sp
; /* buffer pointer */
int curline
; /* line started on */
char tok
[BUFSIZ
]; /* storage buffer */
if ((c
= getc(inf
)) == EOF
)
if (c
== (int)'{') /* it was "struct {" */
for (sp
= tok
;;) { /* get next token */
if ((c
= getc(inf
)) == EOF
)
case '{': /* it was "struct foo{" */
case '\n': /* it was "struct foo\n" */
default: /* probably "struct foo " */
while ((c
= getc(inf
)) != EOF
)
for (savec
= '\0';(c
= getc(inf
)) != EOF
;savec
= c
)
* skip to next char "key"
while((c
= getc(inf
)) != EOF
&& c
!= key
)
for (level
= 1,slash
= star
= 0;(c
= getc(inf
)) != EOF
;)
* Find the yacc tags and put them in.
pfnote("yyparse",lineno
);
while (fgets(line
,sizeof(line
),inf
))
} while (sp
[-1] == '\\');
} while (sp
[-1] == '\\');
if (sp
[1] == '%' && sp
== line
)
if (!brace
&& !in_rule
&& (isalpha(*sp
) ||
*sp
== '.' || *sp
== '_')) {
while (isalnum(*sp
) || *sp
== '_' ||
if (*sp
== ':' || (*sp
== '\0' &&
(void)strncpy(tok
, orig_sp
, toklen
);
(void)strcpy(lbuf
, line
);
lbuf
[strlen(lbuf
) - 1] = '\0';
* first, see if the end-of-comment is on the same line
while ((sp
= index(start
,'*')))
} while(fgets(line
,sizeof(line
),inf
));
* get the line the token of interest occurred on
fseek(inf
,lineftell
,L_SET
);
(void)fgets(lbuf
,sizeof(lbuf
),inf
);
if (cp
= index(lbuf
,'\n'))
fseek(inf
,saveftell
,L_SET
);
dif
= strcmp(node
->entry
,cur_node
->entry
);
if (node
->file
== cur_node
->file
) {
fprintf(stderr
,"Duplicate entry in file %s, line %d: %s\nSecond entry ignored\n",node
->file
,lineno
,node
->entry
);
if (!cur_node
->been_warned
)
fprintf(stderr
,"Duplicate entry in files %s and %s: %s (Warning only)\n",node
->file
,cur_node
->file
,node
->entry
);
cur_node
->been_warned
= YES
;
add_node(node
,cur_node
->left
);
else if (cur_node
->right
)
add_node(node
,cur_node
->right
);
fprintf(outf
,"%s\t%s\t%c^",node
->entry
,node
->file
,searchar
);
for (sp
= node
->pat
; *sp
; sp
++)
else if (*sp
== searchar
)
fprintf(outf
,"\\%c",searchar
);
fprintf(outf
,"%c\n",searchar
);
printf("%s %s %d\n",node
->entry
,node
->file
,(node
->lno
+63)/64);
printf("%-16s%4d %-16s %s\n",node
->entry
,node
->lno
,node
->file
,node
->pat
);
put_entries(node
->right
);
while (fgets(lbuf
, sizeof(lbuf
), inf
)) {
if ( *dbp
== '%' ) dbp
++ ; /* Ratfor escape to fortran */
if (tail("complex") || tail("character"))
while (*cp
&& (*cp
&~' ') == ((*(dbp
+len
))&~' '))
--dbp
; /* force failure */
for (cp
= lbuf
; *cp
; cp
++)
*--cp
= 0; /* zap newline */
if (*dbp
== 0 || !isalpha(*dbp
))
for (cp
= dbp
+1; *cp
&& (isalpha(*cp
) || isdigit(*cp
)); cp
++)
(void)strcpy(nambuf
, dbp
);
if (!(space
= malloc((u_int
)(strlen(str
) + 1)))) {
fputs("ctags: no more space.\n",stderr
);
return(strcpy(space
,str
));
* just look for (def or (DEF
while (fgets(lbuf
, sizeof(lbuf
), inf
)) {
(dbp
[1] == 'D' || dbp
[1] == 'd') &&
(dbp
[2] == 'E' || dbp
[2] == 'e') &&
(dbp
[3] == 'F' || dbp
[3] == 'f')) {
if (striccmp(dbp
, "method") == 0 ||
striccmp(dbp
, "wrapper") == 0 ||
striccmp(dbp
, "whopper") == 0)
for (cp
= lbuf
;*cp
;cp
++);
*--cp
= 0; /* zap newline */
if (!(cp
= index(dbp
,')')))
for (;cp
>= dbp
&& *cp
!= ':';--cp
);
for (;*cp
&& *cp
!= ')' && *cp
!= ' ';++cp
);
for (cp
= dbp
+ 1; *cp
&& *cp
!= '(' && *cp
!= ' '; cp
++);
(void)strcpy(nambuf
,dbp
);
* Compare two strings over the length of the second, ignoring
* case distinctions. If they are the same, return 0. If they
* are different, return the difference of the first two different
* characters. It is assumed that the pattern (second string) is
register char *str
, *pat
;
* return the next non-blank character in the file. After finding
* it, rewind the input file to the starting position.
while ((c
= getc(inf
)) != EOF
)
(void)fseek(inf
,off
,L_SET
);
(void)fseek(inf
,off
,L_SET
);
* toss away code until the next "%%" line.
if (!fgets(buf
,sizeof(buf
),inf
))
if (!strncmp(buf
,"%%",2))