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
findsymbol(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
finddef(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
findallfcns(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
findcalledby(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
findcalledbysub(char * file)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
findcalling(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
findassignments(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 *
findgreppat(void)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 *
findegreppat(char * egreppat)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
findfile(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
findinclude(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
findinit(void)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
findcleanup(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
findterm(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
fileprogress(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
initprogress(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
progress(char * format,long n1,long n2)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
match(void)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
matchrest(void)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 *
getposting(void)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
putpostingref(POSTING * p)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
putref(char * file,char * function)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
putsource(FILE * output)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
putline(FILE * output)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
getstring(char * s)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 *
scanpast(int c)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 *
readblock(void)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
dbseek(long offset)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 *
strtolower(char * s)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 *
filepath(char * file)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