Annotation of src/usr.bin/make/lowparse.c, Revision 1.20
1.6 espie 1: /* $OpenPackages$ */
1.20 ! espie 2: /* $OpenBSD: lowparse.c,v 1.19 2007/05/17 03:37:31 ray Exp $ */
1.1 espie 3:
4: /* low-level parsing functions. */
5:
6: /*
7: * Copyright (c) 1999,2000 Marc Espie.
8: *
9: * Extensive code changes for the OpenBSD project.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
21: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
24: * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31: */
32:
1.9 espie 33: #include <assert.h>
34: #include <stddef.h>
1.1 espie 35: #include <stdio.h>
1.11 espie 36: #include <stdlib.h>
1.10 espie 37: #include <string.h>
1.9 espie 38: #include <unistd.h>
39: #include "config.h"
40: #include "defines.h"
1.1 espie 41: #include "buf.h"
42: #include "lowparse.h"
1.9 espie 43: #include "error.h"
44: #include "lst.h"
45: #include "memory.h"
46:
47: /* XXX check whether we can free filenames at the end, for a proper
48: * definition of `end'. */
1.1 espie 49:
1.9 espie 50: #if 0
1.1 espie 51: static LIST fileNames; /* file names to free at end */
52: #endif
53:
1.9 espie 54: /* Input stream structure, file or string. */
55: typedef struct {
56: const char *fname; /* Name of file */
57: unsigned long lineno; /* Line number */
58: FILE *F; /* Open stream, or NULL if pure string. */
59: char *str; /* Input string, if F == NULL. */
60:
61: /* Line buffer. */
62: char *ptr; /* Where we are. */
63: char *end; /* Don't overdo it. */
1.2 espie 64: } IFile;
65:
1.9 espie 66: static IFile *current; /* IFile being parsed. */
1.2 espie 67:
1.20 ! espie 68: static LIST input_stack; /* Stack of IFiles waiting to be parsed
1.9 espie 69: * (includes and loop reparses) */
1.1 espie 70:
1.20 ! espie 71: /* IFile ctors.
1.9 espie 72: *
73: * obj = new_ifile(filename, filehandle);
74: * Create input object from filename, filehandle. */
75: static IFile *new_ifile(const char *, FILE *);
76: /* obj = new_istring(str, filename, lineno);
77: * Create input object from str, filename, lineno. */
78: static IFile *new_istring(char *, const char *, unsigned long);
79: /* free_ifile(obj);
80: * Discard consumed input object, closing streams, freeing memory. */
1.6 espie 81: static void free_ifile(IFile *);
1.9 espie 82:
83:
1.20 ! espie 84: /* Handling basic character reading.
1.9 espie 85: * c = ParseReadc();
86: * New character c from current input stream, or EOF at end of stream. */
87: #define ParseReadc() current->ptr < current->end ? *current->ptr++ : newline()
88: /* len = newline();
89: * Guts for ParseReadc. Grabs a new line off fgetln when we have
90: * consumed the current line and returns its length. Or EOF at end of
91: * stream. */
1.6 espie 92: static int newline(void);
1.9 espie 93: /* c = skiptoendofline();
94: * Skips to the end of the current line, returns either '\n' or EOF. */
1.6 espie 95: static int skiptoendofline(void);
1.1 espie 96:
97:
1.9 espie 98: /* Helper functions to handle basic parsing. */
99: /* ParseFoldLF(buffer, firstchar);
100: * Grabs logical line into buffer, the first character has already been
101: * read into firstchar. */
102: static void ParseFoldLF(Buffer, int);
1.1 espie 103:
1.9 espie 104: /* firstchar = ParseSkipEmptyLines(buffer);
105: * Scans lines, skipping empty lines. May put some characters into
106: * buffer, returns the first character useful to continue parsing
107: * (e.g., not a backslash or a space. */
108: static int ParseSkipEmptyLines(Buffer);
1.1 espie 109:
110: static IFile *
1.18 espie 111: new_ifile(const char *name, FILE *stream)
1.1 espie 112: {
113: IFile *ifile;
1.9 espie 114: #if 0
1.1 espie 115: Lst_AtEnd(&fileNames, name);
116: #endif
117:
118: ifile = emalloc(sizeof(*ifile));
119: ifile->fname = name;
1.8 espie 120: ifile->str = NULL;
1.9 espie 121: /* Naturally enough, we start reading at line 0. */
1.1 espie 122: ifile->lineno = 0;
123: ifile->F = stream;
124: ifile->ptr = ifile->end = NULL;
125: return ifile;
126: }
127:
128: static void
1.18 espie 129: free_ifile(IFile *ifile)
1.1 espie 130: {
1.7 espie 131: if (ifile->F && fileno(ifile->F) != STDIN_FILENO)
1.6 espie 132: (void)fclose(ifile->F);
1.8 espie 133: free(ifile->str);
1.1 espie 134: /* Note we can't free the file names yet, as they are embedded in GN for
135: * error reports. */
136: free(ifile);
137: }
138:
139: static IFile *
1.18 espie 140: new_istring(char *str, const char *name, unsigned long lineno)
1.1 espie 141: {
142: IFile *ifile;
143:
144: ifile = emalloc(sizeof(*ifile));
1.6 espie 145: /* No malloc, name is always taken from an already existing ifile */
1.1 espie 146: ifile->fname = name;
147: ifile->F = NULL;
1.9 espie 148: /* Strings are used in for loops, so we need to reset the line counter
149: * to an appropriate value. */
1.1 espie 150: ifile->lineno = lineno;
151: ifile->ptr = ifile->str = str;
152: ifile->end = str + strlen(str);
153: return ifile;
154: }
155:
156:
157: void
1.18 espie 158: Parse_FromString(char *str, unsigned long lineno)
1.1 espie 159: {
160: if (DEBUG(FOR))
161: (void)fprintf(stderr, "%s\n----\n", str);
162:
163: if (current != NULL)
1.9 espie 164: Lst_Push(&input_stack, current);
1.1 espie 165: current = new_istring(str, current->fname, lineno);
166: }
167:
168:
1.6 espie 169: void
1.18 espie 170: Parse_FromFile(const char *name, FILE *stream)
1.1 espie 171: {
172: if (current != NULL)
1.9 espie 173: Lst_Push(&input_stack, current);
1.1 espie 174: current = new_ifile(name, stream);
175: }
176:
1.9 espie 177: bool
1.18 espie 178: Parse_NextFile(void)
1.1 espie 179: {
1.9 espie 180: if (current != NULL)
181: free_ifile(current);
182: current = (IFile *)Lst_Pop(&input_stack);
183: return current != NULL;
1.1 espie 184: }
185:
1.5 espie 186: static int
1.18 espie 187: newline(void)
1.1 espie 188: {
189: size_t len;
190:
191: if (current->F) {
192: current->ptr = fgetln(current->F, &len);
193: if (current->ptr) {
194: current->end = current->ptr + len;
195: return *current->ptr++;
196: } else {
197: current->end = NULL;
198: }
199: }
200: return EOF;
201: }
202:
1.6 espie 203: static int
1.18 espie 204: skiptoendofline(void)
1.1 espie 205: {
1.6 espie 206: if (current->F) {
207: if (current->end - current->ptr > 1)
208: current->ptr = current->end - 1;
209: if (*current->ptr == '\n')
210: return *current->ptr++;
211: return EOF;
212: } else {
213: int c;
214:
215: do {
216: c = ParseReadc();
217: } while (c != '\n' && c != EOF);
218: return c;
219: }
1.2 espie 220: }
221:
1.6 espie 222:
1.2 espie 223: char *
1.18 espie 224: Parse_ReadNextConditionalLine(Buffer linebuf)
1.2 espie 225: {
1.6 espie 226: int c;
227:
228: /* If first char isn't dot, skip to end of line, handling \ */
229: while ((c = ParseReadc()) != '.') {
230: for (;c != '\n'; c = ParseReadc()) {
231: if (c == '\\') {
232: c = ParseReadc();
233: if (c == '\n')
234: current->lineno++;
235: }
236: if (c == EOF) {
237: Parse_Error(PARSE_FATAL, "Unclosed conditional");
238: return NULL;
239: }
240: }
241: current->lineno++;
242: }
1.2 espie 243:
1.6 espie 244: /* This is the line we need to copy */
1.9 espie 245: return Parse_ReadUnparsedLine(linebuf, "conditional");
1.6 espie 246: }
1.2 espie 247:
1.6 espie 248: static void
1.18 espie 249: ParseFoldLF(Buffer linebuf, int c)
1.6 espie 250: {
1.2 espie 251: for (;;) {
1.6 espie 252: if (c == '\n') {
253: current->lineno++;
1.2 espie 254: break;
1.6 espie 255: }
256: if (c == EOF)
1.2 espie 257: break;
1.6 espie 258: Buf_AddChar(linebuf, c);
259: c = ParseReadc();
260: while (c == '\\') {
261: c = ParseReadc();
262: if (c == '\n') {
263: Buf_AddSpace(linebuf);
264: current->lineno++;
265: do {
266: c = ParseReadc();
267: } while (c == ' ' || c == '\t');
268: } else {
269: Buf_AddChar(linebuf, '\\');
270: if (c == '\\') {
271: Buf_AddChar(linebuf, '\\');
272: c = ParseReadc();
273: }
274: break;
275: }
276: }
1.2 espie 277: }
278: }
279:
280: char *
1.18 espie 281: Parse_ReadUnparsedLine(Buffer linebuf, const char *type)
1.6 espie 282: {
283: int c;
284:
285: Buf_Reset(linebuf);
286: c = ParseReadc();
287: if (c == EOF) {
288: Parse_Error(PARSE_FATAL, "Unclosed %s", type);
289: return NULL;
290: }
291:
292: /* Handle '\' at beginning of line, since \\n needs special treatment */
293: while (c == '\\') {
1.2 espie 294: c = ParseReadc();
1.6 espie 295: if (c == '\n') {
1.2 espie 296: current->lineno++;
1.6 espie 297: do {
298: c = ParseReadc();
299: } while (c == ' ' || c == '\t');
1.2 espie 300: } else {
1.6 espie 301: Buf_AddChar(linebuf, '\\');
302: if (c == '\\') {
303: Buf_AddChar(linebuf, '\\');
304: c = ParseReadc();
305: }
1.2 espie 306: break;
307: }
308: }
1.6 espie 309: ParseFoldLF(linebuf, c);
310:
311: return Buf_Retrieve(linebuf);
312: }
313:
1.9 espie 314: /* This is a fairly complex function, but without it, we could not skip
315: * blocks of comments without reading them. */
1.6 espie 316: static int
1.18 espie 317: ParseSkipEmptyLines(Buffer linebuf)
1.6 espie 318: {
319: int c; /* the current character */
1.2 espie 320:
1.6 espie 321: for (;;) {
322: Buf_Reset(linebuf);
323: c = ParseReadc();
324: /* Strip leading spaces, fold on '\n' */
325: if (c == ' ') {
326: do {
327: c = ParseReadc();
328: } while (c == ' ' || c == '\t');
329: while (c == '\\') {
330: c = ParseReadc();
331: if (c == '\n') {
332: current->lineno++;
333: do {
334: c = ParseReadc();
335: } while (c == ' ' || c == '\t');
1.2 espie 336: } else {
1.6 espie 337: Buf_AddChar(linebuf, '\\');
338: if (c == '\\') {
339: Buf_AddChar(linebuf, '\\');
340: c = ParseReadc();
1.2 espie 341: }
1.6 espie 342: if (c == EOF)
343: return '\n';
344: else
345: return c;
1.2 espie 346: }
347: }
1.6 espie 348: assert(c != '\t');
1.2 espie 349: }
1.6 espie 350: if (c == '#')
351: c = skiptoendofline();
352: /* Almost identical to spaces, except this occurs after comments
353: * have been taken care of, and we keep the tab itself. */
354: if (c == '\t') {
355: Buf_AddChar(linebuf, '\t');
356: do {
357: c = ParseReadc();
358: } while (c == ' ' || c == '\t');
359: while (c == '\\') {
360: c = ParseReadc();
361: if (c == '\n') {
362: current->lineno++;
1.2 espie 363: do {
1.6 espie 364: c = ParseReadc();
365: } while (c == ' ' || c == '\t');
366: } else {
367: Buf_AddChar(linebuf, '\\');
368: if (c == '\\') {
369: Buf_AddChar(linebuf, '\\');
370: c = ParseReadc();
371: }
372: if (c == EOF)
373: return '\n';
374: else
375: return c;
1.2 espie 376: }
377: }
378: }
1.6 espie 379: if (c == '\n')
380: current->lineno++;
381: else
382: return c;
383: }
384: }
1.2 espie 385:
1.9 espie 386: /* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
1.18 espie 387: * the first tab), handles escaped newlines, and skips over uninteresting
1.20 ! espie 388: * lines.
1.6 espie 389: *
1.18 espie 390: * The line number is incremented, which implies that continuation
391: * lines are numbered with the last line number (we could do better, at a
1.9 espie 392: * price).
1.6 espie 393: *
1.9 espie 394: * Trivial comments are also removed, but we can't do more, as
395: * we don't know which lines are shell commands or not. */
1.6 espie 396: char *
1.18 espie 397: Parse_ReadNormalLine(Buffer linebuf)
1.6 espie 398: {
399: int c; /* the current character */
400:
401: c = ParseSkipEmptyLines(linebuf);
402:
403: if (c == EOF)
404: return NULL;
405: else {
406: ParseFoldLF(linebuf, c);
407: Buf_KillTrailingSpaces(linebuf);
408: return Buf_Retrieve(linebuf);
1.2 espie 409: }
1.1 espie 410: }
411:
412: unsigned long
1.18 espie 413: Parse_Getlineno(void)
1.1 espie 414: {
1.13 espie 415: return current ? current->lineno : 0;
1.1 espie 416: }
417:
418: const char *
1.18 espie 419: Parse_Getfilename(void)
1.1 espie 420: {
1.13 espie 421: return current ? current->fname : NULL;
1.1 espie 422: }
423:
424: #ifdef CLEANUP
425: void
1.18 espie 426: LowParse_Init(void)
1.1 espie 427: {
1.16 espie 428: Static_Lst_Init(&input_stack);
1.1 espie 429: current = NULL;
430: }
431:
432: void
1.18 espie 433: LowParse_End(void)
1.1 espie 434: {
1.9 espie 435: Lst_Destroy(&input_stack, NOFREE); /* Should be empty now */
1.17 espie 436: #if 0
1.9 espie 437: Lst_Destroy(&fileNames, (SimpleProc)free);
438: #endif
1.1 espie 439: }
440: #endif
1.6 espie 441:
1.1 espie 442:
443: void
1.18 espie 444: Parse_ReportErrors(void)
1.1 espie 445: {
1.9 espie 446: if (fatal_errors) {
447: #ifdef CLEANUP
448: while (Parse_NextFile())
449: ;
450: #endif
1.1 espie 451: fprintf(stderr, "Fatal errors encountered -- cannot continue\n");
452: exit(1);
1.9 espie 453: } else
454: assert(current == NULL);
1.1 espie 455: }