1219b2ee8SDavid du Colombier #include <u.h>
2219b2ee8SDavid du Colombier #include <libc.h>
3219b2ee8SDavid du Colombier #include <bio.h>
4219b2ee8SDavid du Colombier #include <regexp.h>
5219b2ee8SDavid du Colombier #include <ctype.h>
6219b2ee8SDavid du Colombier #include "dict.h"
7219b2ee8SDavid du Colombier
8219b2ee8SDavid du Colombier /*
9219b2ee8SDavid du Colombier * Assumed index file structure: lines of form
10219b2ee8SDavid du Colombier * [^\t]+\t[0-9]+
11219b2ee8SDavid du Colombier * First field is key, second is byte offset into dictionary.
12219b2ee8SDavid du Colombier * Should be sorted with args -u -t' ' +0f -1 +0 -1 +1n -2
13219b2ee8SDavid du Colombier */
14219b2ee8SDavid du Colombier typedef struct Addr Addr;
15219b2ee8SDavid du Colombier
16219b2ee8SDavid du Colombier struct Addr {
17219b2ee8SDavid du Colombier int n; /* number of offsets */
18219b2ee8SDavid du Colombier int cur; /* current position within doff array */
19219b2ee8SDavid du Colombier int maxn; /* actual current size of doff array */
20219b2ee8SDavid du Colombier ulong doff[1]; /* doff[maxn], with 0..n-1 significant */
21219b2ee8SDavid du Colombier };
22219b2ee8SDavid du Colombier
23219b2ee8SDavid du Colombier Biobuf binbuf;
24219b2ee8SDavid du Colombier Biobuf boutbuf;
25219b2ee8SDavid du Colombier Biobuf *bin = &binbuf; /* user cmd input */
26219b2ee8SDavid du Colombier Biobuf *bout = &boutbuf; /* output */
27219b2ee8SDavid du Colombier Biobuf *bdict; /* dictionary */
28219b2ee8SDavid du Colombier Biobuf *bindex; /* index file */
29219b2ee8SDavid du Colombier long indextop; /* index offset at end of file */
30219b2ee8SDavid du Colombier int lastcmd; /* last executed command */
31219b2ee8SDavid du Colombier Addr *dot; /* "current" address */
32219b2ee8SDavid du Colombier Dict *dict; /* current dictionary */
33219b2ee8SDavid du Colombier int linelen;
34219b2ee8SDavid du Colombier int breaklen = 60;
35219b2ee8SDavid du Colombier int outinhibit;
36219b2ee8SDavid du Colombier int debug;
37219b2ee8SDavid du Colombier
38219b2ee8SDavid du Colombier void execcmd(int);
39219b2ee8SDavid du Colombier int getpref(char*, Rune*);
40219b2ee8SDavid du Colombier Entry getentry(int);
41219b2ee8SDavid du Colombier int getfield(Rune*);
42219b2ee8SDavid du Colombier long locate(Rune*);
43219b2ee8SDavid du Colombier int parseaddr(char*, char**);
44219b2ee8SDavid du Colombier int parsecmd(char*);
45219b2ee8SDavid du Colombier int search(char*, int);
46219b2ee8SDavid du Colombier long seeknextline(Biobuf*, long);
47219b2ee8SDavid du Colombier void setdotnext(void);
48219b2ee8SDavid du Colombier void setdotprev(void);
49219b2ee8SDavid du Colombier void sortaddr(Addr*);
50219b2ee8SDavid du Colombier void usage(void);
51219b2ee8SDavid du Colombier
52219b2ee8SDavid du Colombier enum {
53219b2ee8SDavid du Colombier Plen=300, /* max length of a search pattern */
54219b2ee8SDavid du Colombier Fieldlen=200, /* max length of an index field */
55219b2ee8SDavid du Colombier Aslots=10, /* initial number of slots in an address */
56219b2ee8SDavid du Colombier };
57219b2ee8SDavid du Colombier
58219b2ee8SDavid du Colombier void
main(int argc,char ** argv)59219b2ee8SDavid du Colombier main(int argc, char **argv)
60219b2ee8SDavid du Colombier {
61219b2ee8SDavid du Colombier int i, cmd, kflag;
62219b2ee8SDavid du Colombier char *line, *p;
63219b2ee8SDavid du Colombier
64219b2ee8SDavid du Colombier Binit(&binbuf, 0, OREAD);
65219b2ee8SDavid du Colombier Binit(&boutbuf, 1, OWRITE);
66219b2ee8SDavid du Colombier kflag = 0;
67219b2ee8SDavid du Colombier line = 0;
68*705edaf8SDavid du Colombier dict = 0;
69*705edaf8SDavid du Colombier for(i=0; dicts[i].name; i++){
70*705edaf8SDavid du Colombier if(access(dicts[i].path, 0)>=0 && access(dicts[i].indexpath, 0)>=0){
71*705edaf8SDavid du Colombier dict = &dicts[i];
72*705edaf8SDavid du Colombier break;
73*705edaf8SDavid du Colombier }
74*705edaf8SDavid du Colombier }
75219b2ee8SDavid du Colombier ARGBEGIN {
76219b2ee8SDavid du Colombier case 'd':
77219b2ee8SDavid du Colombier p = ARGF();
78219b2ee8SDavid du Colombier dict = 0;
79219b2ee8SDavid du Colombier if(p) {
80219b2ee8SDavid du Colombier for(i=0; dicts[i].name; i++)
81219b2ee8SDavid du Colombier if(strcmp(p, dicts[i].name)==0) {
82219b2ee8SDavid du Colombier dict = &dicts[i];
83219b2ee8SDavid du Colombier break;
84219b2ee8SDavid du Colombier }
85219b2ee8SDavid du Colombier }
86219b2ee8SDavid du Colombier if(!dict)
87219b2ee8SDavid du Colombier usage();
88219b2ee8SDavid du Colombier break;
89219b2ee8SDavid du Colombier case 'c':
90219b2ee8SDavid du Colombier line = ARGF();
91219b2ee8SDavid du Colombier if(!line)
92219b2ee8SDavid du Colombier usage();
93219b2ee8SDavid du Colombier break;
94219b2ee8SDavid du Colombier case 'k':
95219b2ee8SDavid du Colombier kflag++;
96219b2ee8SDavid du Colombier break;
97219b2ee8SDavid du Colombier case 'D':
98219b2ee8SDavid du Colombier debug++;
99219b2ee8SDavid du Colombier break;
100219b2ee8SDavid du Colombier default:
101219b2ee8SDavid du Colombier usage();
102219b2ee8SDavid du Colombier ARGEND }
103*705edaf8SDavid du Colombier if(dict == 0){
104*705edaf8SDavid du Colombier err("no dictionaries present on this system");
105*705edaf8SDavid du Colombier exits("nodict");
106*705edaf8SDavid du Colombier }
107*705edaf8SDavid du Colombier
108219b2ee8SDavid du Colombier if(kflag) {
109219b2ee8SDavid du Colombier (*dict->printkey)();
110219b2ee8SDavid du Colombier exits(0);
111219b2ee8SDavid du Colombier }
112219b2ee8SDavid du Colombier if(argc > 1)
113219b2ee8SDavid du Colombier usage();
114219b2ee8SDavid du Colombier else if(argc == 1) {
115219b2ee8SDavid du Colombier if(line)
116219b2ee8SDavid du Colombier usage();
117219b2ee8SDavid du Colombier p = argv[0];
118219b2ee8SDavid du Colombier line = malloc(strlen(p)+5);
119219b2ee8SDavid du Colombier sprint(line, "/%s/P\n", p);
120219b2ee8SDavid du Colombier }
121219b2ee8SDavid du Colombier bdict = Bopen(dict->path, OREAD);
122219b2ee8SDavid du Colombier if(!bdict) {
123219b2ee8SDavid du Colombier err("can't open dictionary %s", dict->path);
124219b2ee8SDavid du Colombier exits("nodict");
125219b2ee8SDavid du Colombier }
126219b2ee8SDavid du Colombier bindex = Bopen(dict->indexpath, OREAD);
127219b2ee8SDavid du Colombier if(!bindex) {
128219b2ee8SDavid du Colombier err("can't open index %s", dict->indexpath);
129219b2ee8SDavid du Colombier exits("noindex");
130219b2ee8SDavid du Colombier }
131219b2ee8SDavid du Colombier indextop = Bseek(bindex, 0L, 2);
132219b2ee8SDavid du Colombier
133219b2ee8SDavid du Colombier dot = malloc(sizeof(Addr)+(Aslots-1)*sizeof(ulong));
134219b2ee8SDavid du Colombier dot->n = 0;
135219b2ee8SDavid du Colombier dot->cur = 0;
136219b2ee8SDavid du Colombier dot->maxn = Aslots;
137219b2ee8SDavid du Colombier lastcmd = 0;
138219b2ee8SDavid du Colombier
139219b2ee8SDavid du Colombier if(line) {
140219b2ee8SDavid du Colombier cmd = parsecmd(line);
141219b2ee8SDavid du Colombier if(cmd)
142219b2ee8SDavid du Colombier execcmd(cmd);
143219b2ee8SDavid du Colombier } else {
144219b2ee8SDavid du Colombier for(;;) {
145219b2ee8SDavid du Colombier Bprint(bout, "*");
146219b2ee8SDavid du Colombier Bflush(bout);
147219b2ee8SDavid du Colombier line = Brdline(bin, '\n');
148219b2ee8SDavid du Colombier linelen = 0;
149219b2ee8SDavid du Colombier if(!line)
150219b2ee8SDavid du Colombier break;
151219b2ee8SDavid du Colombier cmd = parsecmd(line);
152219b2ee8SDavid du Colombier if(cmd) {
153219b2ee8SDavid du Colombier execcmd(cmd);
154219b2ee8SDavid du Colombier lastcmd = cmd;
155219b2ee8SDavid du Colombier }
156219b2ee8SDavid du Colombier }
157219b2ee8SDavid du Colombier }
158219b2ee8SDavid du Colombier exits(0);
159219b2ee8SDavid du Colombier }
160219b2ee8SDavid du Colombier
161219b2ee8SDavid du Colombier void
usage(void)162219b2ee8SDavid du Colombier usage(void)
163219b2ee8SDavid du Colombier {
164219b2ee8SDavid du Colombier int i;
165*705edaf8SDavid du Colombier char *a, *b;
166219b2ee8SDavid du Colombier
167219b2ee8SDavid du Colombier Bprint(bout, "Usage: %s [-d dict] [-k] [-c cmd] [word]\n", argv0);
168*705edaf8SDavid du Colombier Bprint(bout, "dictionaries (brackets mark dictionaries not present on this system):\n");
169*705edaf8SDavid du Colombier for(i = 0; dicts[i].name; i++){
170*705edaf8SDavid du Colombier a = b = "";
171*705edaf8SDavid du Colombier if(access(dicts[i].path, 0)<0 || access(dicts[i].indexpath, 0)<0){
172*705edaf8SDavid du Colombier a = "[";
173*705edaf8SDavid du Colombier b = "]";
174*705edaf8SDavid du Colombier }
175*705edaf8SDavid du Colombier Bprint(bout, " %s%s\t%s%s\n", a, dicts[i].name, dicts[i].desc, b);
176*705edaf8SDavid du Colombier }
177219b2ee8SDavid du Colombier exits("usage");
178219b2ee8SDavid du Colombier }
179219b2ee8SDavid du Colombier
180219b2ee8SDavid du Colombier int
parsecmd(char * line)181219b2ee8SDavid du Colombier parsecmd(char *line)
182219b2ee8SDavid du Colombier {
183219b2ee8SDavid du Colombier char *e;
184219b2ee8SDavid du Colombier int cmd, ans;
185219b2ee8SDavid du Colombier
186219b2ee8SDavid du Colombier if(parseaddr(line, &e) >= 0)
187219b2ee8SDavid du Colombier line = e;
188219b2ee8SDavid du Colombier else
189219b2ee8SDavid du Colombier return 0;
190219b2ee8SDavid du Colombier cmd = *line;
191219b2ee8SDavid du Colombier ans = cmd;
192219b2ee8SDavid du Colombier if(isupper(cmd))
193219b2ee8SDavid du Colombier cmd = tolower(cmd);
194219b2ee8SDavid du Colombier if(!(cmd == 'a' || cmd == 'h' || cmd == 'p' || cmd == 'r' ||
195219b2ee8SDavid du Colombier cmd == '\n')) {
196219b2ee8SDavid du Colombier err("unknown command %c", cmd);
197219b2ee8SDavid du Colombier return 0;
198219b2ee8SDavid du Colombier }
199219b2ee8SDavid du Colombier if(cmd == '\n')
200219b2ee8SDavid du Colombier switch(lastcmd) {
201219b2ee8SDavid du Colombier case 0: ans = 'H'; break;
202219b2ee8SDavid du Colombier case 'H': ans = 'p'; break;
203219b2ee8SDavid du Colombier default : ans = lastcmd; break;
204219b2ee8SDavid du Colombier }
205219b2ee8SDavid du Colombier else if(line[1] != '\n' && line[1] != 0)
206219b2ee8SDavid du Colombier err("extra stuff after command %c ignored", cmd);
207219b2ee8SDavid du Colombier return ans;
208219b2ee8SDavid du Colombier }
209219b2ee8SDavid du Colombier
210219b2ee8SDavid du Colombier void
execcmd(int cmd)211219b2ee8SDavid du Colombier execcmd(int cmd)
212219b2ee8SDavid du Colombier {
213219b2ee8SDavid du Colombier Entry e;
214219b2ee8SDavid du Colombier int cur, doall;
215219b2ee8SDavid du Colombier
216219b2ee8SDavid du Colombier if(isupper(cmd)) {
217219b2ee8SDavid du Colombier doall = 1;
218219b2ee8SDavid du Colombier cmd = tolower(cmd);
219219b2ee8SDavid du Colombier cur = 0;
220219b2ee8SDavid du Colombier } else {
221219b2ee8SDavid du Colombier doall = 0;
222219b2ee8SDavid du Colombier cur = dot->cur;
223219b2ee8SDavid du Colombier }
224219b2ee8SDavid du Colombier
225219b2ee8SDavid du Colombier if(debug && doall && cmd == 'a')
226219b2ee8SDavid du Colombier Bprint(bout, "%d entries, cur=%d\n", dot->n, cur+1);
227219b2ee8SDavid du Colombier for(;;){
228219b2ee8SDavid du Colombier if(cur >= dot->n)
229219b2ee8SDavid du Colombier break;
230219b2ee8SDavid du Colombier if(doall) {
231219b2ee8SDavid du Colombier Bprint(bout, "%d\t", cur+1);
232219b2ee8SDavid du Colombier linelen += 4 + (cur >= 10);
233219b2ee8SDavid du Colombier }
234219b2ee8SDavid du Colombier switch(cmd) {
235219b2ee8SDavid du Colombier case 'a':
236219b2ee8SDavid du Colombier Bprint(bout, "#%lud\n", dot->doff[cur]);
237219b2ee8SDavid du Colombier break;
238219b2ee8SDavid du Colombier case 'h':
239219b2ee8SDavid du Colombier case 'p':
240219b2ee8SDavid du Colombier case 'r':
241219b2ee8SDavid du Colombier e = getentry(cur);
242219b2ee8SDavid du Colombier (*dict->printentry)(e, cmd);
243219b2ee8SDavid du Colombier break;
244219b2ee8SDavid du Colombier }
245219b2ee8SDavid du Colombier cur++;
246219b2ee8SDavid du Colombier if(doall) {
247219b2ee8SDavid du Colombier if(cmd == 'p' || cmd == 'r') {
248219b2ee8SDavid du Colombier Bputc(bout, '\n');
249219b2ee8SDavid du Colombier linelen = 0;
250219b2ee8SDavid du Colombier }
251219b2ee8SDavid du Colombier } else
252219b2ee8SDavid du Colombier break;
253219b2ee8SDavid du Colombier }
254219b2ee8SDavid du Colombier if(cur >= dot->n)
255219b2ee8SDavid du Colombier cur = 0;
256219b2ee8SDavid du Colombier dot->cur = cur;
257219b2ee8SDavid du Colombier }
258219b2ee8SDavid du Colombier
259219b2ee8SDavid du Colombier /*
260219b2ee8SDavid du Colombier * Address syntax: ('.' | '/' re '/' | '!' re '!' | number | '#' number) ('+' | '-')*
261219b2ee8SDavid du Colombier * Answer goes in dot.
262219b2ee8SDavid du Colombier * Return -1 if address starts, but get error.
263219b2ee8SDavid du Colombier * Return 0 if no address.
264219b2ee8SDavid du Colombier */
265219b2ee8SDavid du Colombier int
parseaddr(char * line,char ** eptr)266219b2ee8SDavid du Colombier parseaddr(char *line, char **eptr)
267219b2ee8SDavid du Colombier {
268219b2ee8SDavid du Colombier int delim, plen;
269219b2ee8SDavid du Colombier ulong v;
270219b2ee8SDavid du Colombier char *e;
271219b2ee8SDavid du Colombier char pat[Plen];
272219b2ee8SDavid du Colombier
273219b2ee8SDavid du Colombier if(*line == '/' || *line == '!') {
274219b2ee8SDavid du Colombier /* anchored regular expression match; '!' means no folding */
275219b2ee8SDavid du Colombier if(*line == '/') {
276219b2ee8SDavid du Colombier delim = '/';
277219b2ee8SDavid du Colombier e = strpbrk(line+1, "/\n");
278219b2ee8SDavid du Colombier } else {
279219b2ee8SDavid du Colombier delim = '!';
280219b2ee8SDavid du Colombier e = strpbrk(line+1, "!\n");
281219b2ee8SDavid du Colombier }
282219b2ee8SDavid du Colombier plen = e-line-1;
283219b2ee8SDavid du Colombier if(plen >= Plen-3) {
284219b2ee8SDavid du Colombier err("pattern too big");
285219b2ee8SDavid du Colombier return -1;
286219b2ee8SDavid du Colombier }
287219b2ee8SDavid du Colombier pat[0] = '^';
288219b2ee8SDavid du Colombier memcpy(pat+1, line+1, plen);
289219b2ee8SDavid du Colombier pat[plen+1] = '$';
290219b2ee8SDavid du Colombier pat[plen+2] = 0;
291219b2ee8SDavid du Colombier if(*e == '\n')
292219b2ee8SDavid du Colombier line = e;
293219b2ee8SDavid du Colombier else
294219b2ee8SDavid du Colombier line = e+1;
295219b2ee8SDavid du Colombier if(!search(pat, delim == '/')) {
296219b2ee8SDavid du Colombier err("pattern not found");
297219b2ee8SDavid du Colombier return -1;
298219b2ee8SDavid du Colombier }
299219b2ee8SDavid du Colombier } else if(*line == '#') {
300219b2ee8SDavid du Colombier /* absolute byte offset into dictionary */
301219b2ee8SDavid du Colombier line++;
302219b2ee8SDavid du Colombier if(!isdigit(*line))
303219b2ee8SDavid du Colombier return -1;
304219b2ee8SDavid du Colombier v = strtoul(line, &e, 10);
305219b2ee8SDavid du Colombier line = e;
306219b2ee8SDavid du Colombier dot->doff[0] = v;
307219b2ee8SDavid du Colombier dot->n = 1;
308219b2ee8SDavid du Colombier dot->cur = 0;
309219b2ee8SDavid du Colombier } else if(isdigit(*line)) {
310219b2ee8SDavid du Colombier v = strtoul(line, &e, 10);
311219b2ee8SDavid du Colombier line = e;
312219b2ee8SDavid du Colombier if(v < 1 || v > dot->n)
313219b2ee8SDavid du Colombier err(".%d not in range [1,%d], ignored",
314219b2ee8SDavid du Colombier v, dot->n);
315219b2ee8SDavid du Colombier else
316219b2ee8SDavid du Colombier dot->cur = v-1;
317219b2ee8SDavid du Colombier } else if(*line == '.') {
318219b2ee8SDavid du Colombier line++;
319219b2ee8SDavid du Colombier } else {
320219b2ee8SDavid du Colombier *eptr = line;
321219b2ee8SDavid du Colombier return 0;
322219b2ee8SDavid du Colombier }
323219b2ee8SDavid du Colombier while(*line == '+' || *line == '-') {
324219b2ee8SDavid du Colombier if(*line == '+')
325219b2ee8SDavid du Colombier setdotnext();
326219b2ee8SDavid du Colombier else
327219b2ee8SDavid du Colombier setdotprev();
328219b2ee8SDavid du Colombier line++;
329219b2ee8SDavid du Colombier }
330219b2ee8SDavid du Colombier *eptr = line;
331219b2ee8SDavid du Colombier return 1;
332219b2ee8SDavid du Colombier }
333219b2ee8SDavid du Colombier
334219b2ee8SDavid du Colombier /*
335219b2ee8SDavid du Colombier * Index file is sorted by folded field1.
336219b2ee8SDavid du Colombier * Method: find pre, a folded prefix of r.e. pat,
337219b2ee8SDavid du Colombier * and then low = offset to beginning of
338219b2ee8SDavid du Colombier * line in index file where first match of prefix occurs.
339219b2ee8SDavid du Colombier * Then go through index until prefix no longer matches,
340219b2ee8SDavid du Colombier * adding each line that matches real pattern to dot.
341219b2ee8SDavid du Colombier * Finally, sort dot offsets (uniquing).
342219b2ee8SDavid du Colombier * We know pat len < Plen, and that it is surrounded by ^..$
343219b2ee8SDavid du Colombier */
344219b2ee8SDavid du Colombier int
search(char * pat,int dofold)345219b2ee8SDavid du Colombier search(char *pat, int dofold)
346219b2ee8SDavid du Colombier {
347219b2ee8SDavid du Colombier int needre, prelen, match, n;
348219b2ee8SDavid du Colombier Reprog *re;
349219b2ee8SDavid du Colombier long ioff, v;
350219b2ee8SDavid du Colombier Rune pre[Plen];
351219b2ee8SDavid du Colombier Rune lit[Plen];
352219b2ee8SDavid du Colombier Rune entry[Fieldlen];
353219b2ee8SDavid du Colombier char fpat[Plen];
354219b2ee8SDavid du Colombier
355219b2ee8SDavid du Colombier prelen = getpref(pat+1, pre);
356219b2ee8SDavid du Colombier if(pat[prelen+1] == 0 || pat[prelen+1] == '$') {
357219b2ee8SDavid du Colombier runescpy(lit, pre);
358219b2ee8SDavid du Colombier if(dofold)
359219b2ee8SDavid du Colombier fold(lit);
360219b2ee8SDavid du Colombier needre = 0;
361219b2ee8SDavid du Colombier SET(re);
362219b2ee8SDavid du Colombier } else {
363219b2ee8SDavid du Colombier needre = 1;
364219b2ee8SDavid du Colombier if(dofold) {
365219b2ee8SDavid du Colombier foldre(fpat, pat);
366219b2ee8SDavid du Colombier re = regcomp(fpat);
367219b2ee8SDavid du Colombier } else
368219b2ee8SDavid du Colombier re = regcomp(pat);
369219b2ee8SDavid du Colombier }
370219b2ee8SDavid du Colombier fold(pre);
371219b2ee8SDavid du Colombier ioff = locate(pre);
372219b2ee8SDavid du Colombier if(ioff < 0)
373219b2ee8SDavid du Colombier return 0;
374219b2ee8SDavid du Colombier dot->n = 0;
375219b2ee8SDavid du Colombier Bseek(bindex, ioff, 0);
376219b2ee8SDavid du Colombier for(;;) {
377219b2ee8SDavid du Colombier if(!getfield(entry))
378219b2ee8SDavid du Colombier break;
379219b2ee8SDavid du Colombier if(dofold)
380219b2ee8SDavid du Colombier fold(entry);
381219b2ee8SDavid du Colombier if(needre)
382219b2ee8SDavid du Colombier match = rregexec(re, entry, 0, 0);
383219b2ee8SDavid du Colombier else
384219b2ee8SDavid du Colombier match = (acomp(lit, entry) == 0);
385219b2ee8SDavid du Colombier if(match) {
386219b2ee8SDavid du Colombier if(!getfield(entry))
387219b2ee8SDavid du Colombier break;
388219b2ee8SDavid du Colombier v = runetol(entry);
389219b2ee8SDavid du Colombier if(dot->n >= dot->maxn) {
390219b2ee8SDavid du Colombier n = 2*dot->maxn;
391219b2ee8SDavid du Colombier dot = realloc(dot,
392219b2ee8SDavid du Colombier sizeof(Addr)+(n-1)*sizeof(long));
393219b2ee8SDavid du Colombier if(!dot) {
394219b2ee8SDavid du Colombier err("out of memory");
395219b2ee8SDavid du Colombier exits("nomem");
396219b2ee8SDavid du Colombier }
397219b2ee8SDavid du Colombier dot->maxn = n;
398219b2ee8SDavid du Colombier }
399219b2ee8SDavid du Colombier dot->doff[dot->n++] = v;
400219b2ee8SDavid du Colombier } else {
401219b2ee8SDavid du Colombier if(!dofold)
402219b2ee8SDavid du Colombier fold(entry);
403219b2ee8SDavid du Colombier if(*pre) {
404219b2ee8SDavid du Colombier n = acomp(pre, entry);
405219b2ee8SDavid du Colombier if(n < -1 || (!needre && n < 0))
406219b2ee8SDavid du Colombier break;
407219b2ee8SDavid du Colombier }
408219b2ee8SDavid du Colombier /* get to next index entry */
409219b2ee8SDavid du Colombier if(!getfield(entry))
410219b2ee8SDavid du Colombier break;
411219b2ee8SDavid du Colombier }
412219b2ee8SDavid du Colombier }
413219b2ee8SDavid du Colombier sortaddr(dot);
414219b2ee8SDavid du Colombier dot->cur = 0;
415219b2ee8SDavid du Colombier return dot->n;
416219b2ee8SDavid du Colombier }
417219b2ee8SDavid du Colombier
418219b2ee8SDavid du Colombier /*
419219b2ee8SDavid du Colombier * Return offset in index file of first line whose folded
420219b2ee8SDavid du Colombier * first field has pre as a prefix. -1 if none found.
421219b2ee8SDavid du Colombier */
422219b2ee8SDavid du Colombier long
locate(Rune * pre)423219b2ee8SDavid du Colombier locate(Rune *pre)
424219b2ee8SDavid du Colombier {
425219b2ee8SDavid du Colombier long top, bot, mid;
426219b2ee8SDavid du Colombier Rune entry[Fieldlen];
427219b2ee8SDavid du Colombier
428219b2ee8SDavid du Colombier if(*pre == 0)
429219b2ee8SDavid du Colombier return 0;
430219b2ee8SDavid du Colombier bot = 0;
431219b2ee8SDavid du Colombier top = indextop;
432219b2ee8SDavid du Colombier if(debug>1)
433219b2ee8SDavid du Colombier fprint(2, "locate looking for prefix %S\n", pre);
434219b2ee8SDavid du Colombier for(;;) {
435219b2ee8SDavid du Colombier /*
436219b2ee8SDavid du Colombier * Loop invariant: foldkey(bot) < pre <= foldkey(top)
437219b2ee8SDavid du Colombier * and bot < top, and bot,top point at beginning of lines
438219b2ee8SDavid du Colombier */
439219b2ee8SDavid du Colombier mid = (top+bot) / 2;
440219b2ee8SDavid du Colombier mid = seeknextline(bindex, mid);
441219b2ee8SDavid du Colombier if(debug > 1)
442219b2ee8SDavid du Colombier fprint(2, "bot=%ld, mid=%ld->%ld, top=%ld\n",
443219b2ee8SDavid du Colombier bot, (top+bot) / 2, mid, top);
444219b2ee8SDavid du Colombier if(mid == top || !getfield(entry))
445219b2ee8SDavid du Colombier break;
446219b2ee8SDavid du Colombier if(debug > 1)
447219b2ee8SDavid du Colombier fprint(2, "key=%S\n", entry);
448219b2ee8SDavid du Colombier /*
449219b2ee8SDavid du Colombier * here mid is strictly between bot and top
450219b2ee8SDavid du Colombier */
451219b2ee8SDavid du Colombier fold(entry);
452219b2ee8SDavid du Colombier if(acomp(pre, entry) <= 0)
453219b2ee8SDavid du Colombier top = mid;
454219b2ee8SDavid du Colombier else
455219b2ee8SDavid du Colombier bot = mid;
456219b2ee8SDavid du Colombier }
457219b2ee8SDavid du Colombier /*
458219b2ee8SDavid du Colombier * bot < top, but they don't necessarily point at successive lines
459219b2ee8SDavid du Colombier * Use linear search from bot to find first line that pre is a
460219b2ee8SDavid du Colombier * prefix of
461219b2ee8SDavid du Colombier */
462219b2ee8SDavid du Colombier while((bot = seeknextline(bindex, bot)) <= top) {
463219b2ee8SDavid du Colombier if(!getfield(entry))
464219b2ee8SDavid du Colombier return -1;
465219b2ee8SDavid du Colombier if(debug > 1)
466219b2ee8SDavid du Colombier fprint(2, "key=%S\n", entry);
467219b2ee8SDavid du Colombier fold(entry);
468219b2ee8SDavid du Colombier switch(acomp(pre, entry)) {
469219b2ee8SDavid du Colombier case -2:
470219b2ee8SDavid du Colombier return -1;
471219b2ee8SDavid du Colombier case -1:
472219b2ee8SDavid du Colombier case 0:
473219b2ee8SDavid du Colombier return bot;
474219b2ee8SDavid du Colombier case 1:
475219b2ee8SDavid du Colombier case 2:
476219b2ee8SDavid du Colombier continue;
477219b2ee8SDavid du Colombier }
478219b2ee8SDavid du Colombier }
479219b2ee8SDavid du Colombier return -1;
480219b2ee8SDavid du Colombier
481219b2ee8SDavid du Colombier }
482219b2ee8SDavid du Colombier
483219b2ee8SDavid du Colombier /*
484219b2ee8SDavid du Colombier * Get prefix of non re-metacharacters, runified, into pre,
485219b2ee8SDavid du Colombier * and return length
486219b2ee8SDavid du Colombier */
487219b2ee8SDavid du Colombier int
getpref(char * pat,Rune * pre)488219b2ee8SDavid du Colombier getpref(char *pat, Rune *pre)
489219b2ee8SDavid du Colombier {
490219b2ee8SDavid du Colombier int n, r;
491219b2ee8SDavid du Colombier char *p;
492219b2ee8SDavid du Colombier
493219b2ee8SDavid du Colombier p = pat;
494219b2ee8SDavid du Colombier while(*p) {
495219b2ee8SDavid du Colombier n = chartorune(pre, p);
496219b2ee8SDavid du Colombier r = *pre;
497219b2ee8SDavid du Colombier switch(r) {
498219b2ee8SDavid du Colombier case L'.': case L'*': case L'+': case L'?':
499219b2ee8SDavid du Colombier case L'[': case L']': case L'(': case ')':
500219b2ee8SDavid du Colombier case L'|': case L'^': case L'$':
501219b2ee8SDavid du Colombier *pre = 0;
502219b2ee8SDavid du Colombier return p-pat;
503219b2ee8SDavid du Colombier case L'\\':
504219b2ee8SDavid du Colombier p += n;
505219b2ee8SDavid du Colombier p += chartorune(++pre, p);
506219b2ee8SDavid du Colombier pre++;
507219b2ee8SDavid du Colombier break;
508219b2ee8SDavid du Colombier default:
509219b2ee8SDavid du Colombier p += n;
510219b2ee8SDavid du Colombier pre++;
511219b2ee8SDavid du Colombier }
512219b2ee8SDavid du Colombier }
513219b2ee8SDavid du Colombier return p-pat;
514219b2ee8SDavid du Colombier }
515219b2ee8SDavid du Colombier
516219b2ee8SDavid du Colombier long
seeknextline(Biobuf * b,long off)517219b2ee8SDavid du Colombier seeknextline(Biobuf *b, long off)
518219b2ee8SDavid du Colombier {
519219b2ee8SDavid du Colombier long c;
520219b2ee8SDavid du Colombier
521219b2ee8SDavid du Colombier Bseek(b, off, 0);
522219b2ee8SDavid du Colombier do {
523219b2ee8SDavid du Colombier c = Bgetrune(b);
524219b2ee8SDavid du Colombier } while(c>=0 && c!='\n');
525219b2ee8SDavid du Colombier return Boffset(b);
526219b2ee8SDavid du Colombier }
527219b2ee8SDavid du Colombier
528219b2ee8SDavid du Colombier /*
529219b2ee8SDavid du Colombier * Get next field out of index file (either tab- or nl- terminated)
530219b2ee8SDavid du Colombier * Answer in *rp, assumed to be Fieldlen long.
531219b2ee8SDavid du Colombier * Return 0 if read error first.
532219b2ee8SDavid du Colombier */
533219b2ee8SDavid du Colombier int
getfield(Rune * rp)534219b2ee8SDavid du Colombier getfield(Rune *rp)
535219b2ee8SDavid du Colombier {
536219b2ee8SDavid du Colombier long c;
537219b2ee8SDavid du Colombier int n;
538219b2ee8SDavid du Colombier
539219b2ee8SDavid du Colombier for(n=Fieldlen; n-- > 0; ) {
540219b2ee8SDavid du Colombier if ((c = Bgetrune(bindex)) < 0)
541219b2ee8SDavid du Colombier return 0;
542219b2ee8SDavid du Colombier if(c == '\t' || c == '\n') {
543219b2ee8SDavid du Colombier *rp = L'\0';
544219b2ee8SDavid du Colombier return 1;
545219b2ee8SDavid du Colombier }
546219b2ee8SDavid du Colombier *rp++ = c;
547219b2ee8SDavid du Colombier }
548219b2ee8SDavid du Colombier err("word too long");
549219b2ee8SDavid du Colombier return 0;
550219b2ee8SDavid du Colombier }
551219b2ee8SDavid du Colombier
552219b2ee8SDavid du Colombier /*
553219b2ee8SDavid du Colombier * A compare longs function suitable for qsort
554219b2ee8SDavid du Colombier */
555219b2ee8SDavid du Colombier static int
longcmp(void * av,void * bv)5567dd7cddfSDavid du Colombier longcmp(void *av, void *bv)
557219b2ee8SDavid du Colombier {
558219b2ee8SDavid du Colombier long v;
5597dd7cddfSDavid du Colombier long *a, *b;
5607dd7cddfSDavid du Colombier
5617dd7cddfSDavid du Colombier a = av;
5627dd7cddfSDavid du Colombier b = bv;
563219b2ee8SDavid du Colombier
564219b2ee8SDavid du Colombier v = *a - *b;
565219b2ee8SDavid du Colombier if(v < 0)
566219b2ee8SDavid du Colombier return -1;
567219b2ee8SDavid du Colombier else if(v == 0)
568219b2ee8SDavid du Colombier return 0;
569219b2ee8SDavid du Colombier else
570219b2ee8SDavid du Colombier return 1;
571219b2ee8SDavid du Colombier }
572219b2ee8SDavid du Colombier
573219b2ee8SDavid du Colombier void
sortaddr(Addr * a)574219b2ee8SDavid du Colombier sortaddr(Addr *a)
575219b2ee8SDavid du Colombier {
576219b2ee8SDavid du Colombier int i, j;
577219b2ee8SDavid du Colombier long v;
578219b2ee8SDavid du Colombier
579219b2ee8SDavid du Colombier if(a->n <= 1)
580219b2ee8SDavid du Colombier return;
581219b2ee8SDavid du Colombier
582219b2ee8SDavid du Colombier qsort(a->doff, a->n, sizeof(long), longcmp);
583219b2ee8SDavid du Colombier
584219b2ee8SDavid du Colombier /* remove duplicates */
585219b2ee8SDavid du Colombier for(i=0, j=0; j < a->n; j++) {
586219b2ee8SDavid du Colombier v = a->doff[j];
587219b2ee8SDavid du Colombier if(i > 0 && v == a->doff[i-1])
588219b2ee8SDavid du Colombier continue;
589219b2ee8SDavid du Colombier a->doff[i++] = v;
590219b2ee8SDavid du Colombier }
591219b2ee8SDavid du Colombier a->n = i;
592219b2ee8SDavid du Colombier }
593219b2ee8SDavid du Colombier
594219b2ee8SDavid du Colombier Entry
getentry(int i)595219b2ee8SDavid du Colombier getentry(int i)
596219b2ee8SDavid du Colombier {
597219b2ee8SDavid du Colombier long b, e, n;
598219b2ee8SDavid du Colombier static Entry ans;
599219b2ee8SDavid du Colombier static int anslen = 0;
600219b2ee8SDavid du Colombier
601219b2ee8SDavid du Colombier b = dot->doff[i];
602219b2ee8SDavid du Colombier e = (*dict->nextoff)(b+1);
603219b2ee8SDavid du Colombier ans.doff = b;
604219b2ee8SDavid du Colombier if(e < 0) {
605219b2ee8SDavid du Colombier err("couldn't seek to entry");
606219b2ee8SDavid du Colombier ans.start = 0;
607219b2ee8SDavid du Colombier ans.end = 0;
608219b2ee8SDavid du Colombier } else {
609219b2ee8SDavid du Colombier n = e-b;
610219b2ee8SDavid du Colombier if(n+1 > anslen) {
611219b2ee8SDavid du Colombier ans.start = realloc(ans.start, n+1);
612219b2ee8SDavid du Colombier if(!ans.start) {
613219b2ee8SDavid du Colombier err("out of memory");
614219b2ee8SDavid du Colombier exits("nomem");
615219b2ee8SDavid du Colombier }
616219b2ee8SDavid du Colombier anslen = n+1;
617219b2ee8SDavid du Colombier }
618219b2ee8SDavid du Colombier Bseek(bdict, b, 0);
619219b2ee8SDavid du Colombier n = Bread(bdict, ans.start, n);
620219b2ee8SDavid du Colombier ans.end = ans.start + n;
621219b2ee8SDavid du Colombier *ans.end = 0;
622219b2ee8SDavid du Colombier }
623219b2ee8SDavid du Colombier return ans;
624219b2ee8SDavid du Colombier }
625219b2ee8SDavid du Colombier
626219b2ee8SDavid du Colombier void
setdotnext(void)627219b2ee8SDavid du Colombier setdotnext(void)
628219b2ee8SDavid du Colombier {
629219b2ee8SDavid du Colombier long b;
630219b2ee8SDavid du Colombier
631219b2ee8SDavid du Colombier b = (*dict->nextoff)(dot->doff[dot->cur]+1);
632219b2ee8SDavid du Colombier if(b < 0) {
633219b2ee8SDavid du Colombier err("couldn't find a next entry");
634219b2ee8SDavid du Colombier return;
635219b2ee8SDavid du Colombier }
636219b2ee8SDavid du Colombier dot->doff[0] = b;
637219b2ee8SDavid du Colombier dot->n = 1;
638219b2ee8SDavid du Colombier dot->cur = 0;
639219b2ee8SDavid du Colombier }
640219b2ee8SDavid du Colombier
641219b2ee8SDavid du Colombier void
setdotprev(void)642219b2ee8SDavid du Colombier setdotprev(void)
643219b2ee8SDavid du Colombier {
644219b2ee8SDavid du Colombier int tryback;
645219b2ee8SDavid du Colombier long here, last, p;
646219b2ee8SDavid du Colombier
647219b2ee8SDavid du Colombier if(dot->cur < 0 || dot->cur >= dot->n)
648219b2ee8SDavid du Colombier return;
649219b2ee8SDavid du Colombier tryback = 2000;
650219b2ee8SDavid du Colombier here = dot->doff[dot->cur];
651219b2ee8SDavid du Colombier last = 0;
652219b2ee8SDavid du Colombier while(last == 0) {
653219b2ee8SDavid du Colombier p = here - tryback;
654219b2ee8SDavid du Colombier if(p < 0)
655219b2ee8SDavid du Colombier p = 0;
656219b2ee8SDavid du Colombier for(;;) {
657219b2ee8SDavid du Colombier p = (*dict->nextoff)(p+1);
658219b2ee8SDavid du Colombier if(p < 0)
659219b2ee8SDavid du Colombier return; /* shouldn't happen */
660219b2ee8SDavid du Colombier if(p >= here)
661219b2ee8SDavid du Colombier break;
662219b2ee8SDavid du Colombier last = p;
663219b2ee8SDavid du Colombier }
664219b2ee8SDavid du Colombier if(!last) {
665219b2ee8SDavid du Colombier if(here - tryback < 0) {
666219b2ee8SDavid du Colombier err("can't find a previous entry");
667219b2ee8SDavid du Colombier return;
668219b2ee8SDavid du Colombier }
669219b2ee8SDavid du Colombier tryback = 2*tryback;
670219b2ee8SDavid du Colombier }
671219b2ee8SDavid du Colombier }
672219b2ee8SDavid du Colombier dot->doff[0] = last;
673219b2ee8SDavid du Colombier dot->n = 1;
674219b2ee8SDavid du Colombier dot->cur = 0;
675219b2ee8SDavid du Colombier }
676