GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: libexec/tradcpp/files.c Lines: 85 156 54.5 %
Date: 2017-11-13 Branches: 49 116 42.2 %

Line Branch Exec Source
1
/*-
2
 * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
3
 * All rights reserved.
4
 *
5
 * This code is derived from software contributed to The NetBSD Foundation
6
 * by David A. Holland.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
 * POSSIBILITY OF SUCH DAMAGE.
28
 */
29
30
#include <stdbool.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
#include <fcntl.h>
36
#include <errno.h>
37
38
#include "array.h"
39
#include "mode.h"
40
#include "place.h"
41
#include "files.h"
42
#include "directive.h"
43
44
struct incdir {
45
	const char *name;
46
	bool issystem;
47
};
48
49
DECLARRAY(incdir, static UNUSED);
50
76024
DEFARRAY(incdir, static);
51
52
static struct incdirarray quotepath, bracketpath;
53
54
////////////////////////////////////////////////////////////
55
// management
56
57
static
58
struct incdir *
59
incdir_create(const char *name, bool issystem)
60
{
61
	struct incdir *id;
62
63
13158
	id = domalloc(sizeof(*id));
64
13158
	id->name = name;
65
13158
	id->issystem = issystem;
66
13158
	return id;
67
}
68
69
static
70
void
71
incdir_destroy(struct incdir *id)
72
{
73
26316
	dofree(id, sizeof(*id));
74
13158
}
75
76
void
77
files_init(void)
78
{
79
2924
	incdirarray_init(&quotepath);
80
1462
	incdirarray_init(&bracketpath);
81
1462
}
82
83
38012
DESTROYALL_ARRAY(incdir, );
84
85
void
86
files_cleanup(void)
87
{
88
2924
	incdirarray_destroyall(&quotepath);
89
1462
	incdirarray_cleanup(&quotepath);
90
1462
	incdirarray_destroyall(&bracketpath);
91
1462
	incdirarray_cleanup(&bracketpath);
92
1462
}
93
94
////////////////////////////////////////////////////////////
95
// path setup
96
97
void
98
files_addquotepath(const char *dir, bool issystem)
99
{
100
	struct incdir *id;
101
102
7310
	id = incdir_create(dir, issystem);
103
7310
	incdirarray_add(&quotepath, id, NULL);
104
7310
}
105
106
void
107
files_addbracketpath(const char *dir, bool issystem)
108
{
109
	struct incdir *id;
110
111
5848
	id = incdir_create(dir, issystem);
112
5848
	incdirarray_add(&bracketpath, id, NULL);
113
5848
}
114
115
////////////////////////////////////////////////////////////
116
// parsing
117
118
/*
119
 * Find the end of the logical line. End of line characters that are
120
 * commented out do not count.
121
 */
122
static
123
size_t
124
findeol(const char *buf, size_t start, size_t limit)
125
{
126
	size_t i;
127
	int incomment = 0;
128
	bool inquote = false;
129
	char quote = '\0';
130
131
283578292
	for (i=start; i<limit; i++) {
132
139447022
		if (incomment) {
133
			if (i+1 < limit && buf[i] == '*' && buf[i+1] == '/') {
134
				i++;
135
				incomment = 0;
136
			}
137

281146986
		} else if (!inquote && i+1 < limit &&
138
141435342
			   buf[i] == '/' && buf[i+1] == '*') {
139
			i++;
140
			incomment = 1;
141

139447022
		} else if (i+1 < limit &&
142
139182400
			   buf[i] == '\\' && buf[i+1] != '\n') {
143
			i++;
144

418341066
		} else if (!inquote && (buf[i] == '"' || buf[i] == '\'')) {
145
			inquote = true;
146
			quote = buf[i];
147

139447022
		} else if (inquote && buf[i] == quote) {
148
			inquote = false;
149
139447022
		} else if (buf[i] == '\n') {
150
3890382
			return i;
151
		}
152
	}
153
264622
	return limit;
154
4155004
}
155
156
static
157
unsigned
158
countnls(const char *buf, size_t start, size_t limit)
159
{
160
	size_t i;
161
	unsigned count = 0;
162
163
272205394
	for (i=start; i<limit; i++) {
164
130267124
		if (buf[i] == '\n') {
165
			count++;
166
		}
167
	}
168
3890382
	return count;
169
}
170
171
static
172
void
173
file_read(const struct placefile *pf, int fd, const char *name, bool toplevel)
174
{
175
1462
	struct place linestartplace, nextlinestartplace, ptmp;
176
	size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp;
177
	ssize_t result;
178
	bool ateof = false;
179
	char *buf;
180
181
1462
	place_setfilestart(&linestartplace, pf);
182
1462
	nextlinestartplace = linestartplace;
183
184
	bufmax = 128;
185
	bufend = 0;
186
	linestart = 0;
187
	lineend = 0;
188
1462
	buf = domalloc(bufmax);
189
190
3891844
	while (1) {
191
4156466
		if (lineend >= bufend) {
192
			/* do not have a whole line in the buffer; read more */
193
266084
			assert(bufend >= linestart);
194

530706
			if (linestart > 0 && bufend > linestart) {
195
				/* slide to beginning of buffer */
196
257312
				memmove(buf, buf+linestart, bufend-linestart);
197
				bufend -= linestart;
198
257312
				lineend -= linestart;
199
				linestart = 0;
200
257312
			}
201
266084
			if (bufend >= bufmax) {
202
				/* need bigger buffer */
203
5848
				buf = dorealloc(buf, bufmax, bufmax*2);
204
				bufmax = bufmax*2;
205
5848
			}
206
207
266084
			if (ateof) {
208
				/* don't read again, in case it's a socket */
209
				result = 0;
210
			} else {
211
266084
				result = read(fd, buf+bufend, bufmax - bufend);
212
			}
213
214
266084
			if (result == -1) {
215
				/* read error */
216
				complain(NULL, "%s: %s",
217
					 name, strerror(errno));
218
				complain_fail();
219

267546
			} else if (result == 0 && bufend == linestart) {
220
				/* eof */
221
				ateof = true;
222
				break;
223
264622
			} else if (result == 0) {
224
				/* eof in middle of line */
225
				ateof = true;
226
				ptmp = linestartplace;
227
				ptmp.column += bufend - linestart;
228
				complain(&ptmp, "No newline at end of file");
229
				if (mode.werror) {
230
					complain_fail();
231
				}
232
				assert(bufend < bufmax);
233
				lineend = bufend++;
234
				buf[lineend] = '\n';
235
			} else {
236
264622
				bufend += (size_t)result;
237
264622
				lineend = findeol(buf, linestart, bufend);
238
			}
239
			/* loop in case we still don't have a whole line */
240
264622
			continue;
241
		}
242
243
		/* have a line */
244
3890382
		assert(buf[lineend] == '\n');
245
3890382
		buf[lineend] = '\0';
246
3890382
		nextlinestart = lineend+1;
247
3890382
		nextlinestartplace.line++;
248
249
		/* check for CR/NL */
250

7780764
		if (lineend > 0 && buf[lineend-1] == '\r') {
251
			buf[lineend-1] = '\0';
252
			lineend--;
253
		}
254
255
		/* check for continuation line */
256

7780764
		if (lineend > 0 && buf[lineend-1]=='\\') {
257
			lineend--;
258
			tmp = nextlinestart - lineend;
259
			if (bufend > nextlinestart) {
260
				memmove(buf+lineend, buf+nextlinestart,
261
					bufend - nextlinestart);
262
			}
263
			bufend -= tmp;
264
			nextlinestart -= tmp;
265
			lineend = findeol(buf, linestart, bufend);
266
			/* might not have a whole line, so loop */
267
			continue;
268
		}
269
270
		/* line now goes from linestart to lineend */
271
3890382
		assert(buf[lineend] == '\0');
272
273
		/* count how many commented-out newlines we swallowed */
274
3890382
		nextlinestartplace.line += countnls(buf, linestart, lineend);
275
276
		/* if the line isn't empty, process it */
277
3890382
		if (lineend > linestart) {
278
3890382
			directive_gotline(&linestartplace,
279
3890382
					  buf+linestart, lineend-linestart);
280
3890382
		}
281
282
		linestart = nextlinestart;
283
3890382
		lineend = findeol(buf, linestart, bufend);
284
3890382
		linestartplace = nextlinestartplace;
285
	}
286
287
1462
	if (toplevel) {
288
1462
		directive_goteof(&linestartplace);
289
1462
	}
290
1462
	dofree(buf, bufmax);
291
1462
}
292
293
////////////////////////////////////////////////////////////
294
// path search
295
296
static
297
char *
298
mkfilename(struct place *place, const char *dir, const char *file)
299
{
300
	size_t dlen, flen, rlen;
301
	char *ret;
302
	bool needslash = false;
303
304
	if (dir == NULL) {
305
		dir = place_getparsedir(place);
306
	}
307
308
	dlen = strlen(dir);
309
	flen = strlen(file);
310
	if (dlen > 0 && dir[dlen-1] != '/') {
311
		needslash = true;
312
	}
313
314
	rlen = dlen + (needslash ? 1 : 0) + flen;
315
	ret = domalloc(rlen + 1);
316
	snprintf(ret, rlen+1, "%s%s%s", dir, needslash ? "/" : "", file);
317
	return ret;
318
}
319
320
static
321
int
322
file_tryopen(const char *file)
323
{
324
	int fd;
325
326
	/* XXX check for non-regular files */
327
328
	fd = open(file, O_RDONLY);
329
	if (fd < 0) {
330
		if (errno != ENOENT && errno != ENOTDIR) {
331
			complain(NULL, "%s: %s", file, strerror(errno));
332
		}
333
		return -1;
334
	}
335
336
	return fd;
337
}
338
339
static
340
void
341
file_search(struct place *place, struct incdirarray *path, const char *name)
342
{
343
	unsigned i, num;
344
	struct incdir *id;
345
	const struct placefile *pf;
346
	char *file;
347
	int fd;
348
349
	assert(place != NULL);
350
351
	if (name[0] == '/') {
352
		fd = file_tryopen(name);
353
		if (fd >= 0) {
354
			pf = place_addfile(place, name, true);
355
			file_read(pf, fd, name, false);
356
			close(fd);
357
			return;
358
		}
359
	} else {
360
		num = incdirarray_num(path);
361
		for (i=0; i<num; i++) {
362
			id = incdirarray_get(path, i);
363
			file = mkfilename(place, id->name, name);
364
			fd = file_tryopen(file);
365
			if (fd >= 0) {
366
				pf = place_addfile(place, file, id->issystem);
367
				file_read(pf, fd, file, false);
368
				dostrfree(file);
369
				close(fd);
370
				return;
371
			}
372
			dostrfree(file);
373
		}
374
	}
375
	complain(place, "Include file %s not found", name);
376
	complain_fail();
377
}
378
379
void
380
file_readquote(struct place *place, const char *name)
381
{
382
	file_search(place, &quotepath, name);
383
}
384
385
void
386
file_readbracket(struct place *place, const char *name)
387
{
388
	file_search(place, &bracketpath, name);
389
}
390
391
void
392
file_readabsolute(struct place *place, const char *name)
393
{
394
	const struct placefile *pf;
395
	int fd;
396
397
2924
	assert(place != NULL);
398
399

1462
	if ((name == NULL) || !strcmp(name, "-")) {
400
		fd = STDIN_FILENO;
401
1462
		pf = place_addfile(place, "<standard-input>", false);
402
1462
	} else {
403
		fd = file_tryopen(name);
404
		if (fd < 0) {
405
			complain(NULL, "%s: %s", name, strerror(errno));
406
			die();
407
		}
408
		pf = place_addfile(place, name, false);
409
	}
410
411
1462
	file_read(pf, fd, name, true);
412
413
1462
	if (name != NULL) {
414
		close(fd);
415
	}
416
1462
}