Annotation of src/usr.bin/make/lowparse.c, Revision 1.34
1.34 ! espie 1: /* $OpenBSD: lowparse.c,v 1.33 2014/11/03 12:48:37 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.34 ! espie 168: if (istream->F) {
! 169: if (ferror(istream->F))
! 170: Parse_Error(PARSE_FATAL, "Read error");
! 171: if (fileno(istream->F) != STDIN_FILENO)
! 172: (void)fclose(istream->F);
! 173: }
1.22 espie 174: free(istream->str);
1.33 espie 175: /* Note we can't free the file names, as they are embedded in GN
1.21 espie 176: * for error reports. */
1.22 espie 177: free(istream);
1.1 espie 178: }
179:
1.22 espie 180: static struct input_stream *
1.26 espie 181: new_input_string(char *str, const Location *origin)
1.1 espie 182: {
1.22 espie 183: struct input_stream *istream;
1.1 espie 184:
1.22 espie 185: istream = emalloc(sizeof(*istream));
1.26 espie 186: /* No malloc, name is always taken from an already existing istream
1.33 espie 187: * and strings are used in for loops, so we need to reset the line
188: * counter to an appropriate value. */
1.26 espie 189: istream->origin = *origin;
1.22 espie 190: istream->F = NULL;
191: istream->ptr = istream->str = str;
192: istream->end = str + strlen(str);
193: return istream;
1.1 espie 194: }
195:
196:
197: void
1.18 espie 198: Parse_FromString(char *str, unsigned long lineno)
1.1 espie 199: {
1.26 espie 200: Location origin;
201:
202: origin.fname = current->origin.fname;
203: origin.lineno = lineno;
1.21 espie 204: if (DEBUG(FOR))
205: (void)fprintf(stderr, "%s\n----\n", str);
1.1 espie 206:
1.25 espie 207: Lst_Push(&input_stack, current);
208: assert(current != NULL);
1.26 espie 209: current = new_input_string(str, &origin);
1.1 espie 210: }
211:
212:
1.6 espie 213: void
1.18 espie 214: Parse_FromFile(const char *name, FILE *stream)
1.1 espie 215: {
1.21 espie 216: if (current != NULL)
217: Lst_Push(&input_stack, current);
1.22 espie 218: current = new_input_file(name, stream);
1.1 espie 219: }
220:
1.9 espie 221: bool
1.18 espie 222: Parse_NextFile(void)
1.1 espie 223: {
1.21 espie 224: if (current != NULL)
1.22 espie 225: free_input_stream(current);
226: current = (struct input_stream *)Lst_Pop(&input_stack);
1.21 espie 227: return current != NULL;
1.1 espie 228: }
229:
1.5 espie 230: static int
1.22 espie 231: grab_new_line_and_readchar(void)
1.1 espie 232: {
1.21 espie 233: size_t len;
1.1 espie 234:
1.21 espie 235: if (current->F) {
236: current->ptr = fgetln(current->F, &len);
237: if (current->ptr) {
238: current->end = current->ptr + len;
239: return *current->ptr++;
240: } else {
241: current->end = NULL;
242: }
1.1 espie 243: }
1.21 espie 244: return EOF;
1.1 espie 245: }
246:
1.6 espie 247: static int
1.22 espie 248: skip_to_end_of_line(void)
1.1 espie 249: {
1.21 espie 250: if (current->F) {
251: if (current->end - current->ptr > 1)
252: current->ptr = current->end - 1;
253: if (*current->ptr == '\n')
254: return *current->ptr++;
255: return EOF;
256: } else {
257: int c;
1.6 espie 258:
1.21 espie 259: do {
1.22 espie 260: c = read_char();
1.21 espie 261: } while (c != '\n' && c != EOF);
262: return c;
263: }
1.2 espie 264: }
265:
1.6 espie 266:
1.2 espie 267: char *
1.18 espie 268: Parse_ReadNextConditionalLine(Buffer linebuf)
1.2 espie 269: {
1.21 espie 270: int c;
1.6 espie 271:
272: /* If first char isn't dot, skip to end of line, handling \ */
1.22 espie 273: while ((c = read_char()) != '.') {
274: for (;c != '\n'; c = read_char()) {
1.21 espie 275: if (c == '\\') {
1.22 espie 276: c = read_char();
1.21 espie 277: if (c == '\n')
1.26 espie 278: current->origin.lineno++;
1.21 espie 279: }
1.31 espie 280: if (c == EOF)
281: /* Unclosed conditional, reported by cond.c */
1.21 espie 282: return NULL;
283: }
1.26 espie 284: current->origin.lineno++;
1.6 espie 285: }
1.2 espie 286:
1.6 espie 287: /* This is the line we need to copy */
1.21 espie 288: return Parse_ReadUnparsedLine(linebuf, "conditional");
1.6 espie 289: }
1.2 espie 290:
1.6 espie 291: static void
1.22 espie 292: read_logical_line(Buffer linebuf, int c)
1.6 espie 293: {
1.21 espie 294: for (;;) {
295: if (c == '\n') {
1.26 espie 296: current->origin.lineno++;
1.21 espie 297: break;
298: }
299: if (c == EOF)
300: break;
301: Buf_AddChar(linebuf, c);
1.22 espie 302: c = read_char();
1.21 espie 303: while (c == '\\') {
1.22 espie 304: c = read_char();
1.21 espie 305: if (c == '\n') {
306: Buf_AddSpace(linebuf);
1.26 espie 307: current->origin.lineno++;
1.21 espie 308: do {
1.22 espie 309: c = read_char();
1.21 espie 310: } while (c == ' ' || c == '\t');
311: } else {
312: Buf_AddChar(linebuf, '\\');
313: if (c == '\\') {
314: Buf_AddChar(linebuf, '\\');
1.22 espie 315: c = read_char();
1.21 espie 316: }
317: break;
318: }
1.6 espie 319: }
320: }
1.2 espie 321: }
322:
323: char *
1.18 espie 324: Parse_ReadUnparsedLine(Buffer linebuf, const char *type)
1.6 espie 325: {
1.21 espie 326: int c;
1.6 espie 327:
1.21 espie 328: Buf_Reset(linebuf);
1.22 espie 329: c = read_char();
1.21 espie 330: if (c == EOF) {
331: Parse_Error(PARSE_FATAL, "Unclosed %s", type);
332: return NULL;
333: }
1.6 espie 334:
1.21 espie 335: /* Handle '\' at beginning of line, since \\n needs special treatment */
336: while (c == '\\') {
1.22 espie 337: c = read_char();
1.21 espie 338: if (c == '\n') {
1.26 espie 339: current->origin.lineno++;
1.21 espie 340: do {
1.22 espie 341: c = read_char();
1.21 espie 342: } while (c == ' ' || c == '\t');
343: } else {
344: Buf_AddChar(linebuf, '\\');
345: if (c == '\\') {
346: Buf_AddChar(linebuf, '\\');
1.22 espie 347: c = read_char();
1.21 espie 348: }
349: break;
350: }
1.2 espie 351: }
1.22 espie 352: read_logical_line(linebuf, c);
1.6 espie 353:
1.21 espie 354: return Buf_Retrieve(linebuf);
1.6 espie 355: }
356:
1.9 espie 357: /* This is a fairly complex function, but without it, we could not skip
358: * blocks of comments without reading them. */
1.6 espie 359: static int
1.22 espie 360: skip_empty_lines_and_read_char(Buffer linebuf)
1.6 espie 361: {
1.21 espie 362: int c; /* the current character */
1.2 espie 363:
1.21 espie 364: for (;;) {
365: Buf_Reset(linebuf);
1.22 espie 366: c = read_char();
1.21 espie 367: /* Strip leading spaces, fold on '\n' */
368: if (c == ' ') {
369: do {
1.22 espie 370: c = read_char();
1.21 espie 371: } while (c == ' ' || c == '\t');
372: while (c == '\\') {
1.22 espie 373: c = read_char();
1.21 espie 374: if (c == '\n') {
1.26 espie 375: current->origin.lineno++;
1.21 espie 376: do {
1.22 espie 377: c = read_char();
1.21 espie 378: } while (c == ' ' || c == '\t');
379: } else {
380: Buf_AddChar(linebuf, '\\');
381: if (c == '\\') {
382: Buf_AddChar(linebuf, '\\');
1.22 espie 383: c = read_char();
1.21 espie 384: }
385: if (c == EOF)
386: return '\n';
387: else
388: return c;
389: }
390: }
391: assert(c != '\t');
392: }
393: if (c == '#')
1.22 espie 394: c = skip_to_end_of_line();
1.21 espie 395: /* Almost identical to spaces, except this occurs after
396: * comments have been taken care of, and we keep the tab
397: * itself. */
398: if (c == '\t') {
399: Buf_AddChar(linebuf, '\t');
400: do {
1.22 espie 401: c = read_char();
1.21 espie 402: } while (c == ' ' || c == '\t');
403: while (c == '\\') {
1.22 espie 404: c = read_char();
1.21 espie 405: if (c == '\n') {
1.26 espie 406: current->origin.lineno++;
1.21 espie 407: do {
1.22 espie 408: c = read_char();
1.21 espie 409: } while (c == ' ' || c == '\t');
410: } else {
411: Buf_AddChar(linebuf, '\\');
412: if (c == '\\') {
413: Buf_AddChar(linebuf, '\\');
1.22 espie 414: c = read_char();
1.21 espie 415: }
416: if (c == EOF)
417: return '\n';
418: else
419: return c;
420: }
421: }
1.2 espie 422: }
1.21 espie 423: if (c == '\n')
1.26 espie 424: current->origin.lineno++;
1.21 espie 425: else
1.6 espie 426: return c;
1.2 espie 427: }
1.6 espie 428: }
1.2 espie 429:
1.9 espie 430: /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
1.18 espie 431: * the first tab), handles escaped newlines, and skips over uninteresting
1.20 espie 432: * lines.
1.6 espie 433: *
1.18 espie 434: * The line number is incremented, which implies that continuation
435: * lines are numbered with the last line number (we could do better, at a
1.9 espie 436: * price).
1.6 espie 437: *
1.9 espie 438: * Trivial comments are also removed, but we can't do more, as
439: * we don't know which lines are shell commands or not. */
1.6 espie 440: char *
1.18 espie 441: Parse_ReadNormalLine(Buffer linebuf)
1.6 espie 442: {
1.21 espie 443: int c; /* the current character */
1.6 espie 444:
1.22 espie 445: c = skip_empty_lines_and_read_char(linebuf);
1.6 espie 446:
1.21 espie 447: if (c == EOF)
448: return NULL;
449: else {
1.22 espie 450: read_logical_line(linebuf, c);
1.21 espie 451: return Buf_Retrieve(linebuf);
452: }
1.1 espie 453: }
454:
455: unsigned long
1.18 espie 456: Parse_Getlineno(void)
1.1 espie 457: {
1.26 espie 458: return current ? current->origin.lineno : 0;
1.1 espie 459: }
460:
461: const char *
1.18 espie 462: Parse_Getfilename(void)
1.1 espie 463: {
1.26 espie 464: return current ? current->origin.fname : NULL;
465: }
466:
467: void
1.28 espie 468: Parse_SetLocation(Location *origin)
469: {
470: post_parse = origin;
471: }
472:
473: void
1.26 espie 474: Parse_FillLocation(Location *origin)
475: {
1.28 espie 476: if (post_parse) {
477: *origin = *post_parse;
478: } else {
479: origin->lineno = Parse_Getlineno();
480: origin->fname = Parse_Getfilename();
481: }
1.1 espie 482: }
483:
484: void
1.18 espie 485: Parse_ReportErrors(void)
1.1 espie 486: {
1.31 espie 487: if (fatal_errors)
1.21 espie 488: exit(1);
1.31 espie 489: else
1.21 espie 490: assert(current == NULL);
1.1 espie 491: }