master xplshn/aruu / cmd / posix / 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#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: /* cant 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: /* cant 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: cant
 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: /* cant 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)); /* cant 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 dont */
 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; /* dont 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; /* dont 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) /* dont 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 doesnt show up in any of the header files on any
1151 * system i use, its 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 - shouldnt 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 cant
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}