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