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