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