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

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