|
- /*===========================================================================
- 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 falseT 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 falseT 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 <assert.h>
- #include <ncurses.h>
- #include <regex.h>
- #include <setjmp.h> /* jmp_buf */
-
- /* 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[] = "<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 = false; /* regular expression status */
-
- static bool match(void);
- static bool matchrest(void);
- static POSTING *getposting(void);
- static char *lcasify(const char *s);
- static void findcalledbysub(const char *file, bool macro);
- static void findterm(const char *pattern);
- static void putline(FILE *output);
- static char *find_symbol_or_assignment(const char *pattern, bool assign_flag);
- static bool check_for_assignment(void);
- static void putpostingref(POSTING *p, const char *pat);
- static void putref(int seemore, const char *file, const char *func);
- static void putsource(int seemore, FILE *output);
- static FILE *nonglobalrefs; /* non-global references file */
-
- static sigjmp_buf env; /* setjmp/longjmp buffer */
-
- typedef enum { /* findinit return code */
- NOERROR,
- NOTSYMBOL,
- REGCMPERROR
- } FINDINIT;
-
- typedef char *(*FP)(const char *); /* pointer to function returning a character pointer */
- /* Paralel array to "fields", indexed by "field" */
- FP field_searchers[FIELDS + 1] = {
- findsymbol,
- finddef,
- findcalledby,
- findcalling,
- findstring,
- findstring,
- findregexp,
- findfile,
- findinclude,
- findassign,
- findallfcns /* samuel only */
- };
-
- /**/
- struct TI {
- char *text1;
- char *text2;
- };
- extern struct TI fields[FIELDS + 1];
-
- /* Internal prototypes: */
- static void jumpback(int sig);
-
- static void jumpback(int sig) {
- signal(sig, jumpback);
- siglongjmp(env, 1);
- }
-
- /* find the symbol in the cross-reference */
- char *findsymbol(const char *pattern) {
- return find_symbol_or_assignment(pattern, false);
- }
-
- /* find the symbol in the cross-reference, and look for assignments */
- char *findassign(const char *pattern) {
- return find_symbol_or_assignment(pattern, true);
- }
-
- /* Test reference whether it's an assignment to the symbol found at
- * (global variable) 'blockp' */
- static bool check_for_assignment(void) {
- /* Do the extra work here to determine if this is an
- * assignment or not. Do this by examining the next character
- * or two in blockp */
- char *asgn_char = blockp;
-
- while(isspace((unsigned char)asgn_char[0])) {
- /* skip any whitespace or \n */
- asgn_char++;
- if(asgn_char[0] == '\0') {
- /* get the next block when we reach the end of
- * the current block */
- if(NULL == (asgn_char = read_block())) return false;
- }
- }
- /* check for digraph starting with = */
- if((asgn_char[0] & 0x80) && (dichar1[(asgn_char[0] & 0177) / 8] == '=')) {
- return true;
- }
- /* check for plain '=', not '==' */
- if((asgn_char[0] == '=') &&
- (((asgn_char[1] != '=') && !(asgn_char[1] & 0x80)) ||
- ((asgn_char[1] & 0x80) && (dichar1[(asgn_char[1] & 0177) / 8] != '=')))) {
- return true;
- }
-
- /* check for operator assignments: +=, ... ^= ? */
- if(((asgn_char[0] == '+') || (asgn_char[0] == '-') || (asgn_char[0] == '*') ||
- (asgn_char[0] == '/') || (asgn_char[0] == '%') || (asgn_char[0] == '&') ||
- (asgn_char[0] == '|') || (asgn_char[0] == '^')) &&
- ((asgn_char[1] == '=') ||
- ((asgn_char[1] & 0x80) && (dichar1[(asgn_char[1] & 0177) / 8] == '=')))
-
- ) {
- return true;
- }
-
- /* check for two-letter operator assignments: <<= or >>= ? */
- if(((asgn_char[0] == '<') || (asgn_char[0] == '>')) &&
- (asgn_char[1] == asgn_char[0]) &&
- ((asgn_char[2] == '=') ||
- ((asgn_char[2] & 0x80) && (dichar1[(asgn_char[2] & 0177) / 8] == '='))))
- return true;
- return false;
- }
-
- /* The actual routine that does the work for findsymbol() and
- * findassign() */
- static char *find_symbol_or_assignment(const char *pattern, bool assign_flag) {
- 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 = false;
-
- if((invertedindex == true) && (assign_flag == false)) {
- 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 = true;
- 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 == true) {
- if(caseless == true) { 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 == true) {
-
- /* 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 == true) {
- 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:
- /* if the assignment flag is set then
- * we are looking for assignments and
- * some extra filtering is needed */
- if(assign_flag == true && !check_for_assignment()) goto notmatched;
-
-
- /* output the file, function or macro, and source line */
- if(strcmp(macro, global) && s != macro) {
- putref(0, file, macro);
- } else if(fcndef == true || s != function) {
- fcndef = false;
- putref(0, file, function);
- } else {
- putref(0, file, global);
- }
- }
- notmatched:
- if(blockp == NULL) { return NULL; }
- fcndef = false;
- cp = blockp;
- }
- }
- blockp = cp;
-
- return NULL;
- }
-
- /* find the function definition or #define */
-
- char *finddef(const char *pattern) {
- char file[PATHLEN + 1]; /* source file name */
-
- if(invertedindex == true) {
- 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(const 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(const 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 == true) {
- 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]);
- }
- }
- }
- }
- }
-
- return NULL;
- }
-
- /* find the text in the source files */
-
- char *findstring(const char *pattern) {
- char egreppat[2 * PATLEN];
- char *cp = egreppat;
- const char *pp;
-
- /* translate special characters in the regular expression */
- 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(const 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 <unknown> %ld ") < 0) {
- posterr("Cannot open file %s", file);
- }
- }
- }
- return (egreperror);
- }
-
- /* find matching file names */
-
- char *findfile(const char *dummy) {
- unsigned int i;
-
- (void)dummy; /* unused argument */
-
- for(i = 0; i < nsrcfiles; ++i) {
- char *s;
-
- if(caseless == true) {
- s = lcasify(srcfiles[i]);
- } else {
- s = srcfiles[i];
- }
- if(regexec(®exp, s, (size_t)0, NULL, 0) == 0) {
- (void)fprintf(refsfound, "%s <unknown> 1 <unknown>\n", srcfiles[i]);
- }
- }
-
- return NULL;
- }
-
- /* find files #including this file */
-
- char *findinclude(const char *pattern) {
- char file[PATHLEN + 1]; /* source file name */
-
- if(invertedindex == true) {
- 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 */
-
- int findinit(const char *pattern_) {
- char *pattern = strdup(pattern_);
- int r = NOERROR;
- char buf[PATLEN + 3];
- bool isregexp = false;
- int i;
- char *s;
- unsigned char c;
-
- /* HBB: be nice: free regexp before allocating a new one */
- if(isregexp_valid == true) regfree(®exp);
-
- isregexp_valid = false;
-
- /* remove trailing white space */
- for(s = pattern + strlen(pattern) - 1; isspace((unsigned char)*s); --s) {
- *s = '\0';
- }
-
- /* Make sure pattern is lowercased. Curses
- * mode gets this right all on its own, but at least -L mode
- * doesn't */
- if(caseless == true) { 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) {
- r = REGCMPERROR;
- } else {
- isregexp_valid = true;
- }
- goto end;
- }
- /* see if the pattern is a regular expression */
- if(strpbrk(pattern, "^.[{*+$|(") != NULL) {
- isregexp = true;
- } else {
- /* check for a valid C symbol */
- s = pattern;
- if(!isalpha((unsigned char)*s) && *s != '_') {
- r = NOTSYMBOL;
- goto end;
- }
- while(*++s != '\0') {
- if(!isalnum((unsigned char)*s) && *s != '_') {
- r = NOTSYMBOL;
- goto end;
- }
- }
- /* look for use of the -T option (truncate symbol to 8
- characters) on a database not built with -T */
- if(trun_syms == true && isuptodate == true && dbtruncated == false &&
- s - pattern >= 8) {
- (void)strcpy(pattern + 8, ".*");
- isregexp = true;
- }
- }
- /* if this is a regular expression or letter case is to be ignored */
- /* or there is an inverted index */
- if(isregexp == true || caseless == true || invertedindex == true) {
-
- /* 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 == true && 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)snprintf(buf, sizeof(buf), "^%s$", s);
- if(regcomp(®exp, buf, REG_EXTENDED | REG_NOSUB) != 0) {
- r = REGCMPERROR;
- goto end;
- } else {
- isregexp_valid = true;
- }
- } else {
- /* if requested, truncate a C symbol pattern */
- if(trun_syms == true && 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';
- }
-
- end:
- return r;
- }
-
- 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 == true) {
- fetch_string_from_dbase(string, sizeof(string));
- if(*string == '\0') { return (false); }
- if(caseless == true) {
- return (regexec(®exp, lcasify(string), (size_t)0, NULL, 0) ? false : true);
- } else {
- return (regexec(®exp, string, (size_t)0, NULL, 0) ? false : true);
- }
- }
- /* 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 (true); }
- return (false);
- }
-
- /* put the reference into the file */
-
- static void putref(int seemore, const char *file, const 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 = false, retreat = false;
-
- 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 = true;
- /* 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 == true, that means tmpblockp and blockp
- * point to different blocks. Offset comparison should
- * falseT be performed until they point to the same block.
- */
- if(seemore && Change == false && retreat == false && blockp > tmpblockp) {
- Change = true;
- cp = blockp;
- }
- skiprefchar();
- skiprefchar();
- }
- /* output a piece of the source line */
- putline(output);
- if(retreat == true) retreat = false;
- } while(blockp != NULL && getrefchar() != '\n');
- (void)putc('\n', output);
- if(Change == true) 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(const 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 true and false */
-
- char *findcalledby(const char *pattern) {
- char file[PATHLEN + 1]; /* source file name */
- static char found_caller = 'n'; /* seen calling function? */
- bool macro = false;
-
- if(invertedindex == true) {
- 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 = true;
- /* 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(const 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 == true) {
-
- /* 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 == true) { /* 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 == true) { 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 == true) {
- 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, const char *pat) {
- // initialize function to "unknown" so that the first line of temp1
- // is properly formed if symbol matches a header file entry first time
- static char function[PATLEN + 1] = "unknown"; /* 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(const 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 == false) {
- if(macro == true) { return; }
- break; /* inside a function */
- }
- /* FALLTHROUGH */
-
- case FCNDEF: /* function end (pre 9.5) */
-
- if(invertedindex == false) break;
- /* FALLTHROUGH */
-
- case FCNEND: /* function end */
- case NEWFILE: /* file end */
- return;
- }
- }
- }
-
- /* open the references found file for writing */
- bool writerefsfound(void) {
- if(refsfound == NULL) {
- if((refsfound = myfopen(temp1, "wb")) == NULL) {
- cannotopen(temp1);
- return (false);
- }
- } else {
- (void)fclose(refsfound);
- if((refsfound = myfopen(temp1, "wb")) == NULL) {
- postmsg("Cannot reopen temporary file");
- return (false);
- }
- }
- return (true);
- }
-
- /* Perform token search based on "field" */
- bool search(const char *query) {
- char msg[MSGLEN + 1];
- char *findresult = NULL; /* find function output */
- bool funcexist = true; /* find "function" error */
- FINDINIT rc = NOERROR; /* findinit return code */
- sighandler_t savesig; /* old value of signal */
- FP f; /* searching function */
- int c;
-
- /* open the references found file for writing */
- if(writerefsfound() == false) { return (false); }
- /* find the pattern - stop on an interrupt */
- if(linemode == false) { postmsg("Searching"); }
- searchcount = 0;
- savesig = signal(SIGINT, jumpback);
- if(sigsetjmp(env, 1) == 0) {
- f = field_searchers[field];
- if(f == findregexp || f == findstring) {
- findresult = (*f)(query);
- } else {
- if((nonglobalrefs = myfopen(temp2, "wb")) == NULL) {
- cannotopen(temp2);
- return (false);
- }
- if((rc = findinit(query)) == NOERROR) {
- (void)dbseek(0L); /* read the first block */
- findresult = (*f)(query);
- if(f == findcalledby) funcexist = (*findresult == 'y');
- findcleanup();
-
- /* append the non-global references */
- (void)fclose(nonglobalrefs);
- if((nonglobalrefs = myfopen(temp2, "rb")) == NULL) {
- cannotopen(temp2);
- return (false);
- }
- while((c = getc(nonglobalrefs)) != EOF) {
- (void)putc(c, refsfound);
- }
- }
- (void)fclose(nonglobalrefs);
- }
- }
- signal(SIGINT, savesig);
-
- /* rewind the cross-reference file */
- (void)lseek(symrefs, (long)0, 0);
-
- /* reopen the references found file for reading */
- (void)fclose(refsfound);
- if((refsfound = myfopen(temp1, "rb")) == NULL) {
- cannotopen(temp1);
- return (false);
- }
- totallines = 0;
- disprefs = 0;
-
- /* see if it is empty */
- if((c = getc(refsfound)) == EOF) {
- if(findresult != NULL) {
- (void)snprintf(msg,
- sizeof(msg),
- "Egrep %s in this pattern: %s",
- findresult,
- query);
- } else if(rc == NOTSYMBOL) {
- (void)snprintf(msg, sizeof(msg), "This is not a C symbol: %s", query);
- } else if(rc == REGCMPERROR) {
- (void)snprintf(msg,
- sizeof(msg),
- "Error in this regcomp(3) regular expression: %s",
- query);
-
- } else if(funcexist == false) {
- (void)snprintf(msg,
- sizeof(msg),
- "Function definition does not exist: %s",
- query);
- } else {
- (void)snprintf(msg,
- sizeof(msg),
- "Could not find the %s: %s",
- fields[field].text2,
- query);
- }
- postmsg(msg);
- return (false);
- }
- /* put back the character read */
- (void)ungetc(c, refsfound);
-
- countrefs();
-
- window_change |= CH_RESULT;
-
- return (true);
- }
|