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