/*
* boyer-moore quick string matching
*/
struct Quick
{
char *pat;
char *up; /* match string for upper case of pat */
int len; /* of pat (and up) -1; used for fast search */
uchar jump[256]; /* jump index table */
int miss; /* amount to jump if we falsely match the last char */
};
extern void quickmk(Quick*, char*, int);
extern void quickfree(Quick*);
extern char* quicksearch(Quick*, char*, char*);
/*
* exact matching of a search string
*/
struct Match
{
Match *next;
char *pat; /* null-terminated search string */
char *up; /* upper case of pat */
int len; /* length of both pat and up */
int (*op)(Match*, char*, char*); /* method for this partiticular search */
};
struct Search
{
Quick quick; /* quick match */
Match *match; /* exact matches */
int skip; /* number of matches to skip */
};
struct Fid
{
Lock;
Fid *next;
Fid **last;
uint fid;
int ref; /* number of fcalls using the fid */
int attached; /* fid has beed attached or cloned and not clunked */
int open;
Qid qid;
Search *search; /* search patterns */
char *where; /* current location in the database */
int n; /* number of bytes left in found item */
};
int dostat(int, uchar*, int);
void* emalloc(uint);
void fatal(char*, ...);
Match* mkmatch(Match*, int(*)(Match*, char*, char*), char*);
Match* mkstrmatch(Match*, char*);
char* nextsearch(char*, char*, char**, char**);
int strlook(Match*, char*, char*);
char* strndup(char*, int);
int tolower(int);
int toupper(int);
char* urlunesc(char*, char*);
void usage(void);
char Eperm[] = "permission denied";
char Enotdir[] = "not a directory";
char Enotexist[] = "file does not exist";
char Eisopen[] = "file already open for I/O";
char Einuse[] = "fid is already in use";
char Enofid[] = "no such fid";
char Enotopen[] = "file is not open";
char Ebadsearch[] = "bad search string";
Fs fs;
char *database;
char *edatabase;
int messagesize = 8192+IOHDRSZ;
void
main(int argc, char **argv)
{
Dir *d;
char buf[12], *mnt, *srv;
int fd, p[2], n;
/*
* execute the search
* do a quick match,
* isolate the line in which the occured,
* and try all of the exact matches
*/
char*
searchsearch(Search *search, char *where, char *end, int *np)
{
Match *m;
char *s, *e;
*np = 0;
if(search == nil || where == nil)
return nil;
for(;;){
s = quicksearch(&search->quick, where, end);
if(s == nil)
return nil;
e = memchr(s, '\n', end - s);
if(e == nil)
e = end;
else
e++;
while(s > where && s[-1] != '\n')
s--;
for(m = search->match; m != nil; m = m->next){
if((*m->op)(m, s, e) == 0)
break;
}
/*
* parse a search string of the form
* tag=val&tag1=val1...
*/
Search*
searchparse(char *search, char *esearch)
{
Search *s;
Match *m, *next, **last;
char *tag, *val, *p;
int ok;
s = emalloc(sizeof *s);
s->match = nil;
/*
* acording to the http spec,
* repeated search queries are ingored.
* the last search given is performed on the original object
*/
while((p = memchr(s, '?', esearch - search)) != nil){
search = p + 1;
}
while(search < esearch){
search = nextsearch(search, esearch, &tag, &val);
if(tag == nil)
continue;
/*
* order the matches by probability of occurance
* first cut is just by length
*/
for(ok = 0; !ok; ){
ok = 1;
last = &s->match;
for(m = *last; m && m->next; m = *last){
if(m->next->len > m->len){
next = m->next;
m->next = next->next;
next->next = m;
*last = next;
ok = 0;
}
last = &m->next;
}
}
/*
* convert the best search into a fast lookup
*/
m = s->match;
s->match = m->next;
quickmk(&s->quick, m->pat, 1);
free(m->pat);
free(m->up);
free(m);
return s;
}
void
searchfree(Search *s)
{
Match *m, *next;
if(s == nil)
return;
for(m = s->match; m; m = next){
next = m->next;
free(m->pat);
free(m->up);
free(m);
}
quickfree(&s->quick);
free(s);
}
int
strlook(Match *m, char *str, char *e)
{
char *pat, *up, *s;
int c, pc, fc, fuc, n;
n = m->len;
fc = m->pat[0];
fuc = m->up[0];
for(; str + n <= e; str++){
c = *str;
if(c != fc && c != fuc)
continue;
s = str + 1;
up = m->up + 1;
for(pat = m->pat + 1; pc = *pat; pat++){
c = *s;
if(c != pc && c != *up)
break;
up++;
s++;
}
if(pc == 0)
return 1;
}
return 0;
}
/*
* boyer-moore style pattern matching
* implements an exact match for ascii
* however, if mulitbyte upper-case and lower-case
* characters differ in length or in more than one byte,
* it only implements an approximate match
*/
void
quickmk(Quick *q, char *spat, int ignorecase)
{
char *pat, *up;
uchar *j;
int ep, ea, cp, ca, i, c, n;
/*
* allocate the machine
*/
n = strlen(spat);
if(n == 0){
q->pat = nil;
q->up = nil;
q->len = -1;
return;
}
pat = emalloc(2* n + 2);
q->pat = pat;
up = pat;
if(ignorecase)
up = pat + n + 1;
q->up = up;
while(c = *spat++){
if(ignorecase){
*up++ = toupper(c);
c = tolower(c);
}
*pat++ = c;
}
pat = q->pat;
up = q->up;
pat[n] = up[n] = '\0';
/*
* make the skipping table
*/
if(n > 255)
n = 255;
j = q->jump;
memset(j, n, 256);
n--;
q->len = n;
for(i = 0; i <= n; i++){
j[(uchar)pat[i]] = n - i;
j[(uchar)up[i]] = n - i;
}
/*
* find the minimum safe amount to skip
* if we match the last char but not the whole pat
*/
ep = pat[n];
ea = up[n];
for(i = n - 1; i >= 0; i--){
cp = pat[i];
ca = up[i];
if(cp == ep || cp == ea || ca == ep || ca == ea)
break;
}
q->miss = n - i;
}
len = q->len;
if(len < 0)
return s;
j = q->jump;
pat = q->pat;
up = q->up;
s += len;
ee = e - (len * 4 + 4);
while(s < e){
/*
* look for a match on the last char
*/
while(s < ee && (n = j[(uchar)*s])){
s += n;
s += j[(uchar)*s];
s += j[(uchar)*s];
s += j[(uchar)*s];
}
if(s >= e)
return nil;
while(n = j[(uchar)*s]){
s += n;
if(s >= e)
return nil;
}
/*
* does the string match?
*/
m = s - len;
for(n = 0; c = pat[n]; n++){
mc = *m++;
if(c != mc && mc != up[n])
break;
}
if(!c)
return s - len;
s += q->miss;
}
return nil;
}
void
fsrun(Fs *fs, int fd)
{
Fcall rpc;
char *err;
uchar *buf;
int n;