Annotation of src/usr.bin/make/lowparse.c, Revision 1.7
1.6 espie 1: /* $OpenPackages$ */
1.7 ! espie 2: /* $OpenBSD: lowparse.c,v 1.6 2001/05/03 13:41:07 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:
33: #include <stdarg.h>
34: #include <stdio.h>
35: #include <assert.h>
36: #include "make.h"
37: #include "buf.h"
38: #include "lowparse.h"
39:
40: #ifdef CLEANUP
41: static LIST fileNames; /* file names to free at end */
42: #endif
43:
1.2 espie 44: /* Definitions for handling #include specifications */
45: typedef struct IFile_ {
1.6 espie 46: char *fname; /* name of file */
47: unsigned long lineno; /* line number */
48: FILE *F; /* open stream */
49: char *str; /* read from char area */
50: char *ptr; /* where we are */
51: char *end; /* don't overdo it */
1.2 espie 52: } IFile;
53:
54: static IFile *current;
55:
1.6 espie 56: static LIST includes; /* stack of IFiles generated by
1.1 espie 57: * #includes */
58:
1.6 espie 59: static IFile *new_ifile(char *, FILE *);
60: static IFile *new_istring(char *, char *, unsigned long);
61: static void free_ifile(IFile *);
62: static void ParseVErrorInternal(char *, unsigned long, int, char *, va_list);
63: static int newline(void);
64: static int skiptoendofline(void);
65: static void ParseFoldLF(Buffer, int);
66: static int ParseSkipEmptyLines(Buffer);
1.1 espie 67: static int fatals = 0;
68:
69: /*-
1.6 espie 70: * ParseVErrorInternal --
1.1 espie 71: * Error message abort function for parsing. Prints out the context
72: * of the error (line number and file) as well as the message with
73: * two optional arguments.
74: *
75: * Side Effects:
76: * "fatals" is incremented if the level is PARSE_FATAL.
77: */
78: /* VARARGS */
79: static void
80: #ifdef __STDC__
81: ParseVErrorInternal(char *cfname, unsigned long clineno, int type, char *fmt,
82: va_list ap)
83: #else
84: ParseVErrorInternal(va_alist)
85: va_dcl
86: #endif
87: {
88: (void)fprintf(stderr, "\"%s\", line %lu: ", cfname, clineno);
89: if (type == PARSE_WARNING)
90: (void)fprintf(stderr, "warning: ");
91: (void)vfprintf(stderr, fmt, ap);
92: va_end(ap);
93: (void)fprintf(stderr, "\n");
94: if (type == PARSE_FATAL)
95: fatals ++;
96: }
97:
98: /*-
1.6 espie 99: * Parse_Error --
1.1 espie 100: * External interface to ParseVErrorInternal; uses the default filename
101: * Line number.
102: */
103: /* VARARGS */
104: void
105: #ifdef __STDC__
106: Parse_Error(int type, char *fmt, ...)
107: #else
108: Parse_Error(va_alist)
109: va_dcl
110: #endif
111: {
112: va_list ap;
113: #ifdef __STDC__
114: va_start(ap, fmt);
115: #else
116: int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */
117: char *fmt;
118:
119: va_start(ap);
120: type = va_arg(ap, int);
121: fmt = va_arg(ap, char *);
122: #endif
123:
124: ParseVErrorInternal(current->fname, current->lineno, type, fmt, ap);
125: }
126:
127: static IFile *
128: new_ifile(name, stream)
129: char *name;
130: FILE *stream;
131: {
132: IFile *ifile;
133: #ifdef CLEANUP
134: Lst_AtEnd(&fileNames, name);
135: #endif
136:
137: ifile = emalloc(sizeof(*ifile));
138: ifile->fname = name;
139: /* Naturally enough, we start reading at line 0 */
140: ifile->lineno = 0;
141: ifile->F = stream;
142: ifile->ptr = ifile->end = NULL;
143: return ifile;
144: }
145:
146: static void
147: free_ifile(ifile)
148: IFile *ifile;
149: {
1.7 ! espie 150: if (ifile->F && fileno(ifile->F) != STDIN_FILENO)
1.6 espie 151: (void)fclose(ifile->F);
1.1 espie 152: else
1.6 espie 153: free(ifile->str);
1.1 espie 154: /* Note we can't free the file names yet, as they are embedded in GN for
155: * error reports. */
156: free(ifile);
157: }
158:
159: static IFile *
160: new_istring(str, name, lineno)
161: char *str;
162: char *name;
163: unsigned long lineno;
164: {
165: IFile *ifile;
166:
167: ifile = emalloc(sizeof(*ifile));
1.6 espie 168: /* No malloc, name is always taken from an already existing ifile */
1.1 espie 169: ifile->fname = name;
170: ifile->F = NULL;
171: /* Strings are used from for loops... */
172: ifile->lineno = lineno;
173: ifile->ptr = ifile->str = str;
174: ifile->end = str + strlen(str);
175: return ifile;
176: }
177:
178:
179: /*-
180: *---------------------------------------------------------------------
181: * Parse_FromString --
182: * Start Parsing from the given string
183: *
184: * Side Effects:
185: * A structure is added to the includes Lst and readProc, lineno,
186: * fname and curFILE are altered for the new file
187: *---------------------------------------------------------------------
188: */
189: void
190: Parse_FromString(str, lineno)
1.6 espie 191: char *str;
192: unsigned long lineno;
1.1 espie 193: {
194: if (DEBUG(FOR))
195: (void)fprintf(stderr, "%s\n----\n", str);
196:
197: if (current != NULL)
198: Lst_AtFront(&includes, current);
199: current = new_istring(str, current->fname, lineno);
200: }
201:
202:
1.6 espie 203: void
1.1 espie 204: Parse_FromFile(name, stream)
205: char *name;
206: FILE *stream;
207: {
208: if (current != NULL)
1.6 espie 209: Lst_AtFront(&includes, current);
1.1 espie 210: current = new_ifile(name, stream);
211: }
212:
213: /*-
214: *---------------------------------------------------------------------
1.6 espie 215: * Parse_NextFile --
1.1 espie 216: * Called when EOF is reached in the current file. If we were reading
217: * an include file, the includes stack is popped and things set up
218: * to go back to reading the previous file at the previous location.
219: *
220: * Results:
221: * CONTINUE if there's more to do. DONE if not.
222: *
223: * Side Effects:
224: * The old curFILE, is closed. The includes list is shortened.
225: * lineno, curFILE, and fname are changed if CONTINUE is returned.
226: *---------------------------------------------------------------------
227: */
228: Boolean
229: Parse_NextFile()
230: {
231: IFile *next;
232:
233: next = (IFile *)Lst_DeQueue(&includes);
234: if (next != NULL) {
235: if (current != NULL)
236: free_ifile(current);
237: current = next;
1.6 espie 238: return TRUE;
1.1 espie 239: } else
1.6 espie 240: return FALSE;
1.1 espie 241: }
242:
1.5 espie 243: /* guts for ParseReadc. Grab a new line off fgetln when we hit "\n" */
244: static int
1.1 espie 245: newline()
246: {
247: size_t len;
248:
249: if (current->F) {
250: current->ptr = fgetln(current->F, &len);
251: if (current->ptr) {
252: current->end = current->ptr + len;
253: return *current->ptr++;
254: } else {
255: current->end = NULL;
256: }
257: }
258: return EOF;
259: }
260:
1.6 espie 261: #define ParseReadc() current->ptr < current->end ? *current->ptr++ : newline()
262:
263: /* Take advantage of fgetln: we don't have to scan a whole line to skip it.
264: */
265: static int
266: skiptoendofline()
1.1 espie 267: {
1.6 espie 268: if (current->F) {
269: if (current->end - current->ptr > 1)
270: current->ptr = current->end - 1;
271: if (*current->ptr == '\n')
272: return *current->ptr++;
273: return EOF;
274: } else {
275: int c;
276:
277: do {
278: c = ParseReadc();
279: } while (c != '\n' && c != EOF);
280: return c;
281: }
1.2 espie 282: }
283:
1.6 espie 284:
285: /* ParseSkipGetLine():
286: * Return the first logical line that starts with '.'
1.2 espie 287: */
288: char *
1.6 espie 289: ParseSkipGetLine(linebuf)
290: Buffer linebuf;
1.2 espie 291: {
1.6 espie 292: int c;
293:
294: /* If first char isn't dot, skip to end of line, handling \ */
295: while ((c = ParseReadc()) != '.') {
296: for (;c != '\n'; c = ParseReadc()) {
297: if (c == '\\') {
298: c = ParseReadc();
299: if (c == '\n')
300: current->lineno++;
301: }
302: if (c == EOF) {
303: Parse_Error(PARSE_FATAL, "Unclosed conditional");
304: return NULL;
305: }
306: }
307: current->lineno++;
308: }
1.2 espie 309:
1.6 espie 310: /* This is the line we need to copy */
311: return ParseGetLine(linebuf, "conditional");
312: }
1.2 espie 313:
1.6 espie 314: /* Grab logical line into linebuf.
315: * The first character is already in c. */
316: static void
317: ParseFoldLF(linebuf, c)
318: Buffer linebuf;
319: int c;
320: {
1.2 espie 321: for (;;) {
1.6 espie 322: if (c == '\n') {
323: current->lineno++;
1.2 espie 324: break;
1.6 espie 325: }
326: if (c == EOF)
1.2 espie 327: break;
1.6 espie 328: Buf_AddChar(linebuf, c);
329: c = ParseReadc();
330: while (c == '\\') {
331: c = ParseReadc();
332: if (c == '\n') {
333: Buf_AddSpace(linebuf);
334: current->lineno++;
335: do {
336: c = ParseReadc();
337: } while (c == ' ' || c == '\t');
338: } else {
339: Buf_AddChar(linebuf, '\\');
340: if (c == '\\') {
341: Buf_AddChar(linebuf, '\\');
342: c = ParseReadc();
343: }
344: break;
345: }
346: }
1.2 espie 347: }
348: }
349:
1.6 espie 350: /* ParseGetLine:
351: * Simply get line, no parsing beyond \
1.2 espie 352: */
353: char *
1.6 espie 354: ParseGetLine(linebuf, type)
355: Buffer linebuf;
356: const char *type;
357: {
358: int c;
359:
360: Buf_Reset(linebuf);
361: c = ParseReadc();
362: if (c == EOF) {
363: Parse_Error(PARSE_FATAL, "Unclosed %s", type);
364: return NULL;
365: }
366:
367: /* Handle '\' at beginning of line, since \\n needs special treatment */
368: while (c == '\\') {
1.2 espie 369: c = ParseReadc();
1.6 espie 370: if (c == '\n') {
1.2 espie 371: current->lineno++;
1.6 espie 372: do {
373: c = ParseReadc();
374: } while (c == ' ' || c == '\t');
1.2 espie 375: } else {
1.6 espie 376: Buf_AddChar(linebuf, '\\');
377: if (c == '\\') {
378: Buf_AddChar(linebuf, '\\');
379: c = ParseReadc();
380: }
1.2 espie 381: break;
382: }
383: }
1.6 espie 384: ParseFoldLF(linebuf, c);
385:
386: return Buf_Retrieve(linebuf);
387: }
388:
389: /* Skip all empty lines, and return the first `useful' character.
390: * (This does skip blocks of comments at high speed, which justifies
391: * the complexity of the function.)
392: */
393: static int
394: ParseSkipEmptyLines(linebuf)
395: Buffer linebuf;
396: {
397: int c; /* the current character */
1.2 espie 398:
1.6 espie 399: for (;;) {
400: Buf_Reset(linebuf);
401: c = ParseReadc();
402: /* Strip leading spaces, fold on '\n' */
403: if (c == ' ') {
404: do {
405: c = ParseReadc();
406: } while (c == ' ' || c == '\t');
407: while (c == '\\') {
408: c = ParseReadc();
409: if (c == '\n') {
410: current->lineno++;
411: do {
412: c = ParseReadc();
413: } while (c == ' ' || c == '\t');
1.2 espie 414: } else {
1.6 espie 415: Buf_AddChar(linebuf, '\\');
416: if (c == '\\') {
417: Buf_AddChar(linebuf, '\\');
418: c = ParseReadc();
1.2 espie 419: }
1.6 espie 420: if (c == EOF)
421: return '\n';
422: else
423: return c;
1.2 espie 424: }
425: }
1.6 espie 426: assert(c != '\t');
1.2 espie 427: }
1.6 espie 428: if (c == '#')
429: c = skiptoendofline();
430: /* Almost identical to spaces, except this occurs after comments
431: * have been taken care of, and we keep the tab itself. */
432: if (c == '\t') {
433: Buf_AddChar(linebuf, '\t');
434: do {
435: c = ParseReadc();
436: } while (c == ' ' || c == '\t');
437: while (c == '\\') {
438: c = ParseReadc();
439: if (c == '\n') {
440: current->lineno++;
1.2 espie 441: do {
1.6 espie 442: c = ParseReadc();
443: } while (c == ' ' || c == '\t');
444: } else {
445: Buf_AddChar(linebuf, '\\');
446: if (c == '\\') {
447: Buf_AddChar(linebuf, '\\');
448: c = ParseReadc();
449: }
450: if (c == EOF)
451: return '\n';
452: else
453: return c;
454: return c;
1.2 espie 455: }
456: }
457: }
1.6 espie 458: if (c == '\n')
459: current->lineno++;
460: else
461: return c;
462: }
463: }
1.2 espie 464:
1.6 espie 465: /*-
466: *---------------------------------------------------------------------
467: * ParseReadLine --
468: * Read an entire line from the input file.
469: *
470: * Results:
471: * A line without a new line, or NULL at EOF.
472: *
473: * Notes:
474: * Removes beginning and trailing blanks (but keeps first tab).
475: * Updates line numbers, handles escaped newlines, and skip over
476: * uninteresting lines.
477: * All but trivial comments can't be handled at this point, because we
478: * don't know yet which lines are shell commands or not.
479: *---------------------------------------------------------------------
480: */
481: char *
482: ParseReadLine(linebuf)
483: Buffer linebuf;
484: {
485: int c; /* the current character */
486:
487: c = ParseSkipEmptyLines(linebuf);
488:
489: if (c == EOF)
490: return NULL;
491: else {
492: ParseFoldLF(linebuf, c);
493: Buf_KillTrailingSpaces(linebuf);
494: return Buf_Retrieve(linebuf);
1.2 espie 495: }
1.1 espie 496: }
497:
498: unsigned long
499: Parse_Getlineno()
500: {
501: return current->lineno;
502: }
503:
504: const char *
505: Parse_Getfilename()
506: {
507: return current->fname;
508: }
509:
510: #ifdef CLEANUP
511: void
512: LowParse_Init()
513: {
514: Lst_Init(&includes);
515: current = NULL;
516: }
517:
518: void
519: LowParse_End()
520: {
521: Lst_Destroy(&includes, NOFREE); /* Should be empty now */
522: }
523: #endif
1.6 espie 524:
1.1 espie 525:
526: void
527: Finish_Errors()
528: {
529: if (current != NULL) {
1.6 espie 530: free_ifile(current);
1.1 espie 531: current = NULL;
532: }
533: if (fatals) {
534: fprintf(stderr, "Fatal errors encountered -- cannot continue\n");
535: exit(1);
536: }
537: }