Annotation of src/usr.bin/make/lowparse.c, Revision 1.26
1.26 ! espie 1: /* $OpenBSD: lowparse.c,v 1.25 2010/12/26 13:09:22 espie Exp $ */
1.1 espie 2:
3: /* low-level parsing functions. */
4:
5: /*
6: * Copyright (c) 1999,2000 Marc Espie.
7: *
8: * Extensive code changes for the OpenBSD project.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
20: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
23: * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30: */
31:
1.9 espie 32: #include <assert.h>
33: #include <stddef.h>
1.1 espie 34: #include <stdio.h>
1.11 espie 35: #include <stdlib.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"
1.26 ! espie 45: #include "location.h"
! 46:
1.9 espie 47:
48: /* XXX check whether we can free filenames at the end, for a proper
49: * definition of `end'. */
1.1 espie 50:
1.9 espie 51: #if 0
1.1 espie 52: static LIST fileNames; /* file names to free at end */
53: #endif
54:
1.22 espie 55: /* Input stream structure: file or string.
56: * Files have str == NULL, F != NULL.
57: * Strings have F == NULL, str != NULL.
58: */
59: struct input_stream {
1.26 ! espie 60: Location origin; /* Name of file and line number */
1.21 espie 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 *);
1.26 ! espie 79: /* obj = new_input_string(str, origin);
! 80: * Create input stream from str, origin. */
! 81: static struct input_stream *new_input_string(char *, const Location *);
1.22 espie 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));
1.26 ! espie 123: istream->origin.fname = name;
1.22 espie 124: istream->str = NULL;
1.21 espie 125: /* Naturally enough, we start reading at line 0. */
1.26 ! espie 126: istream->origin.lineno = 0;
1.22 espie 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 *
1.26 ! espie 144: new_input_string(char *str, const Location *origin)
1.1 espie 145: {
1.22 espie 146: struct input_stream *istream;
1.1 espie 147:
1.22 espie 148: istream = emalloc(sizeof(*istream));
1.26 ! espie 149: /* No malloc, name is always taken from an already existing istream
! 150: * and strings are used in for loops, so we need to reset the line counter
! 151: * to an appropriate value. */
! 152: istream->origin = *origin;
1.22 espie 153: istream->F = NULL;
154: istream->ptr = istream->str = str;
155: istream->end = str + strlen(str);
156: return istream;
1.1 espie 157: }
158:
159:
160: void
1.18 espie 161: Parse_FromString(char *str, unsigned long lineno)
1.1 espie 162: {
1.26 ! espie 163: Location origin;
! 164:
! 165: origin.fname = current->origin.fname;
! 166: origin.lineno = lineno;
1.21 espie 167: if (DEBUG(FOR))
168: (void)fprintf(stderr, "%s\n----\n", str);
1.1 espie 169:
1.25 espie 170: Lst_Push(&input_stack, current);
171: assert(current != NULL);
1.26 ! espie 172: current = new_input_string(str, &origin);
1.1 espie 173: }
174:
175:
1.6 espie 176: void
1.18 espie 177: Parse_FromFile(const char *name, FILE *stream)
1.1 espie 178: {
1.21 espie 179: if (current != NULL)
180: Lst_Push(&input_stack, current);
1.22 espie 181: current = new_input_file(name, stream);
1.1 espie 182: }
183:
1.9 espie 184: bool
1.18 espie 185: Parse_NextFile(void)
1.1 espie 186: {
1.21 espie 187: if (current != NULL)
1.22 espie 188: free_input_stream(current);
189: current = (struct input_stream *)Lst_Pop(&input_stack);
1.21 espie 190: return current != NULL;
1.1 espie 191: }
192:
1.5 espie 193: static int
1.22 espie 194: grab_new_line_and_readchar(void)
1.1 espie 195: {
1.21 espie 196: size_t len;
1.1 espie 197:
1.21 espie 198: if (current->F) {
199: current->ptr = fgetln(current->F, &len);
200: if (current->ptr) {
201: current->end = current->ptr + len;
202: return *current->ptr++;
203: } else {
204: current->end = NULL;
205: }
1.1 espie 206: }
1.21 espie 207: return EOF;
1.1 espie 208: }
209:
1.6 espie 210: static int
1.22 espie 211: skip_to_end_of_line(void)
1.1 espie 212: {
1.21 espie 213: if (current->F) {
214: if (current->end - current->ptr > 1)
215: current->ptr = current->end - 1;
216: if (*current->ptr == '\n')
217: return *current->ptr++;
218: return EOF;
219: } else {
220: int c;
1.6 espie 221:
1.21 espie 222: do {
1.22 espie 223: c = read_char();
1.21 espie 224: } while (c != '\n' && c != EOF);
225: return c;
226: }
1.2 espie 227: }
228:
1.6 espie 229:
1.2 espie 230: char *
1.18 espie 231: Parse_ReadNextConditionalLine(Buffer linebuf)
1.2 espie 232: {
1.21 espie 233: int c;
1.6 espie 234:
235: /* If first char isn't dot, skip to end of line, handling \ */
1.22 espie 236: while ((c = read_char()) != '.') {
237: for (;c != '\n'; c = read_char()) {
1.21 espie 238: if (c == '\\') {
1.22 espie 239: c = read_char();
1.21 espie 240: if (c == '\n')
1.26 ! espie 241: current->origin.lineno++;
1.21 espie 242: }
243: if (c == EOF) {
1.23 espie 244: Parse_Error(PARSE_FATAL,
1.21 espie 245: "Unclosed conditional");
246: return NULL;
247: }
248: }
1.26 ! espie 249: current->origin.lineno++;
1.6 espie 250: }
1.2 espie 251:
1.6 espie 252: /* This is the line we need to copy */
1.21 espie 253: return Parse_ReadUnparsedLine(linebuf, "conditional");
1.6 espie 254: }
1.2 espie 255:
1.6 espie 256: static void
1.22 espie 257: read_logical_line(Buffer linebuf, int c)
1.6 espie 258: {
1.21 espie 259: for (;;) {
260: if (c == '\n') {
1.26 ! espie 261: current->origin.lineno++;
1.21 espie 262: break;
263: }
264: if (c == EOF)
265: break;
266: Buf_AddChar(linebuf, c);
1.22 espie 267: c = read_char();
1.21 espie 268: while (c == '\\') {
1.22 espie 269: c = read_char();
1.21 espie 270: if (c == '\n') {
271: Buf_AddSpace(linebuf);
1.26 ! espie 272: current->origin.lineno++;
1.21 espie 273: do {
1.22 espie 274: c = read_char();
1.21 espie 275: } while (c == ' ' || c == '\t');
276: } else {
277: Buf_AddChar(linebuf, '\\');
278: if (c == '\\') {
279: Buf_AddChar(linebuf, '\\');
1.22 espie 280: c = read_char();
1.21 espie 281: }
282: break;
283: }
1.6 espie 284: }
285: }
1.2 espie 286: }
287:
288: char *
1.18 espie 289: Parse_ReadUnparsedLine(Buffer linebuf, const char *type)
1.6 espie 290: {
1.21 espie 291: int c;
1.6 espie 292:
1.21 espie 293: Buf_Reset(linebuf);
1.22 espie 294: c = read_char();
1.21 espie 295: if (c == EOF) {
296: Parse_Error(PARSE_FATAL, "Unclosed %s", type);
297: return NULL;
298: }
1.6 espie 299:
1.21 espie 300: /* Handle '\' at beginning of line, since \\n needs special treatment */
301: while (c == '\\') {
1.22 espie 302: c = read_char();
1.21 espie 303: if (c == '\n') {
1.26 ! espie 304: current->origin.lineno++;
1.21 espie 305: do {
1.22 espie 306: c = read_char();
1.21 espie 307: } while (c == ' ' || c == '\t');
308: } else {
309: Buf_AddChar(linebuf, '\\');
310: if (c == '\\') {
311: Buf_AddChar(linebuf, '\\');
1.22 espie 312: c = read_char();
1.21 espie 313: }
314: break;
315: }
1.2 espie 316: }
1.22 espie 317: read_logical_line(linebuf, c);
1.6 espie 318:
1.21 espie 319: return Buf_Retrieve(linebuf);
1.6 espie 320: }
321:
1.9 espie 322: /* This is a fairly complex function, but without it, we could not skip
323: * blocks of comments without reading them. */
1.6 espie 324: static int
1.22 espie 325: skip_empty_lines_and_read_char(Buffer linebuf)
1.6 espie 326: {
1.21 espie 327: int c; /* the current character */
1.2 espie 328:
1.21 espie 329: for (;;) {
330: Buf_Reset(linebuf);
1.22 espie 331: c = read_char();
1.21 espie 332: /* Strip leading spaces, fold on '\n' */
333: if (c == ' ') {
334: do {
1.22 espie 335: c = read_char();
1.21 espie 336: } while (c == ' ' || c == '\t');
337: while (c == '\\') {
1.22 espie 338: c = read_char();
1.21 espie 339: if (c == '\n') {
1.26 ! espie 340: current->origin.lineno++;
1.21 espie 341: do {
1.22 espie 342: c = read_char();
1.21 espie 343: } while (c == ' ' || c == '\t');
344: } else {
345: Buf_AddChar(linebuf, '\\');
346: if (c == '\\') {
347: Buf_AddChar(linebuf, '\\');
1.22 espie 348: c = read_char();
1.21 espie 349: }
350: if (c == EOF)
351: return '\n';
352: else
353: return c;
354: }
355: }
356: assert(c != '\t');
357: }
358: if (c == '#')
1.22 espie 359: c = skip_to_end_of_line();
1.21 espie 360: /* Almost identical to spaces, except this occurs after
361: * comments have been taken care of, and we keep the tab
362: * itself. */
363: if (c == '\t') {
364: Buf_AddChar(linebuf, '\t');
365: do {
1.22 espie 366: c = read_char();
1.21 espie 367: } while (c == ' ' || c == '\t');
368: while (c == '\\') {
1.22 espie 369: c = read_char();
1.21 espie 370: if (c == '\n') {
1.26 ! espie 371: current->origin.lineno++;
1.21 espie 372: do {
1.22 espie 373: c = read_char();
1.21 espie 374: } while (c == ' ' || c == '\t');
375: } else {
376: Buf_AddChar(linebuf, '\\');
377: if (c == '\\') {
378: Buf_AddChar(linebuf, '\\');
1.22 espie 379: c = read_char();
1.21 espie 380: }
381: if (c == EOF)
382: return '\n';
383: else
384: return c;
385: }
386: }
1.2 espie 387: }
1.21 espie 388: if (c == '\n')
1.26 ! espie 389: current->origin.lineno++;
1.21 espie 390: else
1.6 espie 391: return c;
1.2 espie 392: }
1.6 espie 393: }
1.2 espie 394:
1.9 espie 395: /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
1.18 espie 396: * the first tab), handles escaped newlines, and skips over uninteresting
1.20 espie 397: * lines.
1.6 espie 398: *
1.18 espie 399: * The line number is incremented, which implies that continuation
400: * lines are numbered with the last line number (we could do better, at a
1.9 espie 401: * price).
1.6 espie 402: *
1.9 espie 403: * Trivial comments are also removed, but we can't do more, as
404: * we don't know which lines are shell commands or not. */
1.6 espie 405: char *
1.18 espie 406: Parse_ReadNormalLine(Buffer linebuf)
1.6 espie 407: {
1.21 espie 408: int c; /* the current character */
1.6 espie 409:
1.22 espie 410: c = skip_empty_lines_and_read_char(linebuf);
1.6 espie 411:
1.21 espie 412: if (c == EOF)
413: return NULL;
414: else {
1.22 espie 415: read_logical_line(linebuf, c);
1.21 espie 416: Buf_KillTrailingSpaces(linebuf);
417: return Buf_Retrieve(linebuf);
418: }
1.1 espie 419: }
420:
421: unsigned long
1.18 espie 422: Parse_Getlineno(void)
1.1 espie 423: {
1.26 ! espie 424: return current ? current->origin.lineno : 0;
1.1 espie 425: }
426:
427: const char *
1.18 espie 428: Parse_Getfilename(void)
1.1 espie 429: {
1.26 ! espie 430: return current ? current->origin.fname : NULL;
! 431: }
! 432:
! 433: void
! 434: Parse_FillLocation(Location *origin)
! 435: {
! 436: origin->lineno = Parse_Getlineno();
! 437: origin->fname = Parse_Getfilename();
1.1 espie 438: }
439:
440: #ifdef CLEANUP
441: void
1.18 espie 442: LowParse_Init(void)
1.1 espie 443: {
1.21 espie 444: Static_Lst_Init(&input_stack);
445: current = NULL;
1.1 espie 446: }
447:
448: void
1.18 espie 449: LowParse_End(void)
1.1 espie 450: {
1.21 espie 451: Lst_Destroy(&input_stack, NOFREE); /* Should be empty now */
1.17 espie 452: #if 0
1.21 espie 453: Lst_Destroy(&fileNames, (SimpleProc)free);
1.9 espie 454: #endif
1.1 espie 455: }
456: #endif
1.6 espie 457:
1.1 espie 458:
459: void
1.18 espie 460: Parse_ReportErrors(void)
1.1 espie 461: {
1.21 espie 462: if (fatal_errors) {
1.9 espie 463: #ifdef CLEANUP
1.21 espie 464: while (Parse_NextFile())
465: ;
1.9 espie 466: #endif
1.23 espie 467: fprintf(stderr,
1.21 espie 468: "Fatal errors encountered -- cannot continue\n");
469: exit(1);
470: } else
471: assert(current == NULL);
1.1 espie 472: }