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

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