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

1.2     ! espie       1: /*     $OpenBSD: lowparse.c,v 1.1 2000/06/23 16:39:45 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;
        !           542:                /*FALLTHRU*/
        !           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: }