8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / tools / cscope-fast / find.c
blob3db8298fac5007ba087017361c259fbe0114ede7
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
22 /* Copyright (c) 1988 AT&T */
23 /* All Rights Reserved */
27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
31 #pragma ident "%Z%%M% %I% %E% SMI"
34 * cscope - interactive C symbol or text cross-reference
36 * searching functions
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <libgen.h>
42 #include "global.h"
43 #include "vp.h"
46 * most of these functions have been optimized so their innermost loops have
47 * only one test for the desired character by putting the char and
48 * an end-of-block marker (\0) at the end of the disk block buffer.
49 * When the inner loop exits on the char, an outer loop will see if
50 * the char is followed by a \0. If so, it will read the next block
51 * and restart the inner loop.
54 char block[BUFSIZ + 2]; /* leave room for end-of-block mark */
55 int blocklen; /* length of disk block read */
56 char blockmark; /* mark character to be searched for */
57 long blocknumber; /* block number */
58 char *blockp; /* pointer to current char in block */
59 char lastfilepath[PATHLEN + 1]; /* last file that full path was */
60 /* computed for */
62 static char cpattern[PATLEN + 1]; /* compressed pattern */
63 static long lastfcnoffset; /* last function name offset */
64 static long postingsfound; /* retrieved number of postings */
65 static char *regexp; /* regular expression */
66 static POSTING *postingp; /* retrieved posting set pointer */
67 static long searchcount; /* count of files searched */
68 static long starttime; /* start time for progress messages */
70 static POSTING *getposting(void);
71 static void putsource(FILE *output);
72 static void putref(char *file, char *function);
73 static void findcalledbysub(char *file);
74 static void findterm(void);
75 static void fileprogress(void);
76 static void putpostingref(POSTING *p);
77 static void putline(FILE *output);
78 static char *strtolower(char *s);
79 static char *filepath(char *file);
81 /* find the symbol in the cross-reference */
83 void
84 findsymbol(void)
86 char file[PATHLEN + 1]; /* source file name */
87 char function[PATLEN + 1]; /* function name */
88 char macro[PATLEN + 1]; /* macro name */
89 char symbol[PATLEN + 1]; /* symbol name */
90 char *cp;
91 char c;
92 char *s;
94 if (invertedindex == YES) {
95 long lastline = 0;
96 POSTING *p;
98 findterm();
99 while ((p = getposting()) != NULL) {
100 if (p->type != INCLUDE && p->lineoffset != lastline) {
101 putpostingref(p);
102 lastline = p->lineoffset;
105 return;
107 (void) scanpast('\t'); /* find the end of the header */
108 skiprefchar(); /* skip the file marker */
109 getstring(file); /* save the file name */
110 *function = '\0';
111 /* a macro can be inside a function, but not vice versa */
112 *macro = '\0';
114 /* find the next symbol */
115 /* note: this code was expanded in-line for speed */
116 /* while (scanpast('\n') != NULL) { */
117 /* other macros were replaced by code using cp instead of blockp */
118 cp = blockp;
119 for (;;) {
120 setmark('\n');
121 do { /* innermost loop optimized to only one test */
122 while (*cp != '\n') {
123 ++cp;
125 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL);
127 /* skip the found character */
128 if (cp != NULL && *(++cp + 1) == '\0') {
129 cp = readblock();
131 if (cp == NULL) {
132 break;
134 /* look for a source file or function name */
135 if (*cp == '\t') {
136 blockp = cp;
137 switch (getrefchar()) {
139 case NEWFILE: /* file name */
141 /* save the name */
142 skiprefchar();
143 getstring(file);
145 /* check for the end of the symbols */
146 if (*file == '\0') {
147 return;
149 fileprogress();
150 /* FALLTHROUGH */
152 case FCNEND: /* function end */
153 *function = '\0';
154 goto notmatched; /* don't match name */
156 case FCNDEF: /* function name */
157 s = function;
158 break;
160 case DEFINE: /* could be a macro */
161 if (fileversion >= 10) {
162 s = macro;
163 } else {
164 s = symbol;
166 break;
168 case DEFINEEND:
169 *macro = '\0';
170 goto notmatched; /* don't match name */
172 case INCLUDE: /* #include file */
173 goto notmatched; /* don't match name */
174 default: /* other symbol */
175 s = symbol;
177 /* save the name */
178 skiprefchar();
179 getstring(s);
181 /* see if this is a regular expression pattern */
182 if (regexp != NULL) {
183 if (caseless == YES) {
184 s = strtolower(s);
186 if (*s != '\0' && regex(regexp, s) != NULL) {
187 goto matched;
190 /* match the symbol to the text pattern */
191 else if (strequal(pattern, s)) {
192 goto matched;
194 goto notmatched;
196 /* if this is a regular expression pattern */
197 if (regexp != NULL) {
198 c = *cp;
199 if (c & 0200) { /* digraph char? */
200 c = dichar1[(c & 0177) / 8];
202 /* if this is a symbol */
203 if (isalpha(c) || c == '_') {
204 blockp = cp;
205 getstring(symbol);
206 s = symbol;
207 if (caseless == YES) {
208 s = strtolower(s);
210 /* match the symbol to the regular expression */
211 if (regex(regexp, s) != NULL) {
212 goto matched;
214 goto notmatched;
217 /* match the character to the text pattern */
218 else if (*cp == cpattern[0]) {
219 blockp = cp;
221 /* match the rest of the symbol to the text pattern */
222 if (matchrest()) {
223 s = NULL;
224 matched:
226 * output the file, calling function or macro,
227 * and source line
229 if (*macro != '\0' && s != macro) {
230 putref(file, macro);
231 } else if (s != function) {
232 putref(file, function);
233 } else {
234 putref(file, "");
236 if (blockp == NULL) {
237 return;
240 notmatched:
241 cp = blockp;
244 blockp = cp;
247 /* find the function definition or #define */
249 void
250 finddef(void)
252 char file[PATHLEN + 1]; /* source file name */
253 char function[PATLEN + 1]; /* function name */
254 char macro[PATLEN + 1]; /* macro name */
255 char symbol[PATLEN + 1]; /* symbol name */
256 char *s;
258 if (invertedindex == YES) {
259 POSTING *p;
261 findterm();
262 while ((p = getposting()) != NULL) {
263 switch (p->type) {
264 case DEFINE: /* could be a macro */
265 case FCNDEF:
266 case CLASSDEF:
267 case ENUMDEF:
268 case MEMBERDEF:
269 case STRUCTDEF:
270 case TYPEDEF:
271 case UNIONDEF:
272 case GLOBALDEF: /* other global definition */
273 case LOCALDEF: /* other local definition */
274 case PARAMETER:
275 putpostingref(p);
278 return;
280 /* find the next file name or definition */
281 *function = '\0';
282 /* a macro can be inside a function, but not vice versa */
283 *macro = '\0';
285 while (scanpast('\t') != NULL) {
286 switch (*blockp) {
288 case NEWFILE:
289 skiprefchar(); /* save file name */
290 getstring(file);
291 if (*file == '\0') { /* if end of symbols */
292 return;
294 fileprogress();
295 /* FALLTHROUGH */
297 case FCNEND: /* function end */
298 *function = '\0';
299 break;
301 case FCNDEF: /* function name */
302 s = function;
303 goto def;
305 case DEFINE: /* could be a macro */
306 if (fileversion >= 10) {
307 s = macro;
308 } else {
309 s = symbol;
311 goto def;
313 case DEFINEEND:
314 *macro = '\0';
315 break;
317 case CLASSDEF:
318 case ENUMDEF:
319 case MEMBERDEF:
320 case STRUCTDEF:
321 case TYPEDEF:
322 case UNIONDEF:
323 case GLOBALDEF: /* other global definition */
324 case LOCALDEF: /* other local definition */
325 case PARAMETER:
326 s = symbol;
327 def:
328 /* save the name */
329 skiprefchar();
330 getstring(s);
332 /* see if this is a regular expression pattern */
333 if (regexp != NULL) {
334 if (caseless == YES) {
335 s = strtolower(s);
337 if (*s != '\0' && regex(regexp, s) != NULL) {
338 goto matched;
340 } else if (strequal(pattern, s)) {
341 /* match the symbol to the text pattern */
342 matched:
344 * output the file, calling function or macro,
345 * and source line
347 if (*macro != '\0' && s != macro) {
348 putref(file, macro);
349 } else if (s != function) {
350 putref(file, function);
351 } else {
352 putref(file, "");
359 /* find all function definitions (used by samuel only) */
361 void
362 findallfcns(void)
364 char file[PATHLEN + 1]; /* source file name */
365 char function[PATLEN + 1]; /* function name */
367 /* find the next file name or definition */
368 while (scanpast('\t') != NULL) {
369 switch (*blockp) {
370 case NEWFILE:
371 skiprefchar(); /* save file name */
372 getstring(file);
373 if (*file == '\0') { /* if end of symbols */
374 return;
376 fileprogress();
377 break;
379 case FCNDEF:
380 case CLASSDEF:
381 skiprefchar(); /* save function name */
382 getstring(function);
384 /* output the file, function and source line */
385 putref(file, function);
386 break;
391 /* find the functions called by this function */
393 void
394 findcalledby(void)
396 char file[PATHLEN + 1]; /* source file name */
398 if (invertedindex == YES) {
399 POSTING *p;
401 findterm();
402 while ((p = getposting()) != NULL) {
403 switch (p->type) {
404 case DEFINE: /* could be a macro */
405 case FCNDEF:
406 if (dbseek(p->lineoffset) != -1 &&
407 scanpast('\t') != NULL) { /* skip def */
408 findcalledbysub(srcfiles[p->fileindex]);
412 return;
414 /* find the function definition(s) */
415 while (scanpast('\t') != NULL) {
416 switch (*blockp) {
417 case NEWFILE:
418 skiprefchar(); /* save file name */
419 getstring(file);
420 if (*file == '\0') { /* if end of symbols */
421 return;
423 fileprogress();
424 break;
426 case DEFINE: /* could be a macro */
427 if (fileversion < 10) {
428 break;
430 /* FALLTHROUGH */
432 case FCNDEF:
433 skiprefchar(); /* match name to pattern */
434 if (match()) {
435 findcalledbysub(file);
437 break;
442 static void
443 findcalledbysub(char *file)
445 /* find the next function call or the end of this function */
446 while (scanpast('\t') != NULL) {
447 switch (*blockp) {
448 case DEFINE: /* #define inside a function */
449 if (fileversion >= 10) { /* skip it */
450 while (scanpast('\t') != NULL &&
451 *blockp != DEFINEEND)
454 break;
455 case FCNCALL: /* function call */
457 /* output the file name */
458 (void) fprintf(refsfound, "%s ", filepath(file));
460 /* output the function name */
461 skiprefchar();
462 putline(refsfound);
463 (void) putc(' ', refsfound);
465 /* output the source line */
466 putsource(refsfound);
467 break;
469 case DEFINEEND: /* #define end */
470 case FCNEND: /* function end */
471 case FCNDEF: /* function end (pre 9.5) */
472 case NEWFILE: /* file end */
473 return;
478 /* find the functions calling this function */
480 void
481 findcalling(void)
483 char file[PATHLEN + 1]; /* source file name */
484 char function[PATLEN + 1]; /* function name */
485 char macro[PATLEN + 1]; /* macro name */
487 if (invertedindex == YES) {
488 POSTING *p;
490 findterm();
491 while ((p = getposting()) != NULL) {
492 if (p->type == FCNCALL) {
493 putpostingref(p);
496 return;
498 /* find the next file name or function definition */
499 /* a macro can be inside a function, but not vice versa */
500 *macro = '\0';
502 while (scanpast('\t') != NULL) {
503 switch (*blockp) {
504 case NEWFILE: /* save file name */
505 skiprefchar();
506 getstring(file);
507 if (*file == '\0') { /* if end of symbols */
508 return;
510 fileprogress();
511 /* FALLTHROUGH */
512 case FCNEND: /* function end */
513 *function = '\0';
514 break;
515 case DEFINE: /* could be a macro */
516 if (fileversion >= 10) {
517 skiprefchar();
518 getstring(macro);
520 break;
522 case DEFINEEND:
523 *macro = '\0';
524 break;
526 case FCNDEF: /* save calling function name */
527 skiprefchar();
528 getstring(function);
529 break;
530 case FCNCALL: /* match function called to pattern */
531 skiprefchar();
532 if (match()) {
533 /* output the file, calling function or */
534 /* macro, and source */
535 if (*macro != '\0') {
536 putref(file, macro);
537 } else {
538 putref(file, function);
545 /* find direct assignment to, and increment and decrement of, this variable */
547 void
548 findassignments(void)
550 char file[PATHLEN + 1]; /* source file name */
551 char function[PATLEN + 1]; /* function name */
552 char macro[PATLEN + 1]; /* macro name */
554 if (fileversion < 13) {
555 putmsg("Database built with cscope version < 13 does not "
556 "have assignment information");
557 (void) sleep(3);
558 return;
560 #if CTRACE
561 ctroff();
562 #endif
563 if (invertedindex == YES) {
564 POSTING *p;
566 findterm();
567 while ((p = getposting()) != NULL) {
568 switch (p->type) {
569 case ASSIGNMENT:
570 case GLOBALDEF: /* can have initializer */
571 case LOCALDEF: /* can have initializer */
572 case PARAMETER: /* initial value */
573 putpostingref(p);
576 return;
578 /* find the next file name or function definition */
579 /* a macro can be inside a function, but not vice versa */
580 *macro = '\0';
582 while (scanpast('\t') != NULL) {
583 switch (*blockp) {
584 case NEWFILE: /* save file name */
585 skiprefchar();
586 getstring(file);
587 if (*file == '\0') { /* if end of symbols */
588 return;
590 fileprogress();
591 /* FALLTHROUGH */
592 case FCNEND: /* function end */
593 *function = '\0';
594 break;
595 case DEFINE: /* could be a macro */
596 if (fileversion >= 10) {
597 skiprefchar();
598 getstring(macro);
600 break;
602 case DEFINEEND:
603 *macro = '\0';
604 break;
606 case FCNDEF: /* save calling function name */
607 skiprefchar();
608 getstring(function);
609 break;
610 case ASSIGNMENT: /* match assignment to pattern */
611 case GLOBALDEF: /* can have initializer */
612 case LOCALDEF: /* can have initializer */
613 case PARAMETER: /* initial value */
614 skiprefchar();
615 if (match()) {
616 /* output the file, calling function or */
617 /* macro, and source */
618 if (*macro != '\0') {
619 putref(file, macro);
620 } else {
621 putref(file, function);
628 /* find the grep pattern in the source files */
630 char *
631 findgreppat(void)
633 char egreppat[2 * PATLEN];
634 char *cp, *pp;
636 /* translate egrep special characters in the regular expression */
637 cp = egreppat;
638 for (pp = pattern; *pp != '\0'; ++pp) {
639 if (strchr("+?|()", *pp) != NULL) {
640 *cp++ = '\\';
642 *cp++ = *pp;
644 *cp = '\0';
646 /* search the source files */
647 return (findegreppat(egreppat));
650 /* find this regular expression in the source files */
652 char *
653 findegreppat(char *egreppat)
655 int i;
656 char *egreperror;
657 char msg[MSGLEN + 1];
659 /* compile the pattern */
660 if ((egreperror = egrepinit(egreppat)) == NULL) {
662 /* search the files */
663 for (i = 0; i < nsrcfiles; ++i) {
664 char *file = filepath(srcfiles[i]);
665 fileprogress();
666 if (egrep(file, refsfound, "%s <unknown> %ld ") < 0) {
667 (void) sprintf(msg, "Cannot open file %s",
668 file);
669 putmsg2(msg);
673 return (egreperror);
676 /* find matching file names */
678 void
679 findfile(void)
681 int i;
682 char *s;
684 for (i = 0; i < nsrcfiles; ++i) {
685 s = srcfiles[i];
686 if (caseless == YES) {
687 s = strtolower(s);
689 if (regex(regexp, s) != NULL) {
690 (void) fprintf(refsfound, "%s <unknown> 1 <unknown>\n",
691 filepath(srcfiles[i]));
696 /* find files #including this file */
698 void
699 findinclude(void)
701 char file[PATHLEN + 1]; /* source file name */
703 if (invertedindex == YES) {
704 POSTING *p;
706 findterm();
707 while ((p = getposting()) != NULL) {
708 if (p->type == INCLUDE) {
709 putpostingref(p);
712 return;
714 /* find the next file name or function definition */
715 while (scanpast('\t') != NULL) {
716 switch (*blockp) {
718 case NEWFILE: /* save file name */
719 skiprefchar();
720 getstring(file);
721 if (*file == '\0') { /* if end of symbols */
722 return;
724 fileprogress();
725 break;
727 case INCLUDE: /* match function called to pattern */
728 skiprefchar();
729 /* skip global or local #include marker */
730 skiprefchar();
731 if (match()) {
732 /* output the file and source line */
733 putref(file, "");
739 /* initialize */
741 FINDINIT
742 findinit(void)
744 char buf[PATLEN + 3];
745 BOOL isregexp = NO;
746 int i;
747 char *s;
748 unsigned c;
750 /* remove trailing white space */
751 for (s = pattern + strlen(pattern) - 1; isspace(*s); --s) {
752 *s = '\0';
754 /* allow a partial match for a file name */
755 if (field == FILENAME || field == INCLUDES) {
756 /* allow types.h to match #include <sys/types.h> */
757 if (invertedindex == YES && field == INCLUDES &&
758 strncmp(pattern, ".*", 2) != 0) {
759 (void) sprintf(pattern, ".*%s", strcpy(buf, pattern));
761 if ((regexp = regcmp(pattern, (char *)NULL)) == NULL) {
762 return (REGCMPERROR);
764 return (NOERROR);
766 /* see if the pattern is a regular expression */
767 if (strpbrk(pattern, "^.[{*+$") != NULL) {
768 isregexp = YES;
769 } else {
770 /* check for a valid C symbol */
771 s = pattern;
772 if (!isalpha(*s) && *s != '_') {
773 return (NOTSYMBOL);
775 while (*++s != '\0') {
776 if (!isalnum(*s) && *s != '_') {
777 return (NOTSYMBOL);
781 * look for use of the -T option (truncate symbol to 8
782 * characters) on a database not built with -T
784 if (truncatesyms == YES && isuptodate == YES &&
785 dbtruncated == NO && s - pattern >= 8) {
786 (void) strcpy(pattern + 8, ".*");
787 isregexp = YES;
790 /* if this is a regular expression or letter case is to be ignored */
791 /* or there is an inverted index */
792 if (isregexp == YES || caseless == YES || invertedindex == YES) {
794 /* remove a leading ^ */
795 s = pattern;
796 if (*s == '^') {
797 (void) strcpy(newpat, s + 1);
798 (void) strcpy(s, newpat);
800 /* remove a trailing $ */
801 i = strlen(s) - 1;
802 if (s[i] == '$') {
803 s[i] = '\0';
805 /* if requested, try to truncate a C symbol pattern */
806 if (truncatesyms == YES && strpbrk(s, "[{*+") == NULL) {
807 s[8] = '\0';
809 /* must be an exact match */
811 * note: regcmp doesn't recognize ^*keypad$ as an syntax error
812 * unless it is given as a single arg
814 (void) sprintf(buf, "^%s$", s);
815 if ((regexp = regcmp(buf, (char *)NULL)) == NULL) {
816 return (REGCMPERROR);
818 } else {
819 /* if requested, truncate a C symbol pattern */
820 if (truncatesyms == YES && field <= CALLING) {
821 pattern[8] = '\0';
823 /* compress the string pattern for matching */
824 s = cpattern;
825 for (i = 0; (c = pattern[i]) != '\0'; ++i) {
826 if (dicode1[c] && dicode2[(unsigned)pattern[i + 1]]) {
827 c = (0200 - 2) + dicode1[c] +
828 dicode2[(unsigned)pattern[i + 1]];
829 ++i;
831 *s++ = (char)c;
833 *s = '\0';
835 return (NOERROR);
838 void
839 findcleanup(void)
841 /* discard any regular expression */
842 if (regexp != NULL) {
843 free(regexp);
844 regexp = NULL;
848 /* find this term, which can be a regular expression */
850 static void
851 findterm(void)
853 char *s;
854 int len;
855 char prefix[PATLEN + 1];
856 char term[PATLEN + 1];
858 npostings = 0; /* will be non-zero after database built */
859 lastfcnoffset = 0; /* clear the last function name found */
860 boolclear(); /* clear the posting set */
862 /* get the string prefix (if any) of the regular expression */
863 (void) strcpy(prefix, pattern);
864 if ((s = strpbrk(prefix, ".[{*+")) != NULL) {
865 *s = '\0';
867 /* if letter case is to be ignored */
868 if (caseless == YES) {
871 * convert the prefix to upper case because it is lexically
872 * less than lower case
874 s = prefix;
875 while (*s != '\0') {
876 *s = toupper(*s);
877 ++s;
880 /* find the term lexically >= the prefix */
881 (void) invfind(&invcontrol, prefix);
882 if (caseless == YES) { /* restore lower case */
883 (void) strcpy(prefix, strtolower(prefix));
886 * a null prefix matches the null term in the inverted index,
887 * so move to the first real term
889 if (*prefix == '\0') {
890 (void) invforward(&invcontrol);
892 len = strlen(prefix);
893 do {
894 (void) invterm(&invcontrol, term); /* get the term */
895 s = term;
896 if (caseless == YES) {
897 s = strtolower(s); /* make it lower case */
899 /* if it matches */
900 if (regex(regexp, s) != NULL) {
901 /* add it's postings to the set */
902 if ((postingp = boolfile(&invcontrol,
903 &npostings, OR)) == NULL) {
904 break;
906 } else if (len > 0) {
907 /* if there is a prefix */
910 * if ignoring letter case and the term is out of the
911 * range of possible matches
913 if (caseless == YES) {
914 if (strncmp(term, prefix, len) > 0) {
915 break; /* stop searching */
918 /* if using letter case and the prefix doesn't match */
919 else if (strncmp(term, prefix, len) != 0) {
920 break; /* stop searching */
923 /* display progress about every three seconds */
924 if (++searchcount % 50 == 0) {
925 progress("%ld of %ld symbols matched",
926 searchcount, totalterms);
928 } while (invforward(&invcontrol)); /* while didn't wrap around */
930 /* initialize the progress message for retrieving the references */
931 initprogress();
932 postingsfound = npostings;
935 /* display the file search progress about every three seconds */
937 static void
938 fileprogress(void)
940 if (++searchcount % 10 == 0) {
941 progress("%ld of %ld files searched", searchcount,
942 (long)nsrcfiles);
946 /* initialize the progress message */
948 void
949 initprogress(void)
951 searchcount = 0;
952 starttime = time((long *)NULL);
955 /* display the progress every three seconds */
957 void
958 progress(char *format, long n1, long n2)
960 char msg[MSGLEN + 1];
961 long now;
963 /* print after 2 seconds so the average is nearer 3 seconds */
964 if (linemode == NO && (now = time((long *)NULL)) - starttime >= 2) {
965 starttime = now;
966 (void) sprintf(msg, format, n1, n2);
967 putmsg(msg);
971 /* match the pattern to the string */
973 BOOL
974 match(void)
976 char string[PATLEN + 1];
977 char *s;
979 /* see if this is a regular expression pattern */
980 if (regexp != NULL) {
981 getstring(string);
982 if (*string == '\0') {
983 return (NO);
985 s = string;
986 if (caseless == YES) {
987 s = strtolower(s);
989 return (regex(regexp, s) ? YES : NO);
991 /* it is a string pattern */
992 return ((BOOL)(*blockp == cpattern[0] && matchrest()));
995 /* match the rest of the pattern to the name */
997 BOOL
998 matchrest(void)
1000 int i = 1;
1002 skiprefchar();
1003 do {
1004 while (*blockp == cpattern[i]) {
1005 ++blockp;
1006 ++i;
1008 } while (*(blockp + 1) == '\0' && readblock() != NULL);
1010 if (*blockp == '\n' && cpattern[i] == '\0') {
1011 return (YES);
1013 return (NO);
1016 /* get the next posting for this term */
1018 static POSTING *
1019 getposting(void)
1021 if (npostings-- <= 0) {
1022 return (NULL);
1024 /* display progress about every three seconds */
1025 if (++searchcount % 100 == 0) {
1026 progress("%ld of %ld possible references retrieved",
1027 searchcount, postingsfound);
1029 return (postingp++);
1032 /* put the posting reference into the file */
1034 static void
1035 putpostingref(POSTING *p)
1037 static char function[PATLEN + 1]; /* function name */
1039 if (p->fcnoffset == 0) {
1040 *function = '\0';
1041 } else if (p->fcnoffset != lastfcnoffset) {
1042 if (dbseek(p->fcnoffset) != -1) {
1043 getstring(function);
1044 lastfcnoffset = p->fcnoffset;
1047 if (dbseek(p->lineoffset) != -1) {
1048 putref(srcfiles[p->fileindex], function);
1052 /* put the reference into the file */
1054 static void
1055 putref(char *file, char *function)
1057 FILE *output;
1059 /* put global references first */
1060 if (*function == '\0') {
1061 function = "<global>";
1062 output = refsfound;
1063 } else {
1064 output = nonglobalrefs;
1066 if (fprintf(output, "%s %s ", filepath(file), function) == EOF) {
1067 cannotwrite(temp1);
1068 /* NOTREACHED */
1070 putsource(output);
1073 /* put the source line into the file */
1075 static void
1076 putsource(FILE *output)
1078 char *cp, nextc = '\0';
1080 if (fileversion <= 5) {
1081 (void) scanpast(' ');
1082 putline(output);
1083 (void) putc('\n', output);
1084 return;
1086 /* scan back to the beginning of the source line */
1087 cp = blockp;
1088 while (*cp != '\n' || nextc != '\n') {
1089 nextc = *cp;
1090 if (--cp < block) {
1091 /* read the previous block */
1092 (void) dbseek((blocknumber - 1) * BUFSIZ);
1093 cp = &block[BUFSIZ - 1];
1096 /* there must be a double newline followed by a line number */
1097 blockp = cp;
1098 setmark(' '); /* so getrefchar doesn't skip the last block char */
1099 if (*blockp != '\n' || getrefchar() != '\n' ||
1100 !isdigit(getrefchar()) && fileversion >= 12) {
1101 putmsg("Internal error: cannot get source line from database");
1102 myexit(1);
1104 /* until a double newline is found */
1105 do {
1106 /* skip a symbol type */
1107 if (*blockp == '\t') {
1108 skiprefchar();
1109 skiprefchar();
1111 /* output a piece of the source line */
1112 putline(output);
1113 } while (blockp != NULL && getrefchar() != '\n');
1114 (void) putc('\n', output);
1117 /* put the rest of the cross-reference line into the file */
1119 static void
1120 putline(FILE *output)
1122 char *cp;
1123 unsigned c;
1125 setmark('\n');
1126 cp = blockp;
1127 do {
1128 while ((c = *cp) != '\n') {
1129 /* check for a compressed digraph */
1130 if (c & 0200) {
1131 c &= 0177;
1132 (void) putc(dichar1[c / 8], output);
1133 (void) putc(dichar2[c & 7], output);
1134 } else if (c < ' ') {
1135 /* a compressed keyword */
1136 (void) fputs(keyword[c].text, output);
1137 if (keyword[c].delim != '\0') {
1138 (void) putc(' ', output);
1140 if (keyword[c].delim == '(') {
1141 (void) putc('(', output);
1143 } else {
1144 (void) putc((int)c, output);
1146 ++cp;
1148 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL);
1149 blockp = cp;
1152 /* put the rest of the cross-reference line into the string */
1154 void
1155 getstring(char *s)
1157 char *cp;
1158 unsigned c;
1160 setmark('\n');
1161 cp = blockp;
1162 do {
1163 while ((c = *cp) != '\n') {
1164 if (c & 0200) {
1165 c &= 0177;
1166 *s++ = dichar1[c / 8];
1167 *s++ = dichar2[c & 7];
1168 } else {
1169 *s++ = (char)c;
1171 ++cp;
1173 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL);
1174 blockp = cp;
1175 *s = '\0';
1178 /* scan past the next occurence of this character in the cross-reference */
1180 char *
1181 scanpast(int c)
1183 char *cp;
1185 setmark(c);
1186 cp = blockp;
1187 do { /* innermost loop optimized to only one test */
1188 while (*cp != c) {
1189 ++cp;
1191 } while (*(cp + 1) == '\0' && (cp = readblock()) != NULL);
1192 blockp = cp;
1193 if (cp != NULL) {
1194 skiprefchar(); /* skip the found character */
1196 return (blockp);
1199 /* read a block of the cross-reference */
1201 char *
1202 readblock(void)
1204 /* read the next block */
1205 blocklen = read(symrefs, block, BUFSIZ);
1206 blockp = block;
1208 /* add the search character and end-of-block mark */
1209 block[blocklen] = blockmark;
1210 block[blocklen + 1] = '\0';
1212 /* return NULL on end-of-file */
1213 if (blocklen == 0) {
1214 blockp = NULL;
1215 } else {
1216 ++blocknumber;
1218 return (blockp);
1221 /* seek to the database offset */
1223 long
1224 dbseek(long offset)
1226 long n;
1227 int rc = 0;
1229 if ((n = offset / BUFSIZ) != blocknumber) {
1230 if ((rc = lseek(symrefs, n * BUFSIZ, 0)) == -1) {
1231 myperror("Lseek failed");
1232 (void) sleep(3);
1233 return (rc);
1235 (void) readblock();
1236 blocknumber = n;
1238 blockp = block + offset % BUFSIZ;
1239 return (rc);
1242 /* convert the string to lower case */
1244 static char *
1245 strtolower(char *s)
1247 static char buf[PATLEN + 1];
1248 char *lp = buf;
1250 while (*s != '\0') {
1252 * note: s in not incremented in this line because the BSD
1253 * compatibility tolower macro evaluates its argument twice
1255 *lp++ = tolower(*s);
1256 ++s;
1258 *lp = '\0';
1259 return (buf);
1262 /* if needed, convert a relative path to a full path */
1264 static char *
1265 filepath(char *file)
1267 static char path[PATHLEN + 1];
1268 int i;
1270 if (*file != '/') {
1272 /* if same file as last time, return the same path */
1273 if (strequal(file, lastfilepath)) {
1274 return (path);
1276 (void) strcpy(lastfilepath, file);
1278 /* if requested, prepend a path to a relative file path */
1279 if (prependpath != NULL) {
1280 (void) sprintf(path, "%s/%s", prependpath, file);
1281 return (path);
1284 * if the database was built with a view path, return a
1285 * full path so "cscope -d -f" does not have to be called
1286 * from the build directory with the same view path
1288 if (dbvpndirs > 1) {
1289 for (i = 0; i < dbvpndirs; i++) {
1290 (void) sprintf(path,
1291 "%s/%s", dbvpdirs[i], file);
1292 if (access(path, READ) != -1) {
1293 return (path);
1297 (void) strcpy(path, file); /* for lastfilepath check */
1299 return (file);