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