master xplshn/aruu / cmd / posix / awk / b.c
   1/****************************************************************
   2Copyright (C) Lucent Technologies 1997
   3All Rights Reserved
   4
   5Permission to use, copy, modify, and distribute this software and
   6its documentation for any purpose and without fee is hereby
   7granted, provided that the above copyright notice appear in all
   8copies and that both that the copyright notice and this
   9permission notice and warranty disclaimer appear in supporting
  10documentation, and that the name Lucent Technologies or any of
  11its entities not be used in advertising or publicity pertaining
  12to distribution of the software without specific, written prior
  13permission.
  14
  15LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  16INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
  17IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
  18SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  20IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  22THIS SOFTWARE.
  23****************************************************************/
  24
  25/* lasciate ogne speranza, voi ch'intrate. */
  26
  27#define DEBUG
  28
  29#include "awk.h"
  30#include "awkgram.tab.h"
  31#include <ctype.h>
  32#include <limits.h>
  33#include <stdio.h>
  34#include <stdlib.h>
  35#include <string.h>
  36
  37#define MAXLIN 22
  38
  39#define type(v)   (v)->nobj  /* badly overloaded here */
  40#define info(v)   (v)->ntype /* badly overloaded here */
  41#define left(v)   (v)->narg[0]
  42#define right(v)  (v)->narg[1]
  43#define parent(v) (v)->nnext
  44
  45#define LEAF                                                                                       \
  46  case CCL:                                                                                        \
  47  case NCCL:                                                                                       \
  48  case CHAR:                                                                                       \
  49  case DOT:                                                                                        \
  50  case FINAL:                                                                                      \
  51  case ALL:
  52#define ELEAF case EMPTYRE: /* empty string in regexp */
  53#define UNARY                                                                                      \
  54  case STAR:                                                                                       \
  55  case PLUS:                                                                                       \
  56  case QUEST:
  57
  58/* encoding in tree Nodes:
  59  leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
  60    left is index, right contains value or pointer to value
  61  unary (STAR, PLUS, QUEST): left is child, right is null
  62  binary (CAT, OR): left and right are children
  63  parent contains pointer to parent
  64*/
  65
  66int *setvec;
  67int *tmpset;
  68int  maxsetvec = 0;
  69
  70int                  rtok; /* next token in current re */
  71int                  rlxval;
  72static const uschar *rlxstr;
  73static const uschar *prestr;   /* current position in current re */
  74static const uschar *lastre;   /* origin of last re */
  75static const uschar *lastatom; /* origin of last Atom */
  76static const uschar *starttok;
  77static const uschar *basestr; /* starts with original, replaced during
  78       repetition processing */
  79static const uschar *firstbasestr;
  80
  81static int setcnt;
  82static int poscnt;
  83
  84const char *patbeg;
  85int         patlen;
  86
  87#define NFA 128 /* cache this many dynamic fa's */
  88fa *fatab[NFA];
  89int nfatab = 0; /* entries in fatab */
  90
  91extern int u8_nextlen(const char *s);
  92
  93/* utf-8 mechanism:
  94
  95   For most of Awk, utf-8 strings just "work", since they look like
  96   null-terminated sequences of 8-bit bytes.
  97
  98   Functions like length(), index(), and substr() have to operate
  99   in units of utf-8 characters.  The u8_* functions in run.c
 100   handle this.
 101
 102   Regular expressions are more complicated, since the basic
 103   mechanism of the goto table used 8-bit byte indices into the
 104   gototab entries to compute the next state.  Unicode is a lot
 105   bigger, so the gototab entries are now structs with a character
 106   and a next state. These are sorted by code point and binary
 107   searched.
 108
 109   Throughout the RE mechanism in b.c, utf-8 characters are
 110   converted to their utf-32 value.  This mostly shows up in
 111   cclenter, which expands character class ranges like a-z and now
 112   alpha-omega.  The size of a gototab array is still about 256.
 113   This should be dynamic, but for now things work ok for a single
 114   code page of Unicode, which is the most likely case.
 115
 116   The code changes are localized in run.c and b.c.  I have added a
 117   handful of functions to somewhat better hide the implementation,
 118   but a lot more could be done.
 119
 120 */
 121
 122static int  entry_cmp(const void *l, const void *r);
 123static int  get_gototab(fa *, int, int);
 124static int  set_gototab(fa *, int, int, int);
 125static void clear_gototab(fa *, int);
 126extern int  u8_rune(int *, const char *);
 127
 128static int *
 129intalloc(size_t n, const char *f)
 130{
 131  int *p = (int *)calloc(n, sizeof(int));
 132  if (p == NULL)
 133    overflo(f);
 134  return p;
 135}
 136
 137static void
 138resizesetvec(const char *f)
 139{
 140  if (maxsetvec == 0)
 141    maxsetvec = MAXLIN;
 142  else
 143    maxsetvec *= 4;
 144  setvec = (int *)realloc(setvec, maxsetvec * sizeof(*setvec));
 145  tmpset = (int *)realloc(tmpset, maxsetvec * sizeof(*tmpset));
 146  if (setvec == NULL || tmpset == NULL)
 147    overflo(f);
 148}
 149
 150static void
 151resize_state(fa *f, int state)
 152{
 153  gtt    *p;
 154  uschar *p2;
 155  int   **p3;
 156  int     i, new_count;
 157
 158  if (++state < f->state_count)
 159    return;
 160
 161  new_count = state + 10; /* needs to be tuned */
 162
 163  p = (gtt *)realloc(f->gototab, new_count * sizeof(gtt));
 164  if (p == NULL)
 165    goto out;
 166  f->gototab = p;
 167
 168  p2 = (uschar *)realloc(f->out, new_count * sizeof(f->out[0]));
 169  if (p2 == NULL)
 170    goto out;
 171  f->out = p2;
 172
 173  p3 = (int **)realloc(f->posns, new_count * sizeof(f->posns[0]));
 174  if (p3 == NULL)
 175    goto out;
 176  f->posns = p3;
 177
 178  for (i = f->state_count; i < new_count; ++i) {
 179    memset(f->gototab[i].direct, 0, sizeof(f->gototab[i].direct));
 180    f->gototab[i].entries = (gtte *)calloc(NCHARS, sizeof(gtte));
 181    if (f->gototab[i].entries == NULL)
 182      goto out;
 183    f->gototab[i].allocated = NCHARS;
 184    f->gototab[i].inuse     = 0;
 185    f->out[i]               = 0;
 186    f->posns[i]             = NULL;
 187  }
 188  f->state_count = new_count;
 189  return;
 190out:
 191  overflo(__func__);
 192}
 193
 194fa *
 195makedfa(const char *s, bool anchor) /* returns dfa for reg expr s */
 196{
 197  int        i, use, nuse;
 198  fa        *pfa;
 199  static int now = 1;
 200
 201  if (setvec == NULL) { /* first time through any RE */
 202    resizesetvec(__func__);
 203  }
 204
 205  if (compile_time != RUNNING) /* a constant for sure */
 206    return mkdfa(s, anchor);
 207  for (i = 0; i < nfatab; i++) /* is it there already? */
 208    if (fatab[i]->anchor == anchor && strcmp((const char *)fatab[i]->restr, s) == 0) {
 209      fatab[i]->use = now++;
 210      return fatab[i];
 211    }
 212  pfa = mkdfa(s, anchor);
 213  if (nfatab < NFA) { /* room for another */
 214    fatab[nfatab]      = pfa;
 215    fatab[nfatab]->use = now++;
 216    nfatab++;
 217    return pfa;
 218  }
 219  use  = fatab[0]->use; /* replace least-recently used */
 220  nuse = 0;
 221  for (i = 1; i < nfatab; i++)
 222    if (fatab[i]->use < use) {
 223      use  = fatab[i]->use;
 224      nuse = i;
 225    }
 226  freefa(fatab[nuse]);
 227  fatab[nuse] = pfa;
 228  pfa->use    = now++;
 229  return pfa;
 230}
 231
 232fa *
 233mkdfa(const char *s, bool anchor) /* does the real work of making a dfa */
 234/* anchor = true for anchored matches, else false */
 235{
 236  Node *p, *p1;
 237  fa   *f;
 238
 239  firstbasestr = (const uschar *)s;
 240  basestr      = firstbasestr;
 241  p            = reparse(s);
 242  p1           = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
 243  /* put ALL STAR in front of reg.  exp. */
 244  p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
 245  /* put FINAL after reg.  exp. */
 246
 247  poscnt = 0;
 248  penter(p1); /* enter parent pointers and leaf indices */
 249  if ((f = (fa *)calloc(1, sizeof(fa) + poscnt * sizeof(rrow))) == NULL)
 250    overflo(__func__);
 251  f->accept = poscnt - 1; /* penter has computed number of positions in re */
 252  cfoll(f, p1);           /* set up follow sets */
 253  freetr(p1);
 254  resize_state(f, 1);
 255  f->posns[0]  = intalloc(*(f->re[0].lfollow), __func__);
 256  f->posns[1]  = intalloc(1, __func__);
 257  *f->posns[1] = 0;
 258  f->initstat  = makeinit(f, anchor);
 259  f->anchor    = anchor;
 260  f->restr     = (uschar *)tostring(s);
 261  if (firstbasestr != basestr) {
 262    if (basestr)
 263      xfree(basestr);
 264  }
 265  return f;
 266}
 267
 268int
 269makeinit(fa *f, bool anchor)
 270{
 271  int i, k;
 272
 273  f->curstat = 2;
 274  f->out[2]  = 0;
 275  k          = *(f->re[0].lfollow);
 276  xfree(f->posns[2]);
 277  f->posns[2] = intalloc(k + 1, __func__);
 278  for (i = 0; i <= k; i++) {
 279    (f->posns[2])[i] = (f->re[0].lfollow)[i];
 280  }
 281  if ((f->posns[2])[1] == f->accept)
 282    f->out[2] = 1;
 283  clear_gototab(f, 2);
 284  f->curstat = cgoto(f, 2, HAT);
 285  if (anchor) {
 286    *f->posns[2] = k - 1; /* leave out position 0 */
 287    for (i = 0; i < k; i++) {
 288      (f->posns[0])[i] = (f->posns[2])[i];
 289    }
 290
 291    f->out[0] = f->out[2];
 292    if (f->curstat != 2)
 293      --(*f->posns[f->curstat]);
 294  }
 295  return f->curstat;
 296}
 297
 298void
 299penter(Node *p) /* set up parent pointers and leaf indices */
 300{
 301  switch (type(p)) {
 302    ELEAF
 303    LEAF info(p) = poscnt;
 304    poscnt++;
 305    break;
 306    UNARY
 307    penter(left(p));
 308    parent(left(p)) = p;
 309    break;
 310    case CAT:
 311    case OR:
 312      penter(left(p));
 313      penter(right(p));
 314      parent(left(p))  = p;
 315      parent(right(p)) = p;
 316      break;
 317    case ZERO:
 318      break;
 319    default: /* can't happen */
 320      FATAL("can't happen: unknown type %d in penter", type(p));
 321      break;
 322  }
 323}
 324
 325void
 326freetr(Node *p) /* free parse tree */
 327{
 328  switch (type(p)) {
 329    ELEAF
 330    LEAF xfree(p);
 331    break;
 332    UNARY
 333    case ZERO:
 334      freetr(left(p));
 335      xfree(p);
 336      break;
 337    case CAT:
 338    case OR:
 339      freetr(left(p));
 340      freetr(right(p));
 341      xfree(p);
 342      break;
 343    default: /* can't happen */
 344      FATAL("can't happen: unknown type %d in freetr", type(p));
 345      break;
 346  }
 347}
 348
 349/* in the parsing of regular expressions, metacharacters like . have */
 350/* to be seen literally;  \056 is not a metacharacter. */
 351
 352int
 353hexstr(const uschar **pp, int max) /* find and eval hex string at pp, return new p */
 354{                                  /* only pick up one 8-bit byte (2 chars) */
 355  const uschar *p;
 356  int           n = 0;
 357  int           i;
 358
 359  for (i = 0, p = *pp; i < max && isxdigit(*p); i++, p++) {
 360    if (isdigit((int)*p))
 361      n = 16 * n + *p - '0';
 362    else if (*p >= 'a' && *p <= 'f')
 363      n = 16 * n + *p - 'a' + 10;
 364    else if (*p >= 'A' && *p <= 'F')
 365      n = 16 * n + *p - 'A' + 10;
 366  }
 367  *pp = p;
 368  return n;
 369}
 370
 371#define isoctdigit(c) ((c) >= '0' && (c) <= '7') /* multiple use of arg */
 372
 373int
 374quoted(const uschar **pp) /* pick up next thing after a \\ */
 375                          /* and increment *pp */
 376{
 377  const uschar *p = *pp;
 378  int           c;
 379
 380  /* BUG: should advance by utf-8 char even if makes no sense */
 381
 382  switch ((c = *p++)) {
 383    case 't':
 384      c = '\t';
 385      break;
 386    case 'n':
 387      c = '\n';
 388      break;
 389    case 'f':
 390      c = '\f';
 391      break;
 392    case 'r':
 393      c = '\r';
 394      break;
 395    case 'b':
 396      c = '\b';
 397      break;
 398    case 'v':
 399      c = '\v';
 400      break;
 401    case 'a':
 402      c = '\a';
 403      break;
 404    case '\\':
 405      c = '\\';
 406      break;
 407    case 'x':            /* 2 hex digits follow */
 408      c = hexstr(&p, 2); /* this adds a null if number is invalid */
 409      break;
 410    case 'u': /* unicode char number up to 8 hex digits */
 411      c = hexstr(&p, 8);
 412      break;
 413    default:
 414      if (isoctdigit(c)) { /* \d \dd \ddd */
 415        int n = c - '0';
 416        if (isoctdigit(*p)) {
 417          n = 8 * n + *p++ - '0';
 418          if (isoctdigit(*p))
 419            n = 8 * n + *p++ - '0';
 420        }
 421        c = n;
 422      }
 423  }
 424
 425  *pp = p;
 426  return c;
 427}
 428
 429int *
 430cclenter(const char *argp) /* add a character class */
 431{
 432  int           i, c, c2;
 433  int           n;
 434  const uschar *p = (const uschar *)argp;
 435  int          *bp, *retp;
 436  static int   *buf   = NULL;
 437  static int    bufsz = 100;
 438
 439  if (buf == NULL && (buf = (int *)calloc(bufsz, sizeof(int))) == NULL)
 440    FATAL("out of space for character class [%.10s...] 1", p);
 441  bp = buf;
 442  for (i = 0; *p != 0;) {
 443    n = u8_rune(&c, (const char *)p);
 444    p += n;
 445    if (c == '\\') {
 446      c = quoted(&p);
 447    } else if (c == '-' && i > 0 && bp[-1] != 0) {
 448      if (*p != 0) {
 449        c = bp[-1];
 450        /* c2 = *p++; */
 451        n = u8_rune(&c2, (const char *)p);
 452        p += n;
 453        if (c2 == '\\')
 454          c2 = quoted(&p); /* BUG: sets p, has to
 455                  be u8 size */
 456        if (c > c2) {      /* empty; ignore */
 457          bp--;
 458          i--;
 459          continue;
 460        }
 461        while (c < c2) {
 462          if (i >= bufsz) {
 463            bufsz *= 2;
 464            buf = (int *)realloc(buf, bufsz * sizeof(int));
 465            if (buf == NULL)
 466              FATAL(
 467                  "out of space "
 468                  "for character "
 469                  "class "
 470                  "[%.10s...] 2",
 471                  p
 472              );
 473            bp = buf + i;
 474          }
 475          *bp++ = ++c;
 476          i++;
 477        }
 478        continue;
 479      }
 480    }
 481    if (i >= bufsz) {
 482      bufsz *= 2;
 483      buf = (int *)realloc(buf, bufsz * sizeof(int));
 484      if (buf == NULL)
 485        FATAL(
 486            "out of space for character class "
 487            "[%.10s...] 2",
 488            p
 489        );
 490      bp = buf + i;
 491    }
 492    *bp++ = c;
 493    i++;
 494  }
 495  *bp = 0;
 496  /* DPRINTF("cclenter: in = |%s|, out = |%s|\n", op, buf); BUG: can't
 497   * print array of int */
 498  /* xfree(op);  BUG: what are we freeing here? */
 499  retp = (int *)calloc(bp - buf + 1, sizeof(int));
 500  for (i = 0; i < bp - buf + 1; i++)
 501    retp[i] = buf[i];
 502  return retp;
 503}
 504
 505void
 506overflo(const char *s)
 507{
 508  FATAL("regular expression too big: out of space in %.30s...", s);
 509}
 510
 511void
 512cfoll(fa *f, Node *v) /* enter follow set of each leaf of vertex v into
 513       lfollow[leaf] */
 514{
 515  int  i;
 516  int *p;
 517
 518  switch (type(v)) {
 519    ELEAF
 520    LEAF f->re[info(v)].ltype = type(v);
 521    f->re[info(v)].lval.np    = right(v);
 522    while (f->accept >= maxsetvec) { /* guessing here! */
 523      resizesetvec(__func__);
 524    }
 525    for (i = 0; i <= f->accept; i++)
 526      setvec[i] = 0;
 527    setcnt = 0;
 528    follow(v); /* computes setvec and setcnt */
 529    p                      = intalloc(setcnt + 1, __func__);
 530    f->re[info(v)].lfollow = p;
 531    *p                     = setcnt;
 532    for (i = f->accept; i >= 0; i--)
 533      if (setvec[i] == 1)
 534        *++p = i;
 535    break;
 536    UNARY
 537    cfoll(f, left(v));
 538    break;
 539    case CAT:
 540    case OR:
 541      cfoll(f, left(v));
 542      cfoll(f, right(v));
 543      break;
 544    case ZERO:
 545      break;
 546    default: /* can't happen */
 547      FATAL("can't happen: unknown type %d in cfoll", type(v));
 548  }
 549}
 550
 551int
 552first(Node *p) /* collects initially active leaves of p into setvec */
 553               /* returns 0 if p matches empty string */
 554{
 555  int b, lp;
 556
 557  switch (type(p)) {
 558    ELEAF
 559    LEAF lp = info(p);                               /* look for high-water mark of subscripts */
 560    while (setcnt >= maxsetvec || lp >= maxsetvec) { /* guessing here! */
 561      resizesetvec(__func__);
 562    }
 563    if (type(p) == EMPTYRE) {
 564      setvec[lp] = 0;
 565      return (0);
 566    }
 567    if (setvec[lp] != 1) {
 568      setvec[lp] = 1;
 569      setcnt++;
 570    }
 571    if (type(p) == CCL && (*(int *)right(p)) == 0)
 572      return (0); /* empty CCL */
 573    return (1);
 574    case PLUS:
 575      if (first(left(p)) == 0)
 576        return (0);
 577      return (1);
 578    case STAR:
 579    case QUEST:
 580      first(left(p));
 581      return (0);
 582    case CAT:
 583      if (first(left(p)) == 0 && first(right(p)) == 0)
 584        return (0);
 585      return (1);
 586    case OR:
 587      b = first(right(p));
 588      if (first(left(p)) == 0 || b == 0)
 589        return (0);
 590      return (1);
 591    case ZERO:
 592      return 0;
 593  }
 594  FATAL("can't happen: unknown type %d in first", type(p)); /* can't happen */
 595  return (-1);
 596}
 597
 598void
 599follow(Node *v) /* collects leaves that can follow v into setvec */
 600{
 601  Node *p;
 602
 603  if (type(v) == FINAL)
 604    return;
 605  p = parent(v);
 606  switch (type(p)) {
 607    case STAR:
 608    case PLUS:
 609      first(v);
 610      follow(p);
 611      return;
 612
 613    case OR:
 614    case QUEST:
 615      follow(p);
 616      return;
 617
 618    case CAT:
 619      if (v == left(p)) { /* v is left child of p */
 620        if (first(right(p)) == 0) {
 621          follow(p);
 622          return;
 623        }
 624      } else /* v is right child */
 625        follow(p);
 626      return;
 627  }
 628}
 629
 630int
 631member(int c, int *sarg) /* is c in s? */
 632{
 633  int *s = (int *)sarg;
 634
 635  while (*s)
 636    if (c == *s++)
 637      return (1);
 638  return (0);
 639}
 640
 641static void
 642resize_gototab(fa *f, int state)
 643{
 644  size_t new_size = f->gototab[state].allocated * 2;
 645  gtte  *p        = (gtte *)realloc(f->gototab[state].entries, new_size * sizeof(gtte));
 646  if (p == NULL)
 647    overflo(__func__);
 648
 649  // need to initialize the new memory to zero
 650  size_t orig_size = f->gototab[state].allocated;     // 2nd half of new mem is this size
 651  memset(p + orig_size, 0, orig_size * sizeof(gtte)); // clean it out
 652
 653  f->gototab[state].allocated = new_size; // update gototab info
 654  f->gototab[state].entries   = p;
 655}
 656
 657static int
 658get_gototab(fa *f, int state, int ch) /* hide gototab implementation */
 659{
 660  gtte  key;
 661  gtte *item;
 662
 663  if ((unsigned)ch < GOTO_DIRECT)
 664    return f->gototab[state].direct[ch];
 665
 666  key.ch    = ch;
 667  key.state = 0; /* irrelevant */
 668  item      = (gtte *)bsearch(
 669      &key, f->gototab[state].entries, f->gototab[state].inuse, sizeof(gtte), entry_cmp
 670  );
 671
 672  if (item == NULL)
 673    return 0;
 674  else
 675    return item->state;
 676}
 677
 678static int
 679entry_cmp(const void *l, const void *r)
 680{
 681  const gtte *left, *right;
 682
 683  left  = (const gtte *)l;
 684  right = (const gtte *)r;
 685
 686  return left->ch - right->ch;
 687}
 688
 689static int
 690set_gototab(fa *f, int state, int ch, int val) /* hide gototab implementation */
 691{
 692  if ((unsigned)ch < GOTO_DIRECT) {
 693    f->gototab[state].direct[ch] = val;
 694    return val;
 695  }
 696
 697  if (f->gototab[state].inuse == 0) {
 698    f->gototab[state].entries[0].ch    = ch;
 699    f->gototab[state].entries[0].state = val;
 700    f->gototab[state].inuse++;
 701    return val;
 702  } else if ((unsigned)ch > f->gototab[state].entries[f->gototab[state].inuse - 1].ch) {
 703    // not seen yet, insert and return
 704    gtt *tab = &f->gototab[state];
 705    if (tab->inuse + 1 >= tab->allocated)
 706      resize_gototab(f, state);
 707
 708    f->gototab[state].entries[f->gototab[state].inuse].ch    = ch;
 709    f->gototab[state].entries[f->gototab[state].inuse].state = val;
 710    f->gototab[state].inuse++;
 711    return val;
 712  } else {
 713    // maybe we have it, maybe we don't
 714    gtte  key;
 715    gtte *item;
 716
 717    key.ch    = ch;
 718    key.state = 0; /* irrelevant */
 719    item      = (gtte *)bsearch(
 720        &key, f->gototab[state].entries, f->gototab[state].inuse, sizeof(gtte), entry_cmp
 721    );
 722
 723    if (item != NULL) {
 724      // we have it, update state and return
 725      item->state = val;
 726      return item->state;
 727    }
 728    // otherwise, fall through to insert and reallocate.
 729  }
 730
 731  gtt *tab = &f->gototab[state];
 732  if (tab->inuse + 1 >= tab->allocated)
 733    resize_gototab(f, state);
 734  f->gototab[state].entries[tab->inuse].ch    = ch;
 735  f->gototab[state].entries[tab->inuse].state = val;
 736  ++tab->inuse;
 737
 738  qsort(f->gototab[state].entries, f->gototab[state].inuse, sizeof(gtte), entry_cmp);
 739
 740  return val; /* not used anywhere at the moment */
 741}
 742
 743static void
 744clear_gototab(fa *f, int state)
 745{
 746  memset(f->gototab[state].direct, 0, sizeof(f->gototab[state].direct));
 747  memset(f->gototab[state].entries, 0, f->gototab[state].allocated * sizeof(gtte));
 748  f->gototab[state].inuse = 0;
 749}
 750
 751int
 752match(fa *f, const char *p0) /* shortest match ? */
 753{
 754  int           s, ns;
 755  int           n;
 756  int           rune;
 757  const uschar *p = (const uschar *)p0;
 758
 759  /* return pmatch(f, p0); does it matter whether longest or shortest? */
 760
 761  s = f->initstat;
 762  assert(s < f->state_count);
 763
 764  if (f->out[s])
 765    return (1);
 766  do {
 767    /* assert(*p < NCHARS); */
 768    n = u8_rune(&rune, (const char *)p);
 769    if ((ns = get_gototab(f, s, rune)) != 0)
 770      s = ns;
 771    else
 772      s = cgoto(f, s, rune);
 773    if (f->out[s])
 774      return (1);
 775    if (*p == 0)
 776      break;
 777    p += n;
 778  } while (1); /* was *p++ != 0 */
 779  return (0);
 780}
 781
 782int
 783pmatch(fa *f, const char *p0) /* longest match, for sub */
 784{
 785  int           s, ns;
 786  int           n;
 787  int           rune;
 788  const uschar *p = (const uschar *)p0;
 789  const uschar *q;
 790
 791  s = f->initstat;
 792  assert(s < f->state_count);
 793
 794  patbeg = (const char *)p;
 795  patlen = -1;
 796  do {
 797    q = p;
 798    do {
 799      if (f->out[s]) /* final state */
 800        patlen = q - p;
 801      /* assert(*q < NCHARS); */
 802      n = u8_rune(&rune, (const char *)q);
 803      if ((ns = get_gototab(f, s, rune)) != 0)
 804        s = ns;
 805      else
 806        s = cgoto(f, s, rune);
 807
 808      assert(s < f->state_count);
 809
 810      if (s == 1) { /* no transition */
 811        if (patlen >= 0) {
 812          patbeg = (const char *)p;
 813          return (1);
 814        } else
 815          goto nextin; /* no match */
 816      }
 817      if (*q == 0)
 818        break;
 819      q += n;
 820    } while (1);
 821    q++; /* was *q++ */
 822    if (f->out[s])
 823      patlen = q - p - 1; /* don't count $ */
 824    if (patlen >= 0) {
 825      patbeg = (const char *)p;
 826      return (1);
 827    }
 828  nextin:
 829    s = 2;
 830    if (*p == 0)
 831      break;
 832    n = u8_rune(&rune, (const char *)p);
 833    p += n;
 834  } while (1); /* was *p++ */
 835  return (0);
 836}
 837
 838int
 839nematch(fa *f, const char *p0) /* non-empty match, for sub */
 840{
 841  int           s, ns;
 842  int           n;
 843  int           rune;
 844  const uschar *p = (const uschar *)p0;
 845  const uschar *q;
 846
 847  s = f->initstat;
 848  assert(s < f->state_count);
 849
 850  patbeg = (const char *)p;
 851  patlen = -1;
 852  while (*p) {
 853    q = p;
 854    do {
 855      if (f->out[s]) /* final state */
 856        patlen = q - p;
 857      /* assert(*q < NCHARS); */
 858      n = u8_rune(&rune, (const char *)q);
 859      if ((ns = get_gototab(f, s, rune)) != 0)
 860        s = ns;
 861      else
 862        s = cgoto(f, s, rune);
 863      if (s == 1) { /* no transition */
 864        if (patlen > 0) {
 865          patbeg = (const char *)p;
 866          return (1);
 867        } else
 868          goto nnextin; /* no nonempty match */
 869      }
 870      if (*q == 0)
 871        break;
 872      q += n;
 873    } while (1);
 874    q++;
 875    if (f->out[s])
 876      patlen = q - p - 1; /* don't count $ */
 877    if (patlen > 0) {
 878      patbeg = (const char *)p;
 879      return (1);
 880    }
 881  nnextin:
 882    s = 2;
 883    p++;
 884  }
 885  return (0);
 886}
 887
 888/*
 889 * NAME
 890 *     fnematch
 891 *
 892 * DESCRIPTION
 893 *     A stream-fed version of nematch which transfers characters to a
 894 *     null-terminated buffer. All characters up to and including the last
 895 *     character of the matching text or EOF are placed in the buffer. If
 896 *     a match is found, patbeg and patlen are set appropriately.
 897 *
 898 * RETURN VALUES
 899 *     false    No match found.
 900 *     true     Match found.
 901 */
 902
 903bool
 904fnematch(fa *pfa, FILE *f, char **pbuf, int *pbufsize, int quantum)
 905{
 906  char *i, *j, *k, *buf = *pbuf;
 907  int   bufsize = *pbufsize;
 908  int   c, n, ns, s;
 909
 910  s      = pfa->initstat;
 911  patlen = 0;
 912
 913  /*
 914   * buf <= i <= j <= k <= buf+bufsize
 915   *
 916   * i: origin of active substring
 917   * j: current character
 918   * k: destination of the next getc
 919   */
 920
 921  i = j = k = buf;
 922
 923  do {
 924    /*
 925     * Call u8_rune with at least awk_mb_cur_max ahead in
 926     * the buffer until EOF interferes.
 927     */
 928    if (k - j < (int)awk_mb_cur_max) {
 929      if (k + awk_mb_cur_max > buf + bufsize) {
 930        char *obuf = buf;
 931        adjbuf((char **)&buf, &bufsize, bufsize + awk_mb_cur_max, quantum, 0, "fnematch");
 932
 933        /* buf resized, maybe moved. update pointers */
 934        *pbufsize = bufsize;
 935        if (obuf != buf) {
 936          i     = buf + (i - obuf);
 937          j     = buf + (j - obuf);
 938          k     = buf + (k - obuf);
 939          *pbuf = buf;
 940          if (patlen)
 941            patbeg = buf + (patbeg - obuf);
 942        }
 943      }
 944      for (n = awk_mb_cur_max; n > 0; n--) {
 945        *k++ = (c = getc(f)) != EOF ? c : 0;
 946        if (c == EOF) {
 947          if (ferror(f))
 948            FATAL("fnematch: getc error");
 949          break;
 950        }
 951      }
 952    }
 953
 954    j += u8_rune(&c, j);
 955
 956    if ((ns = get_gototab(pfa, s, c)) != 0)
 957      s = ns;
 958    else
 959      s = cgoto(pfa, s, c);
 960
 961    if (pfa->out[s]) { /* final state */
 962      patbeg = i;
 963      patlen = j - i;
 964      if (c == 0) /* don't count $ */
 965        patlen--;
 966    }
 967
 968    if (c && s != 1)
 969      continue; /* origin i still viable, next j */
 970    if (patlen)
 971      break; /* best match found */
 972
 973    /* no match at origin i, next i and start over */
 974    i += u8_rune(&c, i);
 975    if (c == 0)
 976      break; /* no match */
 977    j = i;
 978    s = 2;
 979  } while (1);
 980
 981  if (patlen) {
 982    /*
 983     * Under no circumstances is the last character fed to
 984     * the automaton part of the match. It is EOF's nullbyte,
 985     * or it sent the automaton into a state with no further
 986     * transitions available (s==1), or both. Room for a
 987     * terminating nullbyte is guaranteed.
 988     *
 989     * ungetc any chars after the end of matching text
 990     * (except for EOF's nullbyte, if present) and null
 991     * terminate the buffer.
 992     */
 993    do
 994      if (*--k && ungetc(*k, f) == EOF)
 995        FATAL("unable to ungetc '%c'", *k);
 996    while (k > patbeg + patlen);
 997    *k = '\0';
 998    return true;
 999  } else
1000    return false;
1001}
1002
1003Node *
1004reparse(const char *p) /* parses regular expression pointed to by p */
1005{                      /* uses relex() to scan regular expression */
1006  Node *np;
1007
1008  DPRINTF("reparse <%s>\n", p);
1009  lastre = prestr = (const uschar *)p; /* prestr points to string to be parsed */
1010  rtok            = relex();
1011  /* GNU compatibility: an empty regexp matches anything */
1012  if (rtok == '\0') {
1013    /* FATAL("empty regular expression"); previous */
1014    return (op2(EMPTYRE, NIL, NIL));
1015  }
1016  np = regexp();
1017  if (rtok != '\0')
1018    FATAL("syntax error in regular expression %s at %s", lastre, prestr);
1019  return (np);
1020}
1021
1022Node *
1023regexp(void) /* top-level parse of reg expr */
1024{
1025  return (alt(concat(primary())));
1026}
1027
1028Node *
1029primary(void)
1030{
1031  Node *np;
1032  int   savelastatom;
1033
1034  switch (rtok) {
1035    case CHAR:
1036      lastatom = starttok;
1037      np       = op2(CHAR, NIL, itonp(rlxval));
1038      rtok     = relex();
1039      return (unary(np));
1040    case ALL:
1041      rtok = relex();
1042      return (unary(op2(ALL, NIL, NIL)));
1043    case EMPTYRE:
1044      rtok = relex();
1045      return (unary(op2(EMPTYRE, NIL, NIL)));
1046    case DOT:
1047      lastatom = starttok;
1048      rtok     = relex();
1049      return (unary(op2(DOT, NIL, NIL)));
1050    case CCL:
1051      np       = op2(CCL, NIL, (Node *)cclenter((const char *)rlxstr));
1052      lastatom = starttok;
1053      rtok     = relex();
1054      return (unary(np));
1055    case NCCL:
1056      np       = op2(NCCL, NIL, (Node *)cclenter((const char *)rlxstr));
1057      lastatom = starttok;
1058      rtok     = relex();
1059      return (unary(np));
1060    case '^':
1061      rtok = relex();
1062      return (unary(op2(CHAR, NIL, itonp(HAT))));
1063    case '$':
1064      rtok = relex();
1065      return (unary(op2(CHAR, NIL, NIL)));
1066    case '(':
1067      lastatom     = starttok;
1068      savelastatom = starttok - basestr; /* Retain over recursion */
1069      rtok         = relex();
1070      if (rtok == ')') { /* special pleading for () */
1071        rtok = relex();
1072        return unary(op2(CCL, NIL, (Node *)cclenter("")));
1073      }
1074      np = regexp();
1075      if (rtok == ')') {
1076        lastatom = basestr + savelastatom; /* Restore */
1077        rtok     = relex();
1078        return (unary(np));
1079      } else {
1080        FATAL("syntax error in regular expression %s at %s", lastre, prestr);
1081        break;
1082      }
1083    default:
1084      FATAL("illegal primary in regular expression %s at %s", lastre, prestr);
1085  }
1086  return 0; /*NOTREACHED*/
1087}
1088
1089Node *
1090concat(Node *np)
1091{
1092  switch (rtok) {
1093    case CHAR:
1094    case DOT:
1095    case ALL:
1096    case CCL:
1097    case NCCL:
1098    case '$':
1099    case '(':
1100      return (concat(op2(CAT, np, primary())));
1101    case EMPTYRE:
1102      rtok = relex();
1103      return (concat(op2(CAT, op2(CCL, NIL, (Node *)cclenter("")), primary())));
1104  }
1105  return (np);
1106}
1107
1108Node *
1109alt(Node *np)
1110{
1111  if (rtok == OR) {
1112    rtok = relex();
1113    return (alt(op2(OR, np, concat(primary()))));
1114  }
1115  return (np);
1116}
1117
1118Node *
1119unary(Node *np)
1120{
1121  switch (rtok) {
1122    case STAR:
1123      rtok = relex();
1124      return (unary(op2(STAR, np, NIL)));
1125    case PLUS:
1126      rtok = relex();
1127      return (unary(op2(PLUS, np, NIL)));
1128    case QUEST:
1129      rtok = relex();
1130      return (unary(op2(QUEST, np, NIL)));
1131    case ZERO:
1132      rtok = relex();
1133      return (unary(op2(ZERO, np, NIL)));
1134    default:
1135      return (np);
1136  }
1137}
1138
1139/*
1140 * Character class definitions conformant to the POSIX locale as
1141 * defined in IEEE P1003.1 draft 7 of June 2001, assuming the source
1142 * and operating character sets are both ASCII (ISO646) or supersets
1143 * thereof.
1144 *
1145 * Note that to avoid overflowing the temporary buffer used in
1146 * relex(), the expanded character class (prior to range expansion)
1147 * must be less than twice the size of their full name.
1148 */
1149
1150/* Because isblank doesn't show up in any of the header files on any
1151 * system i use, it's defined here.  if some other locale has a richer
1152 * definition of "blank", define HAS_ISBLANK and provide your own
1153 * version.
1154 * the parentheses here are an attempt to find a path through the maze
1155 * of macro definition and/or function and/or version provided.  thanks
1156 * to nelson beebe for the suggestion; let's see if it works everywhere.
1157 */
1158
1159/* #define HAS_ISBLANK */
1160#ifndef HAS_ISBLANK
1161
1162int(xisblank)(int c)
1163{
1164  return c == ' ' || c == '\t';
1165}
1166
1167#endif
1168
1169static const struct charclass {
1170  const char *cc_name;
1171  int         cc_namelen;
1172  int (*cc_func)(int);
1173} charclasses[] = {
1174    {"alnum", 5, isalnum},
1175    {"alpha", 5, isalpha},
1176#ifndef HAS_ISBLANK
1177    {"blank", 5, xisblank},
1178#else
1179    {"blank", 5, isblank},
1180#endif
1181    {"cntrl", 5, iscntrl},
1182    {"digit", 5, isdigit},
1183    {"graph", 5, isgraph},
1184    {"lower", 5, islower},
1185    {"print", 5, isprint},
1186    {"punct", 5, ispunct},
1187    {"space", 5, isspace},
1188    {"upper", 5, isupper},
1189    {"xdigit", 6, isxdigit},
1190    {NULL, 0, NULL},
1191};
1192
1193#define REPEAT_SIMPLE        0
1194#define REPEAT_PLUS_APPENDED 1
1195#define REPEAT_WITH_Q        2
1196#define REPEAT_ZERO          3
1197
1198static int
1199replace_repeat(
1200    const uschar *reptok,
1201    int           reptoklen,
1202    const uschar *atom,
1203    int           atomlen,
1204    int           firstnum,
1205    int           secondnum,
1206    int           special_case
1207)
1208{
1209  int     i, j;
1210  uschar *buf           = 0;
1211  int     ret           = 1;
1212  int     init_q        = (firstnum == 0);      /* first added char will be ? */
1213  int     n_q_reps      = secondnum - firstnum; /* m>n, so reduce until {1,m-n} left  */
1214  int     prefix_length = reptok - basestr;     /* prefix includes first rep
1215                                                 */
1216  int suffix_length = strlen((const char *)reptok) - reptoklen; /* string after rep specifier	*/
1217  int size          = prefix_length + suffix_length;
1218
1219  if (firstnum > 1) { /* add room for reps 2 through firstnum */
1220    size += atomlen * (firstnum - 1);
1221  }
1222
1223  /* Adjust size of buffer for special cases */
1224  if (special_case == REPEAT_PLUS_APPENDED) {
1225    size++; /* for the final + */
1226  } else if (special_case == REPEAT_WITH_Q) {
1227    size += init_q + (atomlen + 1) * (n_q_reps - init_q);
1228  } else if (special_case == REPEAT_ZERO) {
1229    size += 2; /* just a null ERE: () */
1230  }
1231  if ((buf = (uschar *)malloc(size + 1)) == NULL)
1232    FATAL("out of space in reg expr %.10s..", lastre);
1233  memcpy(buf, basestr, prefix_length); /* copy prefix	*/
1234  j = prefix_length;
1235  if (special_case == REPEAT_ZERO) {
1236    j -= atomlen;
1237    buf[j++] = '(';
1238    buf[j++] = ')';
1239  }
1240  for (i = 1; i < firstnum; i++) { /* copy x reps 	*/
1241    memcpy(&buf[j], atom, atomlen);
1242    j += atomlen;
1243  }
1244  if (special_case == REPEAT_PLUS_APPENDED) {
1245    buf[j++] = '+';
1246  } else if (special_case == REPEAT_WITH_Q) {
1247    if (init_q)
1248      buf[j++] = '?';
1249    for (i = init_q; i < n_q_reps; i++) { /* copy x? reps */
1250      memcpy(&buf[j], atom, atomlen);
1251      j += atomlen;
1252      buf[j++] = '?';
1253    }
1254  }
1255  memcpy(&buf[j], reptok + reptoklen, suffix_length);
1256  j += suffix_length;
1257  buf[j] = '\0';
1258  /* free old basestr */
1259  if (firstbasestr != basestr) {
1260    if (basestr)
1261      xfree(basestr);
1262  }
1263  basestr = buf;
1264  prestr  = buf + prefix_length;
1265  if (special_case == REPEAT_ZERO) {
1266    prestr -= atomlen;
1267    ret++;
1268  }
1269  return ret;
1270}
1271
1272static int
1273repeat(
1274    const uschar *reptok,
1275    int           reptoklen,
1276    const uschar *atom,
1277    int           atomlen,
1278    int           firstnum,
1279    int           secondnum
1280)
1281{
1282  if (atom == NULL)
1283    return 0;
1284
1285  /*
1286     In general, the repetition specifier or "bound" is replaced here
1287     by an equivalent ERE string, repeating the immediately previous atom
1288     and appending ? and + as needed. Note that the first copy of the
1289     atom is left in place, except in the special_case of a zero-repeat
1290     (i.e., {0}).
1291   */
1292  if (secondnum < 0) { /* means {n,} -> repeat n-1 times followed by PLUS */
1293    if (firstnum < 2) {
1294      /* 0 or 1: should be handled before you get here */
1295      FATAL("internal error");
1296    } else {
1297      return replace_repeat(
1298          reptok, reptoklen, atom, atomlen, firstnum, secondnum, REPEAT_PLUS_APPENDED
1299      );
1300    }
1301  } else if (firstnum == secondnum) { /* {n} or {n,n} -> simply repeat n-1 times */
1302    if (firstnum == 0) {              /* {0} or {0,0} */
1303      /* This case is unusual because the resulting
1304         replacement string might actually be SMALLER than
1305         the original ERE */
1306      return replace_repeat(reptok, reptoklen, atom, atomlen, firstnum, secondnum, REPEAT_ZERO);
1307    } else { /* (firstnum >= 1) */
1308      return replace_repeat(reptok, reptoklen, atom, atomlen, firstnum, secondnum, REPEAT_SIMPLE);
1309    }
1310  } else if (firstnum < secondnum) { /* {n,m} -> repeat n-1 times then alternate  */
1311    /*  x{n,m}  =>  xx...x{1, m-n+1}  =>  xx...x?x?x?..x?	*/
1312    return replace_repeat(reptok, reptoklen, atom, atomlen, firstnum, secondnum, REPEAT_WITH_Q);
1313  } else { /* Error - shouldn't be here (n>m) */
1314    FATAL("internal error");
1315  }
1316  return 0;
1317}
1318
1319int
1320relex(void) /* lexical analyzer for reparse */
1321{
1322  int                     c, n;
1323  int                     cflag;
1324  static uschar          *buf   = NULL;
1325  static int              bufsz = 100;
1326  uschar                 *bp;
1327  const struct charclass *cc;
1328  int                     i;
1329  int                     num, m;
1330  bool                    commafound, digitfound;
1331  const uschar           *startreptok;
1332  static int              parens = 0;
1333
1334rescan:
1335  starttok = prestr;
1336
1337  if ((n = u8_rune(&rlxval, (const char *)prestr)) > 1) {
1338    prestr += n;
1339    starttok = prestr;
1340    return CHAR;
1341  }
1342
1343  switch (c = *prestr++) {
1344    case '|':
1345      return OR;
1346    case '*':
1347      return STAR;
1348    case '+':
1349      return PLUS;
1350    case '?':
1351      return QUEST;
1352    case '.':
1353      return DOT;
1354    case '\0':
1355      prestr--;
1356      return '\0';
1357    case '^':
1358    case '$':
1359      return c;
1360    case '(':
1361      parens++;
1362      return c;
1363    case ')':
1364      if (parens) {
1365        parens--;
1366        return c;
1367      }
1368      /* unmatched close parenthesis; per POSIX, treat as literal */
1369      rlxval = c;
1370      return CHAR;
1371    case '\\':
1372      rlxval = quoted(&prestr);
1373      return CHAR;
1374    default:
1375      rlxval = c;
1376      return CHAR;
1377    case '[':
1378      if (buf == NULL && (buf = (uschar *)malloc(bufsz)) == NULL)
1379        FATAL("out of space in reg expr %.10s..", lastre);
1380      bp = buf;
1381      if (*prestr == '^') {
1382        cflag = 1;
1383        prestr++;
1384      } else
1385        cflag = 0;
1386      n = 5 * strlen((const char *)prestr) + 1; /* BUG: was 2.  what value? */
1387      if (!adjbuf((char **)&buf, &bufsz, n, n, (char **)&bp, "relex1"))
1388        FATAL("out of space for reg expr %.10s...", lastre);
1389      for (;;) {
1390        if ((n = u8_rune(&rlxval, (const char *)prestr)) > 1) {
1391          for (i = 0; i < n; i++)
1392            *bp++ = *prestr++;
1393          continue;
1394        }
1395        if ((c = *prestr++) == '\\') {
1396          *bp++ = '\\';
1397          if ((c = *prestr++) == '\0')
1398            FATAL(
1399                "nonterminated character class "
1400                "%.20s...",
1401                lastre
1402            );
1403          *bp++ = c;
1404          /* } else if (c == '\n') { */
1405          /* 	FATAL("newline in character class
1406           * %.20s...", lastre); */
1407        } else if (c == '[' && *prestr == ':') {
1408          /* POSIX char class names, Dag-Erling Smorgrav,
1409           * des@ofug.org */
1410          for (cc = charclasses; cc->cc_name; cc++)
1411            if (strncmp((const char *)prestr + 1, (const char *)cc->cc_name, cc->cc_namelen) == 0)
1412              break;
1413          if (cc->cc_name != NULL && prestr[1 + cc->cc_namelen] == ':'
1414              && prestr[2 + cc->cc_namelen] == ']') {
1415            prestr += cc->cc_namelen + 3;
1416            /*
1417             * BUG: We begin at 1, instead of 0,
1418             * since we would otherwise prematurely
1419             * terminate the string for classes like
1420             * [[:cntrl:]]. This means that we can't
1421             * match the NUL character, not without
1422             * first adapting the entire program to
1423             * track each string's length.
1424             */
1425            for (i = 1; i <= UCHAR_MAX; i++) {
1426              if (!adjbuf((char **)&buf, &bufsz, bp - buf + 2, 100, (char **)&bp, "relex2"))
1427                FATAL(
1428                    "out of space "
1429                    "for reg expr "
1430                    "%.10s...",
1431                    lastre
1432                );
1433              if (cc->cc_func(i)) {
1434                /* escape backslash */
1435                if (i == '\\') {
1436                  *bp++ = '\\';
1437                  n++;
1438                }
1439
1440                *bp++ = i;
1441                n++;
1442              }
1443            }
1444          } else
1445            *bp++ = c;
1446        } else if (c == '[' && *prestr == '.') {
1447          char collate_char;
1448          prestr++;
1449          collate_char = *prestr++;
1450          if (*prestr == '.' && prestr[1] == ']') {
1451            prestr += 2;
1452            /* Found it: map via locale TBD: for
1453               now, simply return this char.  This
1454               is sufficient to pass conformance
1455               test awk.ex 156
1456             */
1457            if (*prestr == ']') {
1458              prestr++;
1459              rlxval = collate_char;
1460              return CHAR;
1461            }
1462          }
1463        } else if (c == '[' && *prestr == '=') {
1464          char equiv_char;
1465          prestr++;
1466          equiv_char = *prestr++;
1467          if (*prestr == '=' && prestr[1] == ']') {
1468            prestr += 2;
1469            /* Found it: map via locale TBD: for now
1470               simply return this char. This is
1471               sufficient to pass conformance test
1472               awk.ex 156
1473             */
1474            if (*prestr == ']') {
1475              prestr++;
1476              rlxval = equiv_char;
1477              return CHAR;
1478            }
1479          }
1480        } else if (c == '\0') {
1481          FATAL("nonterminated character class %.20s", lastre);
1482        } else if (bp == buf) { /* 1st char is special */
1483          *bp++ = c;
1484        } else if (c == ']') {
1485          *bp++  = 0;
1486          rlxstr = (uschar *)tostring((char *)buf);
1487          if (cflag == 0)
1488            return CCL;
1489          else
1490            return NCCL;
1491        } else
1492          *bp++ = c;
1493      }
1494      break;
1495    case '{':
1496      if (isdigit((int)*(prestr))) {
1497        num         = 0; /* Process as a repetition */
1498        n           = -1;
1499        m           = -1;
1500        commafound  = false;
1501        digitfound  = false;
1502        startreptok = prestr - 1;
1503        /* Remember start of previous atom here ? */
1504      } else { /* just a { char, not a repetition */
1505        rlxval = c;
1506        return CHAR;
1507      }
1508      for (;;) {
1509        if ((c = *prestr++) == '}') {
1510          if (commafound) {
1511            if (digitfound) { /* {n,m} */
1512              m = num;
1513              if (m < n)
1514                FATAL(
1515                    "illegal "
1516                    "repetition "
1517                    "expression: "
1518                    "class %.20s",
1519                    lastre
1520                );
1521              if (n == 0 && m == 1) {
1522                return QUEST;
1523              }
1524            } else { /* {n,} */
1525              if (n == 0)
1526                return STAR;
1527              else if (n == 1)
1528                return PLUS;
1529            }
1530          } else {
1531            if (digitfound) { /* {n} same as {n,n}
1532                               */
1533              n = num;
1534              m = num;
1535            } else { /* {} */
1536              FATAL(
1537                  "illegal repetition "
1538                  "expression: class %.20s",
1539                  lastre
1540              );
1541            }
1542          }
1543          if (repeat(starttok, prestr - starttok, lastatom, startreptok - lastatom, n, m) > 0) {
1544            if (n == 0 && m == 0) {
1545              return ZERO;
1546            }
1547            /* must rescan input for next token */
1548            goto rescan;
1549          }
1550          /* Failed to replace: eat up {...} characters
1551             and treat like just PLUS */
1552          return PLUS;
1553        } else if (c == '\0') {
1554          FATAL("nonterminated character class %.20s", lastre);
1555        } else if (isdigit(c)) {
1556          num = 10 * num + c - '0';
1557          if (num > 255)
1558            FATAL(
1559                "repetition count %.20s too "
1560                "large",
1561                lastre
1562            );
1563          digitfound = true;
1564        } else if (c == ',') {
1565          if (commafound)
1566            FATAL(
1567                "illegal repetition expression: "
1568                "class %.20s",
1569                lastre
1570            );
1571          /* looking for {n,} or {n,m} */
1572          commafound = true;
1573          n          = num;
1574          digitfound = false; /* reset */
1575          num        = 0;
1576        } else {
1577          FATAL(
1578              "illegal repetition expression: class "
1579              "%.20s",
1580              lastre
1581          );
1582        }
1583      }
1584      break;
1585  }
1586}
1587
1588int
1589cgoto(fa *f, int s, int c)
1590{
1591  int *p, *q;
1592  int  i, j, k;
1593
1594  /* assert(c == HAT || c < NCHARS);  BUG: seg fault if disable test */
1595  while (f->accept >= maxsetvec) { /* guessing here! */
1596    resizesetvec(__func__);
1597  }
1598  for (i = 0; i <= f->accept; i++)
1599    setvec[i] = 0;
1600  setcnt = 0;
1601  resize_state(f, s);
1602  /* compute positions of gototab[s,c] into setvec */
1603  p = f->posns[s];
1604  for (i = 1; i <= *p; i++) {
1605    if ((k = f->re[p[i]].ltype) != FINAL) {
1606      if ((k == CHAR && c == ptoi(f->re[p[i]].lval.np)) || (k == DOT && c != 0 && c != HAT)
1607          || (k == ALL && c != 0) || (k == EMPTYRE && c != 0)
1608          || (k == CCL && member(c, (int *)f->re[p[i]].lval.rp))
1609          || (k == NCCL && !member(c, (int *)f->re[p[i]].lval.rp) && c != 0 && c != HAT)) {
1610        q = f->re[p[i]].lfollow;
1611        for (j = 1; j <= *q; j++) {
1612          if (q[j] >= maxsetvec) {
1613            resizesetvec(__func__);
1614          }
1615          if (setvec[q[j]] == 0) {
1616            setcnt++;
1617            setvec[q[j]] = 1;
1618          }
1619        }
1620      }
1621    }
1622  }
1623  /* determine if setvec is a previous state */
1624  tmpset[0] = setcnt;
1625  j         = 1;
1626  for (i = f->accept; i >= 0; i--)
1627    if (setvec[i]) {
1628      tmpset[j++] = i;
1629    }
1630  resize_state(f, f->curstat > s ? f->curstat : s);
1631  /* tmpset == previous state? */
1632  for (i = 1; i <= f->curstat; i++) {
1633    p = f->posns[i];
1634    if ((k = tmpset[0]) != p[0])
1635      goto different;
1636    for (j = 1; j <= k; j++)
1637      if (tmpset[j] != p[j])
1638        goto different;
1639    /* setvec is state i */
1640    if (c != HAT)
1641      set_gototab(f, s, c, i);
1642    return i;
1643  different:;
1644  }
1645
1646  /* add tmpset to current set of states */
1647  ++(f->curstat);
1648  resize_state(f, f->curstat);
1649  clear_gototab(f, f->curstat);
1650  xfree(f->posns[f->curstat]);
1651  p = intalloc(setcnt + 1, __func__);
1652
1653  f->posns[f->curstat] = p;
1654  if (c != HAT)
1655    set_gototab(f, s, c, f->curstat);
1656  for (i = 0; i <= setcnt; i++)
1657    p[i] = tmpset[i];
1658  if (setvec[f->accept])
1659    f->out[f->curstat] = 1;
1660  else
1661    f->out[f->curstat] = 0;
1662  return f->curstat;
1663}
1664
1665void
1666freefa(fa *f) /* free a finite automaton */
1667{
1668  int i;
1669
1670  if (f == NULL)
1671    return;
1672  for (i = 0; i < f->state_count; i++)
1673    xfree(f->gototab[i].entries);
1674  xfree(f->gototab);
1675  for (i = 0; i <= f->curstat; i++)
1676    xfree(f->posns[i]);
1677  for (i = 0; i <= f->accept; i++) {
1678    xfree(f->re[i].lfollow);
1679    if (f->re[i].ltype == CCL || f->re[i].ltype == NCCL)
1680      xfree(f->re[i].lval.np);
1681  }
1682  xfree(f->restr);
1683  xfree(f->out);
1684  xfree(f->posns);
1685  xfree(f->gototab);
1686  xfree(f);
1687}