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