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

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