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