URI:
       cpp.c - scc - simple c99 compiler
  HTML git clone git://git.simple-cc.org/scc
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       cpp.c (21385B)
       ---
            1 #include <assert.h>
            2 #include <ctype.h>
            3 #include <limits.h>
            4 #include <stdio.h>
            5 #include <stdlib.h>
            6 #include <string.h>
            7 #include <time.h>
            8 
            9 #include <scc/cstd.h>
           10 #include <scc/scc.h>
           11 #include "cc1.h"
           12 
           13 struct ifstate {
           14         unsigned char done;
           15         unsigned char enabled;
           16         unsigned char iselse;
           17 };
           18 
           19 static unsigned ncmdlines;
           20 static Symbol *symline, *symfile;
           21 static struct ifstate ifstate[NR_COND];
           22 static int cppoff;
           23 static struct items dirinclude;
           24 
           25 unsigned cppctx;
           26 int disexpand;
           27 int disescape;
           28 
           29 void
           30 defdefine(char *name, char *val, char *source)
           31 {
           32         char buffer[INPUTSIZ+1];
           33         char *def, *fmt = "#define %s %s\n";
           34         Symbol *sym = &(Symbol) {
           35                 .name = name,
           36                 .flags = SDECLARED,
           37         };
           38 
           39         if (!val)
           40                 val = "";
           41         if (strlen(fmt) + strlen(name) + strlen(val) > INPUTSIZ) {
           42                 errorp("macro definition '%s' too big", name);
           43                 return;
           44         }
           45 
           46         sprintf(buffer, fmt, name, val);
           47         lineno = ++ncmdlines;
           48 
           49         addinput(IPARAM, buffer, FAIL);
           50         cpp();
           51         delinput();
           52 }
           53 
           54 void
           55 undefmacro(char *s)
           56 {
           57         killsym(lookup(NS_CPP, s, NOALLOC));
           58 }
           59 
           60 void
           61 icpp(void)
           62 {
           63         struct tm *tm;
           64         time_t t;
           65         static char sdate[14], stime[11];
           66         static struct {
           67                 char *name;
           68                 char *value;
           69         } *bp, list[] = {
           70                 {"__STDC__", "1"},
           71                 {"__STDC_HOSTED__", "1"},
           72                 {"__SCC__", "1"},
           73                 {"__DATE__", sdate},
           74                 {"__TIME__", stime},
           75                 {"__STDC_VERSION__", STDC_VERSION},
           76                 {"__LINE__", NULL},
           77                 {"__FILE__", NULL},
           78                 {"__FLT_EVAL_METHOD__", "0"},
           79                 {NULL, NULL}
           80         };
           81 
           82         t = time(NULL);
           83         tm = localtime(&t);
           84         strftime(sdate, sizeof(sdate), "\"%b %d %Y\"", tm);
           85         strftime(stime, sizeof(stime), "\"%H:%M:%S\"", tm);
           86 
           87         for (bp = list; bp->name; ++bp)
           88                 defdefine(bp->name, bp->value, "built-in");
           89 
           90         symline = lookup(NS_CPP, "__LINE__", ALLOC);
           91         symfile = lookup(NS_CPP, "__FILE__", ALLOC);
           92 
           93         ncmdlines = 0;
           94 }
           95 
           96 static void
           97 nextcpp(Macro *mp)
           98 {
           99         int len, siz;
          100         char *arg;
          101 
          102         next();
          103         if (yytoken == EOFTOK) {
          104                 error("unterminated argument list invoking macro \"%s\"",
          105                       mp->sym->name);
          106         }
          107 
          108         if (yytoken == IDEN)
          109                 yylval.sym->flags |= SUSED;
          110 
          111         len = strlen(yytext);
          112         siz = mp->argsiz;
          113         if (len+1 > INT_MAX - siz) {
          114                 error("too long argument invoking macro \"%s\"",
          115                       mp->sym->name);
          116         }
          117 
          118         arg = xrealloc(mp->arg, siz + len + 1);
          119         if (siz > 0) {
          120                 arg[siz-1] = ' ';
          121                 memcpy(arg + siz, yytext, len+1);
          122         } else {
          123                 memcpy(arg, yytext, len+1);
          124         }
          125 
          126         mp->arg = arg;
          127         mp->argsiz = siz + len + 1;
          128 }
          129 
          130 static void
          131 paren(Macro *mp)
          132 {
          133         for (;;) {
          134                 nextcpp(mp);
          135                 switch (yytoken) {
          136                 case ')':
          137                         return;
          138                 case '(':
          139                         paren(mp);
          140                         break;
          141                 }
          142         }
          143 }
          144 
          145 static char *
          146 parameter(Macro *mp)
          147 {
          148         int siz;
          149         char *s, *begin, *end;
          150         Input *ip = input;
          151 
          152         mp->arg = NULL;
          153         mp->argsiz = 0;
          154         for (;;) {
          155                 nextcpp(mp);
          156                 switch (yytoken) {
          157                 case ')':
          158                 case ',':
          159                         /* remove ","  or ")"*/
          160                         begin = mp->arg;
          161                         end = mp->arg + mp->argsiz - 2;
          162 
          163                         while (end > begin && isspace(end[-1]))
          164                                 --end;
          165                         while (begin < end && isspace(begin[0]))
          166                                 ++begin;
          167 
          168                         siz = end - begin;
          169                         s = memcpy(xmalloc(siz+1), begin, siz);
          170                         s[siz] = '\0';
          171                         free(mp->arg);
          172 
          173                         return s;
          174                 case '(':
          175                         paren(mp);
          176                         break;
          177                 }
          178         }
          179 }
          180 
          181 static int
          182 parsepars(Macro *mp)
          183 {
          184         int n;
          185 
          186         if (mp->npars == -1)
          187                 return 1;
          188         if (ahead() != '(')
          189                 return 0;
          190 
          191         disexpand = 1;
          192         next();
          193         n = 0;
          194 
          195         if (mp->npars == 0 && ahead() == ')') {
          196                 next();
          197         } else {
          198                 do {
          199                         mp->arglist = xrealloc(mp->arglist, (n+1)*sizeof(char *));
          200                         mp->arglist[n] = parameter(mp);
          201                         DBG("MACRO fetched arg '%s'", mp->arglist[n]);
          202                 } while (++n < NR_MACROARG && yytoken == ',');
          203         }
          204 
          205         if (yytoken != ')')
          206                 error("incorrect macro function-alike invocation");
          207         disexpand = 0;
          208 
          209         if (n == NR_MACROARG)
          210                 error("too many parameters in macro \"%s\"", mp->sym->name);
          211         if (n != mp->npars) {
          212                 error("macro \"%s\" received %d arguments, but it takes %d",
          213                       mp->sym->name, n, mp->npars);
          214         }
          215 
          216         return 1;
          217 }
          218 
          219 static int
          220 concatoper(char *def, char *cur)
          221 {
          222         char *s;
          223 
          224         for (s = cur + 4; isspace(*s); ++s)
          225                 ;
          226         if (*s == CONCAT)
          227                 return 1;
          228 
          229         for (s = cur; s > def && isspace(s[-1]); --s)
          230                 ;
          231         if (s > def && s[-1] == CONCAT)
          232                 return 1;
          233 
          234         return 0;
          235 }
          236 
          237 static int
          238 expandarg(char *arg, char *def, char *curdef, char *buf, int bufsiz)
          239 {
          240         int siz;
          241         char *s = buf;
          242 
          243         /* gives priority to concatenation operators */
          244         if (concatoper(def, curdef)) {
          245                 siz = strlen(arg);
          246                 if (siz >= bufsiz) {
          247                         siz = -1;
          248                 } else {
          249                         memcpy(buf, arg, siz);
          250                         buf += siz;
          251                 }
          252         } else {
          253                 addinput(IPARAM, arg, FAIL);
          254                 for (siz = 0; next() != EOFTOK; siz += yylen+1) {
          255                         if (yylen > bufsiz-2) {
          256                                 siz = -1;
          257                                 break;
          258                         }
          259                         memcpy(buf, yytext, yylen);
          260                         bufsiz -= yylen + 1;
          261                         buf += yylen;
          262                         *buf++ = ' ';
          263                 }
          264 
          265                 delinput();
          266         }
          267         *buf = '\0';
          268 
          269         DBG("MACRO parameter '%s' expanded to '%s'", arg, s);
          270 
          271         return siz;
          272 }
          273 
          274 static int
          275 copymacro(Macro *mp)
          276 {
          277         int delim, c, esc;
          278         char *s, *p, *arg, *bp;
          279         int size, bufsiz;
          280 
          281         if (mp->sym == symfile)
          282                 return sprintf(mp->buffer, "\"%s\" ", filenam);
          283         if (mp->sym == symline)
          284                 return sprintf(mp->buffer, "%d ", lineno);
          285 
          286         bp = mp->buffer;
          287         bufsiz = mp->bufsiz;
          288         for (s = mp->def; c = *s; ++s) {
          289                 switch (c) {
          290                 case '\'':
          291                         delim = '\'';
          292                         goto search_delim;
          293                 case '\"':
          294                         delim = '"';
          295                 search_delim:
          296                         esc = 0;
          297                         p = s;
          298                         for (++s; c = *s; ++s) {
          299                                 if (c == '\\' && !esc)
          300                                         esc = 1;
          301                                 else if (c == delim &&!esc)
          302                                         break;
          303                                 else
          304                                         esc = 0;
          305                         }
          306                         size = s - p + 1;
          307                         if (size > bufsiz)
          308                                 goto expansion_too_long;
          309                         memcpy(bp, p, size);
          310                         bufsiz -= size;
          311                         bp += size;
          312                         break;
          313                 case CONCAT:
          314                         /* token concatenation operator */
          315                         DBG("MACRO concat");
          316                         while (bp[-1] == ' ')
          317                                 --bp, ++bufsiz;
          318                         while (s[1] == ' ')
          319                                 ++s;
          320                         break;
          321                 case STRINGIZE:
          322                         /* stringfier operator */
          323                         DBG("MACRO stringize");
          324                         arg = mp->arglist[atoi(s += 2)];
          325                         s += 2;
          326 
          327                         if (bufsiz < 3)
          328                                 goto expansion_too_long;
          329 
          330                         *bp++ = '"';
          331                         while ((c = *arg++) != '\0') {
          332                                 if (c == '"') {
          333                                         if (bufsiz < 3)
          334                                                 goto expansion_too_long;
          335                                         *bp++ = '\\';
          336                                         *bp++ = '"';
          337                                         bufsiz -= 2;
          338                                 } else {
          339                                         if (bufsiz < 2)
          340                                                 goto expansion_too_long;
          341                                         *bp++ = c;
          342                                         bufsiz--;
          343                                 }
          344                         }
          345                         *bp++ = '"';
          346 
          347                         break;
          348                 case MACROPAR:
          349                         /* parameter substitution */
          350                         arg = mp->arglist[atoi(s+1)];
          351                         size = expandarg(arg, mp->def, s, bp, bufsiz);
          352                         if (size < 0)
          353                                 goto expansion_too_long;
          354                         bp += size;
          355                         bufsiz -= size;
          356                         s += 3;
          357                         break;
          358                 default:
          359                         if (bufsiz-- == 0)
          360                                 goto expansion_too_long;
          361                         *bp++ = c;
          362                         break;
          363                 }
          364         }
          365         *bp = '\0';
          366 
          367         return bp - mp->buffer;
          368 
          369 expansion_too_long:
          370         error("macro expansion of \"%s\" too long", mp->sym->name);
          371 }
          372 
          373 static void
          374 addhideset(Input *ip,  Symbol *sym)
          375 {
          376         Symbol **set;
          377         Symbol **p;
          378 
          379         set = ip->macro->hideset;
          380         for (p = set; p < &set[NR_MACROARG] && *p; ++p) {
          381                 if (*p == sym)
          382                         return;
          383         }
          384 
          385         if (p == &set[NR_MACROARG])
          386                 error("too complex macro expansion");
          387 
          388         *p = sym;
          389         DBG("MACRO Adding %s to hideset of %s",
          390             sym->name, ip->macro->sym->name);
          391 }
          392 
          393 static void
          394 hide(Symbol *sym)
          395 {
          396         DBG("SYM: hidding symbol %s %d", sym->name, sym->hide);
          397         sym->hide = 1;
          398 }
          399 
          400 static void
          401 unhide(Symbol *sym)
          402 {
          403         DBG("SYM: unhidding symbol %s %d", sym->name, sym->hide);
          404         sym->hide = 0;
          405 }
          406 
          407 void
          408 delmacro(Macro *mp)
          409 {
          410         int i;
          411         Symbol **p;
          412 
          413         if (!mp)
          414                 return;
          415 
          416         if (mp->arglist) {
          417                 for (i = 0; i < mp->npars; i++)
          418                         free(mp->arglist[i]);
          419         }
          420 
          421         for (p = mp->hideset; p < &mp->hideset[NR_MACROARG] && *p; ++p)
          422                 unhide(*p);
          423 
          424         free(mp->arglist);
          425         free(mp);
          426 }
          427 
          428 Macro *
          429 newmacro(Symbol *sym)
          430 {
          431         Macro *mp;
          432 
          433         mp = xmalloc(sizeof(*mp));
          434         *mp = (Macro) {0};
          435         mp->sym = sym;
          436         mp->def = sym->u.s + 3;
          437         if (sym->u.s)
          438                 mp->npars = atoi(sym->u.s);
          439 
          440         return mp;
          441 }
          442 
          443 int
          444 expand(Symbol *sym)
          445 {
          446         int siz;
          447         Macro *mp;
          448         Input *ip;
          449         Symbol **p;
          450 
          451         DBG("MACRO '%s' detected disexpand=%d hide=%d",
          452             sym->name, disexpand, sym->hide);
          453 
          454         if (disexpand || sym->hide || sym->token != IDEN)
          455                 return 0;
          456 
          457         mp = newmacro(sym);
          458         mp->fname = filenam;
          459 
          460         if (!parsepars(mp)) {
          461                 delmacro(mp);
          462                 return 0;
          463         }
          464 
          465         addinput(IMACRO, mp, FAIL);
          466         mp->buffer = input->line;
          467         mp->bufsiz = INPUTSIZ-1;
          468 
          469         siz = copymacro(mp);
          470         mp->buffer[siz] = '\0';
          471 
          472         for (ip = input; ip; ip = ip->next) {
          473                 if ((ip->flags & ITYPE) == IMACRO)
          474                         addhideset(ip, sym);
          475         }
          476 
          477         for (p = mp->hideset; p < &mp->hideset[NR_MACROARG] && *p; ++p)
          478                 hide(*p);
          479 
          480         DBG("MACRO '%s' expanded to :'%s'", mp->sym->name, mp->buffer);
          481 
          482         return 1;
          483 }
          484 
          485 static int
          486 getpars(Symbol *args[NR_MACROARG])
          487 {
          488         int n, c;
          489         Symbol *sym;
          490 
          491         if (*input->p != '(')
          492                 return -1;
          493 
          494         /* skip the '(' */
          495         next();
          496         next();
          497         if (yytoken == ')')
          498                 return 0;
          499 
          500         n = 0;
          501         do {
          502                 if (n == NR_MACROARG) {
          503                         cpperror("too many parameters in macro");
          504                         return NR_MACROARG;
          505                 }
          506                 if (accept(ELLIPSIS)) {
          507                         args[n++] = NULL;
          508                         break;
          509                 }
          510                 if (yytoken != IDEN) {
          511                         cpperror("macro arguments must be identifiers");
          512                         return NR_MACROARG;
          513                 }
          514                 if ((sym = install(NS_IDEN, yylval.sym)) == NULL) {
          515                         errorp("duplicated macro parameter '%s'", yytext);
          516                 } else {
          517                         sym->flags |= SUSED;
          518                         args[n++] = sym;
          519                 }
          520                 next();
          521         } while (accept(','));
          522 
          523         if (yytoken != ')') {
          524                 cpperror("expected ')' at the end of macro argument list");
          525                 return NR_MACROARG;
          526         }
          527 
          528         return n;
          529 }
          530 
          531 static int
          532 getdefs(Symbol *args[NR_MACROARG], int nargs, char *buffer, size_t bufsiz)
          533 {
          534         size_t len;
          535         char *bp, *p;
          536         Symbol **argp, *sym;
          537         int c, id, token, prevc, ispar;
          538 
          539         while (isspace(*input->p))
          540                 ++input->p;
          541 
          542         bp = buffer;
          543         for (prevc = 0; (c = *input->p) != '\n' && c != '\0'; ++input->p) {
          544                 len = 1;
          545                 ispar = 0;
          546                 token = c;
          547                 sym = NULL;
          548 
          549                 if (c == '#') {
          550                         if (input->p[1] == '#') {
          551                                 token = CONCAT;
          552                                 ++input->p;
          553                         } else {
          554                                 token = STRINGIZE;
          555                         }
          556                 } else if (c == '_' || isalpha(c)) {
          557                         token = IDEN;
          558                         for (p = input->p; isalnum(*p) || *p == '_'; ++p)
          559                                 ;
          560                         len = p - input->p;
          561                         if (len >  INTIDENTSIZ) {
          562                                 cpperror("identifier too long in macro definition");
          563                                 return 0;
          564                         }
          565                         memcpy(yytext, input->p, len);
          566                         yytext[len] = '\0';
          567                         yylen = len;
          568                         input->p = p - 1;
          569                         sym = lookup(NS_IDEN, yytext, NOALLOC);
          570                 } else if (c == '"') {
          571                         next();
          572                         assert(yytoken == STRING);
          573                         token = STRING;
          574                         len = yylen;
          575                 }
          576 
          577                 if (sym && nargs > 0) {
          578                         for (argp = args; argp < &args[nargs]; ++argp) {
          579                                 if (*argp == sym)
          580                                         break;
          581                         }
          582                         if (argp != &args[nargs]) {
          583                                 id = argp - args;
          584                                 sprintf(yytext,
          585                                         "%c%02d%c", MACROPAR, id, MACROPAR);
          586                                 ispar = 1;
          587                                 yylen = len = 4;
          588                         }
          589                 }
          590 
          591                 if (prevc == 0 && token == CONCAT)
          592                         goto wrong_concat;
          593 
          594                 if (prevc == STRINGIZE && !ispar) {
          595                         cpperror("'#' is not followed by a macro parameter");
          596                         return 0;
          597                 }
          598 
          599                 if (len >= bufsiz) {
          600                         cpperror("macro too long");
          601                         return 0;
          602                 }
          603 
          604                 if (token == IDEN || token == STRING)
          605                         memcpy(bp, yytext, yylen);
          606                 else
          607                         *bp = token;
          608 
          609                 bp += len;
          610                 bufsiz -= len;
          611                 prevc = token;
          612         }
          613 
          614 end_loop:
          615         if ((yytoken = c) == '\0')
          616                 yytoken = EOFTOK;
          617         if (prevc == CONCAT)
          618                 goto wrong_concat;
          619         for ( ; bp > buffer && isspace(bp[-1]); --bp);
          620                 ;
          621         *bp = '\0';
          622         return 1;
          623 
          624 wrong_concat:
          625         cpperror("'##' cannot appear at either ends of a macro expansion");
          626         return 0;
          627 }
          628 
          629 static void
          630 define(void)
          631 {
          632         Symbol *sym,*args[NR_MACROARG];
          633         char buff[LINESIZ+1];
          634         int n;
          635 
          636         if (cppoff)
          637                 return;
          638 
          639         disescape = 1;
          640         namespace = NS_CPP;
          641         next();
          642 
          643         if (yytoken != IDEN) {
          644                 cpperror("macro names must be identifiers");
          645                 return;
          646         }
          647         sym = yylval.sym;
          648 
          649         namespace = NS_IDEN;       /* Avoid polution in NS_CPP */
          650         if ((n = getpars(args)) == NR_MACROARG)
          651                 goto delete;
          652         if (n > 0 && !args[n-1])  /* it is a variadic function */
          653                 --n;
          654 
          655         sprintf(buff, "%02d#", n);
          656         if (!getdefs(args, n, buff+3, LINESIZ-3))
          657                 goto delete;
          658 
          659         if (sym->flags & SDECLARED) {
          660                 if (strcmp(sym->u.s, buff) != 0)
          661                         warn("'%s' redefined", sym->name);
          662                 free(sym->u.s);
          663         } else {
          664                 sym = install(NS_CPP, sym);
          665                 sym->flags |= SDECLARED|SSTRING;
          666         }
          667 
          668         sym->u.s = xstrdup(buff);
          669         DBG("MACRO '%s' defined as '%s'", sym->name, buff);
          670         return;
          671 
          672 delete:
          673         killsym(sym);
          674 }
          675 
          676 void
          677 incdir(char *dir)
          678 {
          679         if (!dir || *dir == '\0')
          680                 die("cc1: incorrect -I flag");
          681         newitem(&dirinclude, dir);
          682 }
          683 
          684 static int
          685 includefile(char *dir, char *file, size_t filelen)
          686 {
          687         size_t dirlen;
          688         char path[FILENAME_MAX];
          689 
          690         if (!dir) {
          691                 dirlen = 0;
          692                 if (filelen > FILENAME_MAX-1)
          693                         return 0;
          694         } else {
          695                 dirlen = strlen(dir);
          696                 if (dirlen + filelen > FILENAME_MAX-2)
          697                         return 0;
          698                 memcpy(path, dir, dirlen);
          699                 if (dir[dirlen-1] != '/')
          700                         path[dirlen++] = '/';
          701         }
          702         memcpy(path+dirlen, file, filelen);
          703         path[dirlen + filelen] = '\0';
          704 
          705         return addinput(IFILE, path, NOFAIL);
          706 }
          707 
          708 static char *
          709 cwd(char *buf)
          710 {
          711         char *p, *s = filenam;
          712         size_t len;
          713 
          714         if ((p = strrchr(s, '/')) == NULL)
          715                 return NULL;
          716         if ((len = p - s) >= FILENAME_MAX)
          717                 die("cc1: current work directory too long");
          718         memcpy(buf, s, len);
          719         buf[len] = '\0';
          720         return buf;
          721 }
          722 
          723 static void
          724 include(void)
          725 {
          726         char dir[FILENAME_MAX], file[FILENAME_MAX], *p, **bp;
          727         size_t filelen;
          728         int n;
          729 
          730         if (cppoff)
          731                 return;
          732 
          733         disexpand = 0;
          734         namespace = NS_IDEN;
          735         next();
          736 
          737         switch (*yytext) {
          738         case '<':
          739                 if ((p = strchr(input->begin, '>')) == NULL || p[-1] == '<')
          740                         goto bad_include;
          741                 filelen = p - input->begin;
          742                 if (filelen >= FILENAME_MAX)
          743                         goto too_long;
          744                 memcpy(file, input->begin, filelen);
          745                 file[filelen] = '\0';
          746 
          747                 input->begin = input->p = p+1;
          748                 if (next() != '\n')
          749                         goto trailing_characters;
          750 
          751                 break;
          752         case '"':
          753                 if (yylen < 3)
          754                         goto bad_include;
          755                 filelen = yylen-2;
          756                 if (filelen >= FILENAME_MAX)
          757                         goto too_long;
          758                 memcpy(file, yytext+1, filelen);
          759                 file[filelen] = '\0';
          760 
          761                 if (next() != '\n')
          762                         goto trailing_characters;
          763 
          764                 if (includefile(cwd(dir), file, filelen))
          765                         goto its_done;
          766                 break;
          767         default:
          768                 goto bad_include;
          769         }
          770 
          771         n = dirinclude.n;
          772         for (bp = dirinclude.s; n--; ++bp) {
          773                 if (includefile(*bp, file, filelen))
          774                         goto its_done;
          775         }
          776         cpperror("included file '%s' not found", file);
          777 
          778 its_done:
          779         return;
          780 
          781 trailing_characters:
          782         cpperror("trailing characters after preprocessor directive");
          783         return;
          784 
          785 too_long:
          786         cpperror("too long file name in #include");
          787         return;
          788 
          789 bad_include:
          790         cpperror("#include expects \"FILENAME\" or <FILENAME>");
          791         return;
          792 }
          793 
          794 static void
          795 line(void)
          796 {
          797         long n;
          798         char *endp, *fname;
          799 
          800         if (cppoff)
          801                 return;
          802 
          803         disexpand = 0;
          804         next();
          805         n = strtol(yytext, &endp, 10);
          806         if (n <= 0 || n > USHRT_MAX || *endp != '\0') {
          807                 cpperror("first parameter of #line is not a positive integer");
          808                 return;
          809         }
          810 
          811         next();
          812         if (yytoken == '\n') {
          813                 fname = NULL;
          814         } else {
          815                 if (*yytext != '\"' || yylen == 1) {
          816                         cpperror("second parameter of #line is not a valid filename");
          817                         return;
          818                 }
          819                 fname = yylval.sym->u.s;
          820         }
          821         setloc(fname, n - 1);
          822         if (yytoken != '\n')
          823                 next();
          824 }
          825 
          826 static void
          827 pragma(void)
          828 {
          829         if (cppoff)
          830                 return;
          831         next();
          832         warn("ignoring pragma '%s'", yytext);
          833         *input->p = '\0';
          834         next();
          835 }
          836 
          837 static void
          838 usererr(void)
          839 {
          840         if (cppoff)
          841                 return;
          842         cpperror("#error %s", input->p);
          843         exit(EXIT_FAILURE);
          844         next();
          845 }
          846 
          847 
          848 Node *
          849 defined(void)
          850 {
          851         Symbol *sym;
          852         int paren;
          853 
          854         disexpand = 1;
          855         next();
          856         paren = accept('(');
          857         if (yytoken != IDEN && yytoken != TYPEIDEN)
          858                 cpperror("operator 'defined' requires an identifier");
          859         if (yytoken == TYPEIDEN || !(yylval.sym->flags & SDECLARED))
          860                 sym = zero;
          861         else
          862                 sym = one;
          863         disexpand = 0;
          864         next();
          865         if (paren)
          866                 expect(')');
          867         return constnode(sym);
          868 }
          869 
          870 static void
          871 ifclause(int negate, int isifdef)
          872 {
          873         Symbol *sym;
          874         unsigned n;
          875         int enabled, done;
          876         Node *expr;
          877 
          878         if (cppctx == NR_COND-1)
          879                 error("too many nesting levels of conditional inclusion");
          880         n = cppctx++;
          881         DBG("CPP ifclause updates cppctx=%d", cppctx);
          882 
          883         if (n > 0 && !ifstate[n-1].enabled) {
          884                 done = 1;
          885                 enabled = 0;
          886                 goto disabled;
          887         }
          888 
          889         namespace = NS_CPP;
          890         next();
          891 
          892         if (isifdef) {
          893                 if (yytoken != IDEN) {
          894                         cpperror("no macro name given in #%s directive",
          895                                  (negate) ? "ifndef" : "ifdef");
          896                         return;
          897                 }
          898                 sym = yylval.sym;
          899                 next();
          900                 enabled = (sym->flags & SDECLARED) != 0;
          901                 if (!enabled)
          902                         killsym(sym);
          903         } else {
          904                 /* TODO: catch recovery here */
          905                 if ((expr = constexpr()) == NULL) {
          906                         cpperror("parameter of #if is not an integer constant expression");
          907                         return;
          908                 }
          909                 DBG("CPP if expr=%d", expr->sym->u.i);
          910                 enabled = expr->sym->u.i != 0;
          911                 freetree(expr);
          912         }
          913 
          914         if (negate)
          915                 enabled = !enabled;
          916         done = enabled;
          917 
          918 disabled:
          919         cppoff = !enabled;
          920         DBG("CPP if result=%d", enabled);
          921         ifstate[n].done = done;
          922         ifstate[n].enabled = enabled;
          923         ifstate[n].iselse = 0;
          924 }
          925 
          926 static void
          927 cppif(void)
          928 {
          929         DBG("CPP line=%u if cppctx=%d", lineno, cppctx);
          930         disexpand = 0;
          931         ifclause(0, 0);
          932 }
          933 
          934 static void
          935 ifdef(void)
          936 {
          937         DBG("CPP line=%u ifdef cppctx=%d", lineno, cppctx);
          938         ifclause(0, 1);
          939 }
          940 
          941 static void
          942 ifndef(void)
          943 {
          944         DBG("CPP line=%u ifndef cppctx=%d", lineno, cppctx);
          945         ifclause(1, 1);
          946 }
          947 
          948 static void
          949 cppelse(void)
          950 {
          951         DBG("CPP line=%u else cppctx=%d", lineno, cppctx);
          952 
          953         if (cppctx == 0 || ifstate[cppctx-1].iselse) {
          954                 cpperror("#else without #ifdef/ifndef");
          955                 return;
          956         }
          957 
          958         /*
          959          * If we are disabled by a upper ifdef then ifclause() already
          960          * marked us as disabled and done. So if we are done then we
          961          * disable cpp because or ifclause was true, or it was disabled
          962          * by the upper. If we are not done, then it is our turn.
          963          */
          964         if (ifstate[cppctx-1].done) {
          965                 ifstate[cppctx-1].enabled = 0;
          966                 cppoff = 1;
          967         } else {
          968                 ifstate[cppctx-1].done = 1;
          969                 ifstate[cppctx-1].enabled = 1;
          970                 cppoff = 0;
          971         }
          972         ifstate[cppctx-1].iselse = 1;
          973 
          974         next();
          975 }
          976 
          977 static void
          978 elif(void)
          979 {
          980         DBG("CPP line=%u elif cppctx=%d", lineno, cppctx);
          981 
          982         if (cppctx == 0 || ifstate[cppctx-1].iselse) {
          983                 cpperror("#elif without #ifdef/ifndef");
          984                 return;
          985         }
          986 
          987         /*
          988          * If we are disabled by a upper ifdef then ifclause() already
          989          * marked us as disabled and done. So if we are done then we
          990          * disable cpp because or ifclause was true, or it was disabled
          991          * by the upper. If we are not done, then we have to evaluate
          992          * the if condition.
          993          */
          994         if (ifstate[cppctx-1].done) {
          995                 ifstate[cppctx-1].enabled = 0;
          996                 cppoff = 1;
          997         } else {
          998                 --cppctx;
          999                 DBG("elif updates cppctx=%d", cppctx);
         1000                 cppif();
         1001         }
         1002 }
         1003 
         1004 static void
         1005 endif(void)
         1006 {
         1007         DBG("CPP line=%u endif cppctx=%d", lineno, cppctx);
         1008 
         1009         if (cppctx == 0)
         1010                 error("#endif without #if");
         1011 
         1012         if (cppctx > 1)
         1013                 cppoff = !ifstate[cppctx - 2].enabled;
         1014         else
         1015                 cppoff = 0;
         1016 
         1017         --cppctx;
         1018         DBG("CPP endif updates cppctx=%d", cppctx);
         1019         next();
         1020 }
         1021 
         1022 static void
         1023 undef(void)
         1024 {
         1025         if (cppoff)
         1026                 return;
         1027 
         1028         namespace = NS_CPP;
         1029         next();
         1030         if (yytoken != IDEN) {
         1031                 error("no macro name given in #undef directive");
         1032                 return;
         1033         }
         1034         killsym(yylval.sym);
         1035         next();
         1036 }
         1037 
         1038 int
         1039 cpp(void)
         1040 {
         1041         static struct {
         1042                 unsigned char token;
         1043                 void (*fun)(void);
         1044         } *bp, clauses [] = {
         1045                 {DEFINE, define},
         1046                 {INCLUDE, include},
         1047                 {LINE, line},
         1048                 {IFDEF, ifdef},
         1049                 {IF, cppif},
         1050                 {ELIF, elif},
         1051                 {IFNDEF, ifndef},
         1052                 {ELSE, cppelse},
         1053                 {ENDIF, endif},
         1054                 {UNDEF, undef},
         1055                 {PRAGMA, pragma},
         1056                 {ERROR, usererr},
         1057                 {0, NULL}
         1058         };
         1059         int ns;
         1060         char *p;
         1061 
         1062         for (p = input->p; isspace(*p); ++p)
         1063                 ;
         1064 
         1065         if (*p != '#') {
         1066                 if (cppoff)
         1067                         *input->p = '\0';
         1068                 return cppoff;
         1069         }
         1070         input->p = p+1;
         1071 
         1072         disexpand = 1;
         1073         lexmode = CPPMODE;
         1074         ns = namespace;
         1075         namespace = NS_CPPCLAUSES;
         1076         next();
         1077         namespace = NS_IDEN;
         1078 
         1079         if (yytoken == '\n')
         1080                 goto ret;
         1081 
         1082         for (bp = clauses; bp->token && bp->token != yytoken; ++bp)
         1083                 ;
         1084         if (!bp->token) {
         1085                 errorp("incorrect preprocessor directive '%s'", yytext);
         1086                 goto ret;
         1087         }
         1088 
         1089         DBG("CPP %s", yytext);
         1090 
         1091         /*
         1092          * create a new context to avoid polish the current context,
         1093          * and to get all the symbols freed at the end
         1094          */
         1095         pushctx();
         1096         (*bp->fun)();
         1097         popctx();
         1098 
         1099         /*
         1100          * #include changes the content of input->line, so the correctness
         1101          * of the line must be checked in the own include(), and we have
         1102          * to skip this tests. For the same reason include() is the only
         1103          * function which does not prepare the next token
         1104          */
         1105         if (bp->token == INCLUDE)
         1106                 goto ret;
         1107 
         1108         if (yytoken != '\n' && yytoken != EOFTOK && !cppoff)
         1109                 cpperror("trailing characters after preprocessor directive");
         1110 
         1111 ret:
         1112         disescape = 0;
         1113         disexpand = 0;
         1114         lexmode = CCMODE;
         1115         namespace = ns;
         1116 
         1117         /*
         1118          * at this point we know that the cpp line is processed, and any error
         1119          * is generated but as next is called we cannot be sure that input is
         1120          * valid anymore, but in case of begin valid we want to discard any
         1121          * pending input in the current line
         1122          */
         1123         if (input)
         1124                 *input->p = '\0';
         1125 
         1126         return 1;
         1127 }
         1128 
         1129 void
         1130 ppragmaln(void)
         1131 {
         1132         static char file[FILENAME_MAX];
         1133         static unsigned nline;
         1134         char *s;
         1135 
         1136         putchar('\n');
         1137         if (strcmp(file, filenam)) {
         1138                 strcpy(file, filenam);
         1139                 s = "#line %u \"%s\"\n";
         1140         } else if (nline+1 != lineno) {
         1141                 s = "#line %u\n";
         1142         } else {
         1143                 s = "";
         1144         }
         1145         nline = lineno;
         1146         printf(s, nline, file);
         1147 }
         1148 
         1149 void
         1150 outcpp(void)
         1151 {
         1152         int c;
         1153         char *s, *t;
         1154 
         1155         for (next(); yytoken != EOFTOK; next()) {
         1156                 if (onlyheader)
         1157                         continue;
         1158                 if (yytoken != STRING) {
         1159                         printf("%s ", yytext);
         1160                         continue;
         1161                 }
         1162                 for (s = yytext; (c = *s) != '\0'; ++s) {
         1163                         switch (c) {
         1164                         case '\n':
         1165                                 t = "\\n";
         1166                                 goto print_str;
         1167                         case '\v':
         1168                                 t = "\\v";
         1169                                 goto print_str;
         1170                         case '\b':
         1171                                 t = "\\b";
         1172                                 goto print_str;
         1173                         case '\t':
         1174                                 t = "\\t";
         1175                                 goto print_str;
         1176                         case '\a':
         1177                                 t = "\\a";
         1178                                 goto print_str;
         1179                         case '\f':
         1180                                 t = "\\f";
         1181                                 goto print_str;
         1182                         case '\r':
         1183                                 t = "\\r";
         1184                                 goto print_str;
         1185                         case '"':
         1186                                 if (s == yytext || s[1] == '\0')
         1187                                         goto print_chr;
         1188                                 t = "\\\"";
         1189                                 goto print_str;
         1190                         case '\'':
         1191                                 t = "\\'";
         1192                                 goto print_str;
         1193                         case '\?':
         1194                                 t = "\\\?";
         1195                                 goto print_str;
         1196                         case '\\':
         1197                                 putchar('\\');
         1198                         default:
         1199                         print_chr:
         1200                                 if (!isprint(c))
         1201                                         printf("\\x%x", c);
         1202                                 else
         1203                                         putchar(c);
         1204                                 break;
         1205                         print_str:
         1206                                 fputs(t, stdout);
         1207                                 break;
         1208                         }
         1209                 }
         1210                 putchar(' ');
         1211         }
         1212         putchar('\n');
         1213 }