1325 lines
34 KiB
C
1325 lines
34 KiB
C
/*===========================================================================
|
|
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);
|
|
}
|
|
nextline = 1;
|
|
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);
|
|
}
|