Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2 
      3 Copyright (c) 1993, 1994, 1998 The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24 
     25 */
     26 
     27 #include "def.h"
     28 
     29 static int deftype(char *line, struct filepointer *filep,
     30                    struct inclist *file_red, struct inclist *file,
     31                    int parse_it);
     32 static int zero_value(char *filename, char *exp, struct filepointer *filep,
     33                       struct inclist *file_red);
     34 static struct symtab **slookup(const char *symbol, struct inclist *file);
     35 static boolean merge2defines(struct inclist *file1, struct inclist *file2);
     36 
     37 static const char *const directives[] = {
     38     "if",
     39     "ifdef",
     40     "ifndef",
     41     "else",
     42     "endif",
     43     "define",
     44     "undef",
     45     "include",
     46     "line",
     47     "pragma",
     48     "error",
     49     "ident",
     50     "sccs",
     51     "elif",
     52     "eject",
     53     "warning",
     54     "include_next",
     55     NULL
     56 };
     57 
     58 static int
     59 gobble(struct filepointer *filep, struct inclist *file,
     60        struct inclist *file_red)
     61 {
     62     char *line;
     63 
     64     while ((line = getnextline(filep))) {
     65         int   type = deftype(line, filep, file_red, file, FALSE);
     66 
     67         switch (type) {
     68         case IF:
     69         case IFFALSE:
     70         case IFGUESSFALSE:
     71         case IFDEF:
     72         case IFNDEF:
     73             type = gobble(filep, file, file_red);
     74             while ((type == ELIF) || (type == ELIFFALSE) ||
     75                    (type == ELIFGUESSFALSE))
     76                 type = gobble(filep, file, file_red);
     77             if (type == ELSE)
     78                 (void) gobble(filep, file, file_red);
     79             break;
     80         case ELSE:
     81         case ENDIF:
     82             debug(0, ("%s, line %d: #%s\n",
     83                       file->i_file, filep->f_line, directives[type]));
     84             return (type);
     85         case DEFINE:
     86         case UNDEF:
     87         case INCLUDE:
     88         case INCLUDEDOT:
     89         case PRAGMA:
     90         case ERROR:
     91         case IDENT:
     92         case SCCS:
     93         case EJECT:
     94         case WARNING:
     95         case INCLUDENEXT:
     96         case INCLUDENEXTDOT:
     97             break;
     98         case ELIF:
     99         case ELIFFALSE:
    100         case ELIFGUESSFALSE:
    101             return (type);
    102         case -1:
    103             warning("%s", file_red->i_file);
    104             if (file_red != file)
    105                 warning1(" (reading %s)", file->i_file);
    106             warning1(", line %ld: unknown directive == \"%s\"\n",
    107                      filep->f_line, line);
    108             break;
    109         }
    110     }
    111     return (-1);
    112 }
    113 
    114 /*
    115  * Decide what type of # directive this line is.
    116  */
    117 static int
    118 deftype(char *line, struct filepointer *filep,
    119         struct inclist *file_red, struct inclist *file, int parse_it)
    120 {
    121     char *p;
    122     char *directive, savechar, *q;
    123     int   ret;
    124 
    125     /*
    126      * Parse the directive...
    127      */
    128     directive = line + 1;
    129     while (*directive == ' ' || *directive == '\t')
    130         directive++;
    131 
    132     p = directive;
    133     while ((*p == '_') || (*p >= 'a' && *p <= 'z'))
    134         p++;
    135     savechar = *p;
    136     *p = '\0';
    137     ret = match(directive, directives);
    138     *p = savechar;
    139 
    140     /* If we don't recognize this compiler directive or we happen to just
    141      * be gobbling up text while waiting for an #endif or #elif or #else
    142      * in the case of an #elif we must check the zero_value and return an
    143      * ELIF or an ELIFFALSE.
    144      */
    145 
    146     if (ret == ELIF && !parse_it) {
    147         while (*p == ' ' || *p == '\t')
    148             p++;
    149         /*
    150          * parse an expression.
    151          */
    152         debug(0, ("%s, line %d: #elif %s ", file->i_file, filep->f_line, p));
    153         ret = zero_value(file->i_file, p, filep, file_red);
    154         if (ret != IF) {
    155             debug(0, ("false...\n"));
    156             if (ret == IFFALSE)
    157                 return (ELIFFALSE);
    158             else
    159                 return (ELIFGUESSFALSE);
    160         }
    161         else {
    162             debug(0, ("true...\n"));
    163             return (ELIF);
    164         }
    165     }
    166 
    167     if (ret < 0 || !parse_it)
    168         return (ret);
    169 
    170     /*
    171      * now decide how to parse the directive, and do it.
    172      */
    173     while (*p == ' ' || *p == '\t')
    174         p++;
    175     q = p + strlen(p);
    176     do {
    177         q--;
    178     } while (*q == ' ' || *q == '\t');
    179     q[1] = '\0';
    180     switch (ret) {
    181     case IF:
    182         /*
    183          * parse an expression.
    184          */
    185         ret = zero_value(file->i_file, p, filep, file_red);
    186         debug(0, ("%s, line %d: %s #if %s\n",
    187                   file->i_file, filep->f_line, ret ? "false" : "true", p));
    188         break;
    189     case IFDEF:
    190     case IFNDEF:
    191         debug(0, ("%s, line %d: #%s %s\n",
    192                   file->i_file, filep->f_line, directives[ret], p));
    193     case UNDEF:
    194         /*
    195          * separate the name of a single symbol.
    196          */
    197         while (isalnum(*p) || *p == '_')
    198             *line++ = *p++;
    199         *line = '\0';
    200         break;
    201     case INCLUDE:
    202     case INCLUDENEXT:
    203         debug(2, ("%s, line %d: #include%s %s\n",
    204                   file->i_file, filep->f_line,
    205                   (ret == INCLUDE) ? "" : "_next", p));
    206 
    207         /* Support ANSI macro substitution */
    208         while (1) {
    209             struct symtab **sym;
    210 
    211             if (!*p || *p == '"' || *p == '<')
    212                 break;
    213 
    214             sym = isdefined(p, file_red, NULL);
    215             if (!sym)
    216                 break;
    217 
    218             p = (*sym)->s_value;
    219             debug(3, ("%s : #includes SYMBOL %s = %s\n",
    220                       file->i_incstring, (*sym)->s_name, (*sym)->s_value));
    221             /* mark file as having included a 'soft include' */
    222             file->i_flags |= INCLUDED_SYM;
    223         }
    224 
    225         /*
    226          * Separate the name of the include file.
    227          */
    228         while (*p && *p != '"' && *p != '<')
    229             p++;
    230         if (!*p)
    231             return (-2);
    232         if (*p++ == '"') {
    233             if (ret == INCLUDE)
    234                 ret = INCLUDEDOT;
    235             else
    236                 ret = INCLUDENEXTDOT;
    237             while (*p && *p != '"')
    238                 *line++ = *p++;
    239         }
    240         else
    241             while (*p && *p != '>')
    242                 *line++ = *p++;
    243         *line = '\0';
    244         break;
    245     case DEFINE:
    246         /*
    247          * copy the definition back to the beginning of the line.
    248          */
    249         memmove(line, p, strlen(p) + 1);
    250         break;
    251     case ELSE:
    252     case ENDIF:
    253     case ELIF:
    254     case PRAGMA:
    255     case ERROR:
    256     case IDENT:
    257     case SCCS:
    258     case EJECT:
    259     case WARNING:
    260         debug(0, ("%s, line %d: #%s\n",
    261                   file->i_file, filep->f_line, directives[ret]));
    262         /*
    263          * nothing to do.
    264          */
    265         break;
    266     }
    267     return (ret);
    268 }
    269 
    270 static struct symtab **
    271 fdefined(const char *symbol, struct inclist *file, struct inclist **srcfile)
    272 {
    273     struct symtab **val;
    274     static int      recurse_lvl = 0;
    275 
    276     if (file->i_flags & DEFCHECKED)
    277         return (NULL);
    278     debug(2, ("Looking for %s in %s\n", symbol, file->i_file));
    279     file->i_flags |= DEFCHECKED;
    280     if ((val = slookup(symbol, file)))
    281         debug(1, ("%s defined in %s as %s\n",
    282                   symbol, file->i_file, (*val)->s_value));
    283     if (val == NULL && file->i_list) {
    284         struct inclist **ip;
    285         unsigned int     i;
    286 
    287         for (ip = file->i_list, i = 0; i < file->i_listlen; i++, ip++) {
    288             if (file->i_merged[i] == FALSE) {
    289                 val = fdefined(symbol, *ip, srcfile);
    290                 file->i_merged[i] = merge2defines(file, *ip);
    291                 if (val != NULL)
    292                     break;
    293             }
    294         }
    295     }
    296     else if (val != NULL && srcfile != NULL)
    297         *srcfile = file;
    298     recurse_lvl--;
    299     file->i_flags &= ~DEFCHECKED;
    300 
    301     return (val);
    302 }
    303 
    304 struct symtab **
    305 isdefined(const char *symbol, struct inclist *file, struct inclist **srcfile)
    306 {
    307     struct symtab **val;
    308 
    309     if ((val = slookup(symbol, &maininclist))) {
    310         debug(1, ("%s defined on command line\n", symbol));
    311         if (srcfile != NULL)
    312             *srcfile = &maininclist;
    313         return (val);
    314     }
    315     if ((val = fdefined(symbol, file, srcfile)))
    316         return (val);
    317     debug(1, ("%s not defined in %s\n", symbol, file->i_file));
    318     return (NULL);
    319 }
    320 
    321 /*
    322  * Return type based on if the #if expression evaluates to 0
    323  */
    324 static int
    325 zero_value(char *filename, char *exp,
    326            struct filepointer *filep, struct inclist *file_red)
    327 {
    328     if (cppsetup(filename, exp, filep, file_red))
    329         return (IFFALSE);
    330     else
    331         return (IF);
    332 }
    333 
    334 void
    335 define2(const char *name, const char *val, struct inclist *file)
    336 {
    337     int first, last, below;
    338     struct symtab **sp = NULL, **dest;
    339     struct symtab *stab;
    340 
    341     /* Make space if it's needed */
    342     if (file->i_defs == NULL) {
    343         file->i_defs = mallocarray(SYMTABINC, sizeof(struct symtab *));
    344         file->i_ndefs = 0;
    345     }
    346     else if (!(file->i_ndefs % SYMTABINC))
    347         file->i_defs = reallocarray(file->i_defs, (file->i_ndefs + SYMTABINC),
    348                                     sizeof(struct symtab *));
    349 
    350     if (file->i_defs == NULL)
    351         fatalerr("malloc()/realloc() failure in insert_defn()\n");
    352 
    353     below = first = 0;
    354     last = file->i_ndefs - 1;
    355     while (last >= first) {
    356         /* Fast inline binary search */
    357         const char *s1;
    358         const char *s2;
    359         int middle = (first + last) / 2;
    360 
    361         /* Fast inline strcmp() */
    362         s1 = name;
    363         s2 = file->i_defs[middle]->s_name;
    364         while (*s1++ == *s2++)
    365             if (s2[-1] == '\0')
    366                 break;
    367 
    368         /* If exact match, set sp and break */
    369         if (*--s1 == *--s2) {
    370             sp = file->i_defs + middle;
    371             break;
    372         }
    373 
    374         /* If name > i_defs[middle] ... */
    375         if (*s1 > *s2) {
    376             below = first;
    377             first = middle + 1;
    378         }
    379         /* else ... */
    380         else {
    381             below = last = middle - 1;
    382         }
    383     }
    384 
    385     /* Search is done.  If we found an exact match to the symbol name,
    386        just replace its s_value */
    387     if (sp != NULL) {
    388         debug(1, ("redefining %s from %s to %s in file %s\n",
    389                   name, (*sp)->s_value, val, file->i_file));
    390         free((*sp)->s_value);
    391         (*sp)->s_value = strdup(val);
    392         if ((*sp)->s_value == NULL)
    393             fatalerr("strdup() failure in %s()\n", __func__);
    394         return;
    395     }
    396 
    397     sp = file->i_defs + file->i_ndefs++;
    398     dest = file->i_defs + below + 1;
    399     while (sp > dest) {
    400         *sp = sp[-1];
    401         sp--;
    402     }
    403     stab = malloc(sizeof(struct symtab));
    404     if (stab == NULL)
    405         fatalerr("malloc()/realloc() failure in insert_defn()\n");
    406 
    407     debug(1, ("defining %s to %s in file %s\n", name, val, file->i_file));
    408     stab->s_name = strdup(name);
    409     stab->s_value = strdup(val);
    410     if ((stab->s_name == NULL) || (stab->s_value == NULL))
    411         fatalerr("strdup() failure in %s()\n", __func__);
    412     *sp = stab;
    413 }
    414 
    415 void
    416 define(char *def, struct inclist *file)
    417 {
    418     char *val;
    419 
    420     /* Separate symbol name and its value */
    421     val = def;
    422     while (isalnum(*val) || *val == '_')
    423         val++;
    424     if (*val)
    425         *val++ = '\0';
    426     while (*val == ' ' || *val == '\t')
    427         val++;
    428 
    429     if (!*val)
    430         define2(def, "1", file);
    431     else
    432         define2(def, val, file);
    433 }
    434 
    435 struct symtab **
    436 slookup(const char *symbol, struct inclist *file)
    437 {
    438     int first = 0;
    439     int last;
    440 
    441     if (file == NULL)
    442         return NULL;
    443 
    444     last = file->i_ndefs - 1;
    445 
    446     while (last >= first) {
    447         /* Fast inline binary search */
    448         const char *s1;
    449         const char *s2;
    450         int middle = (first + last) / 2;
    451 
    452         /* Fast inline strcmp() */
    453         s1 = symbol;
    454         s2 = file->i_defs[middle]->s_name;
    455         while (*s1++ == *s2++)
    456             if (s2[-1] == '\0')
    457                 break;
    458 
    459         /* If exact match, we're done */
    460         if (*--s1 == *--s2) {
    461             return file->i_defs + middle;
    462         }
    463 
    464         /* If symbol > i_defs[middle] ... */
    465         if (*s1 > *s2) {
    466             first = middle + 1;
    467         }
    468         /* else ... */
    469         else {
    470             last = middle - 1;
    471         }
    472     }
    473     return (NULL);
    474 }
    475 
    476 static boolean
    477 merge2defines(struct inclist *file1, struct inclist *file2)
    478 {
    479     if ((file1 == NULL) || (file2 == NULL) || !(file2->i_flags & FINISHED))
    480         return 0;
    481 
    482     for (unsigned int i = 0; i < file2->i_listlen; i++)
    483         if (file2->i_merged[i] == FALSE)
    484             return 0;
    485 
    486     {
    487         int first1 = 0;
    488         int last1 = file1->i_ndefs - 1;
    489 
    490         int first2 = 0;
    491         int last2 = file2->i_ndefs - 1;
    492 
    493         int first = 0;
    494         struct symtab **i_defs = NULL;
    495         int deflen = file1->i_ndefs + file2->i_ndefs;
    496 
    497         debug(2, ("merging %s into %s\n", file2->i_file, file1->i_file));
    498 
    499         if (deflen > 0) {
    500             /* make sure deflen % SYMTABINC == 0 is still true */
    501             deflen += (SYMTABINC - deflen % SYMTABINC) % SYMTABINC;
    502             i_defs = mallocarray(deflen, sizeof(struct symtab *));
    503             if (i_defs == NULL)
    504                 return 0;
    505         }
    506 
    507         while ((last1 >= first1) && (last2 >= first2)) {
    508             const char *s1 = file1->i_defs[first1]->s_name;
    509             const char *s2 = file2->i_defs[first2]->s_name;
    510 
    511             if (strcmp(s1, s2) < 0)
    512                 i_defs[first++] = file1->i_defs[first1++];
    513             else if (strcmp(s1, s2) > 0)
    514                 i_defs[first++] = file2->i_defs[first2++];
    515             else {              /* equal */
    516                 i_defs[first++] = file2->i_defs[first2++];
    517                 first1++;
    518             }
    519         }
    520         while (last1 >= first1) {
    521             i_defs[first++] = file1->i_defs[first1++];
    522         }
    523         while (last2 >= first2) {
    524             i_defs[first++] = file2->i_defs[first2++];
    525         }
    526 
    527         if (file1->i_defs)
    528             free(file1->i_defs);
    529         file1->i_defs = i_defs;
    530         file1->i_ndefs = first;
    531 
    532         return 1;
    533     }
    534 }
    535 
    536 void
    537 undefine(const char *symbol, struct inclist *file)
    538 {
    539     struct symtab **ptr;
    540     struct inclist *srcfile;
    541 
    542     while ((ptr = isdefined(symbol, file, &srcfile)) != NULL) {
    543         srcfile->i_ndefs--;
    544         for (; ptr < srcfile->i_defs + srcfile->i_ndefs; ptr++)
    545             *ptr = ptr[1];
    546     }
    547 }
    548 
    549 int
    550 find_includes(struct filepointer *filep, struct inclist *file,
    551               struct inclist *file_red, int recursion, boolean failOK)
    552 {
    553     char *line;
    554 
    555     while ((line = getnextline(filep))) {
    556         int type = deftype(line, filep, file_red, file, TRUE);
    557 
    558         switch (type) {
    559         case IF:
    560  doif:
    561             type = find_includes(filep, file, file_red, recursion + 1, failOK);
    562             while ((type == ELIF) || (type == ELIFFALSE) ||
    563                    (type == ELIFGUESSFALSE))
    564                 type = gobble(filep, file, file_red);
    565             if (type == ELSE)
    566                 gobble(filep, file, file_red);
    567             break;
    568         case IFFALSE:
    569         case IFGUESSFALSE:
    570  doiffalse:
    571         {
    572             boolean recfailOK;
    573 
    574             if (type == IFGUESSFALSE || type == ELIFGUESSFALSE)
    575                 recfailOK = TRUE;
    576             else
    577                 recfailOK = failOK;
    578             type = gobble(filep, file, file_red);
    579             if (type == ELSE)
    580                 find_includes(filep, file, file_red, recursion + 1, recfailOK);
    581             else if (type == ELIF)
    582                 goto doif;
    583             else if ((type == ELIFFALSE) || (type == ELIFGUESSFALSE))
    584                 goto doiffalse;
    585         }
    586             break;
    587         case IFDEF:
    588         case IFNDEF:
    589             if ((type == IFDEF && isdefined(line, file_red, NULL))
    590                 || (type == IFNDEF && !isdefined(line, file_red, NULL))) {
    591                 debug(1, (type == IFNDEF ?
    592                           "line %d: %s !def'd in %s via %s%s\n" : "",
    593                           filep->f_line, line,
    594                           file->i_file, file_red->i_file, ": doit"));
    595                 type = find_includes(filep, file,
    596                                      file_red, recursion + 1, failOK);
    597                 while (type == ELIF || type == ELIFFALSE ||
    598                        type == ELIFGUESSFALSE)
    599                     type = gobble(filep, file, file_red);
    600                 if (type == ELSE)
    601                     gobble(filep, file, file_red);
    602             }
    603             else {
    604                 debug(1, (type == IFDEF ?
    605                           "line %d: %s !def'd in %s via %s%s\n" : "",
    606                           filep->f_line, line,
    607                           file->i_file, file_red->i_file, ": gobble"));
    608                 type = gobble(filep, file, file_red);
    609                 if (type == ELSE)
    610                     find_includes(filep, file, file_red, recursion + 1, failOK);
    611                 else if (type == ELIF)
    612                     goto doif;
    613                 else if (type == ELIFFALSE || type == ELIFGUESSFALSE)
    614                     goto doiffalse;
    615             }
    616             break;
    617         case ELSE:
    618         case ELIFFALSE:
    619         case ELIFGUESSFALSE:
    620         case ELIF:
    621             if (!recursion)
    622                 gobble(filep, file, file_red);
    623         case ENDIF:
    624             if (recursion)
    625                 return (type);
    626         case DEFINE:
    627             define(line, file);
    628             break;
    629         case UNDEF:
    630             if (!*line) {
    631                 warning("%s", file_red->i_file);
    632                 if (file_red != file)
    633                     warning1(" (reading %s)", file->i_file);
    634                 warning1(", line %ld: incomplete undef == \"%s\"\n",
    635                          filep->f_line, line);
    636                 break;
    637             }
    638             undefine(line, file_red);
    639             break;
    640         case INCLUDE:
    641         case INCLUDEDOT:
    642         case INCLUDENEXT:
    643         case INCLUDENEXTDOT:
    644         {
    645             struct inclist *inclist_save = inclistnext;
    646             const char **includedirs_save = includedirsnext;
    647 
    648             debug(2, ("%s, reading %s, includes %s\n",
    649                       file_red->i_file, file->i_file, line));
    650             add_include(filep, file, file_red, line, type, failOK);
    651             inclistnext = inclist_save;
    652             includedirsnext = includedirs_save;
    653         }
    654             break;
    655         case ERROR:
    656         case WARNING:
    657             warning("%s", file_red->i_file);
    658             if (file_red != file)
    659                 warning1(" (reading %s)", file->i_file);
    660             warning1(", line %ld: %s\n", filep->f_line, line);
    661             break;
    662 
    663         case PRAGMA:
    664         case IDENT:
    665         case SCCS:
    666         case EJECT:
    667             break;
    668         case -1:
    669             warning("%s", file_red->i_file);
    670             if (file_red != file)
    671                 warning1(" (reading %s)", file->i_file);
    672             warning1(", line %ld: unknown directive == \"%s\"\n",
    673                      filep->f_line, line);
    674             break;
    675         case -2:
    676             warning("%s", file_red->i_file);
    677             if (file_red != file)
    678                 warning1(" (reading %s)", file->i_file);
    679             warning1(", line %ld: incomplete include == \"%s\"\n",
    680                      filep->f_line, line);
    681             break;
    682         }
    683     }
    684     file->i_flags |= FINISHED;
    685     debug(2, ("finished with %s\n", file->i_file));
    686     return (-1);
    687 }
    688