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