1/*
2
3Copyright (c) 1993, 1994, 1998 The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24
25*/
26
27#include "def.h"
28
29static int deftype(char *line, struct filepointer *filep,
30                   struct inclist *file_red, struct inclist *file,
31                   int parse_it);
32static int zero_value(char *filename, char *exp, struct filepointer *filep,
33                      struct inclist *file_red);
34static struct symtab **slookup(const char *symbol, struct inclist *file);
35static boolean merge2defines(struct inclist *file1, struct inclist *file2);
36
37static 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
58static int
59gobble(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 */
117static int
118deftype(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
270static struct symtab **
271fdefined(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
304struct symtab **
305isdefined(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 */
324static int
325zero_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
334void
335define2(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
415void
416define(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
435struct symtab **
436slookup(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
476static boolean
477merge2defines(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
536void
537undefine(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
549int
550find_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