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

1.25    ! espie       1: /*     $OpenBSD: lowparse.c,v 1.24 2010/07/19 19:46:44 espie Exp $ */
1.1       espie       2:
                      3: /* low-level parsing functions. */
                      4:
                      5: /*
                      6:  * Copyright (c) 1999,2000 Marc Espie.
                      7:  *
                      8:  * Extensive code changes for the OpenBSD project.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                     21:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                     22:  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
                     23:  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
                     24:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
                     25:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     26:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     27:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     28:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
                     29:  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31:
1.9       espie      32: #include <assert.h>
                     33: #include <stddef.h>
1.1       espie      34: #include <stdio.h>
1.11      espie      35: #include <stdlib.h>
1.10      espie      36: #include <string.h>
1.9       espie      37: #include <unistd.h>
                     38: #include "config.h"
                     39: #include "defines.h"
1.1       espie      40: #include "buf.h"
                     41: #include "lowparse.h"
1.9       espie      42: #include "error.h"
                     43: #include "lst.h"
                     44: #include "memory.h"
                     45:
                     46: /* XXX check whether we can free filenames at the end, for a proper
                     47:  * definition of `end'. */
1.1       espie      48:
1.9       espie      49: #if 0
1.1       espie      50: static LIST        fileNames;  /* file names to free at end */
                     51: #endif
                     52:
1.22      espie      53: /* Input stream structure: file or string.
                     54:  * Files have str == NULL, F != NULL.
                     55:  * Strings have F == NULL, str != NULL.
                     56:  */
                     57: struct input_stream {
1.21      espie      58:        const char *fname;      /* Name of file */
                     59:        unsigned long lineno;   /* Line number */
                     60:        FILE *F;                /* Open stream, or NULL if pure string. */
                     61:        char *str;              /* Input string, if F == NULL. */
                     62:
                     63:        /* Line buffer. */
                     64:        char *ptr;              /* Where we are. */
                     65:        char *end;              /* Don't overdo it. */
1.22      espie      66: };
1.2       espie      67:
1.22      espie      68: static struct input_stream *current;   /* the input_stream being parsed. */
1.2       espie      69:
1.22      espie      70: static LIST input_stack;       /* Stack of input_stream waiting to be parsed
1.9       espie      71:                                 * (includes and loop reparses) */
1.1       espie      72:
1.22      espie      73: /* input_stream ctors.
1.9       espie      74:  *
1.22      espie      75:  * obj = new_input_file(filename, filehandle);
                     76:  *     Create input stream from filename, filehandle. */
                     77: static struct input_stream *new_input_file(const char *, FILE *);
                     78: /* obj = new_input_string(str, filename, lineno);
                     79:  *     Create input stream from str, filename, lineno. */
                     80: static struct input_stream *new_input_string(char *, const char *, unsigned long);
                     81: /* free_input_stream(obj);
                     82:  *     Discard consumed input stream, closing files, freeing memory.  */
                     83: static void free_input_stream(struct input_stream *);
1.9       espie      84:
                     85:
1.20      espie      86: /* Handling basic character reading.
1.22      espie      87:  * c = read_char();
1.9       espie      88:  *     New character c from current input stream, or EOF at end of stream. */
1.22      espie      89: #define read_char()    \
                     90:     current->ptr < current->end ? *current->ptr++ : grab_new_line_and_readchar()
                     91: /* char = grab_new_line_and_readchar();
                     92:  *     Guts for read_char. Grabs a new line off fgetln when we have
                     93:  *     consumed the current line and returns the first char, or EOF at end of
1.9       espie      94:  *     stream.  */
1.22      espie      95: static int grab_new_line_and_readchar(void);
                     96: /* c = skip_to_end_of_line();
1.9       espie      97:  *     Skips to the end of the current line, returns either '\n' or EOF.  */
1.22      espie      98: static int skip_to_end_of_line(void);
1.1       espie      99:
                    100:
1.9       espie     101: /* Helper functions to handle basic parsing. */
1.22      espie     102: /* read_logical_line(buffer, firstchar);
1.9       espie     103:  *     Grabs logical line into buffer, the first character has already been
                    104:  *     read into firstchar.  */
1.22      espie     105: static void read_logical_line(Buffer, int);
1.1       espie     106:
1.9       espie     107: /* firstchar = ParseSkipEmptyLines(buffer);
                    108:  *     Scans lines, skipping empty lines. May put some characters into
                    109:  *     buffer, returns the first character useful to continue parsing
                    110:  *     (e.g., not a backslash or a space. */
1.22      espie     111: static int skip_empty_lines_and_read_char(Buffer);
1.1       espie     112:
1.22      espie     113: static struct input_stream *
                    114: new_input_file(const char *name, FILE *stream)
1.1       espie     115: {
1.22      espie     116:        struct input_stream *istream;
1.9       espie     117: #if 0
1.21      espie     118:        Lst_AtEnd(&fileNames, name);
1.1       espie     119: #endif
                    120:
1.22      espie     121:        istream = emalloc(sizeof(*istream));
                    122:        istream->fname = name;
                    123:        istream->str = NULL;
1.21      espie     124:        /* Naturally enough, we start reading at line 0. */
1.22      espie     125:        istream->lineno = 0;
                    126:        istream->F = stream;
                    127:        istream->ptr = istream->end = NULL;
                    128:        return istream;
1.1       espie     129: }
                    130:
                    131: static void
1.22      espie     132: free_input_stream(struct input_stream *istream)
1.1       espie     133: {
1.22      espie     134:        if (istream->F && fileno(istream->F) != STDIN_FILENO)
                    135:                (void)fclose(istream->F);
                    136:        free(istream->str);
1.21      espie     137:        /* Note we can't free the file names yet, as they are embedded in GN
                    138:         * for error reports. */
1.22      espie     139:        free(istream);
1.1       espie     140: }
                    141:
1.22      espie     142: static struct input_stream *
                    143: new_input_string(char *str, const char *name, unsigned long lineno)
1.1       espie     144: {
1.22      espie     145:        struct input_stream *istream;
1.1       espie     146:
1.22      espie     147:        istream = emalloc(sizeof(*istream));
                    148:        /* No malloc, name is always taken from an already existing istream */
                    149:        istream->fname = name;
                    150:        istream->F = NULL;
1.21      espie     151:        /* Strings are used in for loops, so we need to reset the line counter
                    152:         * to an appropriate value. */
1.22      espie     153:        istream->lineno = lineno;
                    154:        istream->ptr = istream->str = str;
                    155:        istream->end = str + strlen(str);
                    156:        return istream;
1.1       espie     157: }
                    158:
                    159:
                    160: void
1.18      espie     161: Parse_FromString(char *str, unsigned long lineno)
1.1       espie     162: {
1.21      espie     163:        if (DEBUG(FOR))
                    164:                (void)fprintf(stderr, "%s\n----\n", str);
1.1       espie     165:
1.25    ! espie     166:        Lst_Push(&input_stack, current);
        !           167:        assert(current != NULL);
1.22      espie     168:        current = new_input_string(str, current->fname, lineno);
1.1       espie     169: }
                    170:
                    171:
1.6       espie     172: void
1.18      espie     173: Parse_FromFile(const char *name, FILE *stream)
1.1       espie     174: {
1.21      espie     175:        if (current != NULL)
                    176:                Lst_Push(&input_stack, current);
1.22      espie     177:        current = new_input_file(name, stream);
1.1       espie     178: }
                    179:
1.9       espie     180: bool
1.18      espie     181: Parse_NextFile(void)
1.1       espie     182: {
1.21      espie     183:        if (current != NULL)
1.22      espie     184:                free_input_stream(current);
                    185:        current = (struct input_stream *)Lst_Pop(&input_stack);
1.21      espie     186:        return current != NULL;
1.1       espie     187: }
                    188:
1.5       espie     189: static int
1.22      espie     190: grab_new_line_and_readchar(void)
1.1       espie     191: {
1.21      espie     192:        size_t len;
1.1       espie     193:
1.21      espie     194:        if (current->F) {
                    195:                current->ptr = fgetln(current->F, &len);
                    196:                if (current->ptr) {
                    197:                        current->end = current->ptr + len;
                    198:                        return *current->ptr++;
                    199:                } else {
                    200:                        current->end = NULL;
                    201:                }
1.1       espie     202:        }
1.21      espie     203:        return EOF;
1.1       espie     204: }
                    205:
1.6       espie     206: static int
1.22      espie     207: skip_to_end_of_line(void)
1.1       espie     208: {
1.21      espie     209:        if (current->F) {
                    210:                if (current->end - current->ptr > 1)
                    211:                        current->ptr = current->end - 1;
                    212:                if (*current->ptr == '\n')
                    213:                        return *current->ptr++;
                    214:                return EOF;
                    215:        } else {
                    216:                int c;
1.6       espie     217:
1.21      espie     218:                do {
1.22      espie     219:                        c = read_char();
1.21      espie     220:                } while (c != '\n' && c != EOF);
                    221:                return c;
                    222:        }
1.2       espie     223: }
                    224:
1.6       espie     225:
1.2       espie     226: char *
1.18      espie     227: Parse_ReadNextConditionalLine(Buffer linebuf)
1.2       espie     228: {
1.21      espie     229:        int c;
1.6       espie     230:
                    231:        /* If first char isn't dot, skip to end of line, handling \ */
1.22      espie     232:        while ((c = read_char()) != '.') {
                    233:                for (;c != '\n'; c = read_char()) {
1.21      espie     234:                        if (c == '\\') {
1.22      espie     235:                                c = read_char();
1.21      espie     236:                                if (c == '\n')
                    237:                                        current->lineno++;
                    238:                        }
                    239:                        if (c == EOF) {
1.23      espie     240:                                Parse_Error(PARSE_FATAL,
1.21      espie     241:                                    "Unclosed conditional");
                    242:                                return NULL;
                    243:                        }
                    244:                }
                    245:                current->lineno++;
1.6       espie     246:        }
1.2       espie     247:
1.6       espie     248:        /* This is the line we need to copy */
1.21      espie     249:        return Parse_ReadUnparsedLine(linebuf, "conditional");
1.6       espie     250: }
1.2       espie     251:
1.6       espie     252: static void
1.22      espie     253: read_logical_line(Buffer linebuf, int c)
1.6       espie     254: {
1.21      espie     255:        for (;;) {
                    256:                if (c == '\n') {
                    257:                        current->lineno++;
                    258:                        break;
                    259:                }
                    260:                if (c == EOF)
                    261:                        break;
                    262:                Buf_AddChar(linebuf, c);
1.22      espie     263:                c = read_char();
1.21      espie     264:                while (c == '\\') {
1.22      espie     265:                        c = read_char();
1.21      espie     266:                        if (c == '\n') {
                    267:                                Buf_AddSpace(linebuf);
                    268:                                current->lineno++;
                    269:                                do {
1.22      espie     270:                                        c = read_char();
1.21      espie     271:                                } while (c == ' ' || c == '\t');
                    272:                        } else {
                    273:                                Buf_AddChar(linebuf, '\\');
                    274:                                if (c == '\\') {
                    275:                                        Buf_AddChar(linebuf, '\\');
1.22      espie     276:                                        c = read_char();
1.21      espie     277:                                }
                    278:                                break;
                    279:                        }
1.6       espie     280:                }
                    281:        }
1.2       espie     282: }
                    283:
                    284: char *
1.18      espie     285: Parse_ReadUnparsedLine(Buffer linebuf, const char *type)
1.6       espie     286: {
1.21      espie     287:        int c;
1.6       espie     288:
1.21      espie     289:        Buf_Reset(linebuf);
1.22      espie     290:        c = read_char();
1.21      espie     291:        if (c == EOF) {
                    292:                Parse_Error(PARSE_FATAL, "Unclosed %s", type);
                    293:                return NULL;
                    294:        }
1.6       espie     295:
1.21      espie     296:        /* Handle '\' at beginning of line, since \\n needs special treatment */
                    297:        while (c == '\\') {
1.22      espie     298:                c = read_char();
1.21      espie     299:                if (c == '\n') {
                    300:                        current->lineno++;
                    301:                        do {
1.22      espie     302:                                c = read_char();
1.21      espie     303:                        } while (c == ' ' || c == '\t');
                    304:                } else {
                    305:                        Buf_AddChar(linebuf, '\\');
                    306:                        if (c == '\\') {
                    307:                                Buf_AddChar(linebuf, '\\');
1.22      espie     308:                                c = read_char();
1.21      espie     309:                        }
                    310:                        break;
                    311:                }
1.2       espie     312:        }
1.22      espie     313:        read_logical_line(linebuf, c);
1.6       espie     314:
1.21      espie     315:        return Buf_Retrieve(linebuf);
1.6       espie     316: }
                    317:
1.9       espie     318: /* This is a fairly complex function, but without it, we could not skip
                    319:  * blocks of comments without reading them. */
1.6       espie     320: static int
1.22      espie     321: skip_empty_lines_and_read_char(Buffer linebuf)
1.6       espie     322: {
1.21      espie     323:        int c;          /* the current character */
1.2       espie     324:
1.21      espie     325:        for (;;) {
                    326:                Buf_Reset(linebuf);
1.22      espie     327:                c = read_char();
1.21      espie     328:                /* Strip leading spaces, fold on '\n' */
                    329:                if (c == ' ') {
                    330:                        do {
1.22      espie     331:                                c = read_char();
1.21      espie     332:                        } while (c == ' ' || c == '\t');
                    333:                        while (c == '\\') {
1.22      espie     334:                                c = read_char();
1.21      espie     335:                                if (c == '\n') {
                    336:                                        current->lineno++;
                    337:                                        do {
1.22      espie     338:                                                c = read_char();
1.21      espie     339:                                        } while (c == ' ' || c == '\t');
                    340:                                } else {
                    341:                                        Buf_AddChar(linebuf, '\\');
                    342:                                        if (c == '\\') {
                    343:                                                Buf_AddChar(linebuf, '\\');
1.22      espie     344:                                                c = read_char();
1.21      espie     345:                                        }
                    346:                                        if (c == EOF)
                    347:                                                return '\n';
                    348:                                        else
                    349:                                                return c;
                    350:                                }
                    351:                        }
                    352:                        assert(c != '\t');
                    353:                }
                    354:                if (c == '#')
1.22      espie     355:                        c = skip_to_end_of_line();
1.21      espie     356:                /* Almost identical to spaces, except this occurs after
                    357:                 * comments have been taken care of, and we keep the tab
                    358:                 * itself.  */
                    359:                if (c == '\t') {
                    360:                        Buf_AddChar(linebuf, '\t');
                    361:                        do {
1.22      espie     362:                                c = read_char();
1.21      espie     363:                        } while (c == ' ' || c == '\t');
                    364:                        while (c == '\\') {
1.22      espie     365:                                c = read_char();
1.21      espie     366:                                if (c == '\n') {
                    367:                                        current->lineno++;
                    368:                                        do {
1.22      espie     369:                                                c = read_char();
1.21      espie     370:                                        } while (c == ' ' || c == '\t');
                    371:                                } else {
                    372:                                        Buf_AddChar(linebuf, '\\');
                    373:                                        if (c == '\\') {
                    374:                                                Buf_AddChar(linebuf, '\\');
1.22      espie     375:                                                c = read_char();
1.21      espie     376:                                        }
                    377:                                        if (c == EOF)
                    378:                                                return '\n';
                    379:                                        else
                    380:                                                return c;
                    381:                                }
                    382:                        }
1.2       espie     383:                }
1.21      espie     384:                if (c == '\n')
                    385:                        current->lineno++;
                    386:                else
1.6       espie     387:                        return c;
1.2       espie     388:        }
1.6       espie     389: }
1.2       espie     390:
1.9       espie     391: /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
1.18      espie     392:  * the first tab), handles escaped newlines, and skips over uninteresting
1.20      espie     393:  * lines.
1.6       espie     394:  *
1.18      espie     395:  * The line number is incremented, which implies that continuation
                    396:  * lines are numbered with the last line number (we could do better, at a
1.9       espie     397:  * price).
1.6       espie     398:  *
1.9       espie     399:  * Trivial comments are also removed, but we can't do more, as
                    400:  * we don't know which lines are shell commands or not.  */
1.6       espie     401: char *
1.18      espie     402: Parse_ReadNormalLine(Buffer linebuf)
1.6       espie     403: {
1.21      espie     404:        int c;          /* the current character */
1.6       espie     405:
1.22      espie     406:        c = skip_empty_lines_and_read_char(linebuf);
1.6       espie     407:
1.21      espie     408:        if (c == EOF)
                    409:                return NULL;
                    410:        else {
1.22      espie     411:                read_logical_line(linebuf, c);
1.21      espie     412:                Buf_KillTrailingSpaces(linebuf);
                    413:                return Buf_Retrieve(linebuf);
                    414:        }
1.1       espie     415: }
                    416:
                    417: unsigned long
1.18      espie     418: Parse_Getlineno(void)
1.1       espie     419: {
1.21      espie     420:        return current ? current->lineno : 0;
1.1       espie     421: }
                    422:
                    423: const char *
1.18      espie     424: Parse_Getfilename(void)
1.1       espie     425: {
1.21      espie     426:        return current ? current->fname : NULL;
1.1       espie     427: }
                    428:
                    429: #ifdef CLEANUP
                    430: void
1.18      espie     431: LowParse_Init(void)
1.1       espie     432: {
1.21      espie     433:        Static_Lst_Init(&input_stack);
                    434:        current = NULL;
1.1       espie     435: }
                    436:
                    437: void
1.18      espie     438: LowParse_End(void)
1.1       espie     439: {
1.21      espie     440:        Lst_Destroy(&input_stack, NOFREE);      /* Should be empty now */
1.17      espie     441: #if 0
1.21      espie     442:        Lst_Destroy(&fileNames, (SimpleProc)free);
1.9       espie     443: #endif
1.1       espie     444: }
                    445: #endif
1.6       espie     446:
1.1       espie     447:
                    448: void
1.18      espie     449: Parse_ReportErrors(void)
1.1       espie     450: {
1.21      espie     451:        if (fatal_errors) {
1.9       espie     452: #ifdef CLEANUP
1.21      espie     453:                while (Parse_NextFile())
                    454:                        ;
1.9       espie     455: #endif
1.23      espie     456:                fprintf(stderr,
1.21      espie     457:                    "Fatal errors encountered -- cannot continue\n");
                    458:                exit(1);
                    459:        } else
                    460:                assert(current == NULL);
1.1       espie     461: }