Annotation of src/usr.bin/make/lowparse.c, Revision 1.6
1.6 ! espie 1: /* $OpenPackages$ */
! 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:
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: {
150: if (ifile->F)
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: }