]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/one-true-awk/b.c
ping: use the monotonic clock to measure durations
[FreeBSD/FreeBSD.git] / contrib / one-true-awk / b.c
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24
25 /* lasciate ogne speranza, voi ch'intrate. */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #define DEBUG
31
32 #include <ctype.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include "awk.h"
38 #include "ytab.h"
39
40 #define HAT     (NCHARS+2)      /* matches ^ in regular expr */
41                                 /* NCHARS is 2**n */
42 #define MAXLIN 22
43
44 #define type(v)         (v)->nobj       /* badly overloaded here */
45 #define info(v)         (v)->ntype      /* badly overloaded here */
46 #define left(v)         (v)->narg[0]
47 #define right(v)        (v)->narg[1]
48 #define parent(v)       (v)->nnext
49
50 #define LEAF    case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
51 #define ELEAF   case EMPTYRE:           /* empty string in regexp */
52 #define UNARY   case STAR: case PLUS: case QUEST:
53
54 /* encoding in tree Nodes:
55         leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
56                 left is index, right contains value or pointer to value
57         unary (STAR, PLUS, QUEST): left is child, right is null
58         binary (CAT, OR): left and right are children
59         parent contains pointer to parent
60 */
61
62
63 int     *setvec;
64 int     *tmpset;
65 int     maxsetvec = 0;
66
67 int     rtok;           /* next token in current re */
68 int     rlxval;
69 static uschar   *rlxstr;
70 static uschar   *prestr;        /* current position in current re */
71 static uschar   *lastre;        /* origin of last re */
72 static uschar   *lastatom;      /* origin of last Atom */
73 static uschar   *starttok;
74 static uschar   *basestr;       /* starts with original, replaced during
75                                    repetition processing */
76 static uschar   *firstbasestr;
77
78 static  int setcnt;
79 static  int poscnt;
80
81 char    *patbeg;
82 int     patlen;
83
84 #define NFA     20      /* cache this many dynamic fa's */
85 fa      *fatab[NFA];
86 int     nfatab  = 0;    /* entries in fatab */
87
88 fa *makedfa(const char *s, int anchor)  /* returns dfa for reg expr s */
89 {
90         int i, use, nuse;
91         fa *pfa;
92         static int now = 1;
93
94         if (setvec == NULL) {   /* first time through any RE */
95                 maxsetvec = MAXLIN;
96                 setvec = (int *) malloc(maxsetvec * sizeof(int));
97                 tmpset = (int *) malloc(maxsetvec * sizeof(int));
98                 if (setvec == NULL || tmpset == NULL)
99                         overflo("out of space initializing makedfa");
100         }
101
102         if (compile_time)       /* a constant for sure */
103                 return mkdfa(s, anchor);
104         for (i = 0; i < nfatab; i++)    /* is it there already? */
105                 if (fatab[i]->anchor == anchor
106                   && strcmp((const char *) fatab[i]->restr, s) == 0) {
107                         fatab[i]->use = now++;
108                         return fatab[i];
109                 }
110         pfa = mkdfa(s, anchor);
111         if (nfatab < NFA) {     /* room for another */
112                 fatab[nfatab] = pfa;
113                 fatab[nfatab]->use = now++;
114                 nfatab++;
115                 return pfa;
116         }
117         use = fatab[0]->use;    /* replace least-recently used */
118         nuse = 0;
119         for (i = 1; i < nfatab; i++)
120                 if (fatab[i]->use < use) {
121                         use = fatab[i]->use;
122                         nuse = i;
123                 }
124         freefa(fatab[nuse]);
125         fatab[nuse] = pfa;
126         pfa->use = now++;
127         return pfa;
128 }
129
130 fa *mkdfa(const char *s, int anchor)    /* does the real work of making a dfa */
131                                 /* anchor = 1 for anchored matches, else 0 */
132 {
133         Node *p, *p1;
134         fa *f;
135
136         firstbasestr = (uschar *) s;
137         basestr = firstbasestr;
138         p = reparse(s);
139         p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
140                 /* put ALL STAR in front of reg.  exp. */
141         p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
142                 /* put FINAL after reg.  exp. */
143
144         poscnt = 0;
145         penter(p1);     /* enter parent pointers and leaf indices */
146         if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL)
147                 overflo("out of space for fa");
148         f->accept = poscnt-1;   /* penter has computed number of positions in re */
149         cfoll(f, p1);   /* set up follow sets */
150         freetr(p1);
151         if ((f->posns[0] = (int *) calloc(*(f->re[0].lfollow), sizeof(int))) == NULL)
152                         overflo("out of space in makedfa");
153         if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL)
154                 overflo("out of space in makedfa");
155         *f->posns[1] = 0;
156         f->initstat = makeinit(f, anchor);
157         f->anchor = anchor;
158         f->restr = (uschar *) tostring(s);
159         if (firstbasestr != basestr) {
160                 if (basestr)
161                         xfree(basestr);
162         }
163         return f;
164 }
165
166 int makeinit(fa *f, int anchor)
167 {
168         int i, k;
169
170         f->curstat = 2;
171         f->out[2] = 0;
172         f->reset = 0;
173         k = *(f->re[0].lfollow);
174         xfree(f->posns[2]);                     
175         if ((f->posns[2] = (int *) calloc(k+1, sizeof(int))) == NULL)
176                 overflo("out of space in makeinit");
177         for (i=0; i <= k; i++) {
178                 (f->posns[2])[i] = (f->re[0].lfollow)[i];
179         }
180         if ((f->posns[2])[1] == f->accept)
181                 f->out[2] = 1;
182         for (i=0; i < NCHARS; i++)
183                 f->gototab[2][i] = 0;
184         f->curstat = cgoto(f, 2, HAT);
185         if (anchor) {
186                 *f->posns[2] = k-1;     /* leave out position 0 */
187                 for (i=0; i < k; i++) {
188                         (f->posns[0])[i] = (f->posns[2])[i];
189                 }
190
191                 f->out[0] = f->out[2];
192                 if (f->curstat != 2)
193                         --(*f->posns[f->curstat]);
194         }
195         return f->curstat;
196 }
197
198 void penter(Node *p)    /* set up parent pointers and leaf indices */
199 {
200         switch (type(p)) {
201         ELEAF
202         LEAF
203                 info(p) = poscnt;
204                 poscnt++;
205                 break;
206         UNARY
207                 penter(left(p));
208                 parent(left(p)) = p;
209                 break;
210         case CAT:
211         case OR:
212                 penter(left(p));
213                 penter(right(p));
214                 parent(left(p)) = p;
215                 parent(right(p)) = p;
216                 break;
217         default:        /* can't happen */
218                 FATAL("can't happen: unknown type %d in penter", type(p));
219                 break;
220         }
221 }
222
223 void freetr(Node *p)    /* free parse tree */
224 {
225         switch (type(p)) {
226         ELEAF
227         LEAF
228                 xfree(p);
229                 break;
230         UNARY
231                 freetr(left(p));
232                 xfree(p);
233                 break;
234         case CAT:
235         case OR:
236                 freetr(left(p));
237                 freetr(right(p));
238                 xfree(p);
239                 break;
240         default:        /* can't happen */
241                 FATAL("can't happen: unknown type %d in freetr", type(p));
242                 break;
243         }
244 }
245
246 /* in the parsing of regular expressions, metacharacters like . have */
247 /* to be seen literally;  \056 is not a metacharacter. */
248
249 int hexstr(uschar **pp) /* find and eval hex string at pp, return new p */
250 {                       /* only pick up one 8-bit byte (2 chars) */
251         uschar *p;
252         int n = 0;
253         int i;
254
255         for (i = 0, p = (uschar *) *pp; i < 2 && isxdigit(*p); i++, p++) {
256                 if (isdigit(*p))
257                         n = 16 * n + *p - '0';
258                 else if (*p >= 'a' && *p <= 'f')
259                         n = 16 * n + *p - 'a' + 10;
260                 else if (*p >= 'A' && *p <= 'F')
261                         n = 16 * n + *p - 'A' + 10;
262         }
263         *pp = (uschar *) p;
264         return n;
265 }
266
267 #define isoctdigit(c) ((c) >= '0' && (c) <= '7')        /* multiple use of arg */
268
269 int quoted(uschar **pp) /* pick up next thing after a \\ */
270                         /* and increment *pp */
271 {
272         uschar *p = *pp;
273         int c;
274
275         if ((c = *p++) == 't')
276                 c = '\t';
277         else if (c == 'n')
278                 c = '\n';
279         else if (c == 'f')
280                 c = '\f';
281         else if (c == 'r')
282                 c = '\r';
283         else if (c == 'b')
284                 c = '\b';
285         else if (c == '\\')
286                 c = '\\';
287         else if (c == 'x') {    /* hexadecimal goo follows */
288                 c = hexstr(&p); /* this adds a null if number is invalid */
289         } else if (isoctdigit(c)) {     /* \d \dd \ddd */
290                 int n = c - '0';
291                 if (isoctdigit(*p)) {
292                         n = 8 * n + *p++ - '0';
293                         if (isoctdigit(*p))
294                                 n = 8 * n + *p++ - '0';
295                 }
296                 c = n;
297         } /* else */
298                 /* c = c; */
299         *pp = p;
300         return c;
301 }
302
303 static int collate_range_cmp(int a, int b)
304 {
305         static char s[2][2];
306
307         if ((uschar)a == (uschar)b)
308                 return 0;
309         s[0][0] = a;
310         s[1][0] = b;
311         return (strcoll(s[0], s[1]));
312 }
313
314 char *cclenter(const char *argp)        /* add a character class */
315 {
316         int i, c, c2;
317         int j;
318         uschar *p = (uschar *) argp;
319         uschar *op, *bp;
320         static uschar *buf = NULL;
321         static int bufsz = 100;
322
323         op = p;
324         if (buf == NULL && (buf = (uschar *) malloc(bufsz)) == NULL)
325                 FATAL("out of space for character class [%.10s...] 1", p);
326         bp = buf;
327         for (i = 0; (c = *p++) != 0; ) {
328                 if (c == '\\') {
329                         c = quoted(&p);
330                 } else if (c == '-' && i > 0 && bp[-1] != 0) {
331                         if (*p != 0) {
332                                 c = bp[-1];
333                                 c2 = *p++;
334                                 if (c2 == '\\')
335                                         c2 = quoted(&p);
336                                 if (collate_range_cmp(c, c2) > 0) {
337                                         bp--;
338                                         i--;
339                                         continue;
340                                 }
341                                 for (j = 0; j < NCHARS; j++) {
342                                         if ((collate_range_cmp(c, j) > 0) ||
343                                             collate_range_cmp(j, c2) > 0)
344                                                 continue;
345                                         if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1"))
346                                                 FATAL("out of space for character class [%.10s...] 2", p);
347                                         *bp++ = j;
348                                         i++;
349                                 }
350                                 continue;
351                         }
352                 }
353                 if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter2"))
354                         FATAL("out of space for character class [%.10s...] 3", p);
355                 *bp++ = c;
356                 i++;
357         }
358         *bp = 0;
359         dprintf( ("cclenter: in = |%s|, out = |%s|\n", op, buf) );
360         xfree(op);
361         return (char *) tostring((char *) buf);
362 }
363
364 void overflo(const char *s)
365 {
366         FATAL("regular expression too big: %.30s...", s);
367 }
368
369 void cfoll(fa *f, Node *v)      /* enter follow set of each leaf of vertex v into lfollow[leaf] */
370 {
371         int i;
372         int *p;
373
374         switch (type(v)) {
375         ELEAF
376         LEAF
377                 f->re[info(v)].ltype = type(v);
378                 f->re[info(v)].lval.np = right(v);
379                 while (f->accept >= maxsetvec) {        /* guessing here! */
380                         maxsetvec *= 4;
381                         setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
382                         tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
383                         if (setvec == NULL || tmpset == NULL)
384                                 overflo("out of space in cfoll()");
385                 }
386                 for (i = 0; i <= f->accept; i++)
387                         setvec[i] = 0;
388                 setcnt = 0;
389                 follow(v);      /* computes setvec and setcnt */
390                 if ((p = (int *) calloc(setcnt+1, sizeof(int))) == NULL)
391                         overflo("out of space building follow set");
392                 f->re[info(v)].lfollow = p;
393                 *p = setcnt;
394                 for (i = f->accept; i >= 0; i--)
395                         if (setvec[i] == 1)
396                                 *++p = i;
397                 break;
398         UNARY
399                 cfoll(f,left(v));
400                 break;
401         case CAT:
402         case OR:
403                 cfoll(f,left(v));
404                 cfoll(f,right(v));
405                 break;
406         default:        /* can't happen */
407                 FATAL("can't happen: unknown type %d in cfoll", type(v));
408         }
409 }
410
411 int first(Node *p)      /* collects initially active leaves of p into setvec */
412                         /* returns 0 if p matches empty string */
413 {
414         int b, lp;
415
416         switch (type(p)) {
417         ELEAF
418         LEAF
419                 lp = info(p);   /* look for high-water mark of subscripts */
420                 while (setcnt >= maxsetvec || lp >= maxsetvec) {        /* guessing here! */
421                         maxsetvec *= 4;
422                         setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
423                         tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
424                         if (setvec == NULL || tmpset == NULL)
425                                 overflo("out of space in first()");
426                 }
427                 if (type(p) == EMPTYRE) {
428                         setvec[lp] = 0;
429                         return(0);
430                 }
431                 if (setvec[lp] != 1) {
432                         setvec[lp] = 1;
433                         setcnt++;
434                 }
435                 if (type(p) == CCL && (*(char *) right(p)) == '\0')
436                         return(0);              /* empty CCL */
437                 else return(1);
438         case PLUS:
439                 if (first(left(p)) == 0) return(0);
440                 return(1);
441         case STAR:
442         case QUEST:
443                 first(left(p));
444                 return(0);
445         case CAT:
446                 if (first(left(p)) == 0 && first(right(p)) == 0) return(0);
447                 return(1);
448         case OR:
449                 b = first(right(p));
450                 if (first(left(p)) == 0 || b == 0) return(0);
451                 return(1);
452         }
453         FATAL("can't happen: unknown type %d in first", type(p));       /* can't happen */
454         return(-1);
455 }
456
457 void follow(Node *v)    /* collects leaves that can follow v into setvec */
458 {
459         Node *p;
460
461         if (type(v) == FINAL)
462                 return;
463         p = parent(v);
464         switch (type(p)) {
465         case STAR:
466         case PLUS:
467                 first(v);
468                 follow(p);
469                 return;
470
471         case OR:
472         case QUEST:
473                 follow(p);
474                 return;
475
476         case CAT:
477                 if (v == left(p)) {     /* v is left child of p */
478                         if (first(right(p)) == 0) {
479                                 follow(p);
480                                 return;
481                         }
482                 } else          /* v is right child */
483                         follow(p);
484                 return;
485         }
486 }
487
488 int member(int c, const char *sarg)     /* is c in s? */
489 {
490         uschar *s = (uschar *) sarg;
491
492         while (*s)
493                 if (c == *s++)
494                         return(1);
495         return(0);
496 }
497
498 int match(fa *f, const char *p0)        /* shortest match ? */
499 {
500         int s, ns;
501         uschar *p = (uschar *) p0;
502
503         s = f->reset ? makeinit(f,0) : f->initstat;
504         if (f->out[s])
505                 return(1);
506         do {
507                 /* assert(*p < NCHARS); */
508                 if ((ns = f->gototab[s][*p]) != 0)
509                         s = ns;
510                 else
511                         s = cgoto(f, s, *p);
512                 if (f->out[s])
513                         return(1);
514         } while (*p++ != 0);
515         return(0);
516 }
517
518 int pmatch(fa *f, const char *p0)       /* longest match, for sub */
519 {
520         int s, ns;
521         uschar *p = (uschar *) p0;
522         uschar *q;
523         int i, k;
524
525         /* s = f->reset ? makeinit(f,1) : f->initstat; */
526         if (f->reset) {
527                 f->initstat = s = makeinit(f,1);
528         } else {
529                 s = f->initstat;
530         }
531         patbeg = (char *) p;
532         patlen = -1;
533         do {
534                 q = p;
535                 do {
536                         if (f->out[s])          /* final state */
537                                 patlen = q-p;
538                         /* assert(*q < NCHARS); */
539                         if ((ns = f->gototab[s][*q]) != 0)
540                                 s = ns;
541                         else
542                                 s = cgoto(f, s, *q);
543                         if (s == 1) {   /* no transition */
544                                 if (patlen >= 0) {
545                                         patbeg = (char *) p;
546                                         return(1);
547                                 }
548                                 else
549                                         goto nextin;    /* no match */
550                         }
551                 } while (*q++ != 0);
552                 if (f->out[s])
553                         patlen = q-p-1; /* don't count $ */
554                 if (patlen >= 0) {
555                         patbeg = (char *) p;
556                         return(1);
557                 }
558         nextin:
559                 s = 2;
560                 if (f->reset) {
561                         for (i = 2; i <= f->curstat; i++)
562                                 xfree(f->posns[i]);
563                         k = *f->posns[0];                       
564                         if ((f->posns[2] = (int *) calloc(k+1, sizeof(int))) == NULL)
565                                 overflo("out of space in pmatch");
566                         for (i = 0; i <= k; i++)
567                                 (f->posns[2])[i] = (f->posns[0])[i];
568                         f->initstat = f->curstat = 2;
569                         f->out[2] = f->out[0];
570                         for (i = 0; i < NCHARS; i++)
571                                 f->gototab[2][i] = 0;
572                 }
573         } while (*p++ != 0);
574         return (0);
575 }
576
577 int nematch(fa *f, const char *p0)      /* non-empty match, for sub */
578 {
579         int s, ns;
580         uschar *p = (uschar *) p0;
581         uschar *q;
582         int i, k;
583
584         /* s = f->reset ? makeinit(f,1) : f->initstat; */
585         if (f->reset) {
586                 f->initstat = s = makeinit(f,1);
587         } else {
588                 s = f->initstat;
589         }
590         patlen = -1;
591         while (*p) {
592                 q = p;
593                 do {
594                         if (f->out[s])          /* final state */
595                                 patlen = q-p;
596                         /* assert(*q < NCHARS); */
597                         if ((ns = f->gototab[s][*q]) != 0)
598                                 s = ns;
599                         else
600                                 s = cgoto(f, s, *q);
601                         if (s == 1) {   /* no transition */
602                                 if (patlen > 0) {
603                                         patbeg = (char *) p;
604                                         return(1);
605                                 } else
606                                         goto nnextin;   /* no nonempty match */
607                         }
608                 } while (*q++ != 0);
609                 if (f->out[s])
610                         patlen = q-p-1; /* don't count $ */
611                 if (patlen > 0 ) {
612                         patbeg = (char *) p;
613                         return(1);
614                 }
615         nnextin:
616                 s = 2;
617                 if (f->reset) {
618                         for (i = 2; i <= f->curstat; i++)
619                                 xfree(f->posns[i]);
620                         k = *f->posns[0];                       
621                         if ((f->posns[2] = (int *) calloc(k+1, sizeof(int))) == NULL)
622                                 overflo("out of state space");
623                         for (i = 0; i <= k; i++)
624                                 (f->posns[2])[i] = (f->posns[0])[i];
625                         f->initstat = f->curstat = 2;
626                         f->out[2] = f->out[0];
627                         for (i = 0; i < NCHARS; i++)
628                                 f->gototab[2][i] = 0;
629                 }
630                 p++;
631         }
632         return (0);
633 }
634
635 Node *reparse(const char *p)    /* parses regular expression pointed to by p */
636 {                       /* uses relex() to scan regular expression */
637         Node *np;
638
639         dprintf( ("reparse <%s>\n", p) );
640         lastre = prestr = (uschar *) p; /* prestr points to string to be parsed */
641         rtok = relex();
642         /* GNU compatibility: an empty regexp matches anything */
643         if (rtok == '\0') {
644                 /* FATAL("empty regular expression"); previous */
645                 return(op2(EMPTYRE, NIL, NIL));
646         }
647         np = regexp();
648         if (rtok != '\0')
649                 FATAL("syntax error in regular expression %s at %s", lastre, prestr);
650         return(np);
651 }
652
653 Node *regexp(void)      /* top-level parse of reg expr */
654 {
655         return (alt(concat(primary())));
656 }
657
658 Node *primary(void)
659 {
660         Node *np;
661         int savelastatom;
662
663         switch (rtok) {
664         case CHAR:
665                 lastatom = starttok;
666                 np = op2(CHAR, NIL, itonp(rlxval));
667                 rtok = relex();
668                 return (unary(np));
669         case ALL:
670                 rtok = relex();
671                 return (unary(op2(ALL, NIL, NIL)));
672         case EMPTYRE:
673                 rtok = relex();
674                 return (unary(op2(EMPTYRE, NIL, NIL)));
675         case DOT:
676                 lastatom = starttok;
677                 rtok = relex();
678                 return (unary(op2(DOT, NIL, NIL)));
679         case CCL:
680                 np = op2(CCL, NIL, (Node*) cclenter((char *) rlxstr));
681                 lastatom = starttok;
682                 rtok = relex();
683                 return (unary(np));
684         case NCCL:
685                 np = op2(NCCL, NIL, (Node *) cclenter((char *) rlxstr));
686                 lastatom = starttok;
687                 rtok = relex();
688                 return (unary(np));
689         case '^':
690                 rtok = relex();
691                 return (unary(op2(CHAR, NIL, itonp(HAT))));
692         case '$':
693                 rtok = relex();
694                 return (unary(op2(CHAR, NIL, NIL)));
695         case '(':
696                 lastatom = starttok;
697                 savelastatom = starttok - basestr; /* Retain over recursion */
698                 rtok = relex();
699                 if (rtok == ')') {      /* special pleading for () */
700                         rtok = relex();
701                         return unary(op2(CCL, NIL, (Node *) tostring("")));
702                 }
703                 np = regexp();
704                 if (rtok == ')') {
705                         lastatom = basestr + savelastatom; /* Restore */
706                         rtok = relex();
707                         return (unary(np));
708                 }
709                 else
710                         FATAL("syntax error in regular expression %s at %s", lastre, prestr);
711         default:
712                 FATAL("illegal primary in regular expression %s at %s", lastre, prestr);
713         }
714         return 0;       /*NOTREACHED*/
715 }
716
717 Node *concat(Node *np)
718 {
719         switch (rtok) {
720         case CHAR: case DOT: case ALL: case CCL: case NCCL: case '$': case '(':
721                 return (concat(op2(CAT, np, primary())));
722         case EMPTYRE:
723                 rtok = relex();
724                 return (concat(op2(CAT, op2(CCL, NIL, (Node *) tostring("")),
725                                 primary())));
726         }
727         return (np);
728 }
729
730 Node *alt(Node *np)
731 {
732         if (rtok == OR) {
733                 rtok = relex();
734                 return (alt(op2(OR, np, concat(primary()))));
735         }
736         return (np);
737 }
738
739 Node *unary(Node *np)
740 {
741         switch (rtok) {
742         case STAR:
743                 rtok = relex();
744                 return (unary(op2(STAR, np, NIL)));
745         case PLUS:
746                 rtok = relex();
747                 return (unary(op2(PLUS, np, NIL)));
748         case QUEST:
749                 rtok = relex();
750                 return (unary(op2(QUEST, np, NIL)));
751         default:
752                 return (np);
753         }
754 }
755
756 /*
757  * Character class definitions conformant to the POSIX locale as
758  * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source
759  * and operating character sets are both ASCII (ISO646) or supersets
760  * thereof.
761  *
762  * Note that to avoid overflowing the temporary buffer used in
763  * relex(), the expanded character class (prior to range expansion)
764  * must be less than twice the size of their full name.
765  */
766
767 /* Because isblank doesn't show up in any of the header files on any
768  * system i use, it's defined here.  if some other locale has a richer
769  * definition of "blank", define HAS_ISBLANK and provide your own
770  * version.
771  * the parentheses here are an attempt to find a path through the maze
772  * of macro definition and/or function and/or version provided.  thanks
773  * to nelson beebe for the suggestion; let's see if it works everywhere.
774  */
775
776 /* #define HAS_ISBLANK */
777 #ifndef HAS_ISBLANK
778
779 int (xisblank)(int c)
780 {
781         return c==' ' || c=='\t';
782 }
783
784 #endif
785
786 struct charclass {
787         const char *cc_name;
788         int cc_namelen;
789         int (*cc_func)(int);
790 } charclasses[] = {
791         { "alnum",      5,      isalnum },
792         { "alpha",      5,      isalpha },
793 #ifndef HAS_ISBLANK
794         { "blank",      5,      xisblank },
795 #else
796         { "blank",      5,      isblank },
797 #endif
798         { "cntrl",      5,      iscntrl },
799         { "digit",      5,      isdigit },
800         { "graph",      5,      isgraph },
801         { "lower",      5,      islower },
802         { "print",      5,      isprint },
803         { "punct",      5,      ispunct },
804         { "space",      5,      isspace },
805         { "upper",      5,      isupper },
806         { "xdigit",     6,      isxdigit },
807         { NULL,         0,      NULL },
808 };
809
810 #define REPEAT_SIMPLE           0
811 #define REPEAT_PLUS_APPENDED    1
812 #define REPEAT_WITH_Q           2
813 #define REPEAT_ZERO             3
814
815 static int
816 replace_repeat(const uschar *reptok, int reptoklen, const uschar *atom,
817                int atomlen, int firstnum, int secondnum, int special_case)
818 {
819         int i, j;
820         uschar *buf = 0;
821         int ret = 1;
822         int init_q = (firstnum==0);             /* first added char will be ? */
823         int n_q_reps = secondnum-firstnum;      /* m>n, so reduce until {1,m-n} left  */
824         int prefix_length = reptok - basestr;   /* prefix includes first rep    */
825         int suffix_length = strlen((char *) reptok) - reptoklen;        /* string after rep specifier   */
826         int size = prefix_length +  suffix_length;
827
828         if (firstnum > 1) {     /* add room for reps 2 through firstnum */
829                 size += atomlen*(firstnum-1);
830         }
831
832         /* Adjust size of buffer for special cases */
833         if (special_case == REPEAT_PLUS_APPENDED) {
834                 size++;         /* for the final + */
835         } else if (special_case == REPEAT_WITH_Q) {
836                 size += init_q + (atomlen+1)* n_q_reps;
837         } else if (special_case == REPEAT_ZERO) {
838                 size += 2;      /* just a null ERE: () */
839         }
840         if ((buf = (uschar *) malloc(size+1)) == NULL)
841                 FATAL("out of space in reg expr %.10s..", lastre);
842         memcpy(buf, basestr, prefix_length);    /* copy prefix  */
843         j = prefix_length;
844         if (special_case == REPEAT_ZERO) {
845                 j -= atomlen;
846                 buf[j++] = '(';
847                 buf[j++] = ')';
848         }
849         for (i=1; i < firstnum; i++) {          /* copy x reps  */
850                 memcpy(&buf[j], atom, atomlen);
851                 j += atomlen;
852         }
853         if (special_case == REPEAT_PLUS_APPENDED) {
854                 buf[j++] = '+';
855         } else if (special_case == REPEAT_WITH_Q) {
856                 if (init_q) buf[j++] = '?';
857                 for (i=0; i < n_q_reps; i++) {  /* copy x? reps */
858                         memcpy(&buf[j], atom, atomlen);
859                         j += atomlen;
860                         buf[j++] = '?';
861                 }
862         }
863         memcpy(&buf[j], reptok+reptoklen, suffix_length);
864         if (special_case == REPEAT_ZERO) {
865                 buf[j+suffix_length] = '\0';
866         } else {
867                 buf[size] = '\0';
868         }
869         /* free old basestr */
870         if (firstbasestr != basestr) {
871                 if (basestr)
872                         xfree(basestr);
873         }
874         basestr = buf;
875         prestr  = buf + prefix_length;
876         if (special_case == REPEAT_ZERO) {
877                 prestr  -= atomlen;
878                 ret++;
879         }
880         return ret;
881 }
882
883 static int repeat(const uschar *reptok, int reptoklen, const uschar *atom,
884                   int atomlen, int firstnum, int secondnum)
885 {
886         /*
887            In general, the repetition specifier or "bound" is replaced here
888            by an equivalent ERE string, repeating the immediately previous atom
889            and appending ? and + as needed. Note that the first copy of the
890            atom is left in place, except in the special_case of a zero-repeat
891            (i.e., {0}).
892          */
893         if (secondnum < 0) {    /* means {n,} -> repeat n-1 times followed by PLUS */
894                 if (firstnum < 2) {
895                         /* 0 or 1: should be handled before you get here */
896                         FATAL("internal error");
897                 } else {
898                         return replace_repeat(reptok, reptoklen, atom, atomlen,
899                                 firstnum, secondnum, REPEAT_PLUS_APPENDED);
900                 }
901         } else if (firstnum == secondnum) {     /* {n} or {n,n} -> simply repeat n-1 times */
902                 if (firstnum == 0) {    /* {0} or {0,0} */
903                         /* This case is unusual because the resulting
904                            replacement string might actually be SMALLER than
905                            the original ERE */
906                         return replace_repeat(reptok, reptoklen, atom, atomlen,
907                                         firstnum, secondnum, REPEAT_ZERO);
908                 } else {                /* (firstnum >= 1) */
909                         return replace_repeat(reptok, reptoklen, atom, atomlen,
910                                         firstnum, secondnum, REPEAT_SIMPLE);
911                 }
912         } else if (firstnum < secondnum) {      /* {n,m} -> repeat n-1 times then alternate  */
913                 /*  x{n,m}  =>  xx...x{1, m-n+1}  =>  xx...x?x?x?..x?   */
914                 return replace_repeat(reptok, reptoklen, atom, atomlen,
915                                         firstnum, secondnum, REPEAT_WITH_Q);
916         } else {        /* Error - shouldn't be here (n>m) */
917                 FATAL("internal error");
918         }
919         return 0;
920 }
921
922 int relex(void)         /* lexical analyzer for reparse */
923 {
924         int c, n;
925         int cflag;
926         static uschar *buf = NULL;
927         static int bufsz = 100;
928         uschar *bp;
929         struct charclass *cc;
930         int i;
931         int num, m, commafound, digitfound;
932         const uschar *startreptok;
933
934 rescan:
935         starttok = prestr;
936
937         switch (c = *prestr++) {
938         case '|': return OR;
939         case '*': return STAR;
940         case '+': return PLUS;
941         case '?': return QUEST;
942         case '.': return DOT;
943         case '\0': prestr--; return '\0';
944         case '^':
945         case '$':
946         case '(':
947         case ')':
948                 return c;
949         case '\\':
950                 rlxval = quoted(&prestr);
951                 return CHAR;
952         default:
953                 rlxval = c;
954                 return CHAR;
955         case '[': 
956                 if (buf == NULL && (buf = (uschar *) malloc(bufsz)) == NULL)
957                         FATAL("out of space in reg expr %.10s..", lastre);
958                 bp = buf;
959                 if (*prestr == '^') {
960                         cflag = 1;
961                         prestr++;
962                 }
963                 else
964                         cflag = 0;
965                 n = 2 * strlen((const char *) prestr)+1;
966                 if (!adjbuf((char **) &buf, &bufsz, n, n, (char **) &bp, "relex1"))
967                         FATAL("out of space for reg expr %.10s...", lastre);
968                 for (; ; ) {
969                         if ((c = *prestr++) == '\\') {
970                                 *bp++ = '\\';
971                                 if ((c = *prestr++) == '\0')
972                                         FATAL("nonterminated character class %.20s...", lastre);
973                                 *bp++ = c;
974                         /* } else if (c == '\n') { */
975                         /*      FATAL("newline in character class %.20s...", lastre); */
976                         } else if (c == '[' && *prestr == ':') {
977                                 /* POSIX char class names, Dag-Erling Smorgrav, des@ofug.org */
978                                 for (cc = charclasses; cc->cc_name; cc++)
979                                         if (strncmp((const char *) prestr + 1, (const char *) cc->cc_name, cc->cc_namelen) == 0)
980                                                 break;
981                                 if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':' &&
982                                     prestr[2 + cc->cc_namelen] == ']') {
983                                         prestr += cc->cc_namelen + 3;
984                                         /*
985                                          * BUG: We begin at 1, instead of 0, since we
986                                          * would otherwise prematurely terminate the
987                                          * string for classes like [[:cntrl:]]. This
988                                          * means that we can't match the NUL character,
989                                          * not without first adapting the entire
990                                          * program to track each string's length.
991                                          */
992                                         for (i = 1; i <= UCHAR_MAX; i++) {
993                                                 if (!adjbuf((char **) &buf, &bufsz, bp-buf+1, 100, (char **) &bp, "relex2"))
994                                                     FATAL("out of space for reg expr %.10s...", lastre);
995                                                 if (cc->cc_func(i)) {
996                                                         *bp++ = i;
997                                                         n++;
998                                                 }
999                                         }
1000                                 } else
1001                                         *bp++ = c;
1002                         } else if (c == '[' && *prestr == '.') {
1003                                 char collate_char;
1004                                 prestr++;
1005                                 collate_char = *prestr++;
1006                                 if (*prestr == '.' && prestr[1] == ']') {
1007                                         prestr += 2;
1008                                         /* Found it: map via locale TBD: for
1009                                            now, simply return this char.  This
1010                                            is sufficient to pass conformance
1011                                            test awk.ex 156
1012                                          */
1013                                         if (*prestr == ']') {
1014                                                 prestr++;
1015                                                 rlxval = collate_char;
1016                                                 return CHAR;
1017                                         }
1018                                 }
1019                         } else if (c == '[' && *prestr == '=') {
1020                                 char equiv_char;
1021                                 prestr++;
1022                                 equiv_char = *prestr++;
1023                                 if (*prestr == '=' && prestr[1] == ']') {
1024                                         prestr += 2;
1025                                         /* Found it: map via locale TBD: for now
1026                                            simply return this char. This is
1027                                            sufficient to pass conformance test
1028                                            awk.ex 156
1029                                          */
1030                                         if (*prestr == ']') {
1031                                                 prestr++;
1032                                                 rlxval = equiv_char;
1033                                                 return CHAR;
1034                                         }
1035                                 }
1036                         } else if (c == '\0') {
1037                                 FATAL("nonterminated character class %.20s", lastre);
1038                         } else if (bp == buf) { /* 1st char is special */
1039                                 *bp++ = c;
1040                         } else if (c == ']') {
1041                                 *bp++ = 0;
1042                                 rlxstr = (uschar *) tostring((char *) buf);
1043                                 if (cflag == 0)
1044                                         return CCL;
1045                                 else
1046                                         return NCCL;
1047                         } else
1048                                 *bp++ = c;
1049                 }
1050                 break;
1051         case '{':
1052                 if (isdigit(*(prestr))) {
1053                         num = 0;        /* Process as a repetition */
1054                         n = -1; m = -1;
1055                         commafound = 0;
1056                         digitfound = 0;
1057                         startreptok = prestr-1;
1058                         /* Remember start of previous atom here ? */
1059                 } else {                /* just a { char, not a repetition */
1060                         rlxval = c;
1061                         return CHAR;
1062                 }
1063                 for (; ; ) {
1064                         if ((c = *prestr++) == '}') {
1065                                 if (commafound) {
1066                                         if (digitfound) { /* {n,m} */
1067                                                 m = num;
1068                                                 if (m<n)
1069                                                         FATAL("illegal repetition expression: class %.20s",
1070                                                                 lastre);
1071                                                 if ((n==0) && (m==1)) {
1072                                                         return QUEST;
1073                                                 }
1074                                         } else {        /* {n,} */
1075                                                 if (n==0) return STAR;
1076                                                 if (n==1) return PLUS;
1077                                         }
1078                                 } else {
1079                                         if (digitfound) { /* {n} same as {n,n} */
1080                                                 n = num;
1081                                                 m = num;
1082                                         } else {        /* {} */
1083                                                 FATAL("illegal repetition expression: class %.20s",
1084                                                         lastre);
1085                                         }
1086                                 }
1087                                 if (repeat(starttok, prestr-starttok, lastatom,
1088                                            startreptok - lastatom, n, m) > 0) {
1089                                         if ((n==0) && (m==0)) {
1090                                                 return EMPTYRE;
1091                                         }
1092                                         /* must rescan input for next token */
1093                                         goto rescan;
1094                                 }
1095                                 /* Failed to replace: eat up {...} characters
1096                                    and treat like just PLUS */
1097                                 return PLUS;
1098                         } else if (c == '\0') {
1099                                 FATAL("nonterminated character class %.20s",
1100                                         lastre);
1101                         } else if (isdigit(c)) {
1102                                 num = 10 * num + c - '0';
1103                                 digitfound = 1;
1104                         } else if (c == ',') {
1105                                 if (commafound)
1106                                         FATAL("illegal repetition expression: class %.20s",
1107                                                 lastre);
1108                                 /* looking for {n,} or {n,m} */
1109                                 commafound = 1;
1110                                 n = num;
1111                                 digitfound = 0; /* reset */
1112                                 num = 0;
1113                         } else {
1114                                 FATAL("illegal repetition expression: class %.20s",
1115                                         lastre);
1116                         }
1117                 }
1118                 break;
1119         }
1120 }
1121
1122 int cgoto(fa *f, int s, int c)
1123 {
1124         int i, j, k;
1125         int *p, *q;
1126
1127         assert(c == HAT || c < NCHARS);
1128         while (f->accept >= maxsetvec) {        /* guessing here! */
1129                 maxsetvec *= 4;
1130                 setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
1131                 tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
1132                 if (setvec == NULL || tmpset == NULL)
1133                         overflo("out of space in cgoto()");
1134         }
1135         for (i = 0; i <= f->accept; i++)
1136                 setvec[i] = 0;
1137         setcnt = 0;
1138         /* compute positions of gototab[s,c] into setvec */
1139         p = f->posns[s];
1140         for (i = 1; i <= *p; i++) {
1141                 if ((k = f->re[p[i]].ltype) != FINAL) {
1142                         if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np))
1143                          || (k == DOT && c != 0 && c != HAT)
1144                          || (k == ALL && c != 0)
1145                          || (k == EMPTYRE && c != 0)
1146                          || (k == CCL && member(c, (char *) f->re[p[i]].lval.up))
1147                          || (k == NCCL && !member(c, (char *) f->re[p[i]].lval.up) && c != 0 && c != HAT)) {
1148                                 q = f->re[p[i]].lfollow;
1149                                 for (j = 1; j <= *q; j++) {
1150                                         if (q[j] >= maxsetvec) {
1151                                                 maxsetvec *= 4;
1152                                                 setvec = (int *) realloc(setvec, maxsetvec * sizeof(int));
1153                                                 tmpset = (int *) realloc(tmpset, maxsetvec * sizeof(int));
1154                                                 if (setvec == NULL || tmpset == NULL)
1155                                                         overflo("cgoto overflow");
1156                                         }
1157                                         if (setvec[q[j]] == 0) {
1158                                                 setcnt++;
1159                                                 setvec[q[j]] = 1;
1160                                         }
1161                                 }
1162                         }
1163                 }
1164         }
1165         /* determine if setvec is a previous state */
1166         tmpset[0] = setcnt;
1167         j = 1;
1168         for (i = f->accept; i >= 0; i--)
1169                 if (setvec[i]) {
1170                         tmpset[j++] = i;
1171                 }
1172         /* tmpset == previous state? */
1173         for (i = 1; i <= f->curstat; i++) {
1174                 p = f->posns[i];
1175                 if ((k = tmpset[0]) != p[0])
1176                         goto different;
1177                 for (j = 1; j <= k; j++)
1178                         if (tmpset[j] != p[j])
1179                                 goto different;
1180                 /* setvec is state i */
1181                 f->gototab[s][c] = i;
1182                 return i;
1183           different:;
1184         }
1185
1186         /* add tmpset to current set of states */
1187         if (f->curstat >= NSTATES-1) {
1188                 f->curstat = 2;
1189                 f->reset = 1;
1190                 for (i = 2; i < NSTATES; i++)
1191                         xfree(f->posns[i]);
1192         } else
1193                 ++(f->curstat);
1194         for (i = 0; i < NCHARS; i++)
1195                 f->gototab[f->curstat][i] = 0;
1196         xfree(f->posns[f->curstat]);
1197         if ((p = (int *) calloc(setcnt+1, sizeof(int))) == NULL)
1198                 overflo("out of space in cgoto");
1199
1200         f->posns[f->curstat] = p;
1201         f->gototab[s][c] = f->curstat;
1202         for (i = 0; i <= setcnt; i++)
1203                 p[i] = tmpset[i];
1204         if (setvec[f->accept])
1205                 f->out[f->curstat] = 1;
1206         else
1207                 f->out[f->curstat] = 0;
1208         return f->curstat;
1209 }
1210
1211
1212 void freefa(fa *f)      /* free a finite automaton */
1213 {
1214         int i;
1215
1216         if (f == NULL)
1217                 return;
1218         for (i = 0; i <= f->curstat; i++)
1219                 xfree(f->posns[i]);
1220         for (i = 0; i <= f->accept; i++) {
1221                 xfree(f->re[i].lfollow);
1222                 if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
1223                         xfree((f->re[i].lval.np));
1224         }
1225         xfree(f->restr);
1226         xfree(f);
1227 }