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: }