1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1988 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 /* 32 * cscope - interactive C symbol or text cross-reference 33 * 34 * searching functions 35 */ 36 37 #include <unistd.h> 38 #include <stdio.h> 39 #include <libgen.h> 40 #include "global.h" 41 #include "vp.h" 42 43 /* 44 * most of these functions have been optimized so their innermost loops have 45 * only one test for the desired character by putting the char and 46 * an end-of-block marker (\0) at the end of the disk block buffer. 47 * When the inner loop exits on the char, an outer loop will see if 48 * the char is followed by a \0. If so, it will read the next block 49 * and restart the inner loop. 50 */ 51 52 char block[BUFSIZ + 2]; /* leave room for end-of-block mark */ 53 int blocklen; /* length of disk block read */ 54 char blockmark; /* mark character to be searched for */ 55 long blocknumber; /* block number */ 56 char *blockp; /* pointer to current char in block */ 57 char lastfilepath[PATHLEN + 1]; /* last file that full path was */ 58 /* computed for */ 59 60 static char cpattern[PATLEN + 1]; /* compressed pattern */ 61 static long lastfcnoffset; /* last function name offset */ 62 static long postingsfound; /* retrieved number of postings */ 63 static char *regexp; /* regular expression */ 64 static POSTING *postingp; /* retrieved posting set pointer */ 65 static long searchcount; /* count of files searched */ 66 static long starttime; /* start time for progress messages */ 67 68 static POSTING *getposting(void); 69 static void putsource(FILE *output); 70 static void putref(char *file, char *function); 71 static void findcalledbysub(char *file); 72 static void findterm(void); 73 static void fileprogress(void); 74 static void putpostingref(POSTING *p); 75 static void putline(FILE *output); 76 static char *strtolower(char *s); 77 static char *filepath(char *file); 78 79 /* find the symbol in the cross-reference */ 80 81 void 82 findsymbol(void) 83 { 84 char file[PATHLEN + 1]; /* source file name */ 85 char function[PATLEN + 1]; /* function name */ 86 char macro[PATLEN + 1]; /* macro name */ 87 char symbol[PATLEN + 1]; /* symbol name */ 88 char *cp; 89 char c; 90 char *s; 91 92 if (invertedindex == YES) { 93 long lastline = 0; 94 POSTING *p; 95 96 findterm(); 97 while ((p = getposting()) != NULL) { 98 if (p->type != INCLUDE && p->lineoffset != lastline) { 99 putpostingref(p); 100 lastline = p->lineoffset; 101 } 102 } 103 return; 104 } 105 (void) scanpast('\t'); /* find the end of the header */ 106 skiprefchar(); /* skip the file marker */ 107 getstring(file); /* save the file name */ 108 *function = '\0'; 109 /* a macro can be inside a function, but not vice versa */ 110 *macro = '\0'; 111 112 /* find the next symbol */ 113 /* note: this code was expanded in-line for speed */ 114 /* while (scanpast('\n') != NULL) { */ 115 /* other macros were replaced by code using cp instead of blockp */ 116 cp = blockp; 117 for (;;) { 118 setmark('\n'); 119 do { /* innermost loop optimized to only one test */ 120 while (*cp != '\n') { 121 ++cp; 122 } 123 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL); 124 125 /* skip the found character */ 126 if (cp != NULL && *(++cp + 1) == '\0') { 127 cp = readblock(); 128 } 129 if (cp == NULL) { 130 break; 131 } 132 /* look for a source file or function name */ 133 if (*cp == '\t') { 134 blockp = cp; 135 switch (getrefchar()) { 136 137 case NEWFILE: /* file name */ 138 139 /* save the name */ 140 skiprefchar(); 141 getstring(file); 142 143 /* check for the end of the symbols */ 144 if (*file == '\0') { 145 return; 146 } 147 fileprogress(); 148 /* FALLTHROUGH */ 149 150 case FCNEND: /* function end */ 151 *function = '\0'; 152 goto notmatched; /* don't match name */ 153 154 case FCNDEF: /* function name */ 155 s = function; 156 break; 157 158 case DEFINE: /* could be a macro */ 159 if (fileversion >= 10) { 160 s = macro; 161 } else { 162 s = symbol; 163 } 164 break; 165 166 case DEFINEEND: 167 *macro = '\0'; 168 goto notmatched; /* don't match name */ 169 170 case INCLUDE: /* #include file */ 171 goto notmatched; /* don't match name */ 172 default: /* other symbol */ 173 s = symbol; 174 } 175 /* save the name */ 176 skiprefchar(); 177 getstring(s); 178 179 /* see if this is a regular expression pattern */ 180 if (regexp != NULL) { 181 if (caseless == YES) { 182 s = strtolower(s); 183 } 184 if (*s != '\0' && regex(regexp, s) != NULL) { 185 goto matched; 186 } 187 } 188 /* match the symbol to the text pattern */ 189 else if (strequal(pattern, s)) { 190 goto matched; 191 } 192 goto notmatched; 193 } 194 /* if this is a regular expression pattern */ 195 if (regexp != NULL) { 196 c = *cp; 197 if (c & 0200) { /* digraph char? */ 198 c = dichar1[(c & 0177) / 8]; 199 } 200 /* if this is a symbol */ 201 if (isalpha(c) || c == '_') { 202 blockp = cp; 203 getstring(symbol); 204 s = symbol; 205 if (caseless == YES) { 206 s = strtolower(s); 207 } 208 /* match the symbol to the regular expression */ 209 if (regex(regexp, s) != NULL) { 210 goto matched; 211 } 212 goto notmatched; 213 } 214 } 215 /* match the character to the text pattern */ 216 else if (*cp == cpattern[0]) { 217 blockp = cp; 218 219 /* match the rest of the symbol to the text pattern */ 220 if (matchrest()) { 221 s = NULL; 222 matched: 223 /* 224 * output the file, calling function or macro, 225 * and source line 226 */ 227 if (*macro != '\0' && s != macro) { 228 putref(file, macro); 229 } else if (s != function) { 230 putref(file, function); 231 } else { 232 putref(file, ""); 233 } 234 if (blockp == NULL) { 235 return; 236 } 237 } 238 notmatched: 239 cp = blockp; 240 } 241 } 242 blockp = cp; 243 } 244 245 /* find the function definition or #define */ 246 247 void 248 finddef(void) 249 { 250 char file[PATHLEN + 1]; /* source file name */ 251 char function[PATLEN + 1]; /* function name */ 252 char macro[PATLEN + 1]; /* macro name */ 253 char symbol[PATLEN + 1]; /* symbol name */ 254 char *s; 255 256 if (invertedindex == YES) { 257 POSTING *p; 258 259 findterm(); 260 while ((p = getposting()) != NULL) { 261 switch (p->type) { 262 case DEFINE: /* could be a macro */ 263 case FCNDEF: 264 case CLASSDEF: 265 case ENUMDEF: 266 case MEMBERDEF: 267 case STRUCTDEF: 268 case TYPEDEF: 269 case UNIONDEF: 270 case GLOBALDEF: /* other global definition */ 271 case LOCALDEF: /* other local definition */ 272 case PARAMETER: 273 putpostingref(p); 274 } 275 } 276 return; 277 } 278 /* find the next file name or definition */ 279 *function = '\0'; 280 /* a macro can be inside a function, but not vice versa */ 281 *macro = '\0'; 282 283 while (scanpast('\t') != NULL) { 284 switch (*blockp) { 285 286 case NEWFILE: 287 skiprefchar(); /* save file name */ 288 getstring(file); 289 if (*file == '\0') { /* if end of symbols */ 290 return; 291 } 292 fileprogress(); 293 /* FALLTHROUGH */ 294 295 case FCNEND: /* function end */ 296 *function = '\0'; 297 break; 298 299 case FCNDEF: /* function name */ 300 s = function; 301 goto def; 302 303 case DEFINE: /* could be a macro */ 304 if (fileversion >= 10) { 305 s = macro; 306 } else { 307 s = symbol; 308 } 309 goto def; 310 311 case DEFINEEND: 312 *macro = '\0'; 313 break; 314 315 case CLASSDEF: 316 case ENUMDEF: 317 case MEMBERDEF: 318 case STRUCTDEF: 319 case TYPEDEF: 320 case UNIONDEF: 321 case GLOBALDEF: /* other global definition */ 322 case LOCALDEF: /* other local definition */ 323 case PARAMETER: 324 s = symbol; 325 def: 326 /* save the name */ 327 skiprefchar(); 328 getstring(s); 329 330 /* see if this is a regular expression pattern */ 331 if (regexp != NULL) { 332 if (caseless == YES) { 333 s = strtolower(s); 334 } 335 if (*s != '\0' && regex(regexp, s) != NULL) { 336 goto matched; 337 } 338 } else if (strequal(pattern, s)) { 339 /* match the symbol to the text pattern */ 340 matched: 341 /* 342 * output the file, calling function or macro, 343 * and source line 344 */ 345 if (*macro != '\0' && s != macro) { 346 putref(file, macro); 347 } else if (s != function) { 348 putref(file, function); 349 } else { 350 putref(file, ""); 351 } 352 } 353 } 354 } 355 } 356 357 /* find all function definitions (used by samuel only) */ 358 359 void 360 findallfcns(void) 361 { 362 char file[PATHLEN + 1]; /* source file name */ 363 char function[PATLEN + 1]; /* function name */ 364 365 /* find the next file name or definition */ 366 while (scanpast('\t') != NULL) { 367 switch (*blockp) { 368 case NEWFILE: 369 skiprefchar(); /* save file name */ 370 getstring(file); 371 if (*file == '\0') { /* if end of symbols */ 372 return; 373 } 374 fileprogress(); 375 break; 376 377 case FCNDEF: 378 case CLASSDEF: 379 skiprefchar(); /* save function name */ 380 getstring(function); 381 382 /* output the file, function and source line */ 383 putref(file, function); 384 break; 385 } 386 } 387 } 388 389 /* find the functions called by this function */ 390 391 void 392 findcalledby(void) 393 { 394 char file[PATHLEN + 1]; /* source file name */ 395 396 if (invertedindex == YES) { 397 POSTING *p; 398 399 findterm(); 400 while ((p = getposting()) != NULL) { 401 switch (p->type) { 402 case DEFINE: /* could be a macro */ 403 case FCNDEF: 404 if (dbseek(p->lineoffset) != -1 && 405 scanpast('\t') != NULL) { /* skip def */ 406 findcalledbysub(srcfiles[p->fileindex]); 407 } 408 } 409 } 410 return; 411 } 412 /* find the function definition(s) */ 413 while (scanpast('\t') != NULL) { 414 switch (*blockp) { 415 case NEWFILE: 416 skiprefchar(); /* save file name */ 417 getstring(file); 418 if (*file == '\0') { /* if end of symbols */ 419 return; 420 } 421 fileprogress(); 422 break; 423 424 case DEFINE: /* could be a macro */ 425 if (fileversion < 10) { 426 break; 427 } 428 /* FALLTHROUGH */ 429 430 case FCNDEF: 431 skiprefchar(); /* match name to pattern */ 432 if (match()) { 433 findcalledbysub(file); 434 } 435 break; 436 } 437 } 438 } 439 440 static void 441 findcalledbysub(char *file) 442 { 443 /* find the next function call or the end of this function */ 444 while (scanpast('\t') != NULL) { 445 switch (*blockp) { 446 case DEFINE: /* #define inside a function */ 447 if (fileversion >= 10) { /* skip it */ 448 while (scanpast('\t') != NULL && 449 *blockp != DEFINEEND) 450 ; 451 } 452 break; 453 case FCNCALL: /* function call */ 454 455 /* output the file name */ 456 (void) fprintf(refsfound, "%s ", filepath(file)); 457 458 /* output the function name */ 459 skiprefchar(); 460 putline(refsfound); 461 (void) putc(' ', refsfound); 462 463 /* output the source line */ 464 putsource(refsfound); 465 break; 466 467 case DEFINEEND: /* #define end */ 468 case FCNEND: /* function end */ 469 case FCNDEF: /* function end (pre 9.5) */ 470 case NEWFILE: /* file end */ 471 return; 472 } 473 } 474 } 475 476 /* find the functions calling this function */ 477 478 void 479 findcalling(void) 480 { 481 char file[PATHLEN + 1]; /* source file name */ 482 char function[PATLEN + 1]; /* function name */ 483 char macro[PATLEN + 1]; /* macro name */ 484 485 if (invertedindex == YES) { 486 POSTING *p; 487 488 findterm(); 489 while ((p = getposting()) != NULL) { 490 if (p->type == FCNCALL) { 491 putpostingref(p); 492 } 493 } 494 return; 495 } 496 /* find the next file name or function definition */ 497 /* a macro can be inside a function, but not vice versa */ 498 *macro = '\0'; 499 500 while (scanpast('\t') != NULL) { 501 switch (*blockp) { 502 case NEWFILE: /* save file name */ 503 skiprefchar(); 504 getstring(file); 505 if (*file == '\0') { /* if end of symbols */ 506 return; 507 } 508 fileprogress(); 509 /* FALLTHROUGH */ 510 case FCNEND: /* function end */ 511 *function = '\0'; 512 break; 513 case DEFINE: /* could be a macro */ 514 if (fileversion >= 10) { 515 skiprefchar(); 516 getstring(macro); 517 } 518 break; 519 520 case DEFINEEND: 521 *macro = '\0'; 522 break; 523 524 case FCNDEF: /* save calling function name */ 525 skiprefchar(); 526 getstring(function); 527 break; 528 case FCNCALL: /* match function called to pattern */ 529 skiprefchar(); 530 if (match()) { 531 /* output the file, calling function or */ 532 /* macro, and source */ 533 if (*macro != '\0') { 534 putref(file, macro); 535 } else { 536 putref(file, function); 537 } 538 } 539 } 540 } 541 } 542 543 /* find direct assignment to, and increment and decrement of, this variable */ 544 545 void 546 findassignments(void) 547 { 548 char file[PATHLEN + 1]; /* source file name */ 549 char function[PATLEN + 1]; /* function name */ 550 char macro[PATLEN + 1]; /* macro name */ 551 552 if (fileversion < 13) { 553 putmsg("Database built with cscope version < 13 does not " 554 "have assignment information"); 555 (void) sleep(3); 556 return; 557 } 558 #if CTRACE 559 ctroff(); 560 #endif 561 if (invertedindex == YES) { 562 POSTING *p; 563 564 findterm(); 565 while ((p = getposting()) != NULL) { 566 switch (p->type) { 567 case ASSIGNMENT: 568 case GLOBALDEF: /* can have initializer */ 569 case LOCALDEF: /* can have initializer */ 570 case PARAMETER: /* initial value */ 571 putpostingref(p); 572 } 573 } 574 return; 575 } 576 /* find the next file name or function definition */ 577 /* a macro can be inside a function, but not vice versa */ 578 *macro = '\0'; 579 580 while (scanpast('\t') != NULL) { 581 switch (*blockp) { 582 case NEWFILE: /* save file name */ 583 skiprefchar(); 584 getstring(file); 585 if (*file == '\0') { /* if end of symbols */ 586 return; 587 } 588 fileprogress(); 589 /* FALLTHROUGH */ 590 case FCNEND: /* function end */ 591 *function = '\0'; 592 break; 593 case DEFINE: /* could be a macro */ 594 if (fileversion >= 10) { 595 skiprefchar(); 596 getstring(macro); 597 } 598 break; 599 600 case DEFINEEND: 601 *macro = '\0'; 602 break; 603 604 case FCNDEF: /* save calling function name */ 605 skiprefchar(); 606 getstring(function); 607 break; 608 case ASSIGNMENT: /* match assignment to pattern */ 609 case GLOBALDEF: /* can have initializer */ 610 case LOCALDEF: /* can have initializer */ 611 case PARAMETER: /* initial value */ 612 skiprefchar(); 613 if (match()) { 614 /* output the file, calling function or */ 615 /* macro, and source */ 616 if (*macro != '\0') { 617 putref(file, macro); 618 } else { 619 putref(file, function); 620 } 621 } 622 } 623 } 624 } 625 626 /* find the grep pattern in the source files */ 627 628 char * 629 findgreppat(void) 630 { 631 char egreppat[2 * PATLEN]; 632 char *cp, *pp; 633 634 /* translate egrep special characters in the regular expression */ 635 cp = egreppat; 636 for (pp = pattern; *pp != '\0'; ++pp) { 637 if (strchr("+?|()", *pp) != NULL) { 638 *cp++ = '\\'; 639 } 640 *cp++ = *pp; 641 } 642 *cp = '\0'; 643 644 /* search the source files */ 645 return (findegreppat(egreppat)); 646 } 647 648 /* find this regular expression in the source files */ 649 650 char * 651 findegreppat(char *egreppat) 652 { 653 int i; 654 char *egreperror; 655 char msg[MSGLEN + 1]; 656 657 /* compile the pattern */ 658 if ((egreperror = egrepinit(egreppat)) == NULL) { 659 660 /* search the files */ 661 for (i = 0; i < nsrcfiles; ++i) { 662 char *file = filepath(srcfiles[i]); 663 fileprogress(); 664 if (egrep(file, refsfound, "%s <unknown> %ld ") < 0) { 665 (void) sprintf(msg, "Cannot open file %s", 666 file); 667 putmsg2(msg); 668 } 669 } 670 } 671 return (egreperror); 672 } 673 674 /* find matching file names */ 675 676 void 677 findfile(void) 678 { 679 int i; 680 char *s; 681 682 for (i = 0; i < nsrcfiles; ++i) { 683 s = srcfiles[i]; 684 if (caseless == YES) { 685 s = strtolower(s); 686 } 687 if (regex(regexp, s) != NULL) { 688 (void) fprintf(refsfound, "%s <unknown> 1 <unknown>\n", 689 filepath(srcfiles[i])); 690 } 691 } 692 } 693 694 /* find files #including this file */ 695 696 void 697 findinclude(void) 698 { 699 char file[PATHLEN + 1]; /* source file name */ 700 701 if (invertedindex == YES) { 702 POSTING *p; 703 704 findterm(); 705 while ((p = getposting()) != NULL) { 706 if (p->type == INCLUDE) { 707 putpostingref(p); 708 } 709 } 710 return; 711 } 712 /* find the next file name or function definition */ 713 while (scanpast('\t') != NULL) { 714 switch (*blockp) { 715 716 case NEWFILE: /* save file name */ 717 skiprefchar(); 718 getstring(file); 719 if (*file == '\0') { /* if end of symbols */ 720 return; 721 } 722 fileprogress(); 723 break; 724 725 case INCLUDE: /* match function called to pattern */ 726 skiprefchar(); 727 /* skip global or local #include marker */ 728 skiprefchar(); 729 if (match()) { 730 /* output the file and source line */ 731 putref(file, ""); 732 } 733 } 734 } 735 } 736 737 /* initialize */ 738 739 FINDINIT 740 findinit(void) 741 { 742 char buf[PATLEN + 3]; 743 BOOL isregexp = NO; 744 int i; 745 char *s; 746 unsigned c; 747 748 /* remove trailing white space */ 749 for (s = pattern + strlen(pattern) - 1; isspace(*s); --s) { 750 *s = '\0'; 751 } 752 /* allow a partial match for a file name */ 753 if (field == FILENAME || field == INCLUDES) { 754 /* allow types.h to match #include <sys/types.h> */ 755 if (invertedindex == YES && field == INCLUDES && 756 strncmp(pattern, ".*", 2) != 0) { 757 (void) sprintf(pattern, ".*%s", strcpy(buf, pattern)); 758 } 759 if ((regexp = regcmp(pattern, (char *)NULL)) == NULL) { 760 return (REGCMPERROR); 761 } 762 return (NOERROR); 763 } 764 /* see if the pattern is a regular expression */ 765 if (strpbrk(pattern, "^.[{*+$") != NULL) { 766 isregexp = YES; 767 } else { 768 /* check for a valid C symbol */ 769 s = pattern; 770 if (!isalpha(*s) && *s != '_') { 771 return (NOTSYMBOL); 772 } 773 while (*++s != '\0') { 774 if (!isalnum(*s) && *s != '_') { 775 return (NOTSYMBOL); 776 } 777 } 778 /* 779 * look for use of the -T option (truncate symbol to 8 780 * characters) on a database not built with -T 781 */ 782 if (truncatesyms == YES && isuptodate == YES && 783 dbtruncated == NO && s - pattern >= 8) { 784 (void) strcpy(pattern + 8, ".*"); 785 isregexp = YES; 786 } 787 } 788 /* if this is a regular expression or letter case is to be ignored */ 789 /* or there is an inverted index */ 790 if (isregexp == YES || caseless == YES || invertedindex == YES) { 791 792 /* remove a leading ^ */ 793 s = pattern; 794 if (*s == '^') { 795 (void) strcpy(newpat, s + 1); 796 (void) strcpy(s, newpat); 797 } 798 /* remove a trailing $ */ 799 i = strlen(s) - 1; 800 if (s[i] == '$') { 801 s[i] = '\0'; 802 } 803 /* if requested, try to truncate a C symbol pattern */ 804 if (truncatesyms == YES && strpbrk(s, "[{*+") == NULL) { 805 s[8] = '\0'; 806 } 807 /* must be an exact match */ 808 /* 809 * note: regcmp doesn't recognize ^*keypad$ as an syntax error 810 * unless it is given as a single arg 811 */ 812 (void) sprintf(buf, "^%s$", s); 813 if ((regexp = regcmp(buf, (char *)NULL)) == NULL) { 814 return (REGCMPERROR); 815 } 816 } else { 817 /* if requested, truncate a C symbol pattern */ 818 if (truncatesyms == YES && field <= CALLING) { 819 pattern[8] = '\0'; 820 } 821 /* compress the string pattern for matching */ 822 s = cpattern; 823 for (i = 0; (c = pattern[i]) != '\0'; ++i) { 824 if (dicode1[c] && dicode2[(unsigned)pattern[i + 1]]) { 825 c = (0200 - 2) + dicode1[c] + 826 dicode2[(unsigned)pattern[i + 1]]; 827 ++i; 828 } 829 *s++ = (char)c; 830 } 831 *s = '\0'; 832 } 833 return (NOERROR); 834 } 835 836 void 837 findcleanup(void) 838 { 839 /* discard any regular expression */ 840 if (regexp != NULL) { 841 free(regexp); 842 regexp = NULL; 843 } 844 } 845 846 /* find this term, which can be a regular expression */ 847 848 static void 849 findterm(void) 850 { 851 char *s; 852 int len; 853 char prefix[PATLEN + 1]; 854 char term[PATLEN + 1]; 855 856 npostings = 0; /* will be non-zero after database built */ 857 lastfcnoffset = 0; /* clear the last function name found */ 858 boolclear(); /* clear the posting set */ 859 860 /* get the string prefix (if any) of the regular expression */ 861 (void) strcpy(prefix, pattern); 862 if ((s = strpbrk(prefix, ".[{*+")) != NULL) { 863 *s = '\0'; 864 } 865 /* if letter case is to be ignored */ 866 if (caseless == YES) { 867 868 /* 869 * convert the prefix to upper case because it is lexically 870 * less than lower case 871 */ 872 s = prefix; 873 while (*s != '\0') { 874 *s = toupper(*s); 875 ++s; 876 } 877 } 878 /* find the term lexically >= the prefix */ 879 (void) invfind(&invcontrol, prefix); 880 if (caseless == YES) { /* restore lower case */ 881 (void) strcpy(prefix, strtolower(prefix)); 882 } 883 /* 884 * a null prefix matches the null term in the inverted index, 885 * so move to the first real term 886 */ 887 if (*prefix == '\0') { 888 (void) invforward(&invcontrol); 889 } 890 len = strlen(prefix); 891 do { 892 (void) invterm(&invcontrol, term); /* get the term */ 893 s = term; 894 if (caseless == YES) { 895 s = strtolower(s); /* make it lower case */ 896 } 897 /* if it matches */ 898 if (regex(regexp, s) != NULL) { 899 /* add it's postings to the set */ 900 if ((postingp = boolfile(&invcontrol, 901 &npostings, OR)) == NULL) { 902 break; 903 } 904 } else if (len > 0) { 905 /* if there is a prefix */ 906 907 /* 908 * if ignoring letter case and the term is out of the 909 * range of possible matches 910 */ 911 if (caseless == YES) { 912 if (strncmp(term, prefix, len) > 0) { 913 break; /* stop searching */ 914 } 915 } 916 /* if using letter case and the prefix doesn't match */ 917 else if (strncmp(term, prefix, len) != 0) { 918 break; /* stop searching */ 919 } 920 } 921 /* display progress about every three seconds */ 922 if (++searchcount % 50 == 0) { 923 progress("%ld of %ld symbols matched", 924 searchcount, totalterms); 925 } 926 } while (invforward(&invcontrol)); /* while didn't wrap around */ 927 928 /* initialize the progress message for retrieving the references */ 929 initprogress(); 930 postingsfound = npostings; 931 } 932 933 /* display the file search progress about every three seconds */ 934 935 static void 936 fileprogress(void) 937 { 938 if (++searchcount % 10 == 0) { 939 progress("%ld of %ld files searched", searchcount, 940 (long)nsrcfiles); 941 } 942 } 943 944 /* initialize the progress message */ 945 946 void 947 initprogress(void) 948 { 949 searchcount = 0; 950 starttime = time((long *)NULL); 951 } 952 953 /* display the progress every three seconds */ 954 955 void 956 progress(char *format, long n1, long n2) 957 { 958 char msg[MSGLEN + 1]; 959 long now; 960 961 /* print after 2 seconds so the average is nearer 3 seconds */ 962 if (linemode == NO && (now = time((long *)NULL)) - starttime >= 2) { 963 starttime = now; 964 (void) sprintf(msg, format, n1, n2); 965 putmsg(msg); 966 } 967 } 968 969 /* match the pattern to the string */ 970 971 BOOL 972 match(void) 973 { 974 char string[PATLEN + 1]; 975 char *s; 976 977 /* see if this is a regular expression pattern */ 978 if (regexp != NULL) { 979 getstring(string); 980 if (*string == '\0') { 981 return (NO); 982 } 983 s = string; 984 if (caseless == YES) { 985 s = strtolower(s); 986 } 987 return (regex(regexp, s) ? YES : NO); 988 } 989 /* it is a string pattern */ 990 return ((BOOL)(*blockp == cpattern[0] && matchrest())); 991 } 992 993 /* match the rest of the pattern to the name */ 994 995 BOOL 996 matchrest(void) 997 { 998 int i = 1; 999 1000 skiprefchar(); 1001 do { 1002 while (*blockp == cpattern[i]) { 1003 ++blockp; 1004 ++i; 1005 } 1006 } while (*(blockp + 1) == '\0' && readblock() != NULL); 1007 1008 if (*blockp == '\n' && cpattern[i] == '\0') { 1009 return (YES); 1010 } 1011 return (NO); 1012 } 1013 1014 /* get the next posting for this term */ 1015 1016 static POSTING * 1017 getposting(void) 1018 { 1019 if (npostings-- <= 0) { 1020 return (NULL); 1021 } 1022 /* display progress about every three seconds */ 1023 if (++searchcount % 100 == 0) { 1024 progress("%ld of %ld possible references retrieved", 1025 searchcount, postingsfound); 1026 } 1027 return (postingp++); 1028 } 1029 1030 /* put the posting reference into the file */ 1031 1032 static void 1033 putpostingref(POSTING *p) 1034 { 1035 static char function[PATLEN + 1]; /* function name */ 1036 1037 if (p->fcnoffset == 0) { 1038 *function = '\0'; 1039 } else if (p->fcnoffset != lastfcnoffset) { 1040 if (dbseek(p->fcnoffset) != -1) { 1041 getstring(function); 1042 lastfcnoffset = p->fcnoffset; 1043 } 1044 } 1045 if (dbseek(p->lineoffset) != -1) { 1046 putref(srcfiles[p->fileindex], function); 1047 } 1048 } 1049 1050 /* put the reference into the file */ 1051 1052 static void 1053 putref(char *file, char *function) 1054 { 1055 FILE *output; 1056 1057 /* put global references first */ 1058 if (*function == '\0') { 1059 function = "<global>"; 1060 output = refsfound; 1061 } else { 1062 output = nonglobalrefs; 1063 } 1064 if (fprintf(output, "%s %s ", filepath(file), function) == EOF) { 1065 cannotwrite(temp1); 1066 /* NOTREACHED */ 1067 } 1068 putsource(output); 1069 } 1070 1071 /* put the source line into the file */ 1072 1073 static void 1074 putsource(FILE *output) 1075 { 1076 char *cp, nextc = '\0'; 1077 1078 if (fileversion <= 5) { 1079 (void) scanpast(' '); 1080 putline(output); 1081 (void) putc('\n', output); 1082 return; 1083 } 1084 /* scan back to the beginning of the source line */ 1085 cp = blockp; 1086 while (*cp != '\n' || nextc != '\n') { 1087 nextc = *cp; 1088 if (--cp < block) { 1089 /* read the previous block */ 1090 (void) dbseek((blocknumber - 1) * BUFSIZ); 1091 cp = &block[BUFSIZ - 1]; 1092 } 1093 } 1094 /* there must be a double newline followed by a line number */ 1095 blockp = cp; 1096 setmark(' '); /* so getrefchar doesn't skip the last block char */ 1097 if (*blockp != '\n' || getrefchar() != '\n' || 1098 !isdigit(getrefchar()) && fileversion >= 12) { 1099 putmsg("Internal error: cannot get source line from database"); 1100 myexit(1); 1101 } 1102 /* until a double newline is found */ 1103 do { 1104 /* skip a symbol type */ 1105 if (*blockp == '\t') { 1106 skiprefchar(); 1107 skiprefchar(); 1108 } 1109 /* output a piece of the source line */ 1110 putline(output); 1111 } while (blockp != NULL && getrefchar() != '\n'); 1112 (void) putc('\n', output); 1113 } 1114 1115 /* put the rest of the cross-reference line into the file */ 1116 1117 static void 1118 putline(FILE *output) 1119 { 1120 char *cp; 1121 unsigned c; 1122 1123 setmark('\n'); 1124 cp = blockp; 1125 do { 1126 while ((c = *cp) != '\n') { 1127 /* check for a compressed digraph */ 1128 if (c & 0200) { 1129 c &= 0177; 1130 (void) putc(dichar1[c / 8], output); 1131 (void) putc(dichar2[c & 7], output); 1132 } else if (c < ' ') { 1133 /* a compressed keyword */ 1134 (void) fputs(keyword[c].text, output); 1135 if (keyword[c].delim != '\0') { 1136 (void) putc(' ', output); 1137 } 1138 if (keyword[c].delim == '(') { 1139 (void) putc('(', output); 1140 } 1141 } else { 1142 (void) putc((int)c, output); 1143 } 1144 ++cp; 1145 } 1146 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL); 1147 blockp = cp; 1148 } 1149 1150 /* put the rest of the cross-reference line into the string */ 1151 1152 void 1153 getstring(char *s) 1154 { 1155 char *cp; 1156 unsigned c; 1157 1158 setmark('\n'); 1159 cp = blockp; 1160 do { 1161 while ((c = *cp) != '\n') { 1162 if (c & 0200) { 1163 c &= 0177; 1164 *s++ = dichar1[c / 8]; 1165 *s++ = dichar2[c & 7]; 1166 } else { 1167 *s++ = (char)c; 1168 } 1169 ++cp; 1170 } 1171 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL); 1172 blockp = cp; 1173 *s = '\0'; 1174 } 1175 1176 /* scan past the next occurence of this character in the cross-reference */ 1177 1178 char * 1179 scanpast(int c) 1180 { 1181 char *cp; 1182 1183 setmark(c); 1184 cp = blockp; 1185 do { /* innermost loop optimized to only one test */ 1186 while (*cp != c) { 1187 ++cp; 1188 } 1189 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL); 1190 blockp = cp; 1191 if (cp != NULL) { 1192 skiprefchar(); /* skip the found character */ 1193 } 1194 return (blockp); 1195 } 1196 1197 /* read a block of the cross-reference */ 1198 1199 char * 1200 readblock(void) 1201 { 1202 /* read the next block */ 1203 blocklen = read(symrefs, block, BUFSIZ); 1204 blockp = block; 1205 1206 /* add the search character and end-of-block mark */ 1207 block[blocklen] = blockmark; 1208 block[blocklen + 1] = '\0'; 1209 1210 /* return NULL on end-of-file */ 1211 if (blocklen == 0) { 1212 blockp = NULL; 1213 } else { 1214 ++blocknumber; 1215 } 1216 return (blockp); 1217 } 1218 1219 /* seek to the database offset */ 1220 1221 long 1222 dbseek(long offset) 1223 { 1224 long n; 1225 int rc = 0; 1226 1227 if ((n = offset / BUFSIZ) != blocknumber) { 1228 if ((rc = lseek(symrefs, n * BUFSIZ, 0)) == -1) { 1229 myperror("Lseek failed"); 1230 (void) sleep(3); 1231 return (rc); 1232 } 1233 (void) readblock(); 1234 blocknumber = n; 1235 } 1236 blockp = block + offset % BUFSIZ; 1237 return (rc); 1238 } 1239 1240 /* convert the string to lower case */ 1241 1242 static char * 1243 strtolower(char *s) 1244 { 1245 static char buf[PATLEN + 1]; 1246 char *lp = buf; 1247 1248 while (*s != '\0') { 1249 /* 1250 * note: s in not incremented in this line because the BSD 1251 * compatibility tolower macro evaluates its argument twice 1252 */ 1253 *lp++ = tolower(*s); 1254 ++s; 1255 } 1256 *lp = '\0'; 1257 return (buf); 1258 } 1259 1260 /* if needed, convert a relative path to a full path */ 1261 1262 static char * 1263 filepath(char *file) 1264 { 1265 static char path[PATHLEN + 1]; 1266 int i; 1267 1268 if (*file != '/') { 1269 1270 /* if same file as last time, return the same path */ 1271 if (strequal(file, lastfilepath)) { 1272 return (path); 1273 } 1274 (void) strcpy(lastfilepath, file); 1275 1276 /* if requested, prepend a path to a relative file path */ 1277 if (prependpath != NULL) { 1278 (void) sprintf(path, "%s/%s", prependpath, file); 1279 return (path); 1280 } 1281 /* 1282 * if the database was built with a view path, return a 1283 * full path so "cscope -d -f" does not have to be called 1284 * from the build directory with the same view path 1285 */ 1286 if (dbvpndirs > 1) { 1287 for (i = 0; i < dbvpndirs; i++) { 1288 (void) sprintf(path, 1289 "%s/%s", dbvpdirs[i], file); 1290 if (access(path, READ) != -1) { 1291 return (path); 1292 } 1293 } 1294 } 1295 (void) strcpy(path, file); /* for lastfilepath check */ 1296 } 1297 return (file); 1298 } 1299