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

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