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

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