[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.3

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