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