GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/make/lowparse.c Lines: 145 188 77.1 %
Date: 2017-11-07 Branches: 102 180 56.7 %

Line Branch Exec Source
1
/*	$OpenBSD: lowparse.c,v 1.35 2016/10/21 16:12:38 espie Exp $ */
2
3
/* low-level parsing functions. */
4
5
/*
6
 * Copyright (c) 1999,2000 Marc Espie.
7
 *
8
 * Extensive code changes for the OpenBSD project.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 * 1. Redistributions of source code must retain the above copyright
14
 *    notice, this list of conditions and the following disclaimer.
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in the
17
 *    documentation and/or other materials provided with the distribution.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
20
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
23
 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
 */
31
32
#include <assert.h>
33
#include <stddef.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
#include "config.h"
39
#include "defines.h"
40
#include "buf.h"
41
#include "lowparse.h"
42
#include "error.h"
43
#include "lst.h"
44
#include "memory.h"
45
#include "pathnames.h"
46
#ifndef LOCATION_TYPE
47
#include "location.h"
48
#endif
49
#include "var.h"
50
51
52
#define READ_MAKEFILES "MAKEFILE_LIST"
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
	Location origin;	/* Name of file and line number */
60
	FILE *F;		/* Open stream, or NULL if pure string. */
61
	char *str;		/* Input string, if F == NULL. */
62
63
	/* Line buffer. */
64
	char *ptr;		/* Where we are. */
65
	char *end;		/* Don't overdo it. */
66
};
67
68
static struct input_stream *current;	/* the input_stream being parsed. */
69
70
static LIST input_stack;	/* Stack of input_stream waiting to be parsed
71
				 * (includes and loop reparses) */
72
73
/* record gnode location for proper reporting at runtime */
74
static Location *post_parse = NULL;
75
76
/* input_stream ctors.
77
 *
78
 * obj = new_input_file(filename, filehandle);
79
 *	Create input stream from filename, filehandle. */
80
static struct input_stream *new_input_file(const char *, FILE *);
81
/* obj = new_input_string(str, origin);
82
 *	Create input stream from str, origin. */
83
static struct input_stream *new_input_string(char *, const Location *);
84
/* free_input_stream(obj);
85
 *	Discard consumed input stream, closing files, freeing memory.  */
86
static void free_input_stream(struct input_stream *);
87
88
89
/* Handling basic character reading.
90
 * c = read_char();
91
 *	New character c from current input stream, or EOF at end of stream. */
92
#define read_char()	\
93
    current->ptr < current->end ? *current->ptr++ : grab_new_line_and_readchar()
94
/* char = grab_new_line_and_readchar();
95
 *	Guts for read_char. Grabs a new line off fgetln when we have
96
 *	consumed the current line and returns the first char, or EOF at end of
97
 *	stream.  */
98
static int grab_new_line_and_readchar(void);
99
/* c = skip_to_end_of_line();
100
 *	Skips to the end of the current line, returns either '\n' or EOF.  */
101
static int skip_to_end_of_line(void);
102
103
104
/* Helper functions to handle basic parsing. */
105
/* read_logical_line(buffer, firstchar);
106
 *	Grabs logical line into buffer, the first character has already been
107
 *	read into firstchar.  */
108
static void read_logical_line(Buffer, int);
109
110
/* firstchar = ParseSkipEmptyLines(buffer);
111
 *	Scans lines, skipping empty lines. May put some characters into
112
 *	buffer, returns the first character useful to continue parsing
113
 *	(e.g., not a backslash or a space. */
114
static int skip_empty_lines_and_read_char(Buffer);
115
116
const char *curdir;
117
size_t curdir_len;
118
119
void
120
Parse_setcurdir(const char *dir)
121
{
122
68248
	curdir = dir;
123
34124
	curdir_len = strlen(dir);
124
34124
}
125
126
static bool
127
startswith(const char *f, const char *s, size_t len)
128
{
129
3279301
	return strncmp(f, s, len) == 0 && f[len] == '/';
130
}
131
132
static const char *
133
simplify(const char *filename)
134
{
135
1002628
	if (startswith(filename, curdir, curdir_len))
136
15293
		return filename + curdir_len + 1;
137
486021
	else if (startswith(filename, _PATH_DEFSYSPATH,
138
	    sizeof(_PATH_DEFSYSPATH)-1)) {
139
	    	size_t sz;
140
		char *buf;
141
302003
		sz = strlen(filename) - sizeof(_PATH_DEFSYSPATH)+3;
142
302003
		buf = emalloc(sz);
143
302003
		snprintf(buf, sz, "<%s>", filename+sizeof(_PATH_DEFSYSPATH));
144
		return buf;
145
	} else
146
184018
		return filename;
147
501314
}
148
149
static struct input_stream *
150
new_input_file(const char *name, FILE *stream)
151
{
152
	struct input_stream *istream;
153
154
1002628
	istream = emalloc(sizeof(*istream));
155
501314
	istream->origin.fname = simplify(name);
156
501314
	Var_Append(READ_MAKEFILES, name);
157
501314
	istream->str = NULL;
158
	/* Naturally enough, we start reading at line 0. */
159
501314
	istream->origin.lineno = 0;
160
501314
	istream->F = stream;
161
501314
	istream->ptr = istream->end = NULL;
162
501314
	return istream;
163
}
164
165
static void
166
free_input_stream(struct input_stream *istream)
167
{
168
34718528
	if (istream->F) {
169

1002628
		if (ferror(istream->F))
170
			Parse_Error(PARSE_FATAL, "Read error");
171

1503942
		if (fileno(istream->F) != STDIN_FILENO)
172
501275
			(void)fclose(istream->F);
173
	}
174
17359264
	free(istream->str);
175
	/* Note we can't free the file names, as they are embedded in GN
176
	 * for error reports. */
177
17359264
	free(istream);
178
17359264
}
179
180
static struct input_stream *
181
new_input_string(char *str, const Location *origin)
182
{
183
	struct input_stream *istream;
184
185
33715900
	istream = emalloc(sizeof(*istream));
186
	/* No malloc, name is always taken from an already existing istream
187
	 * and strings are used in for loops, so we need to reset the line
188
	 * counter to an appropriate value. */
189
16857950
	istream->origin = *origin;
190
16857950
	istream->F = NULL;
191
16857950
	istream->ptr = istream->str = str;
192
16857950
	istream->end = str + strlen(str);
193
16857950
	return istream;
194
}
195
196
197
void
198
Parse_FromString(char *str, unsigned long lineno)
199
{
200
33715900
	Location origin;
201
202
16857950
	origin.fname = current->origin.fname;
203
16857950
	origin.lineno = lineno;
204
16857950
	if (DEBUG(FOR))
205
		(void)fprintf(stderr, "%s\n----\n", str);
206
207
16857950
	Lst_Push(&input_stack, current);
208
16857950
	assert(current != NULL);
209
16857950
	current = new_input_string(str, &origin);
210
16857950
}
211
212
213
void
214
Parse_FromFile(const char *name, FILE *stream)
215
{
216
1002628
	if (current != NULL)
217
433065
		Lst_Push(&input_stack, current);
218
501314
	current = new_input_file(name, stream);
219
501314
}
220
221
bool
222
Parse_NextFile(void)
223
{
224
34718528
	if (current != NULL)
225
17359264
		free_input_stream(current);
226
17359264
	current = Lst_Pop(&input_stack);
227
17359264
	return current != NULL;
228
}
229
230
static int
231
grab_new_line_and_readchar(void)
232
{
233
132051944
	size_t len;
234
235
66025972
	if (current->F) {
236
49168022
		current->ptr = fgetln(current->F, &len);
237
49168022
		if (current->ptr) {
238
48666673
			current->end = current->ptr + len;
239
48666673
			return *current->ptr++;
240
		} else {
241
501349
			current->end = NULL;
242
		}
243
501349
	}
244
17359299
	return EOF;
245
66025972
}
246
247
static int
248
skip_to_end_of_line(void)
249
{
250
27817548
	if (current->F) {
251
2976741
		if (current->end - current->ptr > 1)
252
2937469
			current->ptr = current->end - 1;
253
2976741
		if (*current->ptr == '\n')
254
2976741
			return *current->ptr++;
255
		return EOF;
256
	} else {
257
		int c;
258
259
10932033
		do {
260
1630508478
			c = read_char();
261
543502826
		} while (c != '\n' && c != EOF);
262
		return c;
263
	}
264
13908774
}
265
266
267
char *
268
Parse_ReadNextConditionalLine(Buffer linebuf)
269
{
270
	int c;
271
272
	/* If first char isn't dot, skip to end of line, handling \ */
273

140963174
	while ((c = read_char()) != '.') {
274

6321712074
		for (;c != '\n'; c = read_char()) {
275
1576489872
			if (c == '\\') {
276
5424597
				c = read_char();
277
1808199
				if (c == '\n')
278
1748179
					current->origin.lineno++;
279
			}
280
1576489872
			if (c == EOF)
281
				/* Unclosed conditional, reported by cond.c */
282
				return NULL;
283
		}
284
15752586
		current->origin.lineno++;
285
	}
286
287
	/* This is the line we need to copy */
288
15590566
	return Parse_ReadUnparsedLine(linebuf, "conditional");
289
15590566
}
290
291
static void
292
read_logical_line(Buffer linebuf, int c)
293
{
294
197671380
	for (;;) {
295
3444459437
		if (c == '\n') {
296
98835655
			current->origin.lineno++;
297
98835655
			break;
298
		}
299
3345623782
		if (c == EOF)
300
			break;
301
6691314883
		Buf_AddChar(linebuf, c);
302
10036871241
		c = read_char();
303
6695042923
		while (c == '\\') {
304
12889167
			c = read_char();
305
4296389
			if (c == '\n') {
306
7590876
				Buf_AddSpace(linebuf);
307
3795429
				current->origin.lineno++;
308
3795429
				do {
309
38986470
					c = read_char();
310
12995490
				} while (c == ' ' || c == '\t');
311
			} else {
312
1001922
				Buf_AddChar(linebuf, '\\');
313
500960
				if (c == '\\') {
314
28374
					Buf_AddChar(linebuf, '\\');
315
42561
					c = read_char();
316
14187
				}
317
				break;
318
			}
319
		}
320
	}
321
98835690
}
322
323
char *
324
Parse_ReadUnparsedLine(Buffer linebuf, const char *type)
325
{
326
	int c;
327
328
40412932
	Buf_Reset(linebuf);
329
60619398
	c = read_char();
330
20206466
	if (c == EOF) {
331
		Parse_Error(PARSE_FATAL, "Unclosed %s", type);
332
		return NULL;
333
	}
334
335
	/* Handle '\' at beginning of line, since \\n needs special treatment */
336
20206466
	while (c == '\\') {
337
		c = read_char();
338
		if (c == '\n') {
339
			current->origin.lineno++;
340
			do {
341
				c = read_char();
342
			} while (c == ' ' || c == '\t');
343
		} else {
344
			Buf_AddChar(linebuf, '\\');
345
			if (c == '\\') {
346
				Buf_AddChar(linebuf, '\\');
347
				c = read_char();
348
			}
349
			break;
350
		}
351
	}
352
20206466
	read_logical_line(linebuf, c);
353
354
20206466
	return Buf_Retrieve(linebuf);
355
20206466
}
356
357
/* This is a fairly complex function, but without it, we could not skip
358
 * blocks of comments without reading them. */
359
static int
360
skip_empty_lines_and_read_char(Buffer linebuf)
361
{
362
	int c;		/* the current character */
363
364
214746744
	for (;;) {
365
118758256
		Buf_Reset(linebuf);
366
356274768
		c = read_char();
367
		/* Strip leading spaces, fold on '\n' */
368
118758256
		if (c == ' ') {
369
			do {
370
1637316
				c = read_char();
371
545772
			} while (c == ' ' || c == '\t');
372
78258
			while (c == '\\') {
373
				c = read_char();
374
				if (c == '\n') {
375
					current->origin.lineno++;
376
					do {
377
						c = read_char();
378
					} while (c == ' ' || c == '\t');
379
				} else {
380
					Buf_AddChar(linebuf, '\\');
381
					if (c == '\\') {
382
						Buf_AddChar(linebuf, '\\');
383
						c = read_char();
384
					}
385
					if (c == EOF)
386
						return '\n';
387
					else
388
						return c;
389
				}
390
			}
391
78258
			assert(c != '\t');
392
		}
393
118758256
		if (c == '#')
394
13908774
			c = skip_to_end_of_line();
395
		/* Almost identical to spaces, except this occurs after
396
		 * comments have been taken care of, and we keep the tab
397
		 * itself.  */
398
118758256
		if (c == '\t') {
399
51042858
			Buf_AddChar(linebuf, '\t');
400
25521429
			do {
401
76572237
				c = read_char();
402
25524079
			} while (c == ' ' || c == '\t');
403
25521429
			while (c == '\\') {
404
				c = read_char();
405
				if (c == '\n') {
406
					current->origin.lineno++;
407
					do {
408
						c = read_char();
409
					} while (c == ' ' || c == '\t');
410
				} else {
411
					Buf_AddChar(linebuf, '\\');
412
					if (c == '\\') {
413
						Buf_AddChar(linebuf, '\\');
414
						c = read_char();
415
					}
416
					if (c == EOF)
417
						return '\n';
418
					else
419
						return c;
420
				}
421
			}
422
		}
423
118758256
		if (c == '\n')
424
22769768
			current->origin.lineno++;
425
		else
426
95988488
			return c;
427
	}
428
95988488
}
429
430
/* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
431
 * the first tab), handles escaped newlines, and skips over uninteresting
432
 * lines.
433
 *
434
 * The line number is incremented, which implies that continuation
435
 * lines are numbered with the last line number (we could do better, at a
436
 * price).
437
 *
438
 * Trivial comments are also removed, but we can't do more, as
439
 * we don't know which lines are shell commands or not.  */
440
char *
441
Parse_ReadNormalLine(Buffer linebuf)
442
{
443
	int c;		/* the current character */
444
445
191976976
	c = skip_empty_lines_and_read_char(linebuf);
446
447
95988488
	if (c == EOF)
448
17359264
		return NULL;
449
	else {
450
78629224
		read_logical_line(linebuf, c);
451
78629224
		return Buf_Retrieve(linebuf);
452
	}
453
95988488
}
454
455
unsigned long
456
Parse_Getlineno(void)
457
{
458
184428524
	return current ? current->origin.lineno : 0;
459
}
460
461
const char *
462
Parse_Getfilename(void)
463
{
464
182792516
	return current ? current->origin.fname : NULL;
465
}
466
467
void
468
Parse_SetLocation(Location *origin)
469
{
470
511558
	post_parse = origin;
471
255779
}
472
473
void
474
Parse_FillLocation(Location *origin)
475
{
476
91370252
	if (post_parse) {
477
6
		*origin = *post_parse;
478
6
	} else {
479
45685120
		origin->lineno = Parse_Getlineno();
480
45685120
		origin->fname = Parse_Getfilename();
481
	}
482
45685126
}
483
484
void
485
Parse_ReportErrors(void)
486
{
487
136498
	if (fatal_errors)
488
		exit(1);
489
	else
490
68248
		assert(current == NULL);
491
68248
}