* 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.c 5.1 (Berkeley) 5/31/85";
* ctags: create a tags file
#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 ')' */
#define max(I1,I2) (I1 > I2 ? I1 : I2)
struct nd_st
{ /* sorting structure */
char *entry
; /* function or type name */
char *file
; /* file name */
bool f
; /* use pattern or line no */
int lno
; /* for -x option */
char *pat
; /* search pattern */
bool been_warned
; /* set if noticed dup */
struct nd_st
*left
,*right
; /* left and right sons */
typedef struct nd_st NODE
;
bool number
, /* T if on line starting with # */
term
= FALSE
, /* T if print on terminal */
makefile
= TRUE
, /* T if to creat "tags" file */
gotone
, /* found a func already on line */
/* boolean "func" (see init) */
_wht
[0177],_etk
[0177],_itk
[0177],_btk
[0177],_gd
[0177];
/* typedefs are recognized using a simple finite automata,
* tydef is its state variable.
typedef enum {none
, begin
, middle
, end
} TYST
;
char searchar
= '/'; /* use /.../ searches */
int lineno
; /* line number of current line */
char line
[4*BUFSIZ
], /* current input line */
*curfile
, /* current input file name */
*outfile
= "tags", /* output file */
*white
= " \f\t\n", /* white chars */
*endtk
= " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
*begtk
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz",
/* token starting chars */
*intk
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789",
/* valid in-token chars */
*notgd
= ",;"; /* non-valid after-function chars */
int file_num
; /* current file number */
int aflag
; /* -a: append to tags */
int tflag
; /* -t: create tags for typedefs */
int uflag
; /* -u: update tags */
int wflag
; /* -w: suppress warnings */
int vflag
; /* -v: create vgrind style index output */
int xflag
; /* -x: create cxref style output */
FILE *inf
, /* ioptr for current input file */
*outf
; /* ioptr for tags file */
long lineftell
; /* ftell after getc( inf ) == '\n' */
NODE
*head
; /* the head of the sorted binary tree */
char *rindex(), *index();
while (ac
> 1 && av
[1][0] == '-') {
for (i
=1; av
[1][i
]; i
++) {
printf("Usage: ctags [-BFatuwvx] [-f tagsfile] file ...\n");
init(); /* set up boolean "functions" */
* loop through files finding functions
for (file_num
= 1; file_num
< ac
; file_num
++)
find_entries(av
[file_num
]);
"mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
outfile
, av
[i
], outfile
);
outf
= fopen(outfile
, aflag
? "a" : "w");
sprintf(cmd
, "sort %s -o %s", outfile
, outfile
);
* This routine sets up the boolean psuedo-functions which work
* by seting boolean flags dependent upon the corresponding character
* Every char which is NOT in that string is not a white char. Therefore,
* all of the array "_wht" is set to FALSE, and then the elements
* subscripted by the chars in "white" are set to TRUE. Thus "_wht"
* of a char is TRUE if it is the string "white", else FALSE.
for (i
= 0; i
< 0177; i
++) {
_wht
[i
] = _etk
[i
] = _itk
[i
] = _btk
[i
] = FALSE
;
for (sp
= white
; *sp
; sp
++)
for (sp
= endtk
; *sp
; sp
++)
for (sp
= intk
; *sp
; sp
++)
for (sp
= begtk
; *sp
; sp
++)
for (sp
= notgd
; *sp
; sp
++)
* This routine opens the specified file and calls the function
* which finds the function and type definitions.
if ((inf
= fopen(file
,"r")) == NULL
) {
/* .l implies lisp or lex source code */
if (cp
&& cp
[1] == 'l' && cp
[2] == '\0') {
if (index(";([", first_char()) != NULL
) { /* lisp */
* throw away all the code before the second "%%"
pfnote("yylex", lineno
, TRUE
);
/* .y implies a yacc file */
if (cp
&& cp
[1] == 'y' && cp
[2] == '\0') {
/* if not a .c or .h file, try fortran */
if (cp
&& (cp
[1] != 'c' && cp
[1] != 'h') && cp
[2] == '\0') {
if (PF_funcs(inf
) != 0) {
rewind(inf
); /* no fortran tags found, try C */
bool f
; /* f == TRUE when function */
if ((np
= (NODE
*) malloc(sizeof (NODE
))) == NULL
) {
fprintf(stderr
, "ctags: too many entries to sort\n");
head
= np
= (NODE
*) malloc(sizeof (NODE
));
if (xflag
== 0 && !strcmp(name
, "main")) {
fp
= rindex(curfile
, '/');
sprintf(nbuf
, "M%s", fp
);
np
->entry
= savestr(name
);
np
->left
= np
->right
= 0;
* This routine finds functions and typedefs in C syntax and adds them
register char *token
, *tp
;
bool incomm
, inquote
, inchar
, midtoken
;
number
= gotone
= midtoken
= inquote
= inchar
= incomm
= FALSE
;
while ((*++sp
=c
=getc(inf
)) == '*')
* Too dumb to know about \" not being magic, but
* they usually occur in pairs anyway.
if ((*++sp
=c
=getc(inf
)) == '*')
if (!level
&& tydef
==middle
) {
if (!level
&& !inquote
&& !incomm
&& gotone
== FALSE
) {
if (start_entry(&sp
,token
,tp
,&f
)) {
strncpy(tok
,token
,tp
-token
+1);
gotone
= f
; /* function */
if (c
== ';' && tydef
==end
) /* clean with typedefs */
if (c
== '\n' || sp
> &line
[sizeof (line
) - BUFSIZ
]) {
number
= gotone
= midtoken
= inquote
= inchar
= FALSE
;
* This routine checks to see if the current token is
* at the start of a function, or corresponds to a typedef
* It updates the input line * so that the '(' will be
start_entry(lp
,token
,tp
,f
)
bool firsttok
; /* T if have seen first token in ()'s */
if (!number
) { /* space is not allowed in macro defs */
if (sp
> &line
[sizeof (line
) - BUFSIZ
])
/* the following tries to make it so that a #define a b(c) */
/* doesn't count as a define of b. */
if (!strncmp(token
, "define", 6))
/* check for the typedef cases */
if (tflag
&& !strncmp(token
, "typedef", 7)) {
if (tydef
==begin
&& (!strncmp(token
, "struct", 6) ||
!strncmp(token
, "union", 5) || !strncmp(token
, "enum", 4))) {
while ((*++sp
=c
=getc(inf
)) != ')') {
if (sp
> &line
[sizeof (line
) - BUFSIZ
])
* This line used to confuse ctags:
* This fixes it. A nonwhite char before the first
* token, other than a / (in case of a comment in there)
* makes this not a declaration.
if (begtoken(c
) || c
=='/')
else if (!iswhite(c
) && !firsttok
)
while (iswhite(*++sp
=c
=getc(inf
)))
if (sp
> &line
[sizeof (line
) - BUFSIZ
])
return !bad
&& (!*f
|| isgood(c
));
* Find the yacc tags and put them in.
register char *sp
, *orig_sp
;
register bool in_rule
, toklen
;
pfnote("yyparse", lineno
, TRUE
);
while (fgets(line
, sizeof line
, inf
) != NULL
)
for (sp
= line
; *sp
; sp
++)
} while (sp
[-1] == '\\');
} while (sp
[-1] == '\\');
if (sp
[1] == '%' && sp
== line
)
if (brace
== 0 && !in_rule
&& (isalpha(*sp
) ||
while (isalnum(*sp
) || *sp
== '_' ||
if (*sp
== ':' || (*sp
== '\0' &&
strncpy(tok
, orig_sp
, toklen
);
lbuf
[strlen(lbuf
) - 1] = '\0';
pfnote(tok
, lineno
, TRUE
);
* first, see if the end-of-comment is on the same line
while ((sp
= index(start
, '*')) != NULL
)
} while (fgets(line
, sizeof line
, inf
) != NULL
);
long saveftell
= ftell( inf
);
fseek( inf
, lineftell
, 0 );
fgets(lbuf
, sizeof lbuf
, inf
);
fseek(inf
, saveftell
, 0);
dif
= strcmp(node
->entry
, cur_node
->entry
);
if (node
->file
== cur_node
->file
) {
fprintf(stderr
,"Duplicate entry in file %s, line %d: %s\n",
node
->file
,lineno
,node
->entry
);
fprintf(stderr
,"Second entry ignored\n");
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
= TRUE
;
if (cur_node
->left
!= NULL
)
add_node(node
,cur_node
->left
);
if (cur_node
->right
!= NULL
)
add_node(node
,cur_node
->right
);
if (node
->f
) { /* a function */
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
);
else { /* a typedef; text pattern inadequate */
fprintf(outf
, "%s\t%s\t%d\n",
node
->entry
, node
->file
, node
->lno
);
fprintf(stdout
, "%s %s %d\n",
node
->entry
, node
->file
, (node
->lno
+63)/64);
fprintf(stdout
, "%-16s%4d %-16s %s\n",
node
->entry
, node
->lno
, node
->file
, node
->pat
);
put_entries(node
->right
);
while (fgets(lbuf
, sizeof(lbuf
), fi
)) {
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
++)
dp
= (char *)malloc(len
+1);
* Return the ptr in sp at which the character c last
* appears; NULL if not found
* Identical to v7 rindex, included for portability.
* just look for (def or (DEF
while (fgets(lbuf
, sizeof(lbuf
), fi
)) {
(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
, ')')) == NULL
)
while (cp
>= dbp
&& *cp
!= ':')
while (*cp
&& *cp
!= ')' && *cp
!= ' ')
for (cp
= dbp
+ 1; *cp
&& *cp
!= '(' && *cp
!= ' '; cp
++)
pfnote(nambuf
, lineno
,TRUE
);
* 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 first non-blank character in the file. After
* finding it, rewind the input file so we start at the beginning
while ((c
= getc(inf
)) != EOF
)
if (!isspace(c
) && c
!= '\r') {
* Toss away code until the next "%%" line.
if (fgets(buf
, BUFSIZ
, inf
) == NULL
)
if (strncmp(buf
, "%%", 2) == 0)