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

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