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>910#include "make.h"1112#define MAXREPL 3013#define TABSIZ 6414#define MAXTOKEN FILENAME_MAX15#define ITEM 1281617typedef struct macro Macro;1819enum inputype {20 FTFILE,21 FTEXPAN,22};2324enum {25 STBEGIN,26 STINTERNAL,27 STREPLACE,28 STTO,29 STEND,30};3132struct input {33 int siz;34 int type;3536 FILE *fp;37 struct loc loc;3839 int pos;40 char *buf;4142 struct input *prev;43};4445struct macro {46 char *name;47 char *value;48 int where;4950 struct macro *next;51};5253static struct input *input;54static char token[MAXTOKEN];55static int tok;56static Macro *htab[TABSIZ];5758void59dumpmacros(void)60{61 Macro **pp, *p;6263 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}6869static Macro *70lookup(char *name)71{72 Macro *mp;73 int h = hash(name) & TABSIZ-1;7475 for (mp = htab[h]; mp && strcmp(mp->name, name); mp = mp->next)76 ;7778 if (mp)79 return mp;8081 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;8788 return mp;89}9091static char *92macroinfo(char *name, int *pwhere, Macro **mpp)93{94 char *s, *t;95 int hide, where;96 Macro *mp = lookup(name);9798 hide = 0;99 if (!strcmp(name, "SHELL") || !strcmp(name, "MAKEFLAGS"))100 hide = 1;101102 s = mp->value;103 where = mp->where;104105 if (!hide && (where == UNDEF || where == INTERNAL || eflag)) {106 t = getenv(name);107 if (t) {108 where = ENVIRON;109 s = t;110 }111 }112113 if (pwhere)114 *pwhere = where;115 if (mpp)116 *mpp = mp;117118 return s;119}120121char *122getmacro(char *name)123{124 return macroinfo(name, NULL, NULL);125}126127void128setmacro(char *name, char *val, int where, int export)129{130 int owhere, set;131 char *s;132 Macro *mp;133134 assert(where != ENVIRON);135136 s = macroinfo(name, &owhere, &mp);137138 /*139 * Default values are defined before anything else, and marked140 * as INTERNAL because they are injected as parseable text, and141 * MAKEFILE and INTERNAL variables are always overriden. ENVIRON142 * macros are generated in macroinfo() and this is why this function143 * 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 }164165 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;172173 if (export && strcmp(name, "SHELL") != 0) {174 debug("exporting macro %s", name);175 exportvar(name, val);176 }177 }178}179180void181freeloc(struct loc *loc)182{183 free(loc->fname);184}185186static struct loc *187getloc(void)188{189 struct input *ip;190191 for (ip = input; ip && ip->type != FTFILE; ip = ip->prev)192 ;193 if (!ip)194 return NULL;195196 return &ip->loc;197}198199200void201error(char *fmt, ...)202{203 va_list va;204 struct loc *loc;205206 fprintf(stderr, "make: error: ");207 if ((loc = getloc()) != NULL)208 fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);209210 va_start(va, fmt);211 vfprintf(stderr, fmt, va);212 va_end(va);213 putc('\n', stderr);214215 exit(EXIT_FAILURE);216}217218void219warning(char *fmt, ...)220{221 va_list va;222 struct loc *loc;223224 fprintf(stderr, "make: warning: ");225 if ((loc = getloc()) != NULL)226 fprintf(stderr, "%s:%d: ", loc->fname, loc->lineno);227228 va_start(va, fmt);229 vfprintf(stderr, fmt, va);230 va_end(va);231 putc('\n', stderr);232}233234static void235pop(void)236{237 struct input *ip = input->prev;238239 if (input->type == FTFILE) {240 if (input->fp)241 fclose(input->fp);242 freeloc(&input->loc);243 }244 free(input->buf);245 free(input);246247 input = ip;248}249250static void251push(int type, ...)252{253 int line, len, pos;254 FILE *fp = NULL;255 char *buf, *s, *fname = NULL;256 va_list va;257 struct input *ip;258259 va_start(va, type);260 switch (type) {261 case FTFILE:262 fp = va_arg(va, FILE *);263 s = va_arg(va, char *);264 line = va_arg(va, int);265 fname = estrdup(s);266 buf = emalloc(BUFSIZ);267 pos = len = BUFSIZ;268 break;269 case FTEXPAN:270 s = va_arg(va, char *);271 buf = estrdup(s);272 line = pos = 0;273 len = strlen(s);274 break;275 }276 va_end(va);277278 ip = emalloc(sizeof(*ip));279 ip->siz = len;280 ip->buf = buf;281 ip->type = type;282 ip->fp = fp;283 ip->loc.fname = fname;284 ip->loc.lineno = line;285 ip->pos = pos;286 ip->prev = input;287288 input = ip;289}290291static char *292trim(char *s)293{294 size_t len;295296 while (isspace(*s))297 s++;298299 for (len = strlen(s); len > 0 && isspace(s[len-1]); --len)300 s[len-1] = '\0';301302 return s;303}304305static void306include(char *s)307{308 int len;309 FILE *fp;310 char *fil, *t;311312 s = trim(s);313 fil = expandstring(s, NULL, getloc());314315 t = trim(fil);316 if (strlen(t) != 0) {317 debug("including '%s'", t);318 if ((fp = fopen(t, "r")) == NULL)319 error("opening %s:%s", t, strerror(errno));320 push(FTFILE, fp, t, 0);321 }322323 free(fil);324}325326static char *327nextline(void)328{329 int c;330 FILE *fp;331 char *s, *lim;332333 assert(input->type == FTFILE);334335repeat:336 fp = input->fp;337 if (!fp || feof(fp))338 return NULL;339340 lim = &input->buf[input->siz];341 for (s = input->buf; s < lim; *s++ = c) {342 c = getc(fp);343 if (c == '\n' || c == EOF) {344 input->loc.lineno++;345 *s++ = '\n';346 break;347 }348 if (c > UCHAR_MAX || c < 0)349 error("invalid character '%c' (%d)", c, c);350 }351352353 if (s == lim)354 error("too long line");355 if (ferror(fp))356 error(strerror(errno));357 *s = '\0';358359 if (!strcmp(input->buf, ""))360 goto repeat;361362 if (!strncmp(input->buf, "include", 7) && isblank(input->buf[7])) {363 input->pos = input->siz;364 include(input->buf+7);365 goto repeat;366 }367368 input->pos = 0;369370371 return input->buf;372}373374static int375empty(struct input *ip)376{377 return ip->pos == ip->siz || ip->buf[ip->pos] == '\0';378}379380static int381moreinput(void)382{383 while (input) {384 if (!empty(input))385 break;386387 switch (input->type) {388 case FTEXPAN:389 pop();390 break;391 case FTFILE:392 if (!nextline())393 pop();394 break;395 }396 }397398 return input != NULL;399}400401static int402nextc(void)403{404 if (!moreinput())405 return EOF;406407 return input->buf[input->pos++];408}409410/*411 * This function only can be called after a call to nextc412 * that didn't return EOF. It can return '\0', but as413 * it is used only to check against '$' then it is not414 * a problem.415 */416static int417ahead(void)418{419 return input->buf[input->pos];420}421422static int423back(int c)424{425 if (c == EOF)426 return c;427 assert(input->pos > 0);428 return input->buf[--input->pos] = c;429}430431static void432comment(void)433{434 int c;435436 while ((c = nextc()) != EOF && c != '\n') {437 if (c == '\\' && nextc() == EOF)438 break;439 }440}441442static void443skipspaces(void)444{445 int c;446447 for (c = nextc(); c == ' ' || c == '\t'; c = nextc())448 ;449 back(c);450}451452static int453validchar(int c)454{455 if (c == EOF)456 return 0;457 return c == '.' || c == '/' || c == '_' || c == '-' || isalnum(c);458}459460static char *461expandmacro(char *name)462{463 char *s;464465 s = expandstring(getmacro(name), NULL, getloc());466 debug("macro %s expanded to '%s'", name, s);467468 return s;469}470471static void472replace(char *line, char *repl, char *to)473{474 int siz, at, len, replsiz, tosiz, sep, pos;475 char *oline, *s, *cur, *buf;476477 debug("replacing '%s', with '%s' to '%s'", line, repl, to);478 oline = line;479 tosiz = strlen(to);480 replsiz = strlen(repl);481482 buf = NULL;483 for (pos = 0; *line; pos += siz) {484 cur = NULL;485 siz = 0;486487 for (siz = 0; *line == ' ' || *line == '\t'; ++siz) {488 cur = erealloc(cur, siz+1);489 cur[siz] = *line++;490 }491492 len = strcspn(line, " \t");493 at = len - replsiz;494 if (at < 0 || memcmp(line + at, repl, replsiz)) {495 cur = erealloc(cur, siz + len);496 memcpy(cur + siz, line, len);497 siz += len;498 } else {499 cur = erealloc(cur, siz + at + tosiz);500 memcpy(cur + siz, line, at);501 memcpy(cur + siz + at, to, tosiz);502 siz += at + tosiz;503 }504505 line += len;506 buf = erealloc(buf, pos + siz);507 memcpy(buf + pos, cur, siz);508 free(cur);509 }510511 if (pos > 0) {512 buf = erealloc(buf, pos + 1);513 buf[pos] = '\0';514 debug("\treplace '%s' with '%s'", oline, buf);515 push(FTEXPAN, buf);516 }517518 free(buf);519}520521static void522expandsimple(Target *tp)523{524 char *s;525 Target **p;526 int len, c;527528 switch (c = nextc()) {529 case '@':530 if (!tp || !tp->target)531 return;532 push(FTEXPAN, tp->target);533 break;534 case '<':535 if (!tp || !tp->req)536 return;537 push(FTEXPAN, tp->req);538 break;539 case '*':540 if (!tp || !tp->target)541 return;542 s = strrchr(tp->target, '.');543 if (!s) {544 push(FTEXPAN, tp->target);545 return;546 }547548 len = s - tp->target;549 s = emalloc(len+1);550 memcpy(s, tp->target, len);551 s[len] = '\0';552 push(FTEXPAN, s);553 free(s);554 break;555 case '?':556 if (!tp)557 return;558559 if (tp->req && stamp(tp->req) > tp->stamp) {560 push(FTEXPAN, " ");561 push(FTEXPAN, tp->req);562 }563564 for (p = tp->deps; p && *p; ++p) {565 if (stamp((*p)->name) > tp->stamp) {566 push(FTEXPAN, " ");567 push(FTEXPAN, (*p)->name);568 }569 }570 break;571 default:572 token[0] = c;573 token[1] = '\0';574 s = expandmacro(token);575 push(FTEXPAN, s);576 free(s);577 break;578 }579}580581static int582internal(int ch)583{584 switch (ch) {585 case '@':586 case '?':587 case '*':588 case '<':589 return 1;590 default:591 return 0;592 }593}594595static void596expansion(Target *tp)597{598 int delim, c, repli, toi, namei, st;599 char name[MAXTOKEN], repl[MAXREPL], to[MAXREPL];600 char *s, *erepl;601602 c = nextc();603 if (c == '(')604 delim = ')';605 else if (c == '{')606 delim = '}';607 else608 delim = 0;609610 if (!delim) {611 back(c);612 expandsimple(tp);613 return;614 }615616 s = NULL;617 namei = repli = toi = 0;618 st = STBEGIN;619620 while (st != STEND && (c = nextc()) != EOF) {621 switch (st) {622 case STBEGIN:623 if (c == ':') {624 st = STREPLACE;625 name[namei] = '\0';626 s = expandmacro(name);627 break;628 }629 if (c == delim) {630 name[namei] = '\0';631 s = expandmacro(name);632 goto no_replace;633 }634 if (namei == MAXTOKEN-1)635 error("expansion text too long");636637 if (namei == 0 && internal(c)) {638 name[namei++] = '$';639 name[namei++] = c;640 name[namei] = '\0';641 st = STINTERNAL;642 s = expandstring(name, tp, getloc());643 break;644 }645646 if (!validchar(c))647 error("invalid macro name in expansion");648 name[namei++] = c;649 break;650 case STINTERNAL:651 if (c == delim)652 goto no_replace;653 if (c != ':')654 error("invalid internal macro in expansion");655 st = STREPLACE;656 break;657 case STREPLACE:658 if (c == '=') {659 st = STTO;660 break;661 }662 if (c == delim)663 error("invalid replacement pattern in expansion");664 if (repli == MAXREPL-1)665 error("macro replacement too big");666 repl[repli++] = c;667 break;668 case STTO:669 if (c == delim) {670 st = STEND;671 break;672 }673674 if (toi == MAXREPL-1)675 error("macro substiturion too big");676 to[toi++] = c;677 break;678 }679 }680681 if (c == EOF)682 error("found eof while parsing expansion");683684 repl[repli] = '\0';685 to[toi] = '\0';686687 erepl = expandstring(repl, tp, getloc());688 replace(s, erepl, to);689690 free(erepl);691 free(s);692 return;693694no_replace:695 push(FTEXPAN, s);696 free(s);697}698699/*700 * Horrible hack to do string expansion.701 * We cannot use normal push and nextc because that702 * would consume characters of the current file too.703 * For that reason it cleans the input and it recovers704 * it later.705 */706char *707expandstring(char *line, Target *tp, struct loc *loc)708{709 int c, n;710 char *s;711 struct input *ip = input;712713 input = NULL;714 push(FTFILE, NULL, loc->fname, loc->lineno);715 push(FTEXPAN, line);716717 n = 0;718 s = NULL;719 while ((c = nextc()) != EOF) {720 if (c != '$') {721 s = erealloc(s, ++n);722 s[n-1] = c;723 continue;724 }725726 if ((c = nextc()) == '$') {727 s = erealloc(s, n += 2);728 s[n-2] = '$';729 s[n-1] = '$';730 } else {731 back(c);732 expansion(tp);733 }734 }735736 s = erealloc(s, n+1);737 s[n] = '\0';738 input = ip;739740 return s;741}742743static int744item(void)745{746 int c;747 char *s;748 char buf[MAXTOKEN];749750 for (s = buf; s < &buf[MAXTOKEN] - 1; ) {751 c = nextc();752 if (c == '$' && ahead() != '$')753 expansion(NULL);754 else if (validchar(c))755 *s++ = c;756 else757 break;758 }759 back(c);760761 if (s >= &buf[MAXTOKEN] - 1)762 error("token too long");763 if (s == buf)764 error("invalid empty token");765 *s++ = '\0';766 memcpy(token, buf, s - buf);767768 return ITEM;769}770771static int772next(void)773{774 int c;775776repeat:777 /*778 * It is better to avoid skipspaces() here, because779 * it can generate the need for 2 calls to back(),780 * and we need the character anyway.781 */782 c = nextc();783 if (c == ' ' || c == '\t')784 goto repeat;785786 if (c == '\\') {787 if ((c = nextc()) == '\n')788 goto repeat;789 back(c);790 c = '\\';791 }792793 switch (c) {794 case EOF:795 strcpy(token, "<EOF>");796 tok = EOF;797 break;798 case '$':799 if ((c = nextc()) == '$')800 goto single;801 back(c);802 expansion(NULL);803 goto repeat;804 case '#':805 comment();806 c = '\n';807 case ';':808 case ':':809 case '=':810 case '\n':811 single:812 token[0] = c;813 token[1] = '\0';814 tok = c;815 break;816 default:817 if (!validchar(c))818 error("unexpected character '%c'", c);819 back(c);820 tok = item();821 break;822 }823824 return tok;825}826827static char *828readmacrodef(void)829{830 int n, c;831 char *line;832833 n = 0;834 line = NULL;835 while ((c = nextc()) != EOF) {836 line = erealloc(line, n+1);837 if (c == '\n')838 break;839 if (c == '#') {840 comment();841 break;842 }843 if (c == '\\') {844 if ((c = nextc()) != '\n') {845 back(c);846 c = '\\';847 } else {848 skipspaces();849 c = ' ';850 }851 }852853 line[n++] = c;854 }855 if (c == EOF)856 error("EOF while looking for end of line");857 line[n] = '\0';858859 return line;860}861862static struct action863readcmd(void)864{865 int n, c;866 struct loc *loc;867 struct action act;868869 skipspaces();870871 loc = getloc();872 act.loc.fname = estrdup(loc->fname);873 act.loc.lineno = loc->lineno;874875 n = 0;876 act.line = NULL;877 while ((c = nextc()) != EOF) {878 act.line = erealloc(act.line, n+1);879 if (c == '\n')880 break;881 if (c == '\\') {882 if ((c = nextc()) == '\n') {883 if ((c = nextc()) != '\t')884 back(c);885 continue;886 }887 back(c);888 c = '\\';889 }890 act.line[n++] = c;891 }892 if (c == EOF)893 error("EOF while looking for end of command");894 act.line[n] = '\0';895896 return act;897}898899static void900rule(char *targets[], int ntargets)901{902 int c, i, j, ndeps, nactions;903 struct action *acts;904 char **deps = NULL;905906 if (ntargets == 0)907 error("missing target");908909 for (ndeps = 0; next() == ITEM; ++ndeps) {910 deps = erealloc(deps, (ndeps+1) * sizeof(char *));911 deps[ndeps] = estrdup(token);912 }913914 if (tok != '\n' && tok != ';')915 error("garbage at the end of the line");916917 nactions = 0;918 acts = NULL;919 if (tok == ';') {920 nactions++;921 acts = erealloc(acts, nactions * sizeof(*acts));922 acts[nactions-1] = readcmd();923 }924925 for (;;) {926 if ((c = nextc()) == '#') {927 comment();928 continue;929 }930 if (c != '\t')931 break;932 nactions++;933 acts = erealloc(acts, nactions * sizeof(*acts));934 acts[nactions-1] = readcmd();935 }936 back(c);937938 for (i = 0; i < ntargets; i++) {939 addtarget(targets[i], ndeps);940 for (j = 0; j < ndeps; j++)941 adddep(targets[i], deps[j]);942 if (nactions > 0)943 addrule(targets[i], acts, nactions);944 }945946 for (i = 0; i < ndeps; i++)947 free(deps[i]);948 free(deps);949950 for (i = 0; i < nactions; i++) {951 free(acts[i].line);952 freeloc(&acts[i].loc);953 }954 free(acts);955}956957static void958assign(char *macros[], int where, int n)959{960 char *defs;961962 if (n != 1)963 error("invalid macro definition");964965 skipspaces();966 defs = readmacrodef();967 setmacro(*macros, defs, where, NOEXPORT);968 free(defs);969}970971void972parseinput(int where)973{974 int i, n;975 char **targets;976977 while (moreinput()) {978 n = 0;979 targets = NULL;980981 next();982 if (tok == '\n')983 continue;984985 while (tok == ITEM) {986 n++;987 targets = erealloc(targets, n * sizeof(char *));988 targets[n-1] = estrdup(token);989 next();990 }991992 switch (tok) {993 case ':':994 rule(targets, n);995 break;996 case '=':997 assign(targets, where, n);998 break;999 default:1000 error("unexpected token '%s'(%d)", token, tok);1001 }10021003 for (i = 0; i < n; i++)1004 free(targets[i]);1005 free(targets);1006 }1007}10081009int1010parse(char *fname)1011{1012 FILE *fp;10131014 if (!fname) {1015 fp = stdin;1016 fname = "<stdin>";1017 } else if ((fp = fopen(fname, "r")) == NULL) {1018 return 0;1019 }10201021 debug("parsing %s", fname);1022 push(FTFILE, fp, fname, 0);1023 parseinput(MAKEFILE);10241025 return 1;1026}10271028void1029inject(char *s)1030{1031 push(FTFILE, NULL, "<internal>", 0);1032 push(FTEXPAN, s);1033 parseinput(INTERNAL);1034}