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

1.6       espie       1: /*     $OpenPackages$ */
1.7     ! espie       2: /*     $OpenBSD: lowparse.c,v 1.6 2001/05/03 13:41:07 espie Exp $ */
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: {
1.7     ! espie     150:     if (ifile->F && fileno(ifile->F) != STDIN_FILENO)
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: }