#include "global.h"
#include "build.h"
#include "scanner.h"
#include <assert.h>
#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
#include <ncurses.h>
#else
#include <curses.h>
#endif
#include <regex.h>
static char const rcsid[] = "$Id: find.c,v 1.20 2006/08/20 19:15:54 broeker Exp $";
char *blockp;
char block[BUFSIZ + 2];
int blocklen;
char blockmark;
long blocknumber;
static char global[] = "<global>";
static char cpattern[PATLEN + 1];
static long lastfcnoffset;
static POSTING *postingp;
static long postingsfound;
static regex_t regexp;
static BOOL isregexp_valid = NO;
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);
char *
findsymbol(char *pattern)
{
char file[PATHLEN + 1];
char function[PATLEN + 1];
char macro[PATLEN + 1];
char symbol[PATLEN + 1];
char *cp;
char *s;
size_t s_len = 0;
char firstchar;
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');
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
strcpy(function, global);
strcpy(macro, global);
cp = blockp;
for (;;) {
setmark('\n');
do {
while (*cp != '\n') {
++cp;
}
} while (*(cp + 1) == '\0' && (cp = read_block()) != NULL);
if (cp != NULL && *(++cp + 1) == '\0') {
cp = read_block();
}
if (cp == NULL) {
break;
}
if (*cp == '\t') {
blockp = cp;
switch (getrefchar()) {
case NEWFILE:
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
if (*file == '\0') {
return NULL;
}
progress("Search", searchcount, nsrcfiles);
case FCNEND:
(void) strcpy(function, global);
goto notmatched;
case FCNDEF:
fcndef = YES;
s = function;
s_len = sizeof(function);
break;
case DEFINE:
if (fileversion >= 10) {
s = macro;
s_len = sizeof(macro);
} else {
s = symbol;
s_len = sizeof(symbol);
}
break;
case DEFINEEND:
(void) strcpy(macro, global);
goto notmatched;
case INCLUDE:
goto notmatched;
default:
s = symbol;
s_len = sizeof(symbol);
}
skiprefchar();
fetch_string_from_dbase(s, s_len);
if (isregexp_valid == YES) {
if (caseless == YES) {
s = lcasify(s);
}
if (*s != '\0' && regexec (®exp, s, (size_t)0, NULL, 0) == 0) {
goto matched;
}
}
else if (strequal(pattern, s)) {
goto matched;
}
goto notmatched;
}
if (isregexp_valid == YES) {
if (*cp & 0200) {
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);
}
else {
s = symbol;
}
if (*s != '\0' && regexec (®exp, s, (size_t)0, NULL, 0) == 0) {
goto matched;
}
goto notmatched;
}
}
else if (*cp == cpattern[0]) {
blockp = cp;
if (matchrest()) {
s = NULL;
matched:
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;
}
char *
finddef(char *pattern)
{
char file[PATHLEN + 1];
if (invertedindex == YES) {
POSTING *p;
findterm(pattern);
while ((p = getposting()) != NULL) {
switch (p->type) {
case DEFINE:
case FCNDEF:
case CLASSDEF:
case ENUMDEF:
case MEMBERDEF:
case STRUCTDEF:
case TYPEDEF:
case UNIONDEF:
case GLOBALDEF:
putpostingref(p, pattern);
}
}
return NULL;
}
while (scanpast('\t') != NULL) {
switch (*blockp) {
case NEWFILE:
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
if (*file == '\0') {
return NULL;
}
progress("Search", searchcount, nsrcfiles);
break;
case DEFINE:
case FCNDEF:
case CLASSDEF:
case ENUMDEF:
case MEMBERDEF:
case STRUCTDEF:
case TYPEDEF:
case UNIONDEF:
case GLOBALDEF:
skiprefchar();
if (match()) {
putref(0, file, pattern);
}
break;
}
}
return NULL;
}
char *
findallfcns(char *dummy)
{
char file[PATHLEN + 1];
char function[PATLEN + 1];
(void) dummy;
while (scanpast('\t') != NULL) {
switch (*blockp) {
case NEWFILE:
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
if (*file == '\0') {
return NULL;
}
progress("Search", searchcount, nsrcfiles);
case FCNEND:
(void) strcpy(function, global);
break;
case FCNDEF:
case CLASSDEF:
skiprefchar();
fetch_string_from_dbase(function, sizeof(function));
putref(0, file, function);
break;
}
}
return NULL;
}
char *
findcalling(char *pattern)
{
char file[PATHLEN + 1];
char function[PATLEN + 1];
char tmpfunc[10][PATLEN + 1];
char macro[PATLEN + 1];
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;
}
*macro = '\0';
tmpblockp = 0;
morefuns = 0;
for (i = 0; i < 10; i++) *(tmpfunc[i]) = '\0';
while (scanpast('\t') != NULL) {
switch (*blockp) {
case NEWFILE:
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
if (*file == '\0') {
return NULL;
}
progress("Search", searchcount, nsrcfiles);
(void) strcpy(function, global);
break;
case DEFINE:
if (fileversion >= 10) {
skiprefchar();
fetch_string_from_dbase(macro, sizeof(macro));
}
break;
case DEFINEEND:
*macro = '\0';
break;
case FCNDEF:
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:
skiprefchar();
if (match()) {
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;
}
char *
findstring(char *pattern)
{
char egreppat[2 * PATLEN];
char *cp, *pp;
cp = egreppat;
for (pp = pattern; *pp != '\0'; ++pp) {
if (strchr(".*[\\^$+?|()", *pp) != NULL) {
*cp++ = '\\';
}
*cp++ = *pp;
}
*cp = '\0';
return(findregexp(egreppat));
}
char *
findregexp(char *egreppat)
{
unsigned int i;
char *egreperror;
if ((egreperror = egrepinit(egreppat)) == NULL) {
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);
}
char *
findfile(char *dummy)
{
unsigned int i;
(void) dummy;
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 <unknown> 1 <unknown>\n",
srcfiles[i]);
}
}
return NULL;
}
char *
findinclude(char *pattern)
{
char file[PATHLEN + 1];
if (invertedindex == YES) {
POSTING *p;
findterm(pattern);
while ((p = getposting()) != NULL) {
if (p->type == INCLUDE) {
putpostingref(p, 0);
}
}
return NULL;
}
while (scanpast('\t') != NULL) {
switch (*blockp) {
case NEWFILE:
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
if (*file == '\0') {
return NULL;
}
progress("Search", searchcount, nsrcfiles);
break;
case INCLUDE:
skiprefchar();
skiprefchar();
if (match()) {
putref(0, file, global);
}
}
}
return NULL;
}
FINDINIT
findinit(char *pattern)
{
char buf[PATLEN + 3];
BOOL isregexp = NO;
int i;
char *s;
unsigned char c;
if(isregexp_valid == YES)
regfree(®exp);
isregexp_valid = NO;
for (s = pattern + strlen(pattern) - 1;
isspace((unsigned char)*s);
--s) {
*s = '\0';
}
if (caseless == YES) {
pattern = lcasify(pattern);
}
if (field == FILENAME || field == INCLUDES) {
if (regcomp (®exp, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
return(REGCMPERROR);
} else {
isregexp_valid = YES;
}
return(NOERROR);
}
if (strpbrk(pattern, "^.[{*+$") != NULL) {
isregexp = YES;
} else {
s = pattern;
if (!isalpha((unsigned char)*s) && *s != '_') {
return(NOTSYMBOL);
}
while (*++s != '\0') {
if (!isalnum((unsigned char)*s) && *s != '_') {
return(NOTSYMBOL);
}
}
if (trun_syms == YES && isuptodate == YES &&
dbtruncated == NO && s - pattern >= 8) {
(void) strcpy(pattern + 8, ".*");
isregexp = YES;
}
}
if (isregexp == YES || caseless == YES || invertedindex == YES) {
s = pattern;
if (*s == '^') {
(void) strcpy(newpat, s + 1);
(void) strcpy(s, newpat);
}
i = strlen(s) - 1;
if (s[i] == '$') {
if (i > 0 && s[i-1] == '\\' ) {
s[i-1] = '$';
}
s[i] = '\0';
}
if (trun_syms == YES && strpbrk(s, "[{*+") == NULL) {
s[8] = '\0';
}
(void) sprintf(buf, "^%s$", s);
if (regcomp (®exp, buf, REG_EXTENDED | REG_NOSUB) != 0) {
return(REGCMPERROR);
}
else
{
isregexp_valid = YES;
}
}
else {
if (trun_syms == YES && field <= CALLING) {
pattern[8] = '\0';
}
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)
{
}
static BOOL
match(void)
{
char string[PATLEN + 1];
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);
}
}
return((BOOL) (*blockp == cpattern[0] && matchrest()));
}
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);
}
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);
}
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;
}
cp = tmpblockp = blockp;
while (*cp != '\n' || nextc != '\n') {
nextc = *cp;
if (--cp < block) {
retreat = YES;
(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");
}
do {
if (*blockp == '\t') {
if (seemore && Change == NO && retreat == NO &&
blockp > tmpblockp) {
Change = YES;
cp = blockp;
}
skiprefchar();
skiprefchar();
}
putline(output);
if (retreat == YES) retreat = NO;
} while (blockp != NULL && getrefchar() != '\n');
(void) putc('\n', output);
if (Change == YES) blockp = cp;
}
static void
putline(FILE *output)
{
char *cp;
unsigned c;
setmark('\n');
cp = blockp;
do {
while ((c = (unsigned)(*cp)) != '\n') {
if (c > '\177') {
c &= 0177;
(void) putc(dichar1[c / 8], output);
(void) putc(dichar2[c & 7], output);
}
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;
}
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';
}
char *
scanpast(char c)
{
char *cp;
setmark(c);
cp = blockp;
do {
while (*cp != c) {
++cp;
}
} while (*(cp + 1) == '\0' && (cp = read_block()) != NULL);
blockp = cp;
if (cp != NULL) {
skiprefchar();
}
return(blockp);
}
char *
read_block(void)
{
blocklen = read(symrefs, block, BUFSIZ);
blockp = block;
block[blocklen] = blockmark;
block[blocklen + 1] = '\0';
if (blocklen == 0) {
blockp = NULL;
}
else {
++blocknumber;
}
return(blockp);
}
static char *
lcasify(char *s)
{
static char ls[PATLEN+1];
char *lptr = ls;
while(*s) {
*lptr = tolower((unsigned char)*s);
lptr++;
s++;
}
*lptr = '\0';
return(ls);
}
char *
findcalledby(char *pattern)
{
char file[PATHLEN + 1];
static char found_caller = 'n';
BOOL macro = NO;
if (invertedindex == YES) {
POSTING *p;
findterm(pattern);
while ((p = getposting()) != NULL) {
switch (p->type) {
case DEFINE:
case FCNDEF:
if (dbseek(p->lineoffset) != -1 &&
scanpast('\t') != NULL) {
found_caller = 'y';
findcalledbysub(srcfiles[p->fileindex], macro);
}
}
}
return(&found_caller);
}
while (scanpast('\t') != NULL) {
switch (*blockp) {
case NEWFILE:
skiprefchar();
fetch_string_from_dbase(file, sizeof(file));
if (*file == '\0') {
return(&found_caller);
}
progress("Search", searchcount, nsrcfiles);
break;
case DEFINE:
if (fileversion < 10) {
break;
}
macro = YES;
case FCNDEF:
skiprefchar();
if (match()) {
found_caller = 'y';
findcalledbysub(file, macro);
}
break;
}
}
return (&found_caller);
}
static void
findterm(char *pattern)
{
char *s;
int len;
char prefix[PATLEN + 1];
char term[PATLEN + 1];
npostings = 0;
lastfcnoffset = 0;
boolclear();
(void) strcpy(prefix, pattern);
if ((s = strpbrk(prefix, ".[{*+")) != NULL) {
*s = '\0';
}
if (caseless == YES) {
s = prefix;
while (*s != '\0') {
*s = toupper((unsigned char)*s);
++s;
}
}
(void) invfind(&invcontrol, prefix);
if (caseless == YES) {
(void) strcpy(prefix, lcasify(prefix));
}
if (*prefix == '\0') {
(void) invforward(&invcontrol);
}
len = strlen(prefix);
do {
(void) invterm(&invcontrol, term);
s = term;
if (caseless == YES) {
s = lcasify(s);
}
if (regexec (®exp, s, (size_t)0, NULL, 0) == 0) {
if ((postingp = boolfile(&invcontrol, &npostings, BOOL_OR)) == NULL) {
break;
}
}
else if (len > 0) {
if (caseless == YES) {
if (strncmp(term, prefix, len) > 0) {
break;
}
}
else if (strncmp(term, prefix, len) != 0) {
break;
}
}
if (++searchcount % 50 == 0) {
progress("Symbols matched", searchcount, totalterms);
}
} while (invforward(&invcontrol));
searchcount = 0;
postingsfound = npostings;
}
static POSTING *
getposting(void)
{
if (npostings-- <= 0) {
return(NULL);
}
if (++searchcount % 100 == 0) {
progress("Possible references retrieved", searchcount,
postingsfound);
}
return(postingp++);
}
static void
putpostingref(POSTING *p, char *pat)
{
static char function[PATLEN + 1];
if (p->fcnoffset == 0) {
if (p->type == FCNDEF) {
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);
}
}
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)
{
while (scanpast('\t') != NULL) {
switch (*blockp) {
case DEFINE:
if (fileversion >= 10) {
while (scanpast('\t') != NULL &&
*blockp != DEFINEEND)
;
}
break;
case FCNCALL:
(void) fprintf(refsfound, "%s ", file);
skiprefchar();
putline(refsfound);
(void) putc(' ', refsfound);
putsource(1, refsfound);
break;
case DEFINEEND:
if (invertedindex == NO) {
if (macro == YES) {
return;
}
break;
}
case FCNDEF:
if (invertedindex == NO) break;
case FCNEND:
case NEWFILE:
return;
}
}
}