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

1.6     ! espie       1: /*     $OpenPackages$ */
        !             2: /*     $OpenBSD$ */
1.1       espie       3:
                      4: /* low-level parsing functions. */
                      5:
                      6: /*
                      7:  * Copyright (c) 1999,2000 Marc Espie.
                      8:  *
                      9:  * Extensive code changes for the OpenBSD project.
                     10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  *
                     20:  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
                     21:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                     22:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                     23:  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
                     24:  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
                     25:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
                     26:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     27:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     28:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     29:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
                     30:  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     31:  */
                     32:
                     33: #include <stdarg.h>
                     34: #include <stdio.h>
                     35: #include <assert.h>
                     36: #include "make.h"
                     37: #include "buf.h"
                     38: #include "lowparse.h"
                     39:
                     40: #ifdef CLEANUP
                     41: static LIST        fileNames;  /* file names to free at end */
                     42: #endif
                     43:
1.2       espie      44: /* Definitions for handling #include specifications */
                     45: typedef struct IFile_ {
1.6     ! espie      46:     char               *fname; /* name of file */
        !            47:     unsigned long      lineno; /* line number */
        !            48:     FILE               *F;     /* open stream */
        !            49:     char               *str;   /* read from char area */
        !            50:     char               *ptr;   /* where we are */
        !            51:     char               *end;   /* don't overdo it */
1.2       espie      52: } IFile;
                     53:
                     54: static IFile   *current;
                     55:
1.6     ! espie      56: static LIST    includes;       /* stack of IFiles generated by
1.1       espie      57:                                 * #includes */
                     58:
1.6     ! espie      59: static IFile *new_ifile(char *, FILE *);
        !            60: static IFile *new_istring(char *, char *, unsigned long);
        !            61: static void free_ifile(IFile *);
        !            62: static void ParseVErrorInternal(char *, unsigned long, int, char *, va_list);
        !            63: static int newline(void);
        !            64: static int skiptoendofline(void);
        !            65: static void ParseFoldLF(Buffer, int);
        !            66: static int ParseSkipEmptyLines(Buffer);
1.1       espie      67: static int         fatals = 0;
                     68:
                     69: /*-
1.6     ! espie      70:  * ParseVErrorInternal --
1.1       espie      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: /*-
1.6     ! espie      99:  * Parse_Error --
1.1       espie     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)
1.6     ! espie     151:        (void)fclose(ifile->F);
1.1       espie     152:     else
1.6     ! espie     153:        free(ifile->str);
1.1       espie     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));
1.6     ! espie     168:     /* No malloc, name is always taken from an already existing ifile */
1.1       espie     169:     ifile->fname = name;
                    170:     ifile->F = NULL;
                    171:     /* Strings are used from for loops... */
                    172:     ifile->lineno = lineno;
                    173:     ifile->ptr = ifile->str = str;
                    174:     ifile->end = str + strlen(str);
                    175:     return ifile;
                    176: }
                    177:
                    178:
                    179: /*-
                    180:  *---------------------------------------------------------------------
                    181:  * Parse_FromString  --
                    182:  *     Start Parsing from the given string
                    183:  *
                    184:  * Side Effects:
                    185:  *     A structure is added to the includes Lst and readProc, lineno,
                    186:  *     fname and curFILE are altered for the new file
                    187:  *---------------------------------------------------------------------
                    188:  */
                    189: void
                    190: Parse_FromString(str, lineno)
1.6     ! espie     191:     char               *str;
        !           192:     unsigned long      lineno;
1.1       espie     193: {
                    194:     if (DEBUG(FOR))
                    195:        (void)fprintf(stderr, "%s\n----\n", str);
                    196:
                    197:     if (current != NULL)
                    198:        Lst_AtFront(&includes, current);
                    199:     current = new_istring(str, current->fname, lineno);
                    200: }
                    201:
                    202:
1.6     ! espie     203: void
1.1       espie     204: Parse_FromFile(name, stream)
                    205:     char               *name;
                    206:     FILE               *stream;
                    207: {
                    208:     if (current != NULL)
1.6     ! espie     209:        Lst_AtFront(&includes, current);
1.1       espie     210:     current = new_ifile(name, stream);
                    211: }
                    212:
                    213: /*-
                    214:  *---------------------------------------------------------------------
1.6     ! espie     215:  * Parse_NextFile --
1.1       espie     216:  *     Called when EOF is reached in the current file. If we were reading
                    217:  *     an include file, the includes stack is popped and things set up
                    218:  *     to go back to reading the previous file at the previous location.
                    219:  *
                    220:  * Results:
                    221:  *     CONTINUE if there's more to do. DONE if not.
                    222:  *
                    223:  * Side Effects:
                    224:  *     The old curFILE, is closed. The includes list is shortened.
                    225:  *     lineno, curFILE, and fname are changed if CONTINUE is returned.
                    226:  *---------------------------------------------------------------------
                    227:  */
                    228: Boolean
                    229: Parse_NextFile()
                    230: {
                    231:     IFile *next;
                    232:
                    233:     next = (IFile *)Lst_DeQueue(&includes);
                    234:     if (next != NULL) {
                    235:        if (current != NULL)
                    236:            free_ifile(current);
                    237:        current = next;
1.6     ! espie     238:        return TRUE;
1.1       espie     239:     } else
1.6     ! espie     240:        return FALSE;
1.1       espie     241: }
                    242:
1.5       espie     243: /* guts for ParseReadc. Grab a new line off fgetln when we hit "\n" */
                    244: static int
1.1       espie     245: newline()
                    246: {
                    247:     size_t len;
                    248:
                    249:     if (current->F) {
                    250:        current->ptr = fgetln(current->F, &len);
                    251:        if (current->ptr) {
                    252:            current->end = current->ptr + len;
                    253:            return *current->ptr++;
                    254:        } else {
                    255:            current->end = NULL;
                    256:        }
                    257:     }
                    258:     return EOF;
                    259: }
                    260:
1.6     ! espie     261: #define ParseReadc()   current->ptr < current->end ? *current->ptr++ : newline()
        !           262:
        !           263: /* Take advantage of fgetln: we don't have to scan a whole line to skip it.
        !           264:  */
        !           265: static int
        !           266: skiptoendofline()
1.1       espie     267: {
1.6     ! espie     268:     if (current->F) {
        !           269:        if (current->end - current->ptr > 1)
        !           270:            current->ptr = current->end - 1;
        !           271:        if (*current->ptr == '\n')
        !           272:            return *current->ptr++;
        !           273:        return EOF;
        !           274:     } else {
        !           275:        int c;
        !           276:
        !           277:        do {
        !           278:            c = ParseReadc();
        !           279:        } while (c != '\n' && c != EOF);
        !           280:        return c;
        !           281:     }
1.2       espie     282: }
                    283:
1.6     ! espie     284:
        !           285: /* ParseSkipGetLine():
        !           286:  *     Return the first logical line that starts with '.'
1.2       espie     287:  */
                    288: char *
1.6     ! espie     289: ParseSkipGetLine(linebuf)
        !           290:     Buffer linebuf;
1.2       espie     291: {
1.6     ! espie     292:     int c;
        !           293:
        !           294:        /* If first char isn't dot, skip to end of line, handling \ */
        !           295:     while ((c = ParseReadc()) != '.') {
        !           296:        for (;c != '\n'; c = ParseReadc()) {
        !           297:            if (c == '\\') {
        !           298:                c = ParseReadc();
        !           299:                if (c == '\n')
        !           300:                    current->lineno++;
        !           301:            }
        !           302:            if (c == EOF) {
        !           303:                Parse_Error(PARSE_FATAL, "Unclosed conditional");
        !           304:                return NULL;
        !           305:            }
        !           306:        }
        !           307:        current->lineno++;
        !           308:     }
1.2       espie     309:
1.6     ! espie     310:        /* This is the line we need to copy */
        !           311:     return ParseGetLine(linebuf, "conditional");
        !           312: }
1.2       espie     313:
1.6     ! espie     314: /* Grab logical line into linebuf.
        !           315:  * The first character is already in c. */
        !           316: static void
        !           317: ParseFoldLF(linebuf, c)
        !           318:     Buffer linebuf;
        !           319:     int c;
        !           320: {
1.2       espie     321:     for (;;) {
1.6     ! espie     322:        if (c == '\n') {
        !           323:            current->lineno++;
1.2       espie     324:            break;
1.6     ! espie     325:        }
        !           326:        if (c == EOF)
1.2       espie     327:            break;
1.6     ! espie     328:        Buf_AddChar(linebuf, c);
        !           329:        c = ParseReadc();
        !           330:        while (c == '\\') {
        !           331:            c = ParseReadc();
        !           332:            if (c == '\n') {
        !           333:                Buf_AddSpace(linebuf);
        !           334:                current->lineno++;
        !           335:                do {
        !           336:                    c = ParseReadc();
        !           337:                } while (c == ' ' || c == '\t');
        !           338:            } else {
        !           339:                Buf_AddChar(linebuf, '\\');
        !           340:                if (c == '\\') {
        !           341:                    Buf_AddChar(linebuf, '\\');
        !           342:                    c = ParseReadc();
        !           343:                }
        !           344:                break;
        !           345:            }
        !           346:        }
1.2       espie     347:     }
                    348: }
                    349:
1.6     ! espie     350: /* ParseGetLine:
        !           351:  *     Simply get line, no parsing beyond \
1.2       espie     352:  */
                    353: char *
1.6     ! espie     354: ParseGetLine(linebuf, type)
        !           355:     Buffer     linebuf;
        !           356:     const char *type;
        !           357: {
        !           358:     int c;
        !           359:
        !           360:     Buf_Reset(linebuf);
        !           361:     c = ParseReadc();
        !           362:     if (c == EOF) {
        !           363:        Parse_Error(PARSE_FATAL, "Unclosed %s", type);
        !           364:        return NULL;
        !           365:     }
        !           366:
        !           367:     /* Handle '\' at beginning of line, since \\n needs special treatment */
        !           368:     while (c == '\\') {
1.2       espie     369:        c = ParseReadc();
1.6     ! espie     370:        if (c == '\n') {
1.2       espie     371:            current->lineno++;
1.6     ! espie     372:            do {
        !           373:                c = ParseReadc();
        !           374:            } while (c == ' ' || c == '\t');
1.2       espie     375:        } else {
1.6     ! espie     376:            Buf_AddChar(linebuf, '\\');
        !           377:            if (c == '\\') {
        !           378:                Buf_AddChar(linebuf, '\\');
        !           379:                c = ParseReadc();
        !           380:            }
1.2       espie     381:            break;
                    382:        }
                    383:     }
1.6     ! espie     384:     ParseFoldLF(linebuf, c);
        !           385:
        !           386:     return Buf_Retrieve(linebuf);
        !           387: }
        !           388:
        !           389: /* Skip all empty lines, and return the first `useful' character.
        !           390:  * (This does skip blocks of comments at high speed, which justifies
        !           391:  * the complexity of the function.)
        !           392:  */
        !           393: static int
        !           394: ParseSkipEmptyLines(linebuf)
        !           395:     Buffer     linebuf;
        !           396: {
        !           397:     int        c;              /* the current character */
1.2       espie     398:
1.6     ! espie     399:     for (;;) {
        !           400:        Buf_Reset(linebuf);
        !           401:        c = ParseReadc();
        !           402:        /* Strip leading spaces, fold on '\n' */
        !           403:        if (c == ' ') {
        !           404:            do {
        !           405:                c = ParseReadc();
        !           406:            } while (c == ' ' || c == '\t');
        !           407:            while (c == '\\') {
        !           408:                c = ParseReadc();
        !           409:                if (c == '\n') {
        !           410:                    current->lineno++;
        !           411:                    do {
        !           412:                        c = ParseReadc();
        !           413:                    } while (c == ' ' || c == '\t');
1.2       espie     414:                } else {
1.6     ! espie     415:                    Buf_AddChar(linebuf, '\\');
        !           416:                    if (c == '\\') {
        !           417:                        Buf_AddChar(linebuf, '\\');
        !           418:                        c = ParseReadc();
1.2       espie     419:                    }
1.6     ! espie     420:                    if (c == EOF)
        !           421:                        return '\n';
        !           422:                    else
        !           423:                        return c;
1.2       espie     424:                }
                    425:            }
1.6     ! espie     426:            assert(c != '\t');
1.2       espie     427:        }
1.6     ! espie     428:        if (c == '#')
        !           429:            c = skiptoendofline();
        !           430:        /* Almost identical to spaces, except this occurs after comments
        !           431:         * have been taken care of, and we keep the tab itself.  */
        !           432:        if (c == '\t') {
        !           433:            Buf_AddChar(linebuf, '\t');
        !           434:            do {
        !           435:                c = ParseReadc();
        !           436:            } while (c == ' ' || c == '\t');
        !           437:            while (c == '\\') {
        !           438:                c = ParseReadc();
        !           439:                if (c == '\n') {
        !           440:                    current->lineno++;
1.2       espie     441:                    do {
1.6     ! espie     442:                        c = ParseReadc();
        !           443:                    } while (c == ' ' || c == '\t');
        !           444:                } else {
        !           445:                    Buf_AddChar(linebuf, '\\');
        !           446:                    if (c == '\\') {
        !           447:                        Buf_AddChar(linebuf, '\\');
        !           448:                        c = ParseReadc();
        !           449:                    }
        !           450:                    if (c == EOF)
        !           451:                        return '\n';
        !           452:                    else
        !           453:                        return c;
        !           454:                    return c;
1.2       espie     455:                }
                    456:            }
                    457:        }
1.6     ! espie     458:        if (c == '\n')
        !           459:            current->lineno++;
        !           460:        else
        !           461:            return c;
        !           462:     }
        !           463: }
1.2       espie     464:
1.6     ! espie     465: /*-
        !           466:  *---------------------------------------------------------------------
        !           467:  * ParseReadLine --
        !           468:  *     Read an entire line from the input file.
        !           469:  *
        !           470:  * Results:
        !           471:  *     A line without a new line, or NULL at EOF.
        !           472:  *
        !           473:  * Notes:
        !           474:  *     Removes beginning and trailing blanks (but keeps first tab).
        !           475:  *     Updates line numbers, handles escaped newlines, and skip over
        !           476:  *     uninteresting lines.
        !           477:  *     All but trivial comments can't be handled at this point, because we
        !           478:  *     don't know yet which lines are shell commands or not.
        !           479:  *---------------------------------------------------------------------
        !           480:  */
        !           481: char *
        !           482: ParseReadLine(linebuf)
        !           483:     Buffer     linebuf;
        !           484: {
        !           485:     int        c;              /* the current character */
        !           486:
        !           487:     c = ParseSkipEmptyLines(linebuf);
        !           488:
        !           489:     if (c == EOF)
        !           490:        return NULL;
        !           491:     else {
        !           492:        ParseFoldLF(linebuf, c);
        !           493:        Buf_KillTrailingSpaces(linebuf);
        !           494:        return Buf_Retrieve(linebuf);
1.2       espie     495:     }
1.1       espie     496: }
                    497:
                    498: unsigned long
                    499: Parse_Getlineno()
                    500: {
                    501:     return current->lineno;
                    502: }
                    503:
                    504: const char *
                    505: Parse_Getfilename()
                    506: {
                    507:     return current->fname;
                    508: }
                    509:
                    510: #ifdef CLEANUP
                    511: void
                    512: LowParse_Init()
                    513: {
                    514:     Lst_Init(&includes);
                    515:     current = NULL;
                    516: }
                    517:
                    518: void
                    519: LowParse_End()
                    520: {
                    521:     Lst_Destroy(&includes, NOFREE);    /* Should be empty now */
                    522: }
                    523: #endif
1.6     ! espie     524:
1.1       espie     525:
                    526: void
                    527: Finish_Errors()
                    528: {
                    529:     if (current != NULL) {
1.6     ! espie     530:        free_ifile(current);
1.1       espie     531:        current = NULL;
                    532:     }
                    533:     if (fatals) {
                    534:        fprintf(stderr, "Fatal errors encountered -- cannot continue\n");
                    535:        exit(1);
                    536:     }
                    537: }