master xplshn/aruu / cmd / posix / make / parser.c
   1#include <assert.h>
   2#include <ctype.h>
   3#include <errno.h>
   4#include <limits.h>
   5#include <stdarg.h>
   6#include <stdio.h>
   7#include <stdlib.h>
   8#include <string.h>
   9
  10#include "make.h"
  11
  12#define MAXREPL  30
  13#define TABSIZ   64
  14#define MAXTOKEN FILENAME_MAX
  15#define ITEM     128
  16
  17typedef struct macro Macro;
  18
  19enum inputype {
  20  FTFILE,
  21  FTEXPAN,
  22};
  23
  24enum {
  25  STBEGIN,
  26  STINTERNAL,
  27  STREPLACE,
  28  STTO,
  29  STEND,
  30};
  31
  32struct input {
  33  int siz;
  34  int type;
  35
  36  FILE      *fp;
  37  struct loc loc;
  38
  39  int   pos;
  40  char *buf;
  41
  42  struct input *prev;
  43};
  44
  45struct macro {
  46  char *name;
  47  char *value;
  48  int   where;
  49
  50  struct macro *next;
  51};
  52
  53static struct input *input;
  54static char          token[MAXTOKEN];
  55static int           tok;
  56static Macro        *htab[TABSIZ];
  57
  58void
  59dumpmacros(void)
  60{
  61  Macro **pp, *p;
  62
  63  for (pp = htab; pp < &htab[TABSIZ]; ++pp) {
  64    for (p = *pp; p; p = p->next)
  65      printf("%s = %s\n", p->name, getmacro(p->name));
  66  }
  67}
  68
  69static Macro *
  70lookup(char *name)
  71{
  72  Macro *mp;
  73  int    h = hash(name) & (TABSIZ - 1);
  74
  75  for (mp = htab[h]; mp && strcmp(mp->name, name); mp = mp->next)
  76    ;
  77
  78  if (mp)
  79    return mp;
  80
  81  mp        = emalloc(sizeof(*mp));
  82  mp->name  = estrdup(name);
  83  mp->value = estrdup("");
  84  mp->next  = htab[h];
  85  mp->where = UNDEF;
  86  htab[h]   = mp;
  87
  88  return mp;
  89}
  90
  91static char *
  92macroinfo(char *name, int *pwhere, Macro **mpp)
  93{
  94  char  *s, *t;
  95  int    hide, where;
  96  Macro *mp = lookup(name);
  97
  98  hide = 0;
  99  if (!strcmp(name, "SHELL") || !strcmp(name, "MAKEFLAGS"))
 100    hide = 1;
 101
 102  s     = mp->value;
 103  where = mp->where;
 104
 105  if (!hide && (where == UNDEF || where == INTERNAL || eflag)) {
 106    t = getenv(name);
 107    if (t) {
 108      where = ENVIRON;
 109      s     = t;
 110    }
 111  }
 112
 113  if (pwhere)
 114    *pwhere = where;
 115  if (mpp)
 116    *mpp = mp;
 117
 118  return s;
 119}
 120
 121char *
 122getmacro(char *name)
 123{
 124  return macroinfo(name, NULL, NULL);
 125}
 126
 127void
 128setmacro(char *name, char *val, int where, int export)
 129{
 130  int    owhere, set;
 131  char  *s;
 132  Macro *mp;
 133
 134  assert(where != ENVIRON);
 135
 136  s = macroinfo(name, &owhere, &mp);
 137
 138  /*
 139   *  Default values are defined before anything else, and marked
 140   *  as INTERNAL because they are injected as parseable text, and
 141   *  MAKEFILE and INTERNAL variables are always overriden. ENVIRON
 142   *  macros are generated in macroinfo() and this is why this function
 143   *  should not receive a where == ENVIRON ever.
 144   */
 145  switch (owhere) {
 146    case UNDEF:
 147    case INTERNAL:
 148    case MAKEFILE:
 149      set = 1;
 150      break;
 151    case ENVIRON:
 152      set = (where == MAKEFLAGS || where == CMDLINE);
 153      set |= (where == MAKEFILE && !eflag);
 154      break;
 155    case MAKEFLAGS:
 156      set = (where == CMDLINE || where == MAKEFLAGS);
 157      break;
 158    case CMDLINE:
 159      set = (where == CMDLINE);
 160      break;
 161    default:
 162      abort();
 163  }
 164
 165  if (!set) {
 166    debug("hidding override of %s from '%s' to '%s'", name, s, val);
 167  } else {
 168    debug("override %s from '%s' to '%s'", name, s, val);
 169    free(mp->value);
 170    mp->value = estrdup(val);
 171    mp->where = where;
 172
 173    if (export && strcmp(name, "SHELL") != 0) {
 174      debug("exporting macro %s", name);
 175      exportvar(name, val);
 176    }
 177  }
 178}
 179
 180void
 181freeloc(struct loc *loc)
 182{
 183  free(loc->fname);
 184}
 185
 186static struct loc *
 187getloc(void)
 188{
 189  struct input *ip;
 190
 191  for (ip = input; ip && ip->type != FTFILE; ip = ip->prev)
 192    ;
 193  if (!ip)
 194    return NULL;
 195
 196  return &ip->loc;
 197}
 198
 199void
 200error(char *fmt, ...)
 201{
 202  va_list     va;
 203  struct loc *loc;
 204
 205  fprintf(stderr, "make: error: ");
 206  if ((loc = getloc()) != NULL)
 207    fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);
 208
 209  va_start(va, fmt);
 210  vfprintf(stderr, fmt, va);
 211  va_end(va);
 212  putc('\n', stderr);
 213
 214  exit(EXIT_FAILURE);
 215}
 216
 217void
 218warning(char *fmt, ...)
 219{
 220  va_list     va;
 221  struct loc *loc;
 222
 223  fprintf(stderr, "make: warning: ");
 224  if ((loc = getloc()) != NULL)
 225    fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);
 226
 227  va_start(va, fmt);
 228  vfprintf(stderr, fmt, va);
 229  va_end(va);
 230  putc('\n', stderr);
 231}
 232
 233static void
 234pop(void)
 235{
 236  struct input *ip = input->prev;
 237
 238  if (input->type == FTFILE) {
 239    if (input->fp)
 240      fclose(input->fp);
 241    freeloc(&input->loc);
 242  }
 243  free(input->buf);
 244  free(input);
 245
 246  input = ip;
 247}
 248
 249static void
 250push(int type, ...)
 251{
 252  int           line, len, pos;
 253  FILE         *fp = NULL;
 254  char         *buf, *s, *fname = NULL;
 255  va_list       va;
 256  struct input *ip;
 257
 258  va_start(va, type);
 259  switch (type) {
 260    case FTFILE:
 261      fp    = va_arg(va, FILE *);
 262      s     = va_arg(va, char *);
 263      line  = va_arg(va, int);
 264      fname = estrdup(s);
 265      buf   = emalloc(BUFSIZ);
 266      pos = len = BUFSIZ;
 267      break;
 268    case FTEXPAN:
 269      s    = va_arg(va, char *);
 270      buf  = estrdup(s);
 271      line = pos = 0;
 272      len        = strlen(s);
 273      break;
 274  }
 275  va_end(va);
 276
 277  ip             = emalloc(sizeof(*ip));
 278  ip->siz        = len;
 279  ip->buf        = buf;
 280  ip->type       = type;
 281  ip->fp         = fp;
 282  ip->loc.fname  = fname;
 283  ip->loc.lineno = line;
 284  ip->pos        = pos;
 285  ip->prev       = input;
 286
 287  input = ip;
 288}
 289
 290static char *
 291trim(char *s)
 292{
 293  size_t len;
 294
 295  while (isspace(*s))
 296    s++;
 297
 298  for (len = strlen(s); len > 0 && isspace(s[len - 1]); --len)
 299    s[len - 1] = '\0';
 300
 301  return s;
 302}
 303
 304static void
 305include(char *s)
 306{
 307  FILE *fp;
 308  char *fil, *t, *end;
 309
 310  s = trim(s);
 311  if (*s == '<' || *s == '"') {
 312    char delim = (*s == '<') ? '>' : '"';
 313    s++;
 314    end = strchr(s, delim);
 315    if (end)
 316      *end = '\0';
 317  }
 318  fil = expandstring(s, NULL, getloc());
 319
 320  t = trim(fil);
 321  if (strlen(t) != 0) {
 322    debug("including '%s'", t);
 323    if ((fp = fopen(t, "r")) == NULL)
 324      error("opening %s:%s", t, strerror(errno));
 325    push(FTFILE, fp, t, 0);
 326  }
 327
 328  free(fil);
 329}
 330
 331static void
 332sinclude(char *s)
 333{
 334  FILE *fp;
 335  char *fil, *t, *end;
 336
 337  s = trim(s);
 338  if (*s == '<' || *s == '"') {
 339    char delim = (*s == '<') ? '>' : '"';
 340    s++;
 341    end = strchr(s, delim);
 342    if (end)
 343      *end = '\0';
 344  }
 345  fil = expandstring(s, NULL, getloc());
 346
 347  t = trim(fil);
 348  if (strlen(t) != 0) {
 349    debug("trying to include '%s'", t);
 350    if ((fp = fopen(t, "r")) != NULL)
 351      push(FTFILE, fp, t, 0);
 352    else
 353      debug("skipping missing include %s", t);
 354  }
 355
 356  free(fil);
 357}
 358
 359#define MAXCOND 32
 360static struct {
 361  int active;
 362  int matched;
 363  int parent_active;
 364} condstack[MAXCOND];
 365static int conddepth = 0;
 366
 367static int
 368cond_active(void)
 369{
 370  if (conddepth == 0)
 371    return 1;
 372  return condstack[conddepth].active && condstack[conddepth].parent_active;
 373}
 374
 375static void
 376cond_if(int truth)
 377{
 378  int parent;
 379  if (conddepth >= MAXCOND)
 380    error("too many nested .if directives");
 381  parent = cond_active();
 382  conddepth++;
 383  condstack[conddepth].parent_active = parent;
 384  condstack[conddepth].matched       = truth;
 385  condstack[conddepth].active        = truth;
 386}
 387
 388static void
 389cond_elif(int truth)
 390{
 391  if (conddepth == 0)
 392    error(".elif without .if");
 393  if (condstack[conddepth].matched)
 394    condstack[conddepth].active = 0;
 395  else {
 396    condstack[conddepth].matched = condstack[conddepth].matched || truth;
 397    condstack[conddepth].active  = truth;
 398  }
 399}
 400
 401static void
 402cond_else(void)
 403{
 404  if (conddepth == 0)
 405    error(".else without .if");
 406  condstack[conddepth].active  = !condstack[conddepth].matched;
 407  condstack[conddepth].matched = 1;
 408}
 409
 410static void
 411cond_endif(void)
 412{
 413  if (conddepth == 0)
 414    error(".endif without .if");
 415  conddepth--;
 416}
 417
 418static int
 419eval_if(char *expr)
 420{
 421  char *s, *p, *var, *val, *end;
 422  int   neg = 0, result;
 423
 424  s = trim(expr);
 425  if (!*s)
 426    return 0;
 427
 428  while (*s == '(') {
 429    end = s + strlen(s);
 430    if (end > s && end[-1] == ')')
 431      end[-1] = '\0';
 432    s++;
 433    s = trim(s);
 434  }
 435
 436  if (*s == '!') {
 437    neg = 1;
 438    s++;
 439    s = trim(s);
 440  }
 441
 442  if (strncmp(s, "defined", 7) == 0 && (s[7] == '(' || s[7] == ' ')) {
 443    var = trim(s + 7);
 444    if (*var == '(')
 445      var++;
 446    end = var + strlen(var);
 447    if (end > var && end[-1] == ')')
 448      end[-1] = '\0';
 449    var    = trim(var);
 450    result = getmacro(var)[0] != '\0';
 451    return neg ? !result : result;
 452  }
 453
 454  if (strncmp(s, "make", 4) == 0 && (s[4] == '(' || s[4] == ' ')) {
 455    var = trim(s + 4);
 456    if (*var == '(')
 457      var++;
 458    end = var + strlen(var);
 459    if (end > var && end[-1] == ')')
 460      end[-1] = '\0';
 461    var    = trim(var);
 462    result = getmacro(var)[0] != '\0';
 463    return neg ? !result : result;
 464  }
 465
 466  {
 467    char *expanded = expandstring(s, NULL, getloc());
 468    s              = expanded;
 469  }
 470
 471  p = strstr(s, "==");
 472  if (!p)
 473    p = strstr(s, "!=");
 474  if (p) {
 475    int eq = (p[1] == '=');
 476    *p     = '\0';
 477    val    = trim(p + 2);
 478    var    = trim(s);
 479    {
 480      char *ev = expandstring(var, NULL, getloc());
 481      result   = strcmp(ev, val) == 0;
 482      free(ev);
 483    }
 484    if (!eq)
 485      result = !result;
 486    free(s);
 487    return neg ? !result : result;
 488  }
 489
 490  /* bare expression: true if non-empty after expansion */
 491  result = s[0] != '\0';
 492  free(s);
 493  return neg ? !result : result;
 494}
 495
 496static char *
 497nextline(void)
 498{
 499  int   c;
 500  FILE *fp;
 501  char *s, *lim, d[BUFSIZ];
 502
 503  assert(input->type == FTFILE);
 504
 505repeat:
 506  fp = input->fp;
 507  if (!fp || feof(fp))
 508    return NULL;
 509
 510  lim = &input->buf[input->siz];
 511  for (s = input->buf; s < lim; *s++ = c) {
 512    c = getc(fp);
 513    if (c == '\n' || c == EOF) {
 514      input->loc.lineno++;
 515      *s++ = '\n';
 516      break;
 517    }
 518    if (c > UCHAR_MAX || c < 0)
 519      error("invalid character '%c' (%d)", c, c);
 520  }
 521
 522  if (s == lim)
 523    error("too long line");
 524  if (ferror(fp))
 525    error(strerror(errno));
 526  *s = '\0';
 527
 528  if (!strcmp(input->buf, ""))
 529    goto repeat;
 530
 531  /* bmake style .include / .sinclude / .-include */
 532  if (input->buf[0] == '.') {
 533    size_t dl;
 534    /* copy and strip trailing whitespace/newline so strcmp works */
 535    strncpy(d, input->buf + 1, sizeof(d) - 1);
 536    d[sizeof(d) - 1] = '\0';
 537    dl               = strlen(d);
 538    while (dl > 0 && isspace((unsigned char)d[dl - 1]))
 539      d[--dl] = '\0';
 540    if (strncmp(d, "include", 7) == 0 && isblank(d[7])) {
 541      input->pos = input->siz;
 542      if (cond_active())
 543        include(d + 7);
 544      goto repeat;
 545    }
 546    if (strncmp(d, "sinclude", 8) == 0 && isblank(d[8])) {
 547      input->pos = input->siz;
 548      if (cond_active())
 549        sinclude(d + 8);
 550      goto repeat;
 551    }
 552    if (strncmp(d, "-include", 8) == 0 && isblank(d[8])) {
 553      input->pos = input->siz;
 554      if (cond_active())
 555        sinclude(d + 8);
 556      goto repeat;
 557    }
 558    if (strncmp(d, "optinclude", 10) == 0 && isblank(d[10])) {
 559      input->pos = input->siz;
 560      if (cond_active())
 561        sinclude(d + 10);
 562      goto repeat;
 563    }
 564    /* conditionals */
 565    if (strncmp(d, "if", 2) == 0 && (isblank(d[2]) || d[2] == '\0')) {
 566      if (cond_active()) {
 567        if (d[2] == '\0')
 568          cond_if(eval_if(""));
 569        else
 570          cond_if(eval_if(d + 3));
 571      } else {
 572        cond_if(0);
 573      }
 574      goto repeat;
 575    }
 576    if (strncmp(d, "ifdef", 5) == 0 && (isblank(d[5]) || d[5] == '\0')) {
 577      if (cond_active()) {
 578        char *v = trim(d[5] ? d + 6 : "");
 579        cond_if(getmacro(v)[0] != '\0');
 580      } else {
 581        cond_if(0);
 582      }
 583      goto repeat;
 584    }
 585    if (strncmp(d, "ifndef", 6) == 0 && (isblank(d[6]) || d[6] == '\0')) {
 586      if (cond_active()) {
 587        char *v = trim(d[6] ? d + 7 : "");
 588        cond_if(getmacro(v)[0] == '\0');
 589      } else {
 590        cond_if(0);
 591      }
 592      goto repeat;
 593    }
 594    if (strncmp(d, "elif", 4) == 0 && (isblank(d[4]) || d[4] == '\0')) {
 595      if (condstack[conddepth].parent_active)
 596        cond_elif(eval_if(d[4] ? d + 5 : ""));
 597      else
 598        cond_elif(0);
 599      goto repeat;
 600    }
 601    if (strcmp(d, "else") == 0) {
 602      cond_else();
 603      goto repeat;
 604    }
 605    if (strcmp(d, "endif") == 0) {
 606      cond_endif();
 607      goto repeat;
 608    }
 609    if (strncmp(d, "error", 5) == 0 && isblank(d[5])) {
 610      if (cond_active())
 611        error("%s", trim(d + 6));
 612      goto repeat;
 613    }
 614    if (strncmp(d, "warning", 7) == 0 && isblank(d[7])) {
 615      if (cond_active())
 616        warning("%s", trim(d + 8));
 617      goto repeat;
 618    }
 619  }
 620
 621  /* POSIX include */
 622  if (!strncmp(input->buf, "include", 7) && isblank(input->buf[7])) {
 623    input->pos = input->siz;
 624    if (cond_active())
 625      include(input->buf + 7);
 626    goto repeat;
 627  }
 628
 629  /* sinclude / -include (GNU/POSIX style without dot) */
 630  if ((!strncmp(input->buf, "sinclude", 8) && isblank(input->buf[8]))
 631      || (!strncmp(input->buf, "-include", 8) && isblank(input->buf[8]))) {
 632    input->pos = input->siz;
 633    if (cond_active())
 634      sinclude(input->buf + 8);
 635    goto repeat;
 636  }
 637
 638  /* if we are inside an inactive conditional branch, skip this line */
 639  if (!cond_active()) {
 640    input->pos = input->siz;
 641    goto repeat;
 642  }
 643
 644  input->pos = 0;
 645
 646  return input->buf;
 647}
 648
 649static int
 650empty(struct input *ip)
 651{
 652  return ip->pos == ip->siz || ip->buf[ip->pos] == '\0';
 653}
 654
 655static int
 656moreinput(void)
 657{
 658  while (input) {
 659    if (!empty(input))
 660      break;
 661
 662    switch (input->type) {
 663      case FTEXPAN:
 664        pop();
 665        break;
 666      case FTFILE:
 667        if (!nextline())
 668          pop();
 669        break;
 670    }
 671  }
 672
 673  return input != NULL;
 674}
 675
 676static int
 677nextc(void)
 678{
 679  if (!moreinput())
 680    return EOF;
 681
 682  return input->buf[input->pos++];
 683}
 684
 685/*
 686 * This function only can be called after a call to nextc
 687 * that didn't return EOF. It can return '\0', but as
 688 * it is used only to check against '$' then it is not
 689 * a problem.
 690 */
 691static int
 692ahead(void)
 693{
 694  return input->buf[input->pos];
 695}
 696
 697static int
 698back(int c)
 699{
 700  if (c == EOF)
 701    return c;
 702  assert(input->pos > 0);
 703  return input->buf[--input->pos] = c;
 704}
 705
 706static void
 707comment(void)
 708{
 709  int c;
 710
 711  while ((c = nextc()) != EOF && c != '\n') {
 712    if (c == '\\' && nextc() == EOF)
 713      break;
 714  }
 715}
 716
 717static void
 718skipspaces(void)
 719{
 720  int c;
 721
 722  for (c = nextc(); c == ' ' || c == '\t'; c = nextc())
 723    ;
 724  back(c);
 725}
 726
 727static int
 728validchar(int c)
 729{
 730  if (c == EOF)
 731    return 0;
 732  return c == '.' || c == '/' || c == '_' || c == '-' || isalnum(c);
 733}
 734
 735static char *
 736expandmacro(char *name)
 737{
 738  char *s;
 739
 740  s = expandstring(getmacro(name), NULL, getloc());
 741  debug("macro %s expanded to '%s'", name, s);
 742
 743  return s;
 744}
 745
 746static void
 747replace(char *line, char *repl, char *to)
 748{
 749  int   siz, at, len, replsiz, tosiz, pos;
 750  char *oline, *cur, *buf;
 751
 752  debug("replacing '%s', with '%s' to '%s'", line, repl, to);
 753  oline   = line;
 754  tosiz   = strlen(to);
 755  replsiz = strlen(repl);
 756
 757  buf = NULL;
 758  for (pos = 0; *line; pos += siz) {
 759    cur = NULL;
 760    siz = 0;
 761
 762    for (siz = 0; *line == ' ' || *line == '\t'; ++siz) {
 763      cur      = erealloc(cur, siz + 1);
 764      cur[siz] = *line++;
 765    }
 766
 767    len = strcspn(line, " \t");
 768    at  = len - replsiz;
 769    if (at < 0 || memcmp(line + at, repl, replsiz)) {
 770      cur = erealloc(cur, siz + len);
 771      memcpy(cur + siz, line, len);
 772      siz += len;
 773    } else {
 774      cur = erealloc(cur, siz + at + tosiz);
 775      memcpy(cur + siz, line, at);
 776      memcpy(cur + siz + at, to, tosiz);
 777      siz += at + tosiz;
 778    }
 779
 780    line += len;
 781    buf = erealloc(buf, pos + siz);
 782    memcpy(buf + pos, cur, siz);
 783    free(cur);
 784  }
 785
 786  if (pos > 0) {
 787    buf      = erealloc(buf, pos + 1);
 788    buf[pos] = '\0';
 789    debug("\treplace '%s' with '%s'", oline, buf);
 790    push(FTEXPAN, buf);
 791  }
 792
 793  free(buf);
 794}
 795
 796static void
 797expandsimple(Target *tp)
 798{
 799  char    *s;
 800  Target **p;
 801  int      len, c, first;
 802
 803  switch (c = nextc()) {
 804    case '@':
 805      if (!tp || !tp->target)
 806        return;
 807      push(FTEXPAN, tp->target);
 808      break;
 809    case '<':
 810      if (!tp || !tp->req)
 811        return;
 812      push(FTEXPAN, tp->req);
 813      break;
 814    case '*':
 815      if (!tp || !tp->target)
 816        return;
 817      s = strrchr(tp->target, '.');
 818      if (!s) {
 819        push(FTEXPAN, tp->target);
 820        return;
 821      }
 822
 823      len = s - tp->target;
 824      s   = emalloc(len + 1);
 825      memcpy(s, tp->target, len);
 826      s[len] = '\0';
 827      push(FTEXPAN, s);
 828      free(s);
 829      break;
 830    case '?':
 831      if (!tp)
 832        return;
 833
 834      if (tp->req && stamp(tp->req) > tp->stamp) {
 835        push(FTEXPAN, " ");
 836        push(FTEXPAN, tp->req);
 837      }
 838
 839      for (p = tp->deps; p && *p; ++p) {
 840        if (stamp((*p)->name) > tp->stamp) {
 841          push(FTEXPAN, " ");
 842          push(FTEXPAN, (*p)->name);
 843        }
 844      }
 845      break;
 846    case '>':
 847    case '^':
 848      if (!tp)
 849        return;
 850      first = 1;
 851      if (tp->req) {
 852        push(FTEXPAN, tp->req);
 853        first = 0;
 854      }
 855      for (p = tp->deps; p && *p; ++p) {
 856        if (!first)
 857          push(FTEXPAN, " ");
 858        push(FTEXPAN, (*p)->name);
 859        first = 0;
 860      }
 861      break;
 862    default:
 863      token[0] = c;
 864      token[1] = '\0';
 865      s        = expandmacro(token);
 866      push(FTEXPAN, s);
 867      free(s);
 868      break;
 869  }
 870}
 871
 872static int
 873internal(int ch)
 874{
 875  switch (ch) {
 876    case '@':
 877    case '?':
 878    case '*':
 879    case '<':
 880    case '>':
 881    case '^':
 882      return 1;
 883    default:
 884      return 0;
 885  }
 886}
 887
 888static void
 889expansion(Target *tp)
 890{
 891  int   delim, c, repli, toi, namei, st;
 892  char  name[MAXTOKEN], repl[MAXREPL], to[MAXREPL];
 893  char *s, *erepl;
 894
 895  c = nextc();
 896  if (c == '(')
 897    delim = ')';
 898  else if (c == '{')
 899    delim = '}';
 900  else
 901    delim = 0;
 902
 903  if (!delim) {
 904    back(c);
 905    expandsimple(tp);
 906    return;
 907  }
 908
 909  s     = NULL;
 910  namei = repli = toi = 0;
 911  st                  = STBEGIN;
 912
 913  while (st != STEND && (c = nextc()) != EOF) {
 914    switch (st) {
 915      case STBEGIN:
 916        if (c == ':') {
 917          st          = STREPLACE;
 918          name[namei] = '\0';
 919          s           = expandmacro(name);
 920          break;
 921        }
 922        if (c == delim) {
 923          name[namei] = '\0';
 924          s           = expandmacro(name);
 925          goto no_replace;
 926        }
 927        if (namei == MAXTOKEN - 1)
 928          error("expansion text too long");
 929
 930        if (namei == 0 && internal(c)) {
 931          name[namei++] = '$';
 932          name[namei++] = c;
 933          name[namei]   = '\0';
 934          st            = STINTERNAL;
 935          s             = expandstring(name, tp, getloc());
 936          break;
 937        }
 938
 939        if (!validchar(c))
 940          error("invalid macro name in expansion");
 941        name[namei++] = c;
 942        break;
 943      case STINTERNAL:
 944        if (c == delim)
 945          goto no_replace;
 946        if (c != ':')
 947          error("invalid internal macro in expansion");
 948        st = STREPLACE;
 949        break;
 950      case STREPLACE:
 951        if (c == '=') {
 952          st = STTO;
 953          break;
 954        }
 955        if (c == delim)
 956          error(
 957              "invalid replacement pattern in "
 958              "expansion"
 959          );
 960        if (repli == MAXREPL - 1)
 961          error("macro replacement too big");
 962        repl[repli++] = c;
 963        break;
 964      case STTO:
 965        if (c == delim) {
 966          st = STEND;
 967          break;
 968        }
 969
 970        if (toi == MAXREPL - 1)
 971          error("macro substiturion too big");
 972        to[toi++] = c;
 973        break;
 974    }
 975  }
 976
 977  if (c == EOF)
 978    error("found eof while parsing expansion");
 979
 980  repl[repli] = '\0';
 981  to[toi]     = '\0';
 982
 983  erepl = expandstring(repl, tp, getloc());
 984  replace(s, erepl, to);
 985
 986  free(erepl);
 987  free(s);
 988  return;
 989
 990no_replace:
 991  push(FTEXPAN, s);
 992  free(s);
 993}
 994
 995/*
 996 * Horrible hack to do string expansion.
 997 * We cannot use normal push and nextc because that
 998 * would consume characters of the current file too.
 999 * For that reason it cleans the input and it recovers
1000 * it later.
1001 */
1002char *
1003expandstring(char *line, Target *tp, struct loc *loc)
1004{
1005  int           c, n;
1006  char         *s;
1007  struct input *ip = input;
1008
1009  input = NULL;
1010  push(FTFILE, NULL, loc->fname, loc->lineno);
1011  push(FTEXPAN, line);
1012
1013  n = 0;
1014  s = NULL;
1015  while ((c = nextc()) != EOF) {
1016    if (c != '$') {
1017      s        = erealloc(s, ++n);
1018      s[n - 1] = c;
1019      continue;
1020    }
1021
1022    if ((c = nextc()) == '$') {
1023      s        = erealloc(s, n += 2);
1024      s[n - 2] = '$';
1025      s[n - 1] = '$';
1026    } else {
1027      back(c);
1028      expansion(tp);
1029    }
1030  }
1031
1032  s     = erealloc(s, n + 1);
1033  s[n]  = '\0';
1034  input = ip;
1035
1036  return s;
1037}
1038
1039static int
1040item(void)
1041{
1042  int   c;
1043  char *s;
1044  char  buf[MAXTOKEN];
1045
1046  for (s = buf; s < &buf[MAXTOKEN] - 1;) {
1047    c = nextc();
1048    if (c == '$' && ahead() != '$')
1049      expansion(NULL);
1050    else if (validchar(c))
1051      *s++ = c;
1052    else
1053      break;
1054  }
1055  back(c);
1056
1057  if (s >= &buf[MAXTOKEN] - 1)
1058    error("token too long");
1059  if (s == buf)
1060    error("invalid empty token");
1061  *s++ = '\0';
1062  memcpy(token, buf, s - buf);
1063
1064  return ITEM;
1065}
1066
1067static int
1068next(void)
1069{
1070  int c;
1071
1072repeat:
1073  /*
1074   * It is better to avoid skipspaces() here, because
1075   * it can generate the need for 2 calls to back(),
1076   * and we need the character anyway.
1077   */
1078  c = nextc();
1079  if (c == ' ' || c == '\t')
1080    goto repeat;
1081
1082  if (c == '\\') {
1083    if ((c = nextc()) == '\n')
1084      goto repeat;
1085    back(c);
1086    c = '\\';
1087  }
1088
1089  switch (c) {
1090    case EOF:
1091      strcpy(token, "<EOF>");
1092      tok = EOF;
1093      break;
1094    case '$':
1095      if ((c = nextc()) == '$')
1096        goto single;
1097      back(c);
1098      expansion(NULL);
1099      goto repeat;
1100    case '#':
1101      comment();
1102      c = '\n';
1103      /* fallthrough */
1104    case ';':
1105    case ':':
1106    case '=':
1107    case '\n':
1108    single:
1109      token[0] = c;
1110      token[1] = '\0';
1111      tok      = c;
1112      break;
1113    case '+':
1114      if (nextc() == '=') {
1115        token[0] = '+';
1116        token[1] = '=';
1117        token[2] = '\0';
1118        tok      = '+';
1119        break;
1120      }
1121      error("unexpected character '+'");
1122    case '!':
1123      if (nextc() == '=') {
1124        token[0] = '!';
1125        token[1] = '=';
1126        token[2] = '\0';
1127        tok      = '!';
1128        break;
1129      }
1130      error("unexpected character '!'");
1131    default:
1132      if (!validchar(c))
1133        error("unexpected character '%c'", c);
1134      back(c);
1135      tok = item();
1136      break;
1137  }
1138
1139  return tok;
1140}
1141
1142static char *
1143readmacrodef(void)
1144{
1145  int   n, c;
1146  char *line;
1147
1148  n    = 0;
1149  line = NULL;
1150  while ((c = nextc()) != EOF) {
1151    line = erealloc(line, n + 1);
1152    if (c == '\n')
1153      break;
1154    if (c == '#') {
1155      comment();
1156      break;
1157    }
1158    if (c == '\\') {
1159      if ((c = nextc()) != '\n') {
1160        back(c);
1161        c = '\\';
1162      } else {
1163        skipspaces();
1164        c = ' ';
1165      }
1166    }
1167
1168    line[n++] = c;
1169  }
1170  if (c == EOF)
1171    error("EOF while looking for end of line");
1172  line[n] = '\0';
1173
1174  return line;
1175}
1176
1177static struct action
1178readcmd(void)
1179{
1180  int           n, c;
1181  struct loc   *loc;
1182  struct action act;
1183
1184  skipspaces();
1185
1186  loc            = getloc();
1187  act.loc.fname  = estrdup(loc->fname);
1188  act.loc.lineno = loc->lineno;
1189
1190  n        = 0;
1191  act.line = NULL;
1192  while ((c = nextc()) != EOF) {
1193    act.line = erealloc(act.line, n + 1);
1194    if (c == '\n')
1195      break;
1196    if (c == '\\') {
1197      if ((c = nextc()) == '\n') {
1198        if ((c = nextc()) != '\t')
1199          back(c);
1200        continue;
1201      }
1202      back(c);
1203      c = '\\';
1204    }
1205    act.line[n++] = c;
1206  }
1207  if (c == EOF)
1208    error("EOF while looking for end of command");
1209  act.line[n] = '\0';
1210
1211  return act;
1212}
1213
1214static void
1215rule(char *targets[], int ntargets)
1216{
1217  int            c, i, j, ndeps, nactions;
1218  struct action *acts;
1219  char         **deps = NULL;
1220
1221  if (ntargets == 0)
1222    error("missing target");
1223
1224  for (ndeps = 0; next() == ITEM; ++ndeps) {
1225    deps        = erealloc(deps, (ndeps + 1) * sizeof(char *));
1226    deps[ndeps] = estrdup(token);
1227  }
1228
1229  if (tok != '\n' && tok != ';')
1230    error("garbage at the end of the line");
1231
1232  nactions = 0;
1233  acts     = NULL;
1234  if (tok == ';') {
1235    nactions++;
1236    acts               = erealloc(acts, nactions * sizeof(*acts));
1237    acts[nactions - 1] = readcmd();
1238  }
1239
1240  for (;;) {
1241    if ((c = nextc()) == '#') {
1242      comment();
1243      continue;
1244    }
1245    if (c != '\t')
1246      break;
1247    nactions++;
1248    acts               = erealloc(acts, nactions * sizeof(*acts));
1249    acts[nactions - 1] = readcmd();
1250  }
1251  back(c);
1252
1253  for (i = 0; i < ntargets; i++) {
1254    addtarget(targets[i], ndeps);
1255    for (j = 0; j < ndeps; j++)
1256      adddep(targets[i], deps[j]);
1257    if (nactions > 0)
1258      addrule(targets[i], acts, nactions);
1259  }
1260
1261  for (i = 0; i < ndeps; i++)
1262    free(deps[i]);
1263  free(deps);
1264
1265  for (i = 0; i < nactions; i++) {
1266    free(acts[i].line);
1267    freeloc(&acts[i].loc);
1268  }
1269  free(acts);
1270}
1271
1272static void
1273assign(char *macros[], int where, int n)
1274{
1275  char *defs;
1276
1277  if (n != 1)
1278    error("invalid macro definition");
1279
1280  skipspaces();
1281  defs = readmacrodef();
1282  setmacro(*macros, defs, where, NOEXPORT);
1283  free(defs);
1284}
1285
1286static void
1287assign_append(char *macros[], int where, int n)
1288{
1289  char  *defs, *old, *new;
1290  size_t olen, dlen;
1291
1292  if (n != 1)
1293    error("invalid macro definition");
1294
1295  skipspaces();
1296  defs = readmacrodef();
1297  old  = getmacro(*macros);
1298  olen = strlen(old);
1299  dlen = strlen(defs);
1300  /* old + " " + defs + '\0' */
1301  new = emalloc(olen + (olen > 0 ? 1 : 0) + dlen + 1);
1302  memcpy(new, old, olen);
1303  if (olen > 0)
1304    new[olen++] = ' ';
1305  memcpy(new + olen, defs, dlen);
1306  new[olen + dlen] = '\0';
1307  setmacro(*macros, new, where, NOEXPORT);
1308  free(new);
1309  free(defs);
1310}
1311
1312static void
1313assign_shell(char *macros[], int where, int n)
1314{
1315  char  *cmd, *out;
1316  FILE  *p;
1317  size_t cap, len;
1318  int    c;
1319
1320  if (n != 1)
1321    error("invalid macro definition");
1322
1323  skipspaces();
1324  cmd = readmacrodef();
1325  {
1326    char *expanded = expandstring(cmd, NULL, getloc());
1327    free(cmd);
1328    cmd = expanded;
1329  }
1330  p = wpopen(cmd, NULL, "r");
1331  if (!p)
1332    error("popen: %s", strerror(errno));
1333  cap = 64;
1334  out = emalloc(cap);
1335  len = 0;
1336  while ((c = getc(p)) != EOF) {
1337    if (len + 1 >= cap) {
1338      cap *= 2;
1339      out = erealloc(out, cap);
1340    }
1341    if (c == '\n')
1342      break;
1343    out[len++] = c;
1344  }
1345  out[len] = '\0';
1346  wpclose(p);
1347  setmacro(*macros, out, where, NOEXPORT);
1348  free(out);
1349  free(cmd);
1350}
1351
1352void
1353parseinput(int where)
1354{
1355  int    i, n;
1356  char **targets;
1357
1358  while (moreinput()) {
1359    n       = 0;
1360    targets = NULL;
1361
1362    next();
1363    if (tok == '\n')
1364      continue;
1365
1366    while (tok == ITEM) {
1367      n++;
1368      targets        = erealloc(targets, n * sizeof(char *));
1369      targets[n - 1] = estrdup(token);
1370      next();
1371    }
1372
1373    switch (tok) {
1374      case ':':
1375        rule(targets, n);
1376        break;
1377      case '=':
1378        assign(targets, where, n);
1379        break;
1380      case '+':
1381        assign_append(targets, where, n);
1382        break;
1383      case '!':
1384        assign_shell(targets, where, n);
1385        break;
1386      default:
1387        error("unexpected token '%s'(%d)", token, tok);
1388    }
1389
1390    for (i = 0; i < n; i++)
1391      free(targets[i]);
1392    free(targets);
1393  }
1394}
1395
1396int
1397parse(char *fname)
1398{
1399  FILE *fp;
1400
1401  if (!fname) {
1402    fp    = stdin;
1403    fname = "<stdin>";
1404  } else if ((fp = fopen(fname, "r")) == NULL) {
1405    return 0;
1406  }
1407
1408  debug("parsing %s", fname);
1409  push(FTFILE, fp, fname, 0);
1410  parseinput(MAKEFILE);
1411
1412  return 1;
1413}
1414
1415void
1416inject(char *s)
1417{
1418  push(FTFILE, NULL, "<internal>", 0);
1419  push(FTEXPAN, s);
1420  parseinput(INTERNAL);
1421}