1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
|
/* ctags.c */
/* Author:
* Steve Kirkendall
* 14407 SW Teal Blvd. #C
* Beaverton, OR 97005
* kirkenda@cs.pdx.edu
*/
/* This file contains the complete source to the ctags program. */
/* Special abilities:
* Can also make a "refs" file for use by the "ref" program.
*/
/* Limitations:
* This version of ctags always writes its output to the file "tags".
* It assumes that every command-line argument (but "-r") is a C source file.
* It does not sort the list of tags, unless CFLAGS=-DSORT.
* It does not recognize duplicate definitions.
* It does not try to handle "static" functions in a clever way.
* It probably won't scan ANSI-C source code very well.
*/
/* Implementation:
* Lines are scanned one-at-a-time.
* The context of lines is tracked via a finite state machine.
* Contexts are:
* EXPECTFN - we're looking for a function name.
* ARGS - between function name and its opening {
* BODY - we found a function name, skip to end of body.
*
* Function tags are referenced by a search string, so that lines may be
* inserted or deleted without mucking up the tag search.
*
* Macro tags are referenced by their line number, because 1) they usually
* occur near the top of a file, so their line# won't change much; 2) They
* often contain characters that are hard to search for; and 3) Their #define
* line is likely to be altered.
*
* Each line of the resulting "tags" file describes one tag. Lines begin with
* the tag name, then a tab, then the file name, then a tab, and then either
* a line number or a slash-delimited search string.
*/
#include <ctype.h>
#include <stdio.h>
#include "config.h"
#define REFS "refs"
#if OSK
#define NUMFMT "%%.%ds\t%%s\t%%ld\n"
#define SRCHFMT "%%.%ds\t%%s\t/^%%s$/\n"
#define MAINFMT "M%%.%ds\t%%s\t/^%%s$/\n"
static char fmt[256];
#else
#define NUMFMT "%.*s\t%s\t%ld\n"
#define SRCHFMT "%.*s\t%s\t/^%s$/\n"
#define MAINFMT "M%.*s\t%s\t/^%s$/\n"
#endif
#ifdef VERBOSE
# define SAY(x) fprintf(stderr, "%s\n", x);
#else
# define SAY(x)
#endif
#define EXPECTFN 1
#define ARGS 2
#define BODY 3
extern char *fgets();
char *progname; /* argv[0], used for diagnostic output */
main(argc, argv)
int argc;
char **argv;
{
FILE *fp;
int i;
FILE *refs; /* used to write to the refs file */
#if MSDOS || TOS
char **wildexpand();
argv=wildexpand(&argc, argv);
#endif
/* notice the program name */
progname = argv[0];
/* create the "refs" file if first arg is "-r" */
if (argc > 1 && !strcmp(argv[1], "-r"))
{
/* delete the "-r" flag from the args list */
argc--;
argv++;
/* open the "refs" file for writing */
refs = fopen(REFS, "w");
if (!refs)
{
fprintf(stderr, "%s: could not create \"%s\"\n", progname, REFS);
exit(2);
}
}
else
{
refs = (FILE *)0;
}
/* process each file named on the command line, or complain if none */
if (argc > 1)
{
/* redirect stdout to go to the "tags" file */
if (!freopen("tags", "w", stdout))
{
fprintf(stderr, "%s: could not create \"%s\"\n", progname, TAGS);
exit(2);
}
for (i = 1; i < argc; i++)
{
/* process this named file */
fp = fopen(argv[i], "r");
if (!fp)
{
fprintf(stderr, "%s: could not read \"%s\"\n", progname, argv[i]);
continue;
}
ctags(fp, argv[i], refs);
fclose(fp);
}
#ifdef SORT
/* This is a hack which will sort the tags list. It should
* on UNIX and Minix. You may have trouble with csh. Note
* that the tags list only has to be sorted if you intend to
* use it with the real vi; elvis permits unsorted tags.
*/
fflush(stdout);
#if OSK
fclose(stdout);
system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
#else
system("sort tags >_tags$$; mv _tags$$ tags");
#endif
#endif
exit(0);
}
else
{
fprintf(stderr, "usage: %s *.[ch]\n", progname);
exit(2);
}
}
/* this function finds all tags in a given file */
ctags(fp, name, refs)
FILE *fp; /* stream of the file to scan */
char *name; /* name of the file being scanned */
FILE *refs; /* NULL, or where to write refs lines */
{
int context; /* context - either EXPECTFN, ARGS, or BODY */
long lnum; /* line number */
char text[1000]; /* a line of text from the file */
char *scan; /* used for searching through text */
int len; /* length of the line */
/* for each line of the file... */
for (context = EXPECTFN, lnum = 1; fgets(text, sizeof text, fp); lnum++)
{
#ifdef VERBOSE
switch(context)
{
case EXPECTFN: scan = "EXPECTFN"; break;
case ARGS: scan = "ARGS "; break;
case BODY: scan = "BODY "; break;
default: scan = "context?";
}
fprintf(stderr, "%s:%s", scan, text);
#endif
/* start of body? */
if (text[0] == '{')
{
context = BODY;
SAY("Start of BODY");
continue;
}
/* argument line, to be written to "refs" ? */
if (refs && context == ARGS)
{
if (text[0] != '\t')
{
putc('\t', refs);
}
fputs(text, refs);
SAY("Argument line");
continue;
}
/* ignore empty or indented lines */
if (text[0] <= ' ')
{
SAY("Empty or indented");
continue;
}
/* end of body? */
if (text[0] == '}')
{
context = EXPECTFN;
SAY("End of BODY");
continue;
}
/* ignore lines in the body of a function */
if (context != EXPECTFN)
{
SAY("BODY or ARGS");
continue;
}
/* strip the newline */
len = strlen(text);
text[--len] = '\0';
/* a preprocessor line? */
if (text[0] == '#')
{
/* find the preprocessor directive */
for (scan = &text[1]; isspace(*scan); scan++)
{
}
/* if it's a #define, make a tag out of it */
if (!strncmp(scan, "define", 6))
{
/* find the start of the symbol name */
for (scan += 6; isspace(*scan); scan++)
{
}
/* find the length of the symbol name */
for (len = 1;
isalnum(scan[len]) || scan[len] == '_';
len++)
{
}
#if OSK
sprintf(fmt, NUMFMT, len);
printf(fmt, scan, name, lnum);
#else
printf(NUMFMT, len, scan, name, lnum);
#endif
}
SAY("Preprocessor line");
continue;
}
/* an extern or static declaration? */
if (text[len - 1] == ';'
|| !strncmp(text, "extern", 6)
|| !strncmp(text, "EXTERN", 6)
|| !strncmp(text, "static", 6)
|| !strncmp(text, "PRIVATE", 7))
{
SAY("Extern or static");
continue;
}
/* if we get here & the first punctuation other than "*" is
* a "(" which is immediately preceded by a name, then
* assume the name is that of a function.
*/
for (scan = text; *scan; scan++)
{
if (ispunct(*scan)
&& !isspace(*scan) /* in BSD, spaces are punctuation?*/
&& *scan != '*' && *scan != '_' && *scan != '(')
{
SAY("Funny punctuation");
goto ContinueContinue;
}
if (*scan == '(')
{
/* permit 0 or 1 spaces between name & '(' */
if (scan > text && scan[-1] == ' ')
{
scan--;
}
/* find the start & length of the name */
for (len = 0, scan--;
scan >= text && (isalnum(*scan) || *scan == '_');
scan--, len++)
{
}
scan++;
/* did we find a function? */
if (len > 0)
{
/* found a function! */
if (len == 4 && !strncmp(scan, "main", 4))
{
#if OSK
sprintf(fmt, MAINFMT, strlen(name) - 2);
printf(fmt, name, name, text);
#else
printf(MAINFMT, strlen(name) - 2, name, name, text);
#endif
}
#if OSK
sprintf(fmt, SRCHFMT, len);
printf(fmt, scan, name, text);
#else
printf(SRCHFMT, len, scan, name, text);
#endif
context = ARGS;
/* add a line to refs, if needed */
if (refs)
{
fputs(text, refs);
putc('\n', refs);
}
goto ContinueContinue;
}
}
else
{
SAY("No parenthesis");
}
}
SAY("No punctuation");
ContinueContinue:;
}
}
#if MSDOS || TOS
#define WILDCARD_NO_MAIN
#include "wildcard.c"
#endif
|