From b37e0da0b7dc72ddfa513e319ca71b5f5b8aeb7d Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 13 Nov 2006 22:13:33 +0100 Subject: Initial import --- src/find.c | 1219 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1219 insertions(+) create mode 100644 src/find.c (limited to 'src/find.c') diff --git a/src/find.c b/src/find.c new file mode 100644 index 0000000..f6a6387 --- /dev/null +++ b/src/find.c @@ -0,0 +1,1219 @@ +/*=========================================================================== + Copyright (c) 1998-2000, The Santa Cruz Operation + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + *Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + *Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + *Neither name of The Santa Cruz Operation nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGE. + =========================================================================*/ + +/* cscope - interactive C symbol or text cross-reference + * + * searching functions + */ + +#include "global.h" + +#include "build.h" +#include "scanner.h" /* for token definitions */ + +#include +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif +#include + +static char const rcsid[] = "$Id: find.c,v 1.20 2006/08/20 19:15:54 broeker Exp $"; + +/* most of these functions have been optimized so their innermost loops have + * only one test for the desired character by putting the char and + * an end-of-block marker (\0) at the end of the disk block buffer. + * When the inner loop exits on the char, an outer loop will see if + * the char is followed by a \0. If so, it will read the next block + * and restart the inner loop. + */ + +char *blockp; /* pointer to current char in block */ +char block[BUFSIZ + 2]; /* leave room for end-of-block mark */ +int blocklen; /* length of disk block read */ +char blockmark; /* mark character to be searched for */ +long blocknumber; /* block number */ + +static char global[] = ""; /* dummy global function name */ +static char cpattern[PATLEN + 1]; /* compressed pattern */ +static long lastfcnoffset; /* last function name offset */ +static POSTING *postingp; /* retrieved posting set pointer */ +static long postingsfound; /* retrieved number of postings */ +static regex_t regexp; /* regular expression */ +static BOOL isregexp_valid = NO; /* regular expression status */ + +static BOOL match(void); +static BOOL matchrest(void); +static POSTING *getposting(void); +static char *lcasify(char *s); +static void findcalledbysub(char *file, BOOL macro); +static void findterm(char *pattern); +static void putline(FILE *output); +static void putpostingref(POSTING *p, char *pat); +static void putref(int seemore, char *file, char *func); +static void putsource(int seemore, FILE *output); + + /* find the symbol in the cross-reference */ + +char * +findsymbol(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + char function[PATLEN + 1]; /* function name */ + char macro[PATLEN + 1]; /* macro name */ + char symbol[PATLEN + 1]; /* symbol name */ + char *cp; + char *s; + size_t s_len = 0; + char firstchar; /* first character of a potential symbol */ + BOOL fcndef = NO; + + if (invertedindex == YES) { + long lastline = 0; + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + if (p->type != INCLUDE && p->lineoffset != lastline) { + putpostingref(p, 0); + lastline = p->lineoffset; + } + } + return NULL; + } + + (void) scanpast('\t'); /* find the end of the header */ + skiprefchar(); /* skip the file marker */ + fetch_string_from_dbase(file, sizeof(file)); + strcpy(function, global); /* set the dummy global function name */ + strcpy(macro, global); /* set the dummy global macro name */ + + /* find the next symbol */ + /* note: this code was expanded in-line for speed */ + /* other macros were replaced by code using cp instead of blockp */ + cp = blockp; + for (;;) { + setmark('\n'); + do { /* innermost loop optimized to only one test */ + while (*cp != '\n') { + ++cp; + } + } while (*(cp + 1) == '\0' && (cp = read_block()) != NULL); + + /* skip the found character */ + if (cp != NULL && *(++cp + 1) == '\0') { + cp = read_block(); + } + if (cp == NULL) { + break; + } + /* look for a source file, function, or macro name */ + if (*cp == '\t') { + blockp = cp; + switch (getrefchar()) { + + case NEWFILE: /* file name */ + + /* save the name */ + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + + /* check for the end of the symbols */ + if (*file == '\0') { + return NULL; + } + progress("Search", searchcount, nsrcfiles); + /* FALLTHROUGH */ + + case FCNEND: /* function end */ + (void) strcpy(function, global); + goto notmatched; /* don't match name */ + + case FCNDEF: /* function name */ + fcndef = YES; + s = function; + s_len = sizeof(function); + break; + + case DEFINE: /* macro name */ + if (fileversion >= 10) { + s = macro; + s_len = sizeof(macro); + } else { + s = symbol; + s_len = sizeof(symbol); + } + break; + + case DEFINEEND: /* macro end */ + (void) strcpy(macro, global); + goto notmatched; + + case INCLUDE: /* #include file */ + goto notmatched; /* don't match name */ + + default: /* other symbol */ + s = symbol; + s_len = sizeof(symbol); + } + /* save the name */ + skiprefchar(); + fetch_string_from_dbase(s, s_len); + + /* see if this is a regular expression pattern */ + if (isregexp_valid == YES) { + if (caseless == YES) { + s = lcasify(s); + } + if (*s != '\0' && regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + goto matched; + } + } + /* match the symbol to the text pattern */ + else if (strequal(pattern, s)) { + goto matched; + } + goto notmatched; + } + /* if this is a regular expression pattern */ + if (isregexp_valid == YES) { + + /* if this is a symbol */ + + /************************************************** + * The first character may be a digraph'ed char, so + * unpack it into firstchar, and then test that. + * + * Assume that all digraphed chars have the 8th bit + * set (0200). + **************************************************/ + if (*cp & 0200) { /* digraph char? */ + firstchar = dichar1[(*cp & 0177) / 8]; + } + else { + firstchar = *cp; + } + + if (isalpha((unsigned char)firstchar) || firstchar == '_') { + blockp = cp; + fetch_string_from_dbase(symbol, sizeof(symbol)); + if (caseless == YES) { + s = lcasify(symbol); /* point to lower case version */ + } + else { + s = symbol; + } + + /* match the symbol to the regular expression */ + if (*s != '\0' && regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + goto matched; + } + goto notmatched; + } + } + /* match the character to the text pattern */ + else if (*cp == cpattern[0]) { + blockp = cp; + + /* match the rest of the symbol to the text pattern */ + if (matchrest()) { + s = NULL; + matched: + /* output the file, function or macro, and source line */ + if (strcmp(macro, global) && s != macro) { + putref(0, file, macro); + } + else if (fcndef == YES || s != function) { + fcndef = NO; + putref(0, file, function); + } + else { + putref(0, file, global); + } + if (blockp == NULL) { + return NULL; + } + } + notmatched: + cp = blockp; + } + } + blockp = cp; + + return NULL; +} + /* find the function definition or #define */ + +char * +finddef(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + switch (p->type) { + case DEFINE:/* could be a macro */ + case FCNDEF: + case CLASSDEF: + case ENUMDEF: + case MEMBERDEF: + case STRUCTDEF: + case TYPEDEF: + case UNIONDEF: + case GLOBALDEF:/* other global definition */ + putpostingref(p, pattern); + } + } + return NULL; + } + + + /* find the next file name or definition */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: + skiprefchar(); /* save file name */ + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + break; + + case DEFINE: /* could be a macro */ + case FCNDEF: + case CLASSDEF: + case ENUMDEF: + case MEMBERDEF: + case STRUCTDEF: + case TYPEDEF: + case UNIONDEF: + case GLOBALDEF: /* other global definition */ + skiprefchar(); /* match name to pattern */ + if (match()) { + + /* output the file, function and source line */ + putref(0, file, pattern); + } + break; + } + } + + return NULL; +} + /* find all function definitions (used by samuel only) */ + +char * +findallfcns(char *dummy) +{ + char file[PATHLEN + 1]; /* source file name */ + char function[PATLEN + 1]; /* function name */ + + (void) dummy; /* unused argument */ + + /* find the next file name or definition */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: + skiprefchar(); /* save file name */ + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + /* FALLTHROUGH */ + + case FCNEND: /* function end */ + (void) strcpy(function, global); + break; + + case FCNDEF: + case CLASSDEF: + skiprefchar(); /* save function name */ + fetch_string_from_dbase(function, sizeof(function)); + + /* output the file, function and source line */ + putref(0, file, function); + break; + } + } + return NULL; +} + + /* find the functions calling this function */ + +char * +findcalling(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + char function[PATLEN + 1]; /* function name */ + char tmpfunc[10][PATLEN + 1];/* 10 temporary function names */ + char macro[PATLEN + 1]; /* macro name */ + char *tmpblockp; + int morefuns, i; + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + if (p->type == FCNCALL) { + putpostingref(p, 0); + } + } + return NULL; + } + /* find the next file name or function definition */ + *macro = '\0'; /* a macro can be inside a function, but not vice versa */ + tmpblockp = 0; + morefuns = 0; /* one function definition is normal case */ + for (i = 0; i < 10; i++) *(tmpfunc[i]) = '\0'; + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: /* save file name */ + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + (void) strcpy(function, global); + break; + + case DEFINE: /* could be a macro */ + if (fileversion >= 10) { + skiprefchar(); + fetch_string_from_dbase(macro, sizeof(macro)); + } + break; + + case DEFINEEND: + *macro = '\0'; + break; + + case FCNDEF: /* save calling function name */ + skiprefchar(); + fetch_string_from_dbase(function, sizeof(function)); + for (i = 0; i < morefuns; i++) + if ( !strcmp(tmpfunc[i], function) ) + break; + if (i == morefuns) { + (void) strcpy(tmpfunc[morefuns], function); + if (++morefuns >= 10) morefuns = 9; + } + break; + + case FCNEND: + for (i = 0; i < morefuns; i++) + *(tmpfunc[i]) = '\0'; + morefuns = 0; + break; + + case FCNCALL: /* match function called to pattern */ + skiprefchar(); + if (match()) { + + /* output the file, calling function or macro, and source */ + if (*macro != '\0') { + putref(1, file, macro); + } + else { + tmpblockp = blockp; + for (i = 0; i < morefuns; i++) { + blockp = tmpblockp; + putref(1, file, tmpfunc[i]); + } + } + } + } + } + morefuns = 0; + + return NULL; +} + +/* find the text in the source files */ + +char * +findstring(char *pattern) +{ + char egreppat[2 * PATLEN]; + char *cp, *pp; + + /* translate special characters in the regular expression */ + cp = egreppat; + for (pp = pattern; *pp != '\0'; ++pp) { + if (strchr(".*[\\^$+?|()", *pp) != NULL) { + *cp++ = '\\'; + } + *cp++ = *pp; + } + *cp = '\0'; + + /* search the source files */ + return(findregexp(egreppat)); +} + +/* find this regular expression in the source files */ + +char * +findregexp(char *egreppat) +{ + unsigned int i; + char *egreperror; + + /* compile the pattern */ + if ((egreperror = egrepinit(egreppat)) == NULL) { + + /* search the files */ + for (i = 0; i < nsrcfiles; ++i) { + char *file = filepath(srcfiles[i]); + + progress("Search", searchcount, nsrcfiles); + if (egrep(file, refsfound, "%s %ld ") < 0) { + posterr ("Cannot open file %s", file); + } + } + } + return(egreperror); +} + +/* find matching file names */ + +char * +findfile(char *dummy) +{ + unsigned int i; + + (void) dummy; /* unused argument */ + + for (i = 0; i < nsrcfiles; ++i) { + char *s; + + if (caseless == YES) { + s = lcasify(srcfiles[i]); + } else { + s = srcfiles[i]; + } + if (regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + (void) fprintf(refsfound, "%s 1 \n", + srcfiles[i]); + } + } + + return NULL; +} + +/* find files #including this file */ + +char * +findinclude(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + if (p->type == INCLUDE) { + putpostingref(p, 0); + } + } + return NULL; + } + + /* find the next file name or function definition */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: /* save file name */ + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + break; + + case INCLUDE: /* match function called to pattern */ + skiprefchar(); + skiprefchar(); /* skip global or local #include marker */ + if (match()) { + + /* output the file and source line */ + putref(0, file, global); + } + } + } + + return NULL; +} + +/* initialize */ + +FINDINIT +findinit(char *pattern) +{ + char buf[PATLEN + 3]; + BOOL isregexp = NO; + int i; + char *s; + unsigned char c; /* HBB 20010427: changed uint to uchar */ + + /* HBB: be nice: free regexp before allocating a new one */ + if(isregexp_valid == YES) + regfree(®exp); + + isregexp_valid = NO; + + /* remove trailing white space */ + for (s = pattern + strlen(pattern) - 1; + isspace((unsigned char)*s); + --s) { + *s = '\0'; + } + + /* HBB 20020620: new: make sure pattern is lowercased. Curses + * mode gets this right all on its own, but at least -L mode + * doesn't */ + if (caseless == YES) { + pattern = lcasify(pattern); + } + + /* allow a partial match for a file name */ + if (field == FILENAME || field == INCLUDES) { + if (regcomp (®exp, pattern, REG_EXTENDED | REG_NOSUB) != 0) { + return(REGCMPERROR); + } else { + isregexp_valid = YES; + } + return(NOERROR); + } + /* see if the pattern is a regular expression */ + if (strpbrk(pattern, "^.[{*+$") != NULL) { + isregexp = YES; + } else { + /* check for a valid C symbol */ + s = pattern; + if (!isalpha((unsigned char)*s) && *s != '_') { + return(NOTSYMBOL); + } + while (*++s != '\0') { + if (!isalnum((unsigned char)*s) && *s != '_') { + return(NOTSYMBOL); + } + } + /* look for use of the -T option (truncate symbol to 8 + characters) on a database not built with -T */ + if (trun_syms == YES && isuptodate == YES && + dbtruncated == NO && s - pattern >= 8) { + (void) strcpy(pattern + 8, ".*"); + isregexp = YES; + } + } + /* if this is a regular expression or letter case is to be ignored */ + /* or there is an inverted index */ + if (isregexp == YES || caseless == YES || invertedindex == YES) { + + /* remove a leading ^ */ + s = pattern; + if (*s == '^') { + (void) strcpy(newpat, s + 1); + (void) strcpy(s, newpat); + } + /* remove a trailing $ */ + i = strlen(s) - 1; + if (s[i] == '$') { + if (i > 0 && s[i-1] == '\\' ) { + s[i-1] = '$'; + } + s[i] = '\0'; + } + /* if requested, try to truncate a C symbol pattern */ + if (trun_syms == YES && strpbrk(s, "[{*+") == NULL) { + s[8] = '\0'; + } + /* must be an exact match */ + /* note: regcomp doesn't recognize ^*keypad$ as a syntax error + unless it is given as a single arg */ + (void) sprintf(buf, "^%s$", s); + if (regcomp (®exp, buf, REG_EXTENDED | REG_NOSUB) != 0) { + return(REGCMPERROR); + } + else + { + isregexp_valid = YES; + } + } + else { + /* if requested, truncate a C symbol pattern */ + if (trun_syms == YES && field <= CALLING) { + pattern[8] = '\0'; + } + /* compress the string pattern for matching */ + s = cpattern; + for (i = 0; (c = pattern[i]) != '\0'; ++i) { + if (IS_A_DICODE(c, pattern[i + 1])) { + c = DICODE_COMPRESS(c, pattern[i + 1]); + ++i; + } + *s++ = c; + } + *s = '\0'; + } + return(NOERROR); +} + +void +findcleanup(void) +{ + /* discard any regular expression */ +} + +/* match the pattern to the string */ + +static BOOL +match(void) +{ + char string[PATLEN + 1]; + + /* see if this is a regular expression pattern */ + if (isregexp_valid == YES) { + fetch_string_from_dbase(string, sizeof(string)); + if (*string == '\0') { + return(NO); + } + if (caseless == YES) { + return (regexec (®exp, lcasify(string), (size_t)0, NULL, 0) ? NO : YES); + } + else { + return (regexec (®exp, string, (size_t)0, NULL, 0) ? NO : YES); + } + } + /* it is a string pattern */ + return((BOOL) (*blockp == cpattern[0] && matchrest())); +} + +/* match the rest of the pattern to the name */ + +static BOOL +matchrest(void) +{ + int i = 1; + + skiprefchar(); + do { + while (*blockp == cpattern[i]) { + ++blockp; + ++i; + } + } while (*(blockp + 1) == '\0' && read_block() != NULL); + + if (*blockp == '\n' && cpattern[i] == '\0') { + return(YES); + } + return(NO); +} + +/* put the reference into the file */ + +static void +putref(int seemore, char *file, char *func) +{ + FILE *output; + + if (strcmp(func, global) == 0) { + output = refsfound; + } + else { + output = nonglobalrefs; + } + (void) fprintf(output, "%s %s ", file, func); + putsource(seemore, output); +} + +/* put the source line into the file */ + +static void +putsource(int seemore, FILE *output) +{ + char *tmpblockp; + char *cp, nextc = '\0'; + BOOL Change = NO, retreat = NO; + + if (fileversion <= 5) { + (void) scanpast(' '); + putline(output); + (void) putc('\n', output); + return; + } + /* scan back to the beginning of the source line */ + cp = tmpblockp = blockp; + while (*cp != '\n' || nextc != '\n') { + nextc = *cp; + if (--cp < block) { + retreat = YES; + /* read the previous block */ + (void) dbseek((blocknumber - 1) * BUFSIZ); + cp = &block[BUFSIZ - 1]; + } + } + blockp = cp; + if (*blockp != '\n' || getrefchar() != '\n' || + (!isdigit(getrefchar()) && fileversion >= 12)) { + postfatal("Internal error: cannot get source line from database"); + /* NOTREACHED */ + } + /* until a double newline is found */ + do { + /* skip a symbol type */ + if (*blockp == '\t') { + /* if retreat == YES, that means tmpblockp and blockp + * point to different blocks. Offset comparison should + * NOT be performed until they point to the same block. + */ + if (seemore && Change == NO && retreat == NO && + blockp > tmpblockp) { + Change = YES; + cp = blockp; + } + skiprefchar(); + skiprefchar(); + } + /* output a piece of the source line */ + putline(output); + if (retreat == YES) retreat = NO; + } while (blockp != NULL && getrefchar() != '\n'); + (void) putc('\n', output); + if (Change == YES) blockp = cp; +} + +/* put the rest of the cross-reference line into the file */ + +static void +putline(FILE *output) +{ + char *cp; + unsigned c; + + setmark('\n'); + cp = blockp; + do { + while ((c = (unsigned)(*cp)) != '\n') { + + /* check for a compressed digraph */ + if (c > '\177') { + c &= 0177; + (void) putc(dichar1[c / 8], output); + (void) putc(dichar2[c & 7], output); + } + /* check for a compressed keyword */ + else if (c < ' ') { + (void) fputs(keyword[c].text, output); + if (keyword[c].delim != '\0') { + (void) putc(' ', output); + } + if (keyword[c].delim == '(') { + (void) putc('(', output); + } + } + else { + (void) putc((int) c, output); + } + ++cp; + } + } while (*(cp + 1) == '\0' && (cp = read_block()) != NULL); + blockp = cp; +} + + +/* put the rest of the cross-reference line into the string */ +void +fetch_string_from_dbase(char *s, size_t length) +{ + char *cp; + unsigned int c; + + assert(length > sizeof (char *)); + + setmark('\n'); + cp = blockp; + do { + while (length > 1 && (c = (unsigned int)(*cp)) != '\n') { + if (c >= 0x80 && length > 2) { + c &= 0x7f; + *s++ = dichar1[c / 8]; + *s++ = dichar2[c & 7]; + length -= 2; + } else { + *s++ = c; + length--; + } + ++cp; + } + } while (length > 0 && cp[1] == '\0' && (cp = read_block()) != NULL); + blockp = cp; + *s = '\0'; +} + + +/* scan past the next occurence of this character in the cross-reference */ +char * +scanpast(char c) +{ + char *cp; + + setmark(c); + cp = blockp; + do { /* innermost loop optimized to only one test */ + while (*cp != c) { + ++cp; + } + } while (*(cp + 1) == '\0' && (cp = read_block()) != NULL); + blockp = cp; + if (cp != NULL) { + skiprefchar(); /* skip the found character */ + } + return(blockp); +} + +/* read a block of the cross-reference */ +/* HBB 20040430: renamed from readblock(), to avoid name clash on QNX */ +char * +read_block(void) +{ + /* read the next block */ + blocklen = read(symrefs, block, BUFSIZ); + blockp = block; + + /* add the search character and end-of-block mark */ + block[blocklen] = blockmark; + block[blocklen + 1] = '\0'; + + /* return NULL on end-of-file */ + if (blocklen == 0) { + blockp = NULL; + } + else { + ++blocknumber; + } + return(blockp); +} + +static char * +lcasify(char *s) +{ + static char ls[PATLEN+1]; /* largest possible match string */ + char *lptr = ls; + + while(*s) { + *lptr = tolower((unsigned char)*s); + lptr++; + s++; + } + *lptr = '\0'; + return(ls); +} + +/* find the functions called by this function */ + +/* HBB 2000/05/05: for consitency of calling interface between the + * different 'find...()' functions, this now returns a char pointer, + * too. Implemented as a pointer to static storage containing 'y' or + * 'n', for the boolean result values YES and NO */ + +char * +findcalledby(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + static char found_caller = 'n'; /* seen calling function? */ + BOOL macro = NO; + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + switch (p->type) { + case DEFINE: /* could be a macro */ + case FCNDEF: + if (dbseek(p->lineoffset) != -1 && + scanpast('\t') != NULL) { /* skip def */ + found_caller = 'y'; + findcalledbysub(srcfiles[p->fileindex], macro); + } + } + } + return(&found_caller); + } + /* find the function definition(s) */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: + skiprefchar(); /* save file name */ + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return(&found_caller); + } + progress("Search", searchcount, nsrcfiles); + break; + + case DEFINE: /* could be a macro */ + if (fileversion < 10) { + break; + } + macro = YES; + /* FALLTHROUGH */ + + case FCNDEF: + skiprefchar(); /* match name to pattern */ + if (match()) { + found_caller = 'y'; + findcalledbysub(file, macro); + } + break; + } + } + + return (&found_caller); +} + +/* find this term, which can be a regular expression */ + +static void +findterm(char *pattern) +{ + char *s; + int len; + char prefix[PATLEN + 1]; + char term[PATLEN + 1]; + + npostings = 0; /* will be non-zero after database built */ + lastfcnoffset = 0; /* clear the last function name found */ + boolclear(); /* clear the posting set */ + + /* get the string prefix (if any) of the regular expression */ + (void) strcpy(prefix, pattern); + if ((s = strpbrk(prefix, ".[{*+")) != NULL) { + *s = '\0'; + } + /* if letter case is to be ignored */ + if (caseless == YES) { + + /* convert the prefix to upper case because it is lexically + less than lower case */ + s = prefix; + while (*s != '\0') { + *s = toupper((unsigned char)*s); + ++s; + } + } + /* find the term lexically >= the prefix */ + (void) invfind(&invcontrol, prefix); + if (caseless == YES) { /* restore lower case */ + (void) strcpy(prefix, lcasify(prefix)); + } + /* a null prefix matches the null term in the inverted index, + so move to the first real term */ + if (*prefix == '\0') { + (void) invforward(&invcontrol); + } + len = strlen(prefix); + do { + (void) invterm(&invcontrol, term); /* get the term */ + s = term; + if (caseless == YES) { + s = lcasify(s); /* make it lower case */ + } + /* if it matches */ + if (regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + + /* add its postings to the set */ + if ((postingp = boolfile(&invcontrol, &npostings, BOOL_OR)) == NULL) { + break; + } + } + /* if there is a prefix */ + else if (len > 0) { + + /* if ignoring letter case and the term is out of the + range of possible matches */ + if (caseless == YES) { + if (strncmp(term, prefix, len) > 0) { + break; /* stop searching */ + } + } + /* if using letter case and the prefix doesn't match */ + else if (strncmp(term, prefix, len) != 0) { + break; /* stop searching */ + } + } + /* display progress about every three seconds */ + if (++searchcount % 50 == 0) { + progress("Symbols matched", searchcount, totalterms); + } + } while (invforward(&invcontrol)); /* while didn't wrap around */ + + /* initialize the progress message for retrieving the references */ + searchcount = 0; + postingsfound = npostings; +} + +/* get the next posting for this term */ + +static POSTING * +getposting(void) +{ + if (npostings-- <= 0) { + return(NULL); + } + /* display progress about every three seconds */ + if (++searchcount % 100 == 0) { + progress("Possible references retrieved", searchcount, + postingsfound); + } + return(postingp++); +} + +/* put the posting reference into the file */ + +static void +putpostingref(POSTING *p, char *pat) +{ + static char function[PATLEN + 1]; /* function name */ + + if (p->fcnoffset == 0) { + if (p->type == FCNDEF) { /* need to find the function name */ + if (dbseek(p->lineoffset) != -1) { + scanpast(FCNDEF); + fetch_string_from_dbase(function, + sizeof(function)); + } + } + else if (p->type != FCNCALL) { + strcpy(function, global); + } + } + else if (p->fcnoffset != lastfcnoffset) { + if (dbseek(p->fcnoffset) != -1) { + fetch_string_from_dbase(function, sizeof(function)); + lastfcnoffset = p->fcnoffset; + } + } + if (dbseek(p->lineoffset) != -1) { + if (pat) + putref(0, srcfiles[p->fileindex], pat); + else + putref(0, srcfiles[p->fileindex], function); + } +} + +/* seek to the database offset */ + +long +dbseek(long offset) +{ + long n; + int rc = 0; + + if ((n = offset / BUFSIZ) != blocknumber) { + if ((rc = lseek(symrefs, n * BUFSIZ, 0)) == -1) { + myperror("Lseek failed"); + (void) sleep(3); + return(rc); + } + (void) read_block(); + blocknumber = n; + } + blockp = block + offset % BUFSIZ; + return(rc); +} + +static void +findcalledbysub(char *file, BOOL macro) +{ + /* find the next function call or the end of this function */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case DEFINE: /* #define inside a function */ + if (fileversion >= 10) { /* skip it */ + while (scanpast('\t') != NULL && + *blockp != DEFINEEND) + ; + } + break; + + case FCNCALL: /* function call */ + + /* output the file name */ + (void) fprintf(refsfound, "%s ", file); + + /* output the function name */ + skiprefchar(); + putline(refsfound); + (void) putc(' ', refsfound); + + /* output the source line */ + putsource(1, refsfound); + break; + + case DEFINEEND: /* #define end */ + + if (invertedindex == NO) { + if (macro == YES) { + return; + } + break; /* inside a function */ + } + /* FALLTHROUGH */ + + case FCNDEF: /* function end (pre 9.5) */ + + if (invertedindex == NO) break; + /* FALLTHROUGH */ + + case FCNEND: /* function end */ + case NEWFILE: /* file end */ + return; + } + } +} -- cgit v1.2.3-54-g00ecf