[BACK]Return to lowparse.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Annotation of src/usr.bin/make/lowparse.c, Revision 1.4

1.4     ! espie       1: /*     $OpenBSD: lowparse.c,v 1.3 2000/07/17 23:54:26 espie Exp $ */
1.1       espie       2:
                      3: /* low-level parsing functions. */
                      4:
                      5: /*
                      6:  * Copyright (c) 1999,2000 Marc Espie.
                      7:  *
                      8:  * Extensive code changes for the OpenBSD project.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                     21:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                     22:  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
                     23:  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
                     24:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
                     25:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     26:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     27:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     28:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
                     29:  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31:
                     32: #include <stdarg.h>
                     33: #include <stdio.h>
                     34: #include <assert.h>
                     35: #include "make.h"
                     36: #include "buf.h"
                     37: #include "lowparse.h"
                     38:
                     39: #ifdef CLEANUP
                     40: static LIST        fileNames;  /* file names to free at end */
                     41: #endif
                     42:
1.2       espie      43: /* Definitions for handling #include specifications */
                     44: typedef struct IFile_ {
                     45:     char               *fname; /* name of file */
                     46:     unsigned long              lineno; /* line number */
                     47:     FILE               *F;     /* open stream */
                     48:     char               *str;   /* read from char area */
                     49:     char               *ptr;   /* where we are */
                     50:     char               *end;   /* don't overdo it */
                     51: } IFile;
                     52:
                     53: static IFile   *current;
                     54:
                     55:
                     56:
1.1       espie      57: static LIST     includes;      /* stack of IFiles generated by
                     58:                                 * #includes */
                     59:
                     60: static IFile *new_ifile __P((char *, FILE *));
                     61: static IFile *new_istring __P((char *, char *, unsigned long));
                     62: static void free_ifile __P((IFile *));
                     63: static void ParseVErrorInternal __P((char *, unsigned long, int, char *, va_list));
1.2       espie      64: static int newline __P((void));
                     65: #define ParseReadc()   current->ptr < current->end ? *current->ptr++ : newline()
                     66: static void ParseUnreadc __P((char));
1.1       espie      67: static int         fatals = 0;
                     68:
                     69: /*-
                     70:  * ParseVErrorInternal  --
                     71:  *     Error message abort function for parsing. Prints out the context
                     72:  *     of the error (line number and file) as well as the message with
                     73:  *     two optional arguments.
                     74:  *
                     75:  * Side Effects:
                     76:  *     "fatals" is incremented if the level is PARSE_FATAL.
                     77:  */
                     78: /* VARARGS */
                     79: static void
                     80: #ifdef __STDC__
                     81: ParseVErrorInternal(char *cfname, unsigned long clineno, int type, char *fmt,
                     82:     va_list ap)
                     83: #else
                     84: ParseVErrorInternal(va_alist)
                     85:        va_dcl
                     86: #endif
                     87: {
                     88:        (void)fprintf(stderr, "\"%s\", line %lu: ", cfname, clineno);
                     89:        if (type == PARSE_WARNING)
                     90:                (void)fprintf(stderr, "warning: ");
                     91:        (void)vfprintf(stderr, fmt, ap);
                     92:        va_end(ap);
                     93:        (void)fprintf(stderr, "\n");
                     94:        if (type == PARSE_FATAL)
                     95:                fatals ++;
                     96: }
                     97:
                     98: /*-
                     99:  * Parse_Error  --
                    100:  *     External interface to ParseVErrorInternal; uses the default filename
                    101:  *     Line number.
                    102:  */
                    103: /* VARARGS */
                    104: void
                    105: #ifdef __STDC__
                    106: Parse_Error(int type, char *fmt, ...)
                    107: #else
                    108: Parse_Error(va_alist)
                    109:        va_dcl
                    110: #endif
                    111: {
                    112:        va_list ap;
                    113: #ifdef __STDC__
                    114:        va_start(ap, fmt);
                    115: #else
                    116:        int type;               /* Error type (PARSE_WARNING, PARSE_FATAL) */
                    117:        char *fmt;
                    118:
                    119:        va_start(ap);
                    120:        type = va_arg(ap, int);
                    121:        fmt = va_arg(ap, char *);
                    122: #endif
                    123:
                    124:        ParseVErrorInternal(current->fname, current->lineno, type, fmt, ap);
                    125: }
                    126:
                    127: static IFile *
                    128: new_ifile(name, stream)
                    129:     char *name;
                    130:     FILE *stream;
                    131: {
                    132:     IFile *ifile;
                    133: #ifdef CLEANUP
                    134:     Lst_AtEnd(&fileNames, name);
                    135: #endif
                    136:
                    137:     ifile = emalloc(sizeof(*ifile));
                    138:     ifile->fname = name;
                    139:     /* Naturally enough, we start reading at line 0 */
                    140:     ifile->lineno = 0;
                    141:     ifile->F = stream;
                    142:     ifile->ptr = ifile->end = NULL;
                    143:     return ifile;
                    144: }
                    145:
                    146: static void
                    147: free_ifile(ifile)
                    148:     IFile *ifile;
                    149: {
                    150:     if (ifile->F)
                    151:        (void)fclose(ifile->F);
                    152:     else
                    153:        free(ifile->str);
                    154:     /* Note we can't free the file names yet, as they are embedded in GN for
                    155:      * error reports. */
                    156:     free(ifile);
                    157: }
                    158:
                    159: static IFile *
                    160: new_istring(str, name, lineno)
                    161:     char *str;
                    162:     char *name;
                    163:     unsigned long lineno;
                    164: {
                    165:     IFile *ifile;
                    166:
                    167:     ifile = emalloc(sizeof(*ifile));
                    168:     ifile->fname = name;
                    169:     ifile->F = NULL;
                    170:     /* Strings are used from for loops... */
                    171:     ifile->lineno = lineno;
                    172:     ifile->ptr = ifile->str = str;
                    173:     ifile->end = str + strlen(str);
                    174:     return ifile;
                    175: }
                    176:
                    177:
                    178: /*-
                    179:  *---------------------------------------------------------------------
                    180:  * Parse_FromString  --
                    181:  *     Start Parsing from the given string
                    182:  *
                    183:  * Side Effects:
                    184:  *     A structure is added to the includes Lst and readProc, lineno,
                    185:  *     fname and curFILE are altered for the new file
                    186:  *---------------------------------------------------------------------
                    187:  */
                    188: void
                    189: Parse_FromString(str, lineno)
                    190:     char               *str;
                    191:     unsigned long      lineno;
                    192: {
                    193:     if (DEBUG(FOR))
                    194:        (void)fprintf(stderr, "%s\n----\n", str);
                    195:
                    196:     if (current != NULL)
                    197:        Lst_AtFront(&includes, current);
                    198:     current = new_istring(str, current->fname, lineno);
                    199: }
                    200:
                    201:
                    202: void
                    203: Parse_FromFile(name, stream)
                    204:     char               *name;
                    205:     FILE               *stream;
                    206: {
                    207:     if (current != NULL)
                    208:        Lst_AtFront(&includes, current);
                    209:     current = new_ifile(name, stream);
                    210: }
                    211:
                    212: /*-
                    213:  *---------------------------------------------------------------------
                    214:  * Parse_NextFile --
                    215:  *     Called when EOF is reached in the current file. If we were reading
                    216:  *     an include file, the includes stack is popped and things set up
                    217:  *     to go back to reading the previous file at the previous location.
                    218:  *
                    219:  * Results:
                    220:  *     CONTINUE if there's more to do. DONE if not.
                    221:  *
                    222:  * Side Effects:
                    223:  *     The old curFILE, is closed. The includes list is shortened.
                    224:  *     lineno, curFILE, and fname are changed if CONTINUE is returned.
                    225:  *---------------------------------------------------------------------
                    226:  */
                    227: Boolean
                    228: Parse_NextFile()
                    229: {
                    230:     IFile *next;
                    231:
                    232:     next = (IFile *)Lst_DeQueue(&includes);
                    233:     if (next != NULL) {
                    234:        if (current != NULL)
                    235:            free_ifile(current);
                    236:        current = next;
                    237:        return TRUE;
                    238:     } else
                    239:        return FALSE;
                    240: }
                    241:
                    242:
                    243: int
                    244: newline()
                    245: {
                    246:     size_t len;
                    247:
                    248:     if (current->F) {
                    249:        current->ptr = fgetln(current->F, &len);
                    250:        if (current->ptr) {
                    251:            current->end = current->ptr + len;
                    252:            return *current->ptr++;
                    253:        } else {
                    254:            current->end = NULL;
                    255:        }
                    256:     }
                    257:     return EOF;
                    258: }
                    259:
                    260: void
                    261: ParseUnreadc(c)
                    262:        char c;
                    263: {
                    264:        current->ptr--;
                    265:        *current->ptr = c;
1.2       espie     266: }
                    267:
                    268: /* ParseSkipLine():
                    269:  *     Grab the next line
                    270:  */
                    271: char *
                    272: ParseSkipLine(skip)
                    273:     int skip;          /* Skip lines that don't start with . */
                    274: {
                    275:     char *line;
                    276:     int c, lastc;
                    277:     BUFFER buf;
                    278:
                    279:     Buf_Init(&buf, MAKE_BSIZE);
                    280:
                    281:     for (;;) {
                    282:         Buf_Reset(&buf);
                    283:         lastc = '\0';
                    284:
                    285:         while (((c = ParseReadc()) != '\n' || lastc == '\\')
                    286:                && c != EOF) {
                    287:             if (c == '\n') {
                    288:                 Buf_ReplaceLastChar(&buf, ' ');
                    289:                 current->lineno++;
                    290:
                    291:                 while ((c = ParseReadc()) == ' ' || c == '\t');
                    292:
                    293:                 if (c == EOF)
                    294:                     break;
                    295:             }
                    296:
                    297:             Buf_AddChar(&buf, c);
                    298:             lastc = c;
                    299:         }
                    300:
                    301:         line = Buf_Retrieve(&buf);
                    302:         current->lineno++;
                    303:            /* allow for non-newline terminated lines while skipping */
                    304:        if (line[0] == '.')
                    305:            break;
                    306:
                    307:         if (c == EOF) {
                    308:             Parse_Error(PARSE_FATAL, "Unclosed conditional/for loop");
                    309:             Buf_Destroy(&buf);
                    310:             return NULL;
                    311:         }
                    312:        if (skip == 0)
                    313:            break;
                    314:
                    315:     }
                    316:
                    317:     return line;
                    318: }
                    319:
                    320:
                    321: /*-
                    322:  *---------------------------------------------------------------------
                    323:  * ParseReadLine --
                    324:  *     Read an entire line from the input file. Called only by Parse_File.
                    325:  *     To facilitate escaped newlines and what have you, a character is
                    326:  *     buffered in 'lastc', which is '\0' when no characters have been
                    327:  *     read. When we break out of the loop, c holds the terminating
                    328:  *     character and lastc holds a character that should be added to
                    329:  *     the line (unless we don't read anything but a terminator).
                    330:  *
                    331:  * Results:
                    332:  *     A line w/o its newline
                    333:  *
                    334:  * Side Effects:
                    335:  *     Only those associated with reading a character
                    336:  *---------------------------------------------------------------------
                    337:  */
                    338: char *
                    339: ParseReadLine ()
                    340: {
                    341:     BUFFER       buf;          /* Buffer for current line */
                    342:     register int  c;           /* the current character */
                    343:     register int  lastc;       /* The most-recent character */
                    344:     Boolean      semiNL;       /* treat semi-colons as newlines */
                    345:     Boolean      ignDepOp;     /* TRUE if should ignore dependency operators
                    346:                                 * for the purposes of setting semiNL */
                    347:     Boolean      ignComment;   /* TRUE if should ignore comments (in a
                    348:                                 * shell command */
                    349:     char         *line;        /* Result */
                    350:     char          *ep;         /* to strip trailing blanks */
                    351:
                    352:     semiNL = FALSE;
                    353:     ignDepOp = FALSE;
                    354:     ignComment = FALSE;
                    355:
                    356:     /*
                    357:      * Handle special-characters at the beginning of the line. Either a
                    358:      * leading tab (shell command) or pound-sign (possible conditional)
                    359:      * forces us to ignore comments and dependency operators and treat
                    360:      * semi-colons as semi-colons (by leaving semiNL FALSE). This also
                    361:      * discards completely blank lines.
                    362:      */
                    363:     for (;;) {
                    364:        c = ParseReadc();
                    365:
                    366:        if (c == '\t') {
                    367:            ignComment = ignDepOp = TRUE;
                    368:            break;
                    369:        } else if (c == '\n') {
                    370:            current->lineno++;
                    371:        } else if (c == '#') {
                    372:            ParseUnreadc(c);
                    373:            break;
                    374:        } else {
                    375:            /*
                    376:             * Anything else breaks out without doing anything
                    377:             */
                    378:            break;
                    379:        }
                    380:     }
                    381:
                    382:     if (c != EOF) {
                    383:        lastc = c;
                    384:        Buf_Init(&buf, MAKE_BSIZE);
                    385:
                    386:        while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) &&
                    387:               (c != EOF))
                    388:        {
                    389: test_char:
                    390:            switch(c) {
                    391:            case '\n':
                    392:                /*
                    393:                 * Escaped newline: read characters until a non-space or an
                    394:                 * unescaped newline and replace them all by a single space.
                    395:                 * This is done by storing the space over the backslash and
                    396:                 * dropping through with the next nonspace. If it is a
                    397:                 * semi-colon and semiNL is TRUE, it will be recognized as a
                    398:                 * newline in the code below this...
                    399:                 */
                    400:                current->lineno++;
                    401:                lastc = ' ';
                    402:                while ((c = ParseReadc ()) == ' ' || c == '\t') {
                    403:                    continue;
                    404:                }
                    405:                if (c == EOF || c == '\n') {
                    406:                    goto line_read;
                    407:                } else {
                    408:                    /*
                    409:                     * Check for comments, semiNL's, etc. -- easier than
                    410:                     * ParseUnreadc(c); continue;
                    411:                     */
                    412:                    goto test_char;
                    413:                }
                    414:                /*NOTREACHED*/
                    415:                break;
                    416:
                    417:            case ';':
                    418:                /*
                    419:                 * Semi-colon: Need to see if it should be interpreted as a
                    420:                 * newline
                    421:                 */
                    422:                if (semiNL) {
                    423:                    /*
                    424:                     * To make sure the command that may be following this
                    425:                     * semi-colon begins with a tab, we push one back into the
                    426:                     * input stream. This will overwrite the semi-colon in the
                    427:                     * buffer. If there is no command following, this does no
                    428:                     * harm, since the newline remains in the buffer and the
                    429:                     * whole line is ignored.
                    430:                     */
                    431:                    ParseUnreadc('\t');
                    432:                    goto line_read;
                    433:                }
                    434:                break;
                    435:            case '=':
                    436:                if (!semiNL) {
                    437:                    /*
                    438:                     * Haven't seen a dependency operator before this, so this
                    439:                     * must be a variable assignment -- don't pay attention to
                    440:                     * dependency operators after this.
                    441:                     */
                    442:                    ignDepOp = TRUE;
                    443:                } else if (lastc == ':' || lastc == '!') {
                    444:                    /*
                    445:                     * Well, we've seen a dependency operator already, but it
                    446:                     * was the previous character, so this is really just an
                    447:                     * expanded variable assignment. Revert semi-colons to
                    448:                     * being just semi-colons again and ignore any more
                    449:                     * dependency operators.
                    450:                     *
                    451:                     * XXX: Note that a line like "foo : a:=b" will blow up,
                    452:                     * but who'd write a line like that anyway?
                    453:                     */
                    454:                    ignDepOp = TRUE; semiNL = FALSE;
                    455:                }
                    456:                break;
                    457:            case '#':
                    458:                if (!ignComment) {
                    459:                    if (
                    460: #if 0
                    461:                    compatMake &&
                    462: #endif
                    463:                    (lastc != '\\')) {
                    464:                        /*
                    465:                         * If the character is a hash mark and it isn't escaped
                    466:                         * (or we're being compatible), the thing is a comment.
                    467:                         * Skip to the end of the line.
                    468:                         */
                    469:                        do {
                    470:                            c = ParseReadc();
                    471:                        } while ((c != '\n') && (c != EOF));
                    472:                        goto line_read;
                    473:                    } else {
                    474:                        /*
                    475:                         * Don't add the backslash. Just let the # get copied
                    476:                         * over.
                    477:                         */
                    478:                        lastc = c;
                    479:                        continue;
                    480:                    }
                    481:                }
                    482:                break;
                    483:            case ':':
                    484:            case '!':
                    485:                if (!ignDepOp && (c == ':' || c == '!')) {
                    486:                    /*
                    487:                     * A semi-colon is recognized as a newline only on
                    488:                     * dependency lines. Dependency lines are lines with a
                    489:                     * colon or an exclamation point. Ergo...
                    490:                     */
                    491:                    semiNL = TRUE;
                    492:                }
                    493:                break;
                    494:            }
                    495:            /*
                    496:             * Copy in the previous character and save this one in lastc.
                    497:             */
                    498:            Buf_AddChar(&buf, lastc);
                    499:            lastc = c;
                    500:
                    501:        }
                    502:     line_read:
                    503:        current->lineno++;
                    504:
                    505:        if (lastc != '\0')
                    506:            Buf_AddChar(&buf, lastc);
                    507:        line = Buf_Retrieve(&buf);
                    508:
                    509:        /*
                    510:         * Strip trailing blanks and tabs from the line.
                    511:         * Do not strip a blank or tab that is preceeded by
                    512:         * a '\'
                    513:         */
                    514:        ep = line;
                    515:        while (*ep)
                    516:            ++ep;
                    517:        while (ep > line + 1 && (ep[-1] == ' ' || ep[-1] == '\t')) {
                    518:            if (ep > line + 1 && ep[-2] == '\\')
                    519:                break;
                    520:            --ep;
                    521:        }
                    522:        *ep = 0;
                    523:
                    524:        if (line[0] == '.') {
                    525:            /*
                    526:             * The line might be a conditional. Ask the conditional module
                    527:             * about it and act accordingly
                    528:             */
                    529:            switch (Cond_Eval (line)) {
                    530:            case COND_SKIP:
                    531:                /*
                    532:                 * Skip to next conditional that evaluates to COND_PARSE.
                    533:                 */
                    534:                do {
                    535:                    free (line);
                    536:                    line = ParseSkipLine(1);
                    537:                } while (line && Cond_Eval(line) != COND_PARSE);
                    538:                if (line == NULL)
                    539:                    break;
1.3       espie     540:                /* FALLTHROUGH */
1.2       espie     541:            case COND_PARSE:
                    542:                free(line);
                    543:                line = ParseReadLine();
                    544:                break;
                    545:            case COND_INVALID:
                    546:                {
                    547:                For *loop;
                    548:
                    549:                loop = For_Eval(line);
                    550:                if (loop != NULL) {
                    551:                    Boolean ok;
                    552:
                    553:                    free(line);
                    554:                    do {
                    555:                        /* Find the matching endfor.  */
                    556:                        line = ParseSkipLine(0);
                    557:                        if (line == NULL) {
                    558:                            Parse_Error(PARSE_FATAL,
                    559:                                     "Unexpected end of file in for loop.\n");
                    560:                            return line;
                    561:                        }
                    562:                        ok = For_Accumulate(loop, line);
                    563:                        free(line);
                    564:                    } while (ok);
                    565:                    For_Run(loop);
                    566:                    line = ParseReadLine();
                    567:                }
                    568:                break;
                    569:                }
                    570:            }
                    571:        }
                    572:        return (line);
                    573:
                    574:     } else {
                    575:        /*
                    576:         * Hit end-of-file, so return a NULL line to indicate this.
                    577:         */
                    578:        return((char *)NULL);
                    579:     }
1.1       espie     580: }
                    581:
                    582: unsigned long
                    583: Parse_Getlineno()
                    584: {
                    585:     return current->lineno;
                    586: }
                    587:
                    588: const char *
                    589: Parse_Getfilename()
                    590: {
                    591:     return current->fname;
                    592: }
                    593:
                    594: #ifdef CLEANUP
                    595: void
                    596: LowParse_Init()
                    597: {
                    598:     Lst_Init(&includes);
                    599:     current = NULL;
                    600: }
                    601:
                    602: void
                    603: LowParse_End()
                    604: {
                    605:     Lst_Destroy(&includes, NOFREE);    /* Should be empty now */
                    606: }
                    607: #endif
                    608:
                    609:
                    610: void
                    611: Finish_Errors()
                    612: {
                    613:     if (current != NULL) {
                    614:        free_ifile(current);
                    615:        current = NULL;
                    616:     }
                    617:     if (fatals) {
                    618:        fprintf(stderr, "Fatal errors encountered -- cannot continue\n");
                    619:        exit(1);
                    620:     }
                    621: }