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