xref: /freebsd/usr.bin/ctags/ctags.c (revision 3157ba21)
1 /*
2  * Copyright (c) 1987, 1993, 1994, 1995
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1987, 1993, 1994, 1995\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif
39 
40 #if 0
41 #ifndef lint
42 static char sccsid[] = "@(#)ctags.c	8.4 (Berkeley) 2/7/95";
43 #endif
44 #endif
45 
46 #include <sys/cdefs.h>
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 __FBSDID("$FreeBSD$");
50 
51 #include <err.h>
52 #include <limits.h>
53 #include <locale.h>
54 #include <regex.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 
60 #include "ctags.h"
61 
62 /*
63  * ctags: create a tags file
64  */
65 
66 NODE	*head;			/* head of the sorted binary tree */
67 
68 				/* boolean "func" (see init()) */
69 bool	_wht[256], _etk[256], _itk[256], _btk[256], _gd[256];
70 
71 FILE	*inf;			/* ioptr for current input file */
72 FILE	*outf;			/* ioptr for tags file */
73 
74 long	lineftell;		/* ftell after getc( inf ) == '\n' */
75 
76 int	lineno;			/* line number of current line */
77 int	dflag;			/* -d: non-macro defines */
78 int	tflag;			/* -t: create tags for typedefs */
79 int	vflag;			/* -v: vgrind style index output */
80 int	wflag;			/* -w: suppress warnings */
81 int	xflag;			/* -x: cxref style output */
82 
83 char	*curfile;		/* current input file name */
84 char	searchar = '/';		/* use /.../ searches by default */
85 char	lbuf[LINE_MAX];
86 
87 void	init(void);
88 void	find_entries(char *);
89 static void usage(void);
90 
91 int
92 main(int argc, char **argv)
93 {
94 	static const char	*outfile = "tags";	/* output file */
95 	int	aflag;				/* -a: append to tags */
96 	int	uflag;				/* -u: update tags */
97 	int	exit_val;			/* exit value */
98 	int	step;				/* step through args */
99 	int	ch;				/* getopts char */
100 
101 	setlocale(LC_ALL, "");
102 
103 	aflag = uflag = NO;
104 	tflag = YES;
105 	while ((ch = getopt(argc, argv, "BFTadf:tuwvx")) != -1)
106 		switch(ch) {
107 		case 'B':
108 			searchar = '?';
109 			break;
110 		case 'F':
111 			searchar = '/';
112 			break;
113 		case 'T':
114 			tflag = NO;
115 			break;
116 		case 'a':
117 			aflag++;
118 			break;
119 		case 'd':
120 			dflag++;
121 			break;
122 		case 'f':
123 			outfile = optarg;
124 			break;
125 		case 't':
126 			tflag = YES;
127 			break;
128 		case 'u':
129 			uflag++;
130 			break;
131 		case 'w':
132 			wflag++;
133 			break;
134 		case 'v':
135 			vflag++;
136 		case 'x':
137 			xflag++;
138 			break;
139 		case '?':
140 		default:
141 			usage();
142 		}
143 	argv += optind;
144 	argc -= optind;
145 	if (!argc)
146 		usage();
147 
148 	if (!xflag)
149 		setlocale(LC_COLLATE, "C");
150 
151 	init();
152 
153 	for (exit_val = step = 0; step < argc; ++step)
154 		if (!(inf = fopen(argv[step], "r"))) {
155 			warn("%s", argv[step]);
156 			exit_val = 1;
157 		}
158 		else {
159 			curfile = argv[step];
160 			find_entries(argv[step]);
161 			(void)fclose(inf);
162 		}
163 
164 	if (head) {
165 		if (xflag)
166 			put_entries(head);
167 		else {
168 			if (uflag) {
169 				FILE *oldf;
170 				regex_t *regx;
171 
172 				if ((oldf = fopen(outfile, "r")) == NULL)
173 					err(1, "opening %s", outfile);
174 				if (unlink(outfile))
175 					err(1, "unlinking %s", outfile);
176 				if ((outf = fopen(outfile, "w")) == NULL)
177 					err(1, "recreating %s", outfile);
178 				if ((regx = calloc(argc, sizeof(regex_t))) == NULL)
179 					err(1, "RE alloc");
180 				for (step = 0; step < argc; step++) {
181 					(void)strcpy(lbuf, "\t");
182 					(void)strlcat(lbuf, argv[step], LINE_MAX);
183 					(void)strlcat(lbuf, "\t", LINE_MAX);
184 					if (regcomp(regx + step, lbuf,
185 					    REG_NOSPEC))
186 						warn("RE compilation failed");
187 				}
188 nextline:
189 				while (fgets(lbuf, LINE_MAX, oldf)) {
190 					for (step = 0; step < argc; step++)
191 						if (regexec(regx + step,
192 						    lbuf, 0, NULL, 0) == 0)
193 							goto nextline;
194 					fputs(lbuf, outf);
195 				}
196 				for (step = 0; step < argc; step++)
197 					regfree(regx + step);
198 				free(regx);
199 				fclose(oldf);
200 				fclose(outf);
201 				++aflag;
202 			}
203 			if (!(outf = fopen(outfile, aflag ? "a" : "w")))
204 				err(1, "%s", outfile);
205 			put_entries(head);
206 			(void)fclose(outf);
207 			if (uflag) {
208 				pid_t pid;
209 
210 				if ((pid = fork()) == -1)
211 					err(1, "fork failed");
212 				else if (pid == 0) {
213 					execlp("sort", "sort", "-o", outfile,
214 					    outfile, NULL);
215 					err(1, "exec of sort failed");
216 				}
217 				/* Just assume the sort went OK. The old code
218 				   did not do any checks either. */
219 				(void)wait(NULL);
220 			}
221 		}
222 	}
223 	exit(exit_val);
224 }
225 
226 static void
227 usage(void)
228 {
229 	(void)fprintf(stderr, "usage: ctags [-BFTaduwvx] [-f tagsfile] file ...\n");
230 	exit(1);
231 }
232 
233 /*
234  * init --
235  *	this routine sets up the boolean pseudo-functions which work by
236  *	setting boolean flags dependent upon the corresponding character.
237  *	Every char which is NOT in that string is false with respect to
238  *	the pseudo-function.  Therefore, all of the array "_wht" is NO
239  *	by default and then the elements subscripted by the chars in
240  *	CWHITE are set to YES.  Thus, "_wht" of a char is YES if it is in
241  *	the string CWHITE, else NO.
242  */
243 void
244 init(void)
245 {
246 	int		i;
247 	const unsigned char	*sp;
248 
249 	for (i = 0; i < 256; i++) {
250 		_wht[i] = _etk[i] = _itk[i] = _btk[i] = NO;
251 		_gd[i] = YES;
252 	}
253 #define	CWHITE	" \f\t\n"
254 	for (sp = CWHITE; *sp; sp++)	/* white space chars */
255 		_wht[*sp] = YES;
256 #define	CTOKEN	" \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?"
257 	for (sp = CTOKEN; *sp; sp++)	/* token ending chars */
258 		_etk[*sp] = YES;
259 #define	CINTOK	"ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789"
260 	for (sp = CINTOK; *sp; sp++)	/* valid in-token chars */
261 		_itk[*sp] = YES;
262 #define	CBEGIN	"ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
263 	for (sp = CBEGIN; *sp; sp++)	/* token starting chars */
264 		_btk[*sp] = YES;
265 #define	CNOTGD	",;"
266 	for (sp = CNOTGD; *sp; sp++)	/* invalid after-function chars */
267 		_gd[*sp] = NO;
268 }
269 
270 /*
271  * find_entries --
272  *	this routine opens the specified file and calls the function
273  *	which searches the file.
274  */
275 void
276 find_entries(char *file)
277 {
278 	char	*cp;
279 
280 	lineno = 0;				/* should be 1 ?? KB */
281 	if ((cp = strrchr(file, '.'))) {
282 		if (cp[1] == 'l' && !cp[2]) {
283 			int	c;
284 
285 			for (;;) {
286 				if (GETC(==, EOF))
287 					return;
288 				if (!iswhite(c)) {
289 					rewind(inf);
290 					break;
291 				}
292 			}
293 #define	LISPCHR	";(["
294 /* lisp */		if (strchr(LISPCHR, c)) {
295 				l_entries();
296 				return;
297 			}
298 /* lex */		else {
299 				/*
300 				 * we search all 3 parts of a lex file
301 				 * for C references.  This may be wrong.
302 				 */
303 				toss_yysec();
304 				(void)strcpy(lbuf, "%%$");
305 				pfnote("yylex", lineno);
306 				rewind(inf);
307 			}
308 		}
309 /* yacc */	else if (cp[1] == 'y' && !cp[2]) {
310 			/*
311 			 * we search only the 3rd part of a yacc file
312 			 * for C references.  This may be wrong.
313 			 */
314 			toss_yysec();
315 			(void)strcpy(lbuf, "%%$");
316 			pfnote("yyparse", lineno);
317 			y_entries();
318 		}
319 /* fortran */	else if ((cp[1] != 'c' && cp[1] != 'h') && !cp[2]) {
320 			if (PF_funcs())
321 				return;
322 			rewind(inf);
323 		}
324 	}
325 /* C */	c_entries();
326 }
327