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

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