1 /* 2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* Copyright (c) 1988 AT&T */ 7 /* All Rights Reserved */ 8 9 /* 10 * Copyright (c) 1980 Regents of the University of California. 11 * All rights reserved. The Berkeley software License Agreement 12 * specifies the terms and conditions for redistribution. 13 */ 14 15 /* 16 * Modify ctags to handle C++ in C_entries(), etc: 17 * - Handles C++ comment token "//" 18 * - Handles C++ scope operator "::". 19 * This helps to distinguish between xyz() 20 * definition and X::xyz() definition. 21 * - Recognizes C++ reserved word "class" in typedef processing 22 * (for "-t" option) 23 * - Handles Sun C++ special file name extensions: .c, .C, .cc, and .cxx. 24 * - Handles overloaded unary/binary operator names 25 * Doesn't handle yet: 26 * - inline functions in class definition (currently they get 27 * swallowed within a class definition) 28 * - Tags with scope operator :: with spaces in between, 29 * e.g. classz ::afunc 30 * 31 * Enhance operator functions support: 32 * - Control flow involving operator tokens scanning are 33 * consistent with that of other function tokens - original 34 * hacking method for 2.0 is removed. This will accurately 35 * identify tags for declarations of the form 'operator+()' 36 * (bugid 1027806) as well as allowing spaces in between 37 * 'operator' and 'oprtk', e.g. 'operator + ()'. 38 * 39 */ 40 41 #ifndef lint 42 char copyright[] = "@(#) Copyright (c) 1980 Regents of the University of " 43 "California.\nAll rights reserved.\n"; 44 #endif 45 46 #pragma ident "%Z%%M% %I% %E% SMI" 47 /* from UCB 5.1 5/31/85 */ 48 49 #include <stdio.h> 50 #include <ctype.h> 51 #include <locale.h> 52 #include <unistd.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <limits.h> 56 #include <sys/types.h> 57 #include <sys/stat.h> 58 59 /* 60 * ctags: create a tags file 61 */ 62 63 #define bool char 64 65 #define TRUE (1) 66 #define FALSE (0) 67 68 #define CPFLAG 3 /* # of bytes in a flag */ 69 70 #define iswhite(arg) (_wht[arg]) /* T if char is white */ 71 #define begtoken(arg) (_btk[arg]) /* T if char can start token */ 72 #define intoken(arg) (_itk[arg]) /* T if char can be in token */ 73 #define endtoken(arg) (_etk[arg]) /* T if char ends tokens */ 74 #define isgood(arg) (_gd[arg]) /* T if char can be after ')' */ 75 76 #define optoken(arg) (_opr[arg]) /* T if char can be */ 77 /* an overloaded operator token */ 78 79 #define max(I1, I2) (I1 > I2 ? I1 : I2) 80 81 struct nd_st { /* sorting structure */ 82 char *entry; /* function or type name */ 83 char *file; /* file name */ 84 bool f; /* use pattern or line no */ 85 int lno; /* for -x option */ 86 char *pat; /* search pattern */ 87 bool been_warned; /* set if noticed dup */ 88 struct nd_st *left, *right; /* left and right sons */ 89 }; 90 91 long ftell(); 92 typedef struct nd_st NODE; 93 94 static bool 95 number, /* T if on line starting with # */ 96 gotone, /* found a func already on line */ 97 /* boolean "func" (see init) */ 98 _wht[0177], _etk[0177], _itk[0177], _btk[0177], _gd[0177]; 99 100 /* boolean array for overloadable operator symbols */ 101 static bool _opr[0177]; 102 103 /* 104 * typedefs are recognized using a simple finite automata, 105 * tydef is its state variable. 106 */ 107 typedef enum {none, begin, begin_rec, begin_tag, middle, end } TYST; 108 109 static TYST tydef = none; 110 111 static char searchar = '/'; /* use /.../ searches */ 112 113 static int lineno; /* line number of current line */ 114 static char 115 line[4*BUFSIZ], /* current input line */ 116 *curfile, /* current input file name */ 117 *outfile = "tags", /* output file */ 118 *white = " \f\t\n", /* white chars */ 119 *endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?", 120 /* token ending chars */ 121 *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz", 122 /* token starting chars */ 123 *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" 124 "0123456789", 125 /* valid in-token chars */ 126 *notgd = ",;"; /* non-valid after-function chars */ 127 128 static char *oprtk = " =-+%*/&|^~!<>[]()"; /* overloadable operators */ 129 130 static int file_num; /* current file number */ 131 static int aflag; /* -a: append to tags */ 132 133 #ifndef XPG4 /* XPG4: handle typedefs by default */ 134 static int tflag; /* -t: create tags for typedefs */ 135 #endif /* !XPG4 */ 136 137 static int uflag; /* -u: update tags */ 138 static int wflag; /* -w: suppress warnings */ 139 static int vflag; /* -v: create vgrind style index output */ 140 static int xflag; /* -x: create cxref style output */ 141 142 static char lbuf[LINE_MAX]; 143 144 static FILE 145 *inf, /* ioptr for current input file */ 146 *outf; /* ioptr for tags file */ 147 148 static long lineftell; /* ftell after getc( inf ) == '\n' */ 149 150 static NODE *head; /* the head of the sorted binary tree */ 151 152 #ifdef __STDC__ 153 char *strrchr(), *strchr(); 154 #else 155 char *rindex(), *index(); 156 #endif 157 158 static int infile_fail; /* Count of bad opens. Fix bug ID #1082298 */ 159 160 static char *dbp = lbuf; 161 static int pfcnt; 162 163 static int mac; /* our modified argc, after parseargs() */ 164 static char **mav; /* our modified argv, after parseargs() */ 165 166 167 /* our local functions: */ 168 static void init(); 169 static void find_entries(char *file); 170 static void pfnote(); 171 static void C_entries(); 172 static int start_entry(char **lp, char *token, int *f); 173 static void Y_entries(); 174 static char *toss_comment(char *start); 175 static void getline(long int where); 176 static void free_tree(NODE *node); 177 static void add_node(NODE *node, NODE *cur_node); 178 static void put_entries(NODE *node); 179 static int PF_funcs(FILE *fi); 180 static int tail(char *cp); 181 static void takeprec(); 182 static void getit(); 183 static char *savestr(char *cp); 184 static void L_funcs(FILE *fi); 185 static void L_getit(int special); 186 static int striccmp(char *str, char *pat); 187 static int first_char(); 188 static void toss_yysec(); 189 static void Usage(); 190 static void parseargs(int ac, char **av); 191 192 int 193 main(int ac, char *av[]) 194 { 195 int i; 196 char cmd[100]; 197 198 (void) setlocale(LC_ALL, ""); 199 #if !defined(TEXT_DOMAIN) 200 #define TEXT_DOMAIN "SYS_TEST" 201 #endif 202 (void) textdomain(TEXT_DOMAIN); 203 204 parseargs(ac, av); 205 206 while ((i = getopt(mac, mav, "aBFtuvwxf:")) != EOF) { 207 switch (i) { 208 case 'a': /* -a: Append output to existing tags file */ 209 aflag++; 210 break; 211 212 case 'B': /* -B: Use backward search patterns (?...?) */ 213 searchar = '?'; 214 break; 215 216 case 'F': /* -F: Use forward search patterns (/.../) */ 217 searchar = '/'; 218 break; 219 220 case 't': /* -t: Create tags for typedefs. */ 221 /* for XPG4 , we silently ignore "-t". */ 222 #ifndef XPG4 223 tflag++; 224 #endif /* !XPG4 */ 225 break; 226 227 case 'u': /* -u: Update the specified tags file */ 228 uflag++; 229 break; 230 231 case 'v': /* -v: Index listing on stdout */ 232 vflag++; 233 xflag++; 234 break; 235 236 case 'w': /* -w: Suppress warnings */ 237 wflag++; 238 break; 239 240 case 'x': /* -x: Produce a simple index */ 241 xflag++; 242 break; 243 244 case 'f': /* -f tagsfile: output to tagsfile */ 245 outfile = strdup(optarg); 246 break; 247 248 default: 249 Usage(); /* never returns */ 250 break; 251 } 252 } 253 254 /* if we didn't specify any source code to parse, complain and die. */ 255 if (optind == mac) { 256 Usage(); /* never returns */ 257 } 258 259 260 init(); /* set up boolean "functions" */ 261 /* 262 * loop through files finding functions 263 */ 264 for (file_num = optind; file_num < mac; file_num++) 265 find_entries(mav[file_num]); 266 267 if (xflag) { 268 put_entries(head); 269 exit(infile_fail > 0 ? 2 : 0); /* Fix for 1082298 */ 270 } 271 if (uflag) { 272 for (i = 1; i < mac; i++) { 273 (void) sprintf(cmd, 274 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS", 275 outfile, mav[i], outfile); 276 (void) system(cmd); 277 } 278 aflag++; 279 } 280 outf = fopen(outfile, aflag ? "a" : "w"); 281 if (outf == NULL) { 282 perror(outfile); 283 exit(1); 284 } 285 put_entries(head); 286 (void) fclose(outf); 287 if (uflag) { 288 (void) sprintf(cmd, "sort %s -o %s", outfile, outfile); 289 (void) system(cmd); 290 } 291 return (infile_fail > 0 ? 2 : 0); /* Fix for #1082298 */ 292 } 293 294 /* 295 * This routine sets up the boolean psuedo-functions which work 296 * by seting boolean flags dependent upon the corresponding character 297 * Every char which is NOT in that string is not a white char. Therefore, 298 * all of the array "_wht" is set to FALSE, and then the elements 299 * subscripted by the chars in "white" are set to TRUE. Thus "_wht" 300 * of a char is TRUE if it is the string "white", else FALSE. 301 */ 302 static void 303 init() 304 { 305 char *sp; 306 int i; 307 308 for (i = 0; i < 0177; i++) { 309 _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE; 310 _opr[i] = FALSE; /* initialize boolean */ 311 /* array of operator symbols */ 312 _gd[i] = TRUE; 313 } 314 for (sp = white; *sp; sp++) 315 _wht[*sp] = TRUE; 316 for (sp = endtk; *sp; sp++) 317 _etk[*sp] = TRUE; 318 for (sp = intk; *sp; sp++) 319 _itk[*sp] = TRUE; 320 for (sp = begtk; *sp; sp++) 321 _btk[*sp] = TRUE; 322 323 /* mark overloadable operator symbols */ 324 for (sp = oprtk; *sp; sp++) 325 _opr[*sp] = TRUE; 326 327 for (sp = notgd; *sp; sp++) 328 _gd[*sp] = FALSE; 329 } 330 331 /* 332 * This routine opens the specified file and calls the function 333 * which finds the function and type definitions. 334 */ 335 static void 336 find_entries(file) 337 char *file; 338 { 339 char *cp; 340 struct stat st; 341 342 /* skip anything that isn't a regular file */ 343 if (stat(file, &st) == 0 && !S_ISREG(st.st_mode)) 344 return; 345 346 if ((inf = fopen(file, "r")) == NULL) { 347 perror(file); 348 infile_fail++; /* Count bad opens. ID #1082298 */ 349 return; 350 } 351 curfile = savestr(file); 352 lineno = 0; 353 #ifdef __STDC__ 354 cp = strrchr(file, '.'); 355 #else 356 cp = rindex(file, '.'); 357 #endif 358 /* .l implies lisp or lex source code */ 359 if (cp && cp[1] == 'l' && cp[2] == '\0') { 360 #ifdef __STDC__ 361 if (strchr(";([", first_char()) != NULL) /* lisp */ 362 #else 363 if (index(";([", first_char()) != NULL) /* lisp */ 364 #endif 365 { 366 L_funcs(inf); 367 (void) fclose(inf); 368 return; 369 } else { /* lex */ 370 /* 371 * throw away all the code before the second "%%" 372 */ 373 toss_yysec(); 374 getline(lineftell); 375 pfnote("yylex", lineno, TRUE); 376 toss_yysec(); 377 C_entries(); 378 (void) fclose(inf); 379 return; 380 } 381 } 382 /* .y implies a yacc file */ 383 if (cp && cp[1] == 'y' && cp[2] == '\0') { 384 toss_yysec(); 385 Y_entries(); 386 C_entries(); 387 (void) fclose(inf); 388 return; 389 } 390 391 /* 392 * Add in file name extension support for Sun C++ which 393 * permits .C/.c (AT&T), .cc (G++) and .cxx (Gloksp.) 394 */ 395 396 /* if not a .c, .C, .cc, .cxx or .h file, try fortran */ 397 if (cp && (cp[1] != 'C' && cp[1] != 'c' && cp[1] != 'h') && 398 cp[2] == '\0' && (strcmp(cp, ".cc") == 0) && 399 (strcmp(cp, ".cxx") == 0)) { 400 if (PF_funcs(inf) != 0) { 401 (void) fclose(inf); 402 return; 403 } 404 rewind(inf); /* no fortran tags found, try C */ 405 } 406 C_entries(); 407 (void) fclose(inf); 408 } 409 410 static void 411 pfnote(name, ln, f) 412 char *name; 413 int ln; 414 bool f; /* f == TRUE when function */ 415 { 416 char *fp; 417 NODE *np; 418 char *nametk; /* hold temporary tokens from name */ 419 char nbuf[BUFSIZ]; 420 421 if ((np = malloc(sizeof (NODE))) == NULL) { 422 (void) fprintf(stderr, 423 gettext("ctags: too many entries to sort\n")); 424 put_entries(head); 425 free_tree(head); 426 head = np = (NODE *) malloc(sizeof (NODE)); 427 } 428 if (xflag == 0 && (strcmp(name, "main") == 0)) { 429 #ifdef __STDC__ 430 fp = strrchr(curfile, '/'); 431 #else 432 fp = rindex(curfile, '/'); 433 #endif 434 if (fp == 0) 435 fp = curfile; 436 else 437 fp++; 438 (void) sprintf(nbuf, "M%s", fp); 439 #ifdef __STDC__ 440 fp = strrchr(nbuf, '.'); 441 #else 442 fp = rindex(nbuf, '.'); 443 #endif 444 /* Chop off .cc and .cxx as well as .c, .h, etc */ 445 if (fp && ((fp[2] == 0) || (fp[2] == 'c' && fp[3] == 0) || 446 (fp[3] == 'x' && fp[4] == 0))) 447 *fp = 0; 448 name = nbuf; 449 } 450 451 /* remove in-between blanks operator function tags */ 452 #ifdef __STDC__ 453 if (strchr(name, ' ') != NULL) 454 #else 455 if (index(name, ' ') != NULL) 456 #endif 457 { 458 (void) strcpy(name, (char *) strtok(name, " ")); 459 while (nametk = (char *) strtok(0, " ")) 460 (void) strcat(name, nametk); 461 } 462 np->entry = savestr(name); 463 np->file = curfile; 464 np->f = f; 465 np->lno = ln; 466 np->left = np->right = 0; 467 if (xflag == 0) { 468 lbuf[50] = 0; 469 (void) strcat(lbuf, "$"); 470 lbuf[50] = 0; 471 } 472 np->pat = savestr(lbuf); 473 if (head == NULL) 474 head = np; 475 else 476 add_node(np, head); 477 } 478 479 /* 480 * This routine finds functions and typedefs in C syntax and adds them 481 * to the list. 482 */ 483 static void 484 C_entries() 485 { 486 int c; 487 char *token, *tp; 488 bool incomm, inquote, inchar, midtoken, isoperator, optfound; 489 int level; 490 char *sp; 491 char tok[BUFSIZ]; 492 long int tokftell; 493 494 number = gotone = midtoken = inquote = inchar = 495 incomm = isoperator = optfound = FALSE; 496 497 level = 0; 498 sp = tp = token = line; 499 lineno++; 500 lineftell = tokftell = ftell(inf); 501 for (;;) { 502 *sp = c = getc(inf); 503 if (feof(inf)) 504 break; 505 if (c == '\n') { 506 lineftell = ftell(inf); 507 lineno++; 508 } else if (c == '\\') { 509 c = *++sp = getc(inf); 510 if ((c == '\n') || (c == EOF)) { /* c == EOF, 1091005 */ 511 lineftell = ftell(inf); 512 lineno++; 513 c = ' '; 514 } 515 } else if (incomm) { 516 if (c == '*') { 517 while ((*++sp = c = getc(inf)) == '*') 518 continue; 519 520 /* c == EOF 1091005 */ 521 if ((c == '\n') || (c == EOF)) { 522 lineftell = ftell(inf); 523 lineno++; 524 } 525 526 if (c == '/') 527 incomm = FALSE; 528 } 529 } else if (inquote) { 530 /* 531 * Too dumb to know about \" not being magic, but 532 * they usually occur in pairs anyway. 533 */ 534 if (c == '"') 535 inquote = FALSE; 536 continue; 537 } else if (inchar) { 538 if (c == '\'') 539 inchar = FALSE; 540 continue; 541 } else if (midtoken == TRUE) { /* if white space omitted */ 542 goto dotoken; 543 } else switch (c) { 544 case '"': 545 inquote = TRUE; 546 continue; 547 case '\'': 548 inchar = TRUE; 549 continue; 550 case '/': 551 *++sp = c = getc(inf); 552 /* Handles the C++ comment token "//" */ 553 if (c == '*') 554 incomm = TRUE; 555 else if (c == '/') { 556 /* 557 * Skip over all the characters after 558 * "//" until a newline character. Now also 559 * includes fix for 1091005, check for EOF. 560 */ 561 do { 562 c = getc(inf); 563 /* 1091005: */ 564 } while ((c != '\n') && (c != EOF)); 565 566 567 /* 568 * Fixed bugid 1030014 569 * Return the current position of the 570 * file after the newline. 571 */ 572 lineftell = ftell(inf); 573 lineno++; 574 *--sp = c; 575 } 576 else 577 (void) ungetc(*sp, inf); 578 continue; 579 case '#': 580 if (sp == line) 581 number = TRUE; 582 continue; 583 case '{': 584 if ((tydef == begin_rec) || (tydef == begin_tag)) { 585 tydef = middle; 586 } 587 level++; 588 continue; 589 case '}': 590 /* 591 * Heuristic for function or structure end; 592 * common for #ifdef/#else blocks to add extra "{" 593 */ 594 if (sp == line) 595 level = 0; /* reset */ 596 else 597 level--; 598 if (!level && tydef == middle) { 599 tydef = end; 600 } 601 if (!level && tydef == none) /* Fix for #1034126 */ 602 goto dotoken; 603 continue; 604 } 605 606 dotoken: 607 608 609 if (!level && !inquote && !incomm && gotone == FALSE) { 610 if (midtoken) { 611 if (endtoken(c)) { 612 613 /* 614 * 615 * ':' +---> ':' -> midtok 616 * 617 * +---> operator{+,-, etc} -> midtok 618 * (continue) 619 * +---> endtok 620 */ 621 /* 622 * Enhance operator function support and 623 * fix bugid 1027806 624 * 625 * For operator token, scanning will continue until 626 * '(' is found. Spaces between 'operater' and 627 * 'oprtk' are allowed (e.g. 'operator + ()'), but 628 * will be removed when the actual entry for the tag 629 * is made. 630 * Note that functions of the form 'operator ()(int)' 631 * will be recognized, but 'operator ()' will not, 632 * even though this is legitimate in C. 633 */ 634 635 if (optoken(c)) { 636 if (isoperator) { 637 if (optfound) { 638 if (c != '(') { 639 tp++; 640 goto next_char; 641 } 642 } else { 643 if (c != ' ') { 644 optfound = TRUE; 645 } 646 tp++; 647 goto next_char; 648 } 649 } else { 650 /* start: this code shifted left for cstyle */ 651 char *backptr = tp - 7; 652 if (strncmp(backptr, "operator", 8) == 0) { 653 /* This is an overloaded operator */ 654 isoperator = TRUE; 655 if (c != ' ') { 656 optfound = TRUE; 657 } 658 659 tp++; 660 goto next_char; 661 } else if (c == '~') { 662 /* This is a destructor */ 663 tp++; 664 goto next_char; 665 } 666 /* end: above code shifted left for cstyle */ 667 } 668 } else if (c == ':') { 669 if ((*++sp = getc(inf)) == ':') { 670 tp += 2; 671 c = *sp; 672 goto next_char; 673 } else { 674 (void) ungetc (*sp, inf); 675 --sp; 676 } 677 } 678 679 /* start: this code shifted left for cstyle */ 680 { 681 int f; 682 int pfline = lineno; 683 684 if (start_entry(&sp, token, &f)) { 685 (void) strncpy(tok, token, tp-token+1); 686 tok[tp-token+1] = 0; 687 getline(tokftell); 688 pfnote(tok, pfline, f); 689 gotone = f; /* function */ 690 } 691 692 isoperator = optfound = midtoken = FALSE; 693 token = sp; 694 } 695 /* end: above code shifted left for cstyle */ 696 } else if (intoken(c)) 697 tp++; 698 } else if (begtoken(c)) { 699 token = tp = sp; 700 midtoken = TRUE; 701 tokftell = lineftell; 702 } 703 } 704 next_char: 705 if (c == ';' && tydef == end) /* clean with typedefs */ 706 tydef = none; 707 sp++; 708 /* The "c == }" was added to fix #1034126 */ 709 if (c == '\n' ||c == '}'|| sp > &line[sizeof (line) - BUFSIZ]) { 710 tp = token = sp = line; 711 number = gotone = midtoken = inquote = 712 inchar = isoperator = optfound = FALSE; 713 } 714 } 715 } 716 717 /* 718 * This routine checks to see if the current token is 719 * at the start of a function, or corresponds to a typedef 720 * It updates the input line * so that the '(' will be 721 * in it when it returns. 722 */ 723 static int 724 start_entry(lp, token, f) 725 char **lp, *token; 726 int *f; 727 { 728 char *sp; 729 int c; 730 static bool found; 731 bool firsttok; /* T if have seen first token in ()'s */ 732 int bad; 733 734 *f = 1; /* a function */ 735 sp = *lp; 736 c = *sp; 737 bad = FALSE; 738 if (!number) { /* space is not allowed in macro defs */ 739 while (iswhite(c)) { 740 *++sp = c = getc(inf); 741 if ((c == '\n') || (c == EOF)) { /* c==EOF, #1091005 */ 742 lineno++; 743 lineftell = ftell(inf); 744 if (sp > &line[sizeof (line) - BUFSIZ]) 745 goto ret; 746 } 747 } 748 /* the following tries to make it so that a #define a b(c) */ 749 /* doesn't count as a define of b. */ 750 } else { 751 if (strncmp(token, "define", 6) == 0) 752 found = 0; 753 else 754 found++; 755 if (found >= 2) { 756 gotone = TRUE; 757 badone: bad = TRUE; 758 goto ret; 759 } 760 } 761 /* check for the typedef cases */ 762 #ifdef XPG4 763 if (strncmp(token, "typedef", 7) == 0) { 764 #else /* !XPG4 */ 765 if (tflag && (strncmp(token, "typedef", 7) == 0)) { 766 #endif /* XPG4 */ 767 tydef = begin; 768 goto badone; 769 } 770 /* Handles 'class' besides 'struct' etc. */ 771 if (tydef == begin && ((strncmp(token, "struct", 6) == 0) || 772 (strncmp(token, "class", 5) == 0) || 773 (strncmp(token, "union", 5) == 0)|| 774 (strncmp(token, "enum", 4) == 0))) { 775 tydef = begin_rec; 776 goto badone; 777 } 778 if (tydef == begin) { 779 tydef = end; 780 goto badone; 781 } 782 if (tydef == begin_rec) { 783 tydef = begin_tag; 784 goto badone; 785 } 786 if (tydef == begin_tag) { 787 tydef = end; 788 goto gottydef; /* Fall through to "tydef==end" */ 789 } 790 791 gottydef: 792 if (tydef == end) { 793 *f = 0; 794 goto ret; 795 } 796 if (c != '(') 797 goto badone; 798 firsttok = FALSE; 799 while ((*++sp = c = getc(inf)) != ')') { 800 if ((c == '\n') || (c == EOF)) { /* c == EOF Fix for #1091005 */ 801 lineftell = ftell(inf); 802 lineno++; 803 if (sp > &line[sizeof (line) - BUFSIZ]) 804 goto ret; 805 } 806 /* 807 * This line used to confuse ctags: 808 * int (*oldhup)(); 809 * This fixes it. A nonwhite char before the first 810 * token, other than a / (in case of a comment in there) 811 * makes this not a declaration. 812 */ 813 if (begtoken(c) || c == '/') 814 firsttok = TRUE; 815 else if (!iswhite(c) && !firsttok) 816 goto badone; 817 } 818 while (iswhite(*++sp = c = getc(inf))) 819 if ((c == '\n') || (c == EOF)) { /* c == EOF fix for #1091005 */ 820 lineno++; 821 lineftell = ftell(inf); 822 if (sp > &line[sizeof (line) - BUFSIZ]) 823 break; 824 } 825 ret: 826 *lp = --sp; 827 if (c == '\n') 828 lineno--; 829 (void) ungetc(c, inf); 830 return (!bad && (!*f || isgood(c))); 831 /* hack for typedefs */ 832 } 833 834 /* 835 * Y_entries: 836 * Find the yacc tags and put them in. 837 */ 838 static void 839 Y_entries() 840 { 841 char *sp, *orig_sp; 842 int brace; 843 bool in_rule, toklen; 844 char tok[BUFSIZ]; 845 846 brace = 0; 847 getline(lineftell); 848 pfnote("yyparse", lineno, TRUE); 849 while (fgets(line, sizeof (line), inf) != NULL) 850 for (sp = line; *sp; sp++) 851 switch (*sp) { 852 case '\n': 853 lineno++; 854 /* FALLTHROUGH */ 855 case ' ': 856 case '\t': 857 case '\f': 858 case '\r': 859 break; 860 case '"': 861 do { 862 while (*++sp != '"') 863 continue; 864 } while (sp[-1] == '\\'); 865 break; 866 case '\'': 867 do { 868 while (*++sp != '\'') 869 continue; 870 } while (sp[-1] == '\\'); 871 break; 872 case '/': 873 if (*++sp == '*') 874 sp = toss_comment(sp); 875 else 876 --sp; 877 break; 878 case '{': 879 brace++; 880 break; 881 case '}': 882 brace--; 883 break; 884 case '%': 885 if (sp[1] == '%' && sp == line) 886 return; 887 break; 888 case '|': 889 case ';': 890 in_rule = FALSE; 891 break; 892 default: 893 if (brace == 0 && !in_rule && (isalpha(*sp) || 894 *sp == '.' || 895 *sp == '_')) { 896 orig_sp = sp; 897 ++sp; 898 while (isalnum(*sp) || *sp == '_' || 899 *sp == '.') 900 sp++; 901 toklen = sp - orig_sp; 902 while (isspace(*sp)) 903 sp++; 904 if (*sp == ':' || (*sp == '\0' && 905 first_char() == ':')) { 906 (void) strncpy(tok, 907 orig_sp, toklen); 908 tok[toklen] = '\0'; 909 (void) strcpy(lbuf, line); 910 lbuf[strlen(lbuf) - 1] = '\0'; 911 pfnote(tok, lineno, TRUE); 912 in_rule = TRUE; 913 } 914 else 915 sp--; 916 } 917 break; 918 } 919 } 920 921 static char * 922 toss_comment(start) 923 char *start; 924 { 925 char *sp; 926 927 /* 928 * first, see if the end-of-comment is on the same line 929 */ 930 do { 931 #ifdef __STDC__ 932 while ((sp = strchr(start, '*')) != NULL) 933 #else 934 while ((sp = index(start, '*')) != NULL) 935 #endif 936 if (sp[1] == '/') 937 return (++sp); 938 else 939 start = (++sp); 940 start = line; 941 lineno++; 942 } while (fgets(line, sizeof (line), inf) != NULL); 943 944 /* 945 * running this through lint revealed that the original version 946 * of this routine didn't explicitly return something; while 947 * the return value was always used!. so i've added this 948 * next line. 949 */ 950 return (sp); 951 } 952 953 static void 954 getline(where) 955 long int where; 956 { 957 long saveftell = ftell(inf); 958 char *cp; 959 960 (void) fseek(inf, where, 0); 961 (void) fgets(lbuf, sizeof (lbuf), inf); 962 #ifdef __STDC__ 963 cp = strrchr(lbuf, '\n'); 964 #else 965 cp = rindex(lbuf, '\n'); 966 #endif 967 if (cp) 968 *cp = 0; 969 (void) fseek(inf, saveftell, 0); 970 } 971 972 static void 973 free_tree(node) 974 NODE *node; 975 { 976 extern void cfree(); 977 978 while (node) { 979 free_tree(node->right); 980 cfree(node); 981 node = node->left; 982 } 983 } 984 985 static void 986 add_node(node, cur_node) 987 NODE *node, *cur_node; 988 { 989 int dif; 990 991 dif = strcmp(node->entry, cur_node->entry); 992 if (dif == 0) { 993 if (node->file == cur_node->file) { 994 if (!wflag) { 995 (void) fprintf(stderr, 996 gettext("Duplicate entry in file %s, line %d: %s\n"), 997 node->file, lineno, node->entry); 998 (void) fprintf(stderr, 999 gettext("Second entry ignored\n")); 1000 } 1001 return; 1002 } 1003 if (!cur_node->been_warned) 1004 if (!wflag) { 1005 (void) fprintf(stderr, gettext("Duplicate " 1006 "entry in files %s and %s: %s " 1007 "(Warning only)\n"), 1008 node->file, cur_node->file, 1009 node->entry); 1010 } 1011 cur_node->been_warned = TRUE; 1012 return; 1013 } 1014 1015 if (dif < 0) { 1016 if (cur_node->left != NULL) 1017 add_node(node, cur_node->left); 1018 else 1019 cur_node->left = node; 1020 return; 1021 } 1022 if (cur_node->right != NULL) 1023 add_node(node, cur_node->right); 1024 else 1025 cur_node->right = node; 1026 } 1027 1028 static void 1029 put_entries(node) 1030 NODE *node; 1031 { 1032 char *sp; 1033 1034 if (node == NULL) 1035 return; 1036 put_entries(node->left); 1037 1038 /* 1039 * while the code in the following #ifdef section could be combined, 1040 * it's explicitly separated here to make maintainance easier. 1041 */ 1042 #ifdef XPG4 1043 /* 1044 * POSIX 2003: we no longer have a "-t" flag; the logic is 1045 * automatically assumed to be "turned on" here. 1046 */ 1047 if (xflag == 0) { 1048 (void) fprintf(outf, "%s\t%s\t%c^", 1049 node->entry, node->file, searchar); 1050 for (sp = node->pat; *sp; sp++) 1051 if (*sp == '\\') 1052 (void) fprintf(outf, "\\\\"); 1053 else if (*sp == searchar) 1054 (void) fprintf(outf, "\\%c", searchar); 1055 else 1056 (void) putc(*sp, outf); 1057 (void) fprintf(outf, "%c\n", searchar); 1058 } else if (vflag) 1059 (void) fprintf(stdout, "%s %s %d\n", 1060 node->entry, node->file, (node->lno+63)/64); 1061 else 1062 (void) fprintf(stdout, "%-16s %4d %-16s %s\n", 1063 node->entry, node->lno, node->file, node->pat); 1064 #else /* XPG4 */ 1065 /* 1066 * original way of doing things. "-t" logic is only turned on 1067 * when the user has specified it via a command-line argument. 1068 */ 1069 if (xflag == 0) 1070 if (node->f) { /* a function */ 1071 (void) fprintf(outf, "%s\t%s\t%c^", 1072 node->entry, node->file, searchar); 1073 for (sp = node->pat; *sp; sp++) 1074 if (*sp == '\\') 1075 (void) fprintf(outf, "\\\\"); 1076 else if (*sp == searchar) 1077 (void) fprintf(outf, "\\%c", searchar); 1078 else 1079 (void) putc(*sp, outf); 1080 (void) fprintf(outf, "%c\n", searchar); 1081 } else { /* a typedef; text pattern inadequate */ 1082 (void) fprintf(outf, "%s\t%s\t%d\n", 1083 node->entry, node->file, node->lno); 1084 } else if (vflag) 1085 (void) fprintf(stdout, "%s %s %d\n", 1086 node->entry, node->file, (node->lno+63)/64); 1087 else 1088 (void) fprintf(stdout, "%-16s %4d %-16s %s\n", 1089 node->entry, node->lno, node->file, node->pat); 1090 #endif /* XPG4 */ 1091 put_entries(node->right); 1092 } 1093 1094 1095 static int 1096 PF_funcs(fi) 1097 FILE *fi; 1098 { 1099 1100 pfcnt = 0; 1101 while (fgets(lbuf, sizeof (lbuf), fi)) { 1102 lineno++; 1103 dbp = lbuf; 1104 if (*dbp == '%') dbp++; /* Ratfor escape to fortran */ 1105 while (isspace(*dbp)) 1106 dbp++; 1107 if (*dbp == 0) 1108 continue; 1109 switch (*dbp |' ') { 1110 1111 case 'i': 1112 if (tail("integer")) 1113 takeprec(); 1114 break; 1115 case 'r': 1116 if (tail("real")) 1117 takeprec(); 1118 break; 1119 case 'l': 1120 if (tail("logical")) 1121 takeprec(); 1122 break; 1123 case 'c': 1124 if (tail("complex") || tail("character")) 1125 takeprec(); 1126 break; 1127 case 'd': 1128 if (tail("double")) { 1129 while (isspace(*dbp)) 1130 dbp++; 1131 if (*dbp == 0) 1132 continue; 1133 if (tail("precision")) 1134 break; 1135 continue; 1136 } 1137 break; 1138 } 1139 while (isspace(*dbp)) 1140 dbp++; 1141 if (*dbp == 0) 1142 continue; 1143 switch (*dbp|' ') { 1144 1145 case 'f': 1146 if (tail("function")) 1147 getit(); 1148 continue; 1149 case 's': 1150 if (tail("subroutine")) 1151 getit(); 1152 continue; 1153 case 'p': 1154 if (tail("program")) { 1155 getit(); 1156 continue; 1157 } 1158 if (tail("procedure")) 1159 getit(); 1160 continue; 1161 } 1162 } 1163 return (pfcnt); 1164 } 1165 1166 static int 1167 tail(cp) 1168 char *cp; 1169 { 1170 int len = 0; 1171 1172 while (*cp && (*cp&~' ') == ((*(dbp+len))&~' ')) 1173 cp++, len++; 1174 if (*cp == 0) { 1175 dbp += len; 1176 return (1); 1177 } 1178 return (0); 1179 } 1180 1181 static void 1182 takeprec() 1183 { 1184 1185 while (isspace(*dbp)) 1186 dbp++; 1187 if (*dbp != '*') 1188 return; 1189 dbp++; 1190 while (isspace(*dbp)) 1191 dbp++; 1192 if (!isdigit(*dbp)) { 1193 --dbp; /* force failure */ 1194 return; 1195 } 1196 do 1197 dbp++; 1198 while (isdigit(*dbp)); 1199 } 1200 1201 static void 1202 getit() 1203 { 1204 char *cp; 1205 char c; 1206 char nambuf[BUFSIZ]; 1207 1208 for (cp = lbuf; *cp; cp++) 1209 ; 1210 *--cp = 0; /* zap newline */ 1211 while (isspace(*dbp)) 1212 dbp++; 1213 if (*dbp == 0 || !isalpha(*dbp) || !isascii(*dbp)) 1214 return; 1215 for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++) 1216 continue; 1217 c = cp[0]; 1218 cp[0] = 0; 1219 (void) strcpy(nambuf, dbp); 1220 cp[0] = c; 1221 pfnote(nambuf, lineno, TRUE); 1222 pfcnt++; 1223 } 1224 1225 static char * 1226 savestr(cp) 1227 char *cp; 1228 { 1229 int len; 1230 char *dp; 1231 1232 len = strlen(cp); 1233 dp = (char *)malloc(len+1); 1234 (void) strcpy(dp, cp); 1235 1236 return (dp); 1237 } 1238 1239 #ifndef __STDC__ 1240 /* 1241 * Return the ptr in sp at which the character c last 1242 * appears; NULL if not found 1243 * 1244 * Identical to v7 rindex, included for portability. 1245 */ 1246 1247 static char * 1248 rindex(sp, c) 1249 char *sp, c; 1250 { 1251 char *r; 1252 1253 r = NULL; 1254 do { 1255 if (*sp == c) 1256 r = sp; 1257 } while (*sp++); 1258 return (r); 1259 } 1260 #endif 1261 1262 /* 1263 * lisp tag functions 1264 * just look for (def or (DEF 1265 */ 1266 1267 static void 1268 L_funcs(fi) 1269 FILE *fi; 1270 { 1271 int special; 1272 1273 pfcnt = 0; 1274 while (fgets(lbuf, sizeof (lbuf), fi)) { 1275 lineno++; 1276 dbp = lbuf; 1277 if (dbp[0] == '(' && 1278 (dbp[1] == 'D' || dbp[1] == 'd') && 1279 (dbp[2] == 'E' || dbp[2] == 'e') && 1280 (dbp[3] == 'F' || dbp[3] == 'f')) { 1281 dbp += 4; 1282 if (striccmp(dbp, "method") == 0 || 1283 striccmp(dbp, "wrapper") == 0 || 1284 striccmp(dbp, "whopper") == 0) 1285 special = TRUE; 1286 else 1287 special = FALSE; 1288 while (!isspace(*dbp)) 1289 dbp++; 1290 while (isspace(*dbp)) 1291 dbp++; 1292 L_getit(special); 1293 } 1294 } 1295 } 1296 1297 static void 1298 L_getit(special) 1299 int special; 1300 { 1301 char *cp; 1302 char c; 1303 char nambuf[BUFSIZ]; 1304 1305 for (cp = lbuf; *cp; cp++) 1306 continue; 1307 *--cp = 0; /* zap newline */ 1308 if (*dbp == 0) 1309 return; 1310 if (special) { 1311 #ifdef __STDC__ 1312 if ((cp = strchr(dbp, ')')) == NULL) 1313 #else 1314 if ((cp = index(dbp, ')')) == NULL) 1315 #endif 1316 return; 1317 while (cp >= dbp && *cp != ':') 1318 cp--; 1319 if (cp < dbp) 1320 return; 1321 dbp = cp; 1322 while (*cp && *cp != ')' && *cp != ' ') 1323 cp++; 1324 } 1325 else 1326 for (cp = dbp + 1; *cp && *cp != '(' && *cp != ' '; cp++) 1327 continue; 1328 c = cp[0]; 1329 cp[0] = 0; 1330 (void) strcpy(nambuf, dbp); 1331 cp[0] = c; 1332 pfnote(nambuf, lineno, TRUE); 1333 pfcnt++; 1334 } 1335 1336 /* 1337 * striccmp: 1338 * Compare two strings over the length of the second, ignoring 1339 * case distinctions. If they are the same, return 0. If they 1340 * are different, return the difference of the first two different 1341 * characters. It is assumed that the pattern (second string) is 1342 * completely lower case. 1343 */ 1344 static int 1345 striccmp(str, pat) 1346 char *str, *pat; 1347 { 1348 int c1; 1349 1350 while (*pat) { 1351 if (isupper(*str)) 1352 c1 = tolower(*str); 1353 else 1354 c1 = *str; 1355 if (c1 != *pat) 1356 return (c1 - *pat); 1357 pat++; 1358 str++; 1359 } 1360 return (0); 1361 } 1362 1363 /* 1364 * first_char: 1365 * Return the first non-blank character in the file. After 1366 * finding it, rewind the input file so we start at the beginning 1367 * again. 1368 */ 1369 static int 1370 first_char() 1371 { 1372 int c; 1373 long off; 1374 1375 off = ftell(inf); 1376 while ((c = getc(inf)) != EOF) 1377 if (!isspace(c) && c != '\r') { 1378 (void) fseek(inf, off, 0); 1379 return (c); 1380 } 1381 (void) fseek(inf, off, 0); 1382 return (EOF); 1383 } 1384 1385 /* 1386 * toss_yysec: 1387 * Toss away code until the next "%%" line. 1388 */ 1389 static void 1390 toss_yysec() 1391 { 1392 char buf[BUFSIZ]; 1393 1394 for (;;) { 1395 lineftell = ftell(inf); 1396 if (fgets(buf, BUFSIZ, inf) == NULL) 1397 return; 1398 lineno++; 1399 if (strncmp(buf, "%%", 2) == 0) 1400 return; 1401 } 1402 } 1403 1404 static void 1405 Usage() 1406 { 1407 #ifdef XPG4 1408 (void) fprintf(stderr, gettext("Usage:\tctags [-aBFuvw] " 1409 #else /* !XPG4 */ 1410 (void) fprintf(stderr, gettext("Usage:\tctags [-aBFtuvw] " 1411 #endif /* XPG4 */ 1412 "[-f tagsfile] file ...\n")); 1413 (void) fprintf(stderr, gettext("OR:\tctags [-x] file ...\n")); 1414 exit(1); 1415 } 1416 1417 1418 /* 1419 * parseargs(): modify the args 1420 * the purpose of this routine is to transform any ancient argument 1421 * usage into a format which is acceptable to getopt(3C), so that we 1422 * retain backwards Solaris 2.[0-4] compatibility. 1423 * 1424 * This routine allows us to make full use of getopts, without any 1425 * funny argument processing in main(). 1426 * 1427 * The other alternative would be to hand-craft the processed arguments 1428 * during and after getopt(3C) - which usually leads to uglier code 1429 * in main(). I've opted to keep the ugliness isolated down here, 1430 * instead of in main(). 1431 * 1432 * In a nutshell, if the user has used the old Solaris syntax of: 1433 * ctags [-aBFtuvwx] [-f tagsfile] filename ... 1434 * We simply change this into: 1435 * ctags [-a] [-B] [-F] [-t] [-u] [-v] [-w] [-x] [-f tags] file... 1436 * 1437 * If the user has specified the new getopt(3C) syntax, we merely 1438 * copy that into our modified argument space. 1439 */ 1440 static void 1441 parseargs(ac, av) 1442 int ac; /* argument count */ 1443 char **av; /* ptr to original argument space */ 1444 { 1445 int i; /* current argument */ 1446 int a; /* used to parse combined arguments */ 1447 int fflag; /* 1 = we're only parsing filenames */ 1448 size_t sz; /* size of the argument */ 1449 size_t mav_sz; /* size of our psuedo argument space */ 1450 1451 i = mac = fflag = 0; /* proper initializations */ 1452 1453 mav_sz = (size_t) ((ac + 1) * sizeof (char *)); 1454 if ((mav = malloc(mav_sz)) == (char **) NULL) { 1455 perror("Can't malloc argument space"); 1456 exit(1); 1457 } 1458 1459 /* for each argument, see if we need to change things: */ 1460 for (; (av[i] != (char *) NULL) && (av[i][0] != (char) NULL); i++) { 1461 1462 if (strcmp(av[i], "--") == 0) { 1463 fflag = 1; /* just handle filenames now */ 1464 } 1465 1466 sz = strlen(&av[i][0]); /* get this arg's size */ 1467 1468 /* 1469 * if the argument starts with a "-", and has more than 1470 * 1 flag, then we have to search through each character, 1471 * and separate any flags which have been combined. 1472 * 1473 * so, if we've found a "-" string which needs separating: 1474 */ 1475 if (fflag == 0 && /* not handling filename args */ 1476 av[i][0] == '-' && /* and this is a flag */ 1477 sz > 2) { /* and there's more than 1 flag */ 1478 /* then for each flag after the "-" sign: */ 1479 for (a = 1; av[i][a]; a++) { 1480 /* copy the flag into mav space. */ 1481 if (a > 1) { 1482 /* 1483 * we need to call realloc() after the 1484 * 1st combined flag, because "ac" 1485 * doesn't include combined args. 1486 */ 1487 mav_sz += sizeof (char *); 1488 if ((mav = realloc(mav, mav_sz)) == 1489 (char **) NULL) { 1490 perror("Can't realloc " 1491 "argument space"); 1492 exit(1); 1493 } 1494 } 1495 1496 if ((mav[mac] = malloc((size_t) CPFLAG)) == 1497 (char *) NULL) { 1498 perror("Can't malloc argument space"); 1499 exit(1); 1500 } 1501 (void) sprintf(mav[mac], "-%c", av[i][a]); 1502 ++mac; 1503 } 1504 } else { 1505 /* otherwise, just copy the argument: */ 1506 if ((mav[mac] = malloc(sz + 1)) == (char *) NULL) { 1507 perror("Can't malloc argument space"); 1508 exit(1); 1509 } 1510 (void) strcpy(mav[mac], av[i]); 1511 ++mac; 1512 } 1513 } 1514 1515 mav[mac] = (char *) NULL; 1516 } 1517