Home | History | Annotate | Line # | Download | only in ctags
      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