1 /* $NetBSD: ctags.c,v 1.18 2024/10/31 01:50:20 kre Exp $ */ 2 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 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #if defined(__COPYRIGHT) && !defined(lint) 38 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994, 1995\ 39 The Regents of the University of California. All rights reserved."); 40 #endif /* not lint */ 41 42 #if defined(__RCSID) && !defined(lint) 43 #if 0 44 static char sccsid[] = "@(#)ctags.c 8.4 (Berkeley) 2/7/95"; 45 #endif 46 __RCSID("$NetBSD: ctags.c,v 1.18 2024/10/31 01:50:20 kre Exp $"); 47 #endif /* not lint */ 48 49 #include <err.h> 50 #include <limits.h> 51 #include <stdio.h> 52 #include <string.h> 53 #include <stdlib.h> 54 #include <unistd.h> 55 56 #include "ctags.h" 57 58 /* 59 * ctags: create a tags file 60 */ 61 62 NODE *head; /* head of the sorted binary tree */ 63 64 /* boolean "func" (see init()) */ 65 bool _wht[256], _etk[256], _itk[256], _btk[256], _gd[256]; 66 67 FILE *inf; /* ioptr for current input file */ 68 FILE *outf; /* ioptr for tags file */ 69 70 long lineftell; /* ftell after getc( inf ) == '\n' */ 71 72 int lineno; /* line number of current line */ 73 int dflag; /* -d: non-macro defines */ 74 int tflag; /* -t: create tags for typedefs */ 75 int vflag; /* -v: vgrind style index output */ 76 int wflag; /* -w: suppress warnings */ 77 int xflag; /* -x: cxref style output */ 78 79 char *curfile; /* current input file name */ 80 char searchar = '/'; /* use /.../ searches by default */ 81 char lbuf[LINE_MAX]; 82 83 void init(void); 84 void find_entries(char *); 85 86 int 87 main(int argc, char **argv) 88 { 89 static const char *outfile = "tags"; /* output file */ 90 int aflag; /* -a: append to tags */ 91 int uflag; /* -u: update tags */ 92 int exit_val; /* exit value */ 93 int step; /* step through args */ 94 int ch; /* getopts char */ 95 char *cmd; /* not nice */ 96 char tname[128]; /* disgusting */ 97 size_t sz; 98 99 aflag = uflag = NO; 100 while ((ch = getopt(argc, argv, "BFadf:tuwvx")) != -1) 101 switch(ch) { 102 case 'B': 103 searchar = '?'; 104 break; 105 case 'F': 106 searchar = '/'; 107 break; 108 case 'a': 109 aflag++; 110 break; 111 case 'd': 112 dflag++; 113 break; 114 case 'f': 115 outfile = optarg; 116 break; 117 case 't': 118 tflag++; 119 break; 120 case 'u': 121 uflag++; 122 break; 123 case 'w': 124 wflag++; 125 break; 126 case 'v': 127 vflag++; 128 /* FALLTHROUGH */ 129 case 'x': 130 xflag++; 131 break; 132 case '?': 133 default: 134 goto usage; 135 } 136 argv += optind; 137 argc -= optind; 138 if (!argc) { 139 usage:; (void)fprintf(stderr, 140 "usage: ctags [-BFadtuwvx] [-f tagsfile] file ...\n"); 141 exit(EXIT_FAILURE); 142 } 143 144 if ((sz = shquote(outfile, tname, sizeof tname)) >= sizeof tname) { 145 /* nb: (size_t)-1 > sizeof tname */ 146 if (sz == (size_t)-1) 147 err(EXIT_FAILURE, "Output file name '%s' too long", 148 outfile); 149 else 150 errx(EXIT_FAILURE, "Output file name '%s' too long", 151 outfile); 152 } 153 154 init(); 155 156 exit_val = EXIT_SUCCESS; 157 for (step = 0; step < argc; ++step) 158 if (!(inf = fopen(argv[step], "r"))) { 159 warn("%s", argv[step]); 160 exit_val = EXIT_FAILURE; 161 } 162 else { 163 curfile = argv[step]; 164 find_entries(argv[step]); 165 (void)fclose(inf); 166 } 167 168 if (head) { 169 if (xflag) 170 put_entries(head); 171 else { 172 if (uflag) { 173 for (step = 0; step < argc; step++) { 174 char pattern[140]; 175 176 if (asprintf(&cmd, "\t%s\t", 177 argv[step]) == -1) 178 err(EXIT_FAILURE, 179 "Cannot generate '\\t%s\\t'", 180 argv[step]); 181 182 if ((sz = shquote(cmd, pattern, 183 sizeof pattern)) >= sizeof pattern) 184 { 185 if (sz == (size_t)-1) 186 err(EXIT_FAILURE, "'%s': " 187 "quoting pattern", cmd); 188 else 189 errx(EXIT_FAILURE, "'%s': " 190 "failed to quote", cmd); 191 } 192 (void)free(cmd); 193 194 if (asprintf(&cmd, 195 "OTAGS=$(mktemp -t tags.$$) || exit\n" 196 "\ttest -n \"${OTAGS}\" || exit\n" 197 "\ttrap 'rm -f \"${OTAGS}\"' EXIT\n" 198 "\tmv %s \"${OTAGS}\" || exit\n" 199 "\tfgrep -v %s \"${OTAGS}\" >%s\n" 200 "\tX=$? ; " 201 "test \"$X\" -le 1 || exit $X", 202 tname, pattern, tname) == -1) 203 err(EXIT_FAILURE, 204 "Command to update %s for -u" 205 " %s", 206 argv[step], outfile); 207 208 if (system(cmd) != 0) 209 errx(EXIT_FAILURE, 210 "Update (-u) of %s failed. " 211 "Cmd:\n %s", outfile, cmd); 212 213 (void)free(cmd); 214 } 215 ++aflag; 216 } 217 if (!(outf = fopen(outfile, aflag ? "a" : "w"))) 218 err(EXIT_FAILURE, "%s", outfile); 219 put_entries(head); 220 (void)fflush(outf); 221 if (ferror(outf)) 222 err(EXIT_FAILURE, "output error (%s)", outfile); 223 (void)fclose(outf); 224 if (uflag) { 225 if (asprintf(&cmd, "sort -o %s %s", 226 tname, tname) == -1) 227 err(EXIT_FAILURE, 228 "sort command (-u) for %s", 229 outfile); 230 if (system(cmd) != 0) 231 errx(EXIT_FAILURE, "-u: sort %s failed" 232 "\t[ %s ]", outfile, cmd); 233 (void)free(cmd); 234 } 235 } 236 } 237 if ((vflag || xflag) && (fflush(stdout) != 0 || ferror(stdout) != 0)) 238 errx(EXIT_FAILURE, "write error (stdout)"); 239 exit(exit_val); 240 } 241 242 /* 243 * init -- 244 * this routine sets up the boolean pseudo-functions which work by 245 * setting boolean flags dependent upon the corresponding character. 246 * Every char which is NOT in that string is false with respect to 247 * the pseudo-function. Therefore, all of the array "_wht" is NO 248 * by default and then the elements subscripted by the chars in 249 * CWHITE are set to YES. Thus, "_wht" of a char is YES if it is in 250 * the string CWHITE, else NO. 251 */ 252 void 253 init(void) 254 { 255 int i; 256 const char *sp; 257 258 for (i = 0; i < 256; i++) { 259 _wht[i] = _etk[i] = _itk[i] = _btk[i] = NO; 260 _gd[i] = YES; 261 } 262 #define CWHITE " \f\t\n" 263 for (sp = CWHITE; *sp; sp++) /* white space chars */ 264 _wht[(unsigned)*sp] = YES; 265 #define CTOKEN " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?" 266 for (sp = CTOKEN; *sp; sp++) /* token ending chars */ 267 _etk[(unsigned)*sp] = YES; 268 #define CINTOK "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789" 269 for (sp = CINTOK; *sp; sp++) /* valid in-token chars */ 270 _itk[(unsigned)*sp] = YES; 271 #define CBEGIN "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" 272 for (sp = CBEGIN; *sp; sp++) /* token starting chars */ 273 _btk[(unsigned)*sp] = YES; 274 #define CNOTGD ",;" 275 for (sp = CNOTGD; *sp; sp++) /* invalid after-function chars */ 276 _gd[(unsigned)*sp] = NO; 277 } 278 279 /* 280 * find_entries -- 281 * this routine opens the specified file and calls the function 282 * which searches the file. 283 */ 284 void 285 find_entries(char *file) 286 { 287 char *cp; 288 289 lineno = 0; /* should be 1 ?? KB */ 290 if ((cp = strrchr(file, '.')) != NULL) { 291 if (cp[1] == 'l' && !cp[2]) { 292 int c; 293 294 for (;;) { 295 if (GETC(==, EOF)) 296 return; 297 if (!iswhite(c)) { 298 rewind(inf); 299 break; 300 } 301 } 302 #define LISPCHR ";([" 303 /* lisp */ if (strchr(LISPCHR, c)) { 304 l_entries(); 305 return; 306 } 307 /* lex */ else { 308 /* 309 * we search all 3 parts of a lex file 310 * for C references. This may be wrong. 311 */ 312 toss_yysec(); 313 (void)strlcpy(lbuf, "%%$", sizeof(lbuf)); 314 pfnote("yylex", lineno); 315 rewind(inf); 316 } 317 } 318 /* yacc */ else if (cp[1] == 'y' && !cp[2]) { 319 /* 320 * we search only the 3rd part of a yacc file 321 * for C references. This may be wrong. 322 */ 323 toss_yysec(); 324 (void)strlcpy(lbuf, "%%$", sizeof(lbuf)); 325 pfnote("yyparse", lineno); 326 y_entries(); 327 } 328 /* fortran */ else if ((cp[1] != 'c' && cp[1] != 'h') && !cp[2]) { 329 if (PF_funcs()) 330 return; 331 rewind(inf); 332 } 333 } 334 /* C */ c_entries(); 335 } 336