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

1.6       espie       1: /*     $OpenPackages$ */
1.16    ! espie       2: /*     $OpenBSD: lowparse.c,v 1.15 2001/11/11 06:02:06 deraadt 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:
1.9       espie      33: #include <assert.h>
                     34: #include <stddef.h>
1.1       espie      35: #include <stdio.h>
1.11      espie      36: #include <stdlib.h>
1.10      espie      37: #include <string.h>
1.9       espie      38: #include <unistd.h>
                     39: #include "config.h"
                     40: #include "defines.h"
1.1       espie      41: #include "buf.h"
                     42: #include "lowparse.h"
1.9       espie      43: #include "error.h"
                     44: #include "lst.h"
                     45: #include "memory.h"
                     46:
                     47: /* XXX check whether we can free filenames at the end, for a proper
                     48:  * definition of `end'. */
1.1       espie      49:
1.9       espie      50: #if 0
1.1       espie      51: static LIST        fileNames;  /* file names to free at end */
                     52: #endif
                     53:
1.9       espie      54: /* Input stream structure, file or string. */
                     55: typedef struct {
                     56:     const char         *fname; /* Name of file */
                     57:     unsigned long      lineno; /* Line number */
                     58:     FILE               *F;     /* Open stream, or NULL if pure string. */
                     59:     char               *str;   /* Input string, if F == NULL. */
                     60:
                     61:     /* Line buffer. */
                     62:     char               *ptr;   /* Where we are. */
                     63:     char               *end;   /* Don't overdo it. */
1.2       espie      64: } IFile;
                     65:
1.9       espie      66: static IFile   *current;       /* IFile being parsed. */
1.2       espie      67:
1.9       espie      68: static LIST    input_stack;    /* Stack of IFiles waiting to be parsed
                     69:                                 * (includes and loop reparses) */
1.1       espie      70:
1.9       espie      71: /* IFile ctors.
                     72:  *
                     73:  * obj = new_ifile(filename, filehandle);
                     74:  *     Create input object from filename, filehandle. */
                     75: static IFile *new_ifile(const char *, FILE *);
                     76: /* obj = new_istring(str, filename, lineno);
                     77:  *     Create input object from str, filename, lineno. */
                     78: static IFile *new_istring(char *, const char *, unsigned long);
                     79: /* free_ifile(obj);
                     80:  *     Discard consumed input object, closing streams, freeing memory.  */
1.6       espie      81: static void free_ifile(IFile *);
1.9       espie      82:
                     83:
                     84: /* Handling basic character reading.
                     85:  * c = ParseReadc();
                     86:  *     New character c from current input stream, or EOF at end of stream. */
                     87: #define ParseReadc()   current->ptr < current->end ? *current->ptr++ : newline()
                     88: /* len = newline();
                     89:  *     Guts for ParseReadc. Grabs a new line off fgetln when we have
                     90:  *     consumed the current line and returns its length. Or EOF at end of
                     91:  *     stream.  */
1.6       espie      92: static int newline(void);
1.9       espie      93: /* c = skiptoendofline();
                     94:  *     Skips to the end of the current line, returns either '\n' or EOF.  */
1.6       espie      95: static int skiptoendofline(void);
1.1       espie      96:
                     97:
1.9       espie      98: /* Helper functions to handle basic parsing. */
                     99: /* ParseFoldLF(buffer, firstchar);
                    100:  *     Grabs logical line into buffer, the first character has already been
                    101:  *     read into firstchar.  */
                    102: static void ParseFoldLF(Buffer, int);
1.1       espie     103:
1.9       espie     104: /* firstchar = ParseSkipEmptyLines(buffer);
                    105:  *     Scans lines, skipping empty lines. May put some characters into
                    106:  *     buffer, returns the first character useful to continue parsing
                    107:  *     (e.g., not a backslash or a space. */
                    108: static int ParseSkipEmptyLines(Buffer);
1.1       espie     109:
                    110: static IFile *
                    111: new_ifile(name, stream)
1.9       espie     112:     const char *name;
1.1       espie     113:     FILE *stream;
                    114: {
                    115:     IFile *ifile;
1.9       espie     116: #if 0
1.1       espie     117:     Lst_AtEnd(&fileNames, name);
                    118: #endif
                    119:
                    120:     ifile = emalloc(sizeof(*ifile));
                    121:     ifile->fname = name;
1.8       espie     122:     ifile->str = NULL;
1.9       espie     123:     /* Naturally enough, we start reading at line 0. */
1.1       espie     124:     ifile->lineno = 0;
                    125:     ifile->F = stream;
                    126:     ifile->ptr = ifile->end = NULL;
                    127:     return ifile;
                    128: }
                    129:
                    130: static void
                    131: free_ifile(ifile)
                    132:     IFile *ifile;
                    133: {
1.7       espie     134:     if (ifile->F && fileno(ifile->F) != STDIN_FILENO)
1.6       espie     135:        (void)fclose(ifile->F);
1.8       espie     136:     free(ifile->str);
1.1       espie     137:     /* Note we can't free the file names yet, as they are embedded in GN for
                    138:      * error reports. */
                    139:     free(ifile);
                    140: }
                    141:
                    142: static IFile *
                    143: new_istring(str, name, lineno)
                    144:     char *str;
1.9       espie     145:     const char *name;
1.1       espie     146:     unsigned long lineno;
                    147: {
                    148:     IFile *ifile;
                    149:
                    150:     ifile = emalloc(sizeof(*ifile));
1.6       espie     151:     /* No malloc, name is always taken from an already existing ifile */
1.1       espie     152:     ifile->fname = name;
                    153:     ifile->F = NULL;
1.9       espie     154:     /* Strings are used in for loops, so we need to reset the line counter
                    155:      * to an appropriate value. */
1.1       espie     156:     ifile->lineno = lineno;
                    157:     ifile->ptr = ifile->str = str;
                    158:     ifile->end = str + strlen(str);
                    159:     return ifile;
                    160: }
                    161:
                    162:
                    163: void
                    164: Parse_FromString(str, lineno)
1.6       espie     165:     char               *str;
                    166:     unsigned long      lineno;
1.1       espie     167: {
                    168:     if (DEBUG(FOR))
                    169:        (void)fprintf(stderr, "%s\n----\n", str);
                    170:
                    171:     if (current != NULL)
1.9       espie     172:        Lst_Push(&input_stack, current);
1.1       espie     173:     current = new_istring(str, current->fname, lineno);
                    174: }
                    175:
                    176:
1.6       espie     177: void
1.1       espie     178: Parse_FromFile(name, stream)
1.9       espie     179:     const char         *name;
1.1       espie     180:     FILE               *stream;
                    181: {
                    182:     if (current != NULL)
1.9       espie     183:        Lst_Push(&input_stack, current);
1.1       espie     184:     current = new_ifile(name, stream);
                    185: }
                    186:
1.9       espie     187: bool
1.1       espie     188: Parse_NextFile()
                    189: {
1.9       espie     190:     if (current != NULL)
                    191:        free_ifile(current);
                    192:     current = (IFile *)Lst_Pop(&input_stack);
                    193:     return current != NULL;
1.1       espie     194: }
                    195:
1.5       espie     196: static int
1.1       espie     197: newline()
                    198: {
                    199:     size_t len;
                    200:
                    201:     if (current->F) {
                    202:        current->ptr = fgetln(current->F, &len);
                    203:        if (current->ptr) {
                    204:            current->end = current->ptr + len;
                    205:            return *current->ptr++;
                    206:        } else {
                    207:            current->end = NULL;
                    208:        }
                    209:     }
                    210:     return EOF;
                    211: }
                    212:
1.6       espie     213: static int
                    214: skiptoendofline()
1.1       espie     215: {
1.6       espie     216:     if (current->F) {
                    217:        if (current->end - current->ptr > 1)
                    218:            current->ptr = current->end - 1;
                    219:        if (*current->ptr == '\n')
                    220:            return *current->ptr++;
                    221:        return EOF;
                    222:     } else {
                    223:        int c;
                    224:
                    225:        do {
                    226:            c = ParseReadc();
                    227:        } while (c != '\n' && c != EOF);
                    228:        return c;
                    229:     }
1.2       espie     230: }
                    231:
1.6       espie     232:
1.2       espie     233: char *
1.9       espie     234: Parse_ReadNextConditionalLine(linebuf)
1.6       espie     235:     Buffer linebuf;
1.2       espie     236: {
1.6       espie     237:     int c;
                    238:
                    239:        /* If first char isn't dot, skip to end of line, handling \ */
                    240:     while ((c = ParseReadc()) != '.') {
                    241:        for (;c != '\n'; c = ParseReadc()) {
                    242:            if (c == '\\') {
                    243:                c = ParseReadc();
                    244:                if (c == '\n')
                    245:                    current->lineno++;
                    246:            }
                    247:            if (c == EOF) {
                    248:                Parse_Error(PARSE_FATAL, "Unclosed conditional");
                    249:                return NULL;
                    250:            }
                    251:        }
                    252:        current->lineno++;
                    253:     }
1.2       espie     254:
1.6       espie     255:        /* This is the line we need to copy */
1.9       espie     256:     return Parse_ReadUnparsedLine(linebuf, "conditional");
1.6       espie     257: }
1.2       espie     258:
1.6       espie     259: static void
                    260: ParseFoldLF(linebuf, c)
                    261:     Buffer linebuf;
                    262:     int c;
                    263: {
1.2       espie     264:     for (;;) {
1.6       espie     265:        if (c == '\n') {
                    266:            current->lineno++;
1.2       espie     267:            break;
1.6       espie     268:        }
                    269:        if (c == EOF)
1.2       espie     270:            break;
1.6       espie     271:        Buf_AddChar(linebuf, c);
                    272:        c = ParseReadc();
                    273:        while (c == '\\') {
                    274:            c = ParseReadc();
                    275:            if (c == '\n') {
                    276:                Buf_AddSpace(linebuf);
                    277:                current->lineno++;
                    278:                do {
                    279:                    c = ParseReadc();
                    280:                } while (c == ' ' || c == '\t');
                    281:            } else {
                    282:                Buf_AddChar(linebuf, '\\');
                    283:                if (c == '\\') {
                    284:                    Buf_AddChar(linebuf, '\\');
                    285:                    c = ParseReadc();
                    286:                }
                    287:                break;
                    288:            }
                    289:        }
1.2       espie     290:     }
                    291: }
                    292:
                    293: char *
1.9       espie     294: Parse_ReadUnparsedLine(linebuf, type)
1.6       espie     295:     Buffer     linebuf;
                    296:     const char *type;
                    297: {
                    298:     int c;
                    299:
                    300:     Buf_Reset(linebuf);
                    301:     c = ParseReadc();
                    302:     if (c == EOF) {
                    303:        Parse_Error(PARSE_FATAL, "Unclosed %s", type);
                    304:        return NULL;
                    305:     }
                    306:
                    307:     /* Handle '\' at beginning of line, since \\n needs special treatment */
                    308:     while (c == '\\') {
1.2       espie     309:        c = ParseReadc();
1.6       espie     310:        if (c == '\n') {
1.2       espie     311:            current->lineno++;
1.6       espie     312:            do {
                    313:                c = ParseReadc();
                    314:            } while (c == ' ' || c == '\t');
1.2       espie     315:        } else {
1.6       espie     316:            Buf_AddChar(linebuf, '\\');
                    317:            if (c == '\\') {
                    318:                Buf_AddChar(linebuf, '\\');
                    319:                c = ParseReadc();
                    320:            }
1.2       espie     321:            break;
                    322:        }
                    323:     }
1.6       espie     324:     ParseFoldLF(linebuf, c);
                    325:
                    326:     return Buf_Retrieve(linebuf);
                    327: }
                    328:
1.9       espie     329: /* This is a fairly complex function, but without it, we could not skip
                    330:  * blocks of comments without reading them. */
1.6       espie     331: static int
                    332: ParseSkipEmptyLines(linebuf)
                    333:     Buffer     linebuf;
                    334: {
                    335:     int        c;              /* the current character */
1.2       espie     336:
1.6       espie     337:     for (;;) {
                    338:        Buf_Reset(linebuf);
                    339:        c = ParseReadc();
                    340:        /* Strip leading spaces, fold on '\n' */
                    341:        if (c == ' ') {
                    342:            do {
                    343:                c = ParseReadc();
                    344:            } while (c == ' ' || c == '\t');
                    345:            while (c == '\\') {
                    346:                c = ParseReadc();
                    347:                if (c == '\n') {
                    348:                    current->lineno++;
                    349:                    do {
                    350:                        c = ParseReadc();
                    351:                    } while (c == ' ' || c == '\t');
1.2       espie     352:                } else {
1.6       espie     353:                    Buf_AddChar(linebuf, '\\');
                    354:                    if (c == '\\') {
                    355:                        Buf_AddChar(linebuf, '\\');
                    356:                        c = ParseReadc();
1.2       espie     357:                    }
1.6       espie     358:                    if (c == EOF)
                    359:                        return '\n';
                    360:                    else
                    361:                        return c;
1.2       espie     362:                }
                    363:            }
1.6       espie     364:            assert(c != '\t');
1.2       espie     365:        }
1.6       espie     366:        if (c == '#')
                    367:            c = skiptoendofline();
                    368:        /* Almost identical to spaces, except this occurs after comments
                    369:         * have been taken care of, and we keep the tab itself.  */
                    370:        if (c == '\t') {
                    371:            Buf_AddChar(linebuf, '\t');
                    372:            do {
                    373:                c = ParseReadc();
                    374:            } while (c == ' ' || c == '\t');
                    375:            while (c == '\\') {
                    376:                c = ParseReadc();
                    377:                if (c == '\n') {
                    378:                    current->lineno++;
1.2       espie     379:                    do {
1.6       espie     380:                        c = ParseReadc();
                    381:                    } while (c == ' ' || c == '\t');
                    382:                } else {
                    383:                    Buf_AddChar(linebuf, '\\');
                    384:                    if (c == '\\') {
                    385:                        Buf_AddChar(linebuf, '\\');
                    386:                        c = ParseReadc();
                    387:                    }
                    388:                    if (c == EOF)
                    389:                        return '\n';
                    390:                    else
                    391:                        return c;
                    392:                    return c;
1.2       espie     393:                }
                    394:            }
                    395:        }
1.6       espie     396:        if (c == '\n')
                    397:            current->lineno++;
                    398:        else
                    399:            return c;
                    400:     }
                    401: }
1.2       espie     402:
1.9       espie     403: /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
1.12      espie     404:  * the first tab), handles escaped newlines, and skip over uninteresting
1.9       espie     405:  * lines.
1.6       espie     406:  *
1.9       espie     407:  * The line number is advanced, which implies that continuation
                    408:  * lines are numbered with the last line no (we could do better, at a
                    409:  * price).
1.6       espie     410:  *
1.9       espie     411:  * Trivial comments are also removed, but we can't do more, as
                    412:  * we don't know which lines are shell commands or not.  */
1.6       espie     413: char *
1.9       espie     414: Parse_ReadNormalLine(linebuf)
1.6       espie     415:     Buffer     linebuf;
                    416: {
                    417:     int        c;              /* the current character */
                    418:
                    419:     c = ParseSkipEmptyLines(linebuf);
                    420:
                    421:     if (c == EOF)
                    422:        return NULL;
                    423:     else {
                    424:        ParseFoldLF(linebuf, c);
                    425:        Buf_KillTrailingSpaces(linebuf);
                    426:        return Buf_Retrieve(linebuf);
1.2       espie     427:     }
1.1       espie     428: }
                    429:
                    430: unsigned long
                    431: Parse_Getlineno()
                    432: {
1.13      espie     433:     return current ? current->lineno : 0;
1.1       espie     434: }
                    435:
                    436: const char *
                    437: Parse_Getfilename()
                    438: {
1.13      espie     439:     return current ? current->fname : NULL;
1.1       espie     440: }
                    441:
                    442: #ifdef CLEANUP
                    443: void
                    444: LowParse_Init()
                    445: {
1.16    ! espie     446:     Static_Lst_Init(&input_stack);
1.1       espie     447:     current = NULL;
                    448: }
                    449:
                    450: void
                    451: LowParse_End()
                    452: {
1.9       espie     453:     Lst_Destroy(&input_stack, NOFREE); /* Should be empty now */
                    454: #ifdef 0
                    455:     Lst_Destroy(&fileNames, (SimpleProc)free);
                    456: #endif
1.1       espie     457: }
                    458: #endif
1.6       espie     459:
1.1       espie     460:
                    461: void
1.9       espie     462: Parse_ReportErrors()
1.1       espie     463: {
1.9       espie     464:     if (fatal_errors) {
                    465: #ifdef CLEANUP
                    466:        while (Parse_NextFile())
                    467:                ;
                    468: #endif
1.1       espie     469:        fprintf(stderr, "Fatal errors encountered -- cannot continue\n");
                    470:        exit(1);
1.9       espie     471:     } else
                    472:        assert(current == NULL);
1.1       espie     473: }