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