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