1 /*
2  * (c) Thomas Pornin 1999 - 2002
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  * 4. The name of the authors may not be used to endorse or promote
13  *    products derived from this software without specific prior written
14  *    permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 #include "tune.h"
31 #include <stdio.h>
32 #include <string.h>
33 #include <stddef.h>
34 #include <limits.h>
35 #include <time.h>
36 #include "ucppi.h"
37 #include "mem.h"
38 #include "nhash.h"
39 
40 /*
41  * Assertion support. Each assertion is indexed by its predicate, and
42  * the list of 'questions' which yield a true answer.
43  */
44 
45 #ifdef UCPP_REENTRANT
46 
47 #define assertions		(REENTR->_assert.assertions)
48 #define assertions_init_done	(REENTR->_assert.assertions_init_done)
49 
50 #else
51 
52 static HTT assertions;
53 static int assertions_init_done = 0;
54 
55 #endif /* UCPP_REENTRANT */
56 
new_assertion(void)57 static struct assert *new_assertion(void)
58 {
59 	struct assert *a = getmem(sizeof(struct assert));
60 
61 	a->nbval = 0;
62 	return a;
63 }
64 
del_token_fifo(struct token_fifo * tf)65 static void del_token_fifo(struct token_fifo *tf)
66 {
67 	size_t i;
68 
69 	for (i = 0; i < tf->nt; i ++)
70 		if (S_TOKEN(tf->t[i].type)) freemem(tf->t[i].name);
71 	if (tf->nt) freemem(tf->t);
72 }
73 
del_assertion(void * va)74 static void del_assertion(void *va)
75 {
76 	struct assert *a = va;
77 	size_t i;
78 
79 	for (i = 0; i < a->nbval; i ++) del_token_fifo(a->val + i);
80 	if (a->nbval) freemem(a->val);
81 	freemem(a);
82 }
83 
84 #ifdef UCPP_CLONE
85 
clone_token_fifo(struct token_fifo * dst,const struct token_fifo * src)86 static void clone_token_fifo(struct token_fifo *dst, const struct token_fifo *src)
87 {
88 	size_t i;
89 
90 	dst->art = src->art;
91 	if (src->nt) {
92 		dst->nt = 0;
93 		for (i = 0; i < src->nt; i ++) {
94 			aol(dst->t, dst->nt, src->t[i], TOKEN_LIST_MEMG);
95 			if (S_TOKEN(src->t[i].type))
96 				dst->t[i].name = sdup(src->t[i].name);
97 		}
98 	} else
99 		dst->nt = src->nt;
100 }
101 
clone_assertion(const void * va)102 static void *clone_assertion(const void *va)
103 {
104 	const struct assert *src = va;
105 	struct assert *dst = getmem(sizeof(struct assert));
106 	size_t i;
107 
108 	if (src->nbval > 0) {
109 		dst->nbval = 0;
110 		for (i = 0; i < src->nbval; i ++) {
111 			struct token_fifo tf;
112 			clone_token_fifo(&tf, &src->val[i]);
113 			aol(dst->val, dst->nbval, tf, TOKEN_LIST_MEMG);
114 		}
115 	} else
116 		dst->nbval = src->nbval;
117 	return dst;
118 }
119 
120 #endif /* UCPP_CLONE */
121 
122 /*
123  * print the contents of a token list
124  */
print_token_fifo(pCPP_ struct token_fifo * tf)125 static void print_token_fifo(pCPP_ struct token_fifo *tf)
126 {
127 	size_t i;
128 
129 	for (i = 0; i < tf->nt; i ++)
130 		if (ttMWS(tf->t[i].type)) fputc(' ', emit_output);
131 		else fputs(token_name(tf->t + i), emit_output);
132 }
133 
134 /*
135  * print all assertions related to a given name
136  */
137 #ifdef UCPP_REENTRANT
print_assert(void * re,void * va)138 static void print_assert(void *re, void *va)
139 #else
140 static void print_assert(void *va)
141 #endif
142 {
143 	struct assert *a = va;
144 #ifdef UCPP_REENTRANT
145 	struct CPP *REENTR = re;
146 #endif
147 	size_t i;
148 
149 	for (i = 0; i < a->nbval; i ++) {
150 		fprintf(emit_output, "#assert %s(", HASH_ITEM_NAME(a));
151 		print_token_fifo(aCPP_ a->val + i);
152 		fprintf(emit_output, ")\n");
153 	}
154 }
155 
156 /*
157  * compare two token_fifo, return 0 if they are identical, 1 otherwise.
158  * All whitespace tokens are considered identical, but sequences of
159  * whitespace are not shrinked.
160  */
cmp_token_list(struct token_fifo * f1,struct token_fifo * f2)161 int cmp_token_list(struct token_fifo *f1, struct token_fifo *f2)
162 {
163 	size_t i;
164 
165 	if (f1->nt != f2->nt) return 1;
166 	for (i = 0; i < f1->nt; i ++) {
167 		if (ttMWS(f1->t[i].type) && ttMWS(f2->t[i].type)) continue;
168 		if (f1->t[i].type != f2->t[i].type) return 1;
169 		if (f1->t[i].type == MACROARG
170 			&& f1->t[i].line != f2->t[i].line) return 1;
171 		if (S_TOKEN(f1->t[i].type)
172 			&& strcmp(f1->t[i].name, f2->t[i].name)) return 1;
173 	}
174 	return 0;
175 }
176 
177 /*
178  * for #assert
179  * Assertions are not part of the ISO-C89 standard, but they are sometimes
180  * encountered, for instance in Solaris standard include files.
181  */
handle_assert(pCPP_ struct lexer_state * ls)182 int handle_assert(pCPP_ struct lexer_state *ls)
183 {
184 	int ina = 0, ltww;
185 	struct token t;
186 	struct token_fifo *atl = 0;
187 	struct assert *a;
188 	char *aname;
189 	int ret = -1;
190 	long l = ls->line;
191 	int nnp;
192 	size_t i;
193 
194 	while (!next_token(aCPP_ ls)) {
195 		if (ls->ctok->type == NEWLINE) break;
196 		if (ttMWS(ls->ctok->type)) continue;
197 		if (ls->ctok->type == NAME) {
198 			if (!(a = HTT_get(&assertions, ls->ctok->name))) {
199 				a = new_assertion();
200 				aname = sdup(ls->ctok->name);
201 				ina = 1;
202 			}
203 			goto handle_assert_next;
204 		}
205 		error(aCPP_ l, "illegal assertion name for #assert");
206 		goto handle_assert_warp_ign;
207 	}
208 	goto handle_assert_trunc;
209 
210 handle_assert_next:
211 	while (!next_token(aCPP_ ls)) {
212 		if (ls->ctok->type == NEWLINE) break;
213 		if (ttMWS(ls->ctok->type)) continue;
214 		if (ls->ctok->type != LPAR) {
215 			error(aCPP_ l, "syntax error in #assert");
216 			goto handle_assert_warp_ign;
217 		}
218 		goto handle_assert_next2;
219 	}
220 	goto handle_assert_trunc;
221 
222 handle_assert_next2:
223 	atl = getmem(sizeof(struct token_fifo));
224 	atl->art = atl->nt = 0;
225 	for (nnp = 1, ltww = 1; nnp && !next_token(aCPP_ ls);) {
226 		if (ls->ctok->type == NEWLINE) break;
227 		if (ltww && ttMWS(ls->ctok->type)) continue;
228 		ltww = ttMWS(ls->ctok->type);
229 		if (ls->ctok->type == LPAR) nnp ++;
230 		else if (ls->ctok->type == RPAR) {
231 			if (!(-- nnp)) goto handle_assert_next3;
232 		}
233 		t.type = ls->ctok->type;
234 		if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
235 		aol(atl->t, atl->nt, t, TOKEN_LIST_MEMG);
236 	}
237 	goto handle_assert_trunc;
238 
239 handle_assert_next3:
240 	while (!next_token(aCPP_ ls) && ls->ctok->type != NEWLINE) {
241 		if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) {
242 			warning(aCPP_ l, "trailing garbage in #assert");
243 		}
244 	}
245 	if (atl->nt && ttMWS(atl->t[atl->nt - 1].type) && (-- atl->nt) == 0)
246 		freemem(atl->t);
247 	if (atl->nt == 0) {
248 		error(aCPP_ l, "void assertion in #assert");
249 		goto handle_assert_error;
250 	}
251 	for (i = 0; i < a->nbval && cmp_token_list(atl, a->val + i); i ++);
252 	if (i != a->nbval) {
253 		/* we already have it */
254 		ret = 0;
255 		goto handle_assert_error;
256 	}
257 
258 	/* This is a new assertion. Let's keep it. */
259 	aol(a->val, a->nbval, *atl, TOKEN_LIST_MEMG);
260 	if (ina) {
261 		HTT_put(&assertions, a, aname);
262 		freemem(aname);
263 	}
264 	if (emit_assertions) {
265 		fprintf(emit_output, "#assert %s(", HASH_ITEM_NAME(a));
266 		print_token_fifo(aCPP_ atl);
267 		fputs(")\n", emit_output);
268 	}
269 	freemem(atl);
270 	return 0;
271 
272 handle_assert_trunc:
273 	error(aCPP_ l, "unfinished #assert");
274 handle_assert_error:
275 	if (atl) {
276 		del_token_fifo(atl);
277 		freemem(atl);
278 	}
279 	if (ina) {
280 		freemem(aname);
281 		freemem(a);
282 	}
283 	return ret;
284 handle_assert_warp_ign:
285 	while (!next_token(aCPP_ ls) && ls->ctok->type != NEWLINE);
286 	if (ina) {
287 		freemem(aname);
288 		freemem(a);
289 	}
290 	return ret;
291 }
292 
293 /*
294  * for #unassert
295  */
handle_unassert(pCPP_ struct lexer_state * ls)296 int handle_unassert(pCPP_ struct lexer_state *ls)
297 {
298 	int ltww;
299 	struct token t;
300 	struct token_fifo atl;
301 	struct assert *a;
302 	int ret = -1;
303 	long l = ls->line;
304 	int nnp;
305 	size_t i;
306 
307 	atl.art = atl.nt = 0;
308 	while (!next_token(aCPP_ ls)) {
309 		if (ls->ctok->type == NEWLINE) break;
310 		if (ttMWS(ls->ctok->type)) continue;
311 		if (ls->ctok->type == NAME) {
312 			if (!(a = HTT_get(&assertions, ls->ctok->name))) {
313 				ret = 0;
314 				goto handle_unassert_warp;
315 			}
316 			goto handle_unassert_next;
317 		}
318 		error(aCPP_ l, "illegal assertion name for #unassert");
319 		goto handle_unassert_warp;
320 	}
321 	goto handle_unassert_trunc;
322 
323 handle_unassert_next:
324 	while (!next_token(aCPP_ ls)) {
325 		if (ls->ctok->type == NEWLINE) break;
326 		if (ttMWS(ls->ctok->type)) continue;
327 		if (ls->ctok->type != LPAR) {
328 			error(aCPP_ l, "syntax error in #unassert");
329 			goto handle_unassert_warp;
330 		}
331 		goto handle_unassert_next2;
332 	}
333 	if (emit_assertions)
334 		fprintf(emit_output, "#unassert %s\n", HASH_ITEM_NAME(a));
335 	HTT_del(&assertions, HASH_ITEM_NAME(a));
336 	return 0;
337 
338 handle_unassert_next2:
339 	for (nnp = 1, ltww = 1; nnp && !next_token(aCPP_ ls);) {
340 		if (ls->ctok->type == NEWLINE) break;
341 		if (ltww && ttMWS(ls->ctok->type)) continue;
342 		ltww = ttMWS(ls->ctok->type);
343 		if (ls->ctok->type == LPAR) nnp ++;
344 		else if (ls->ctok->type == RPAR) {
345 			if (!(-- nnp)) goto handle_unassert_next3;
346 		}
347 		t.type = ls->ctok->type;
348 		if (S_TOKEN(t.type)) t.name = sdup(ls->ctok->name);
349 		aol(atl.t, atl.nt, t, TOKEN_LIST_MEMG);
350 	}
351 	goto handle_unassert_trunc;
352 
353 handle_unassert_next3:
354 	while (!next_token(aCPP_ ls) && ls->ctok->type != NEWLINE) {
355 		if (!ttWHI(ls->ctok->type) && (ls->flags & WARN_STANDARD)) {
356 			warning(aCPP_ l, "trailing garbage in #unassert");
357 		}
358 	}
359 	if (atl.nt && ttMWS(atl.t[atl.nt - 1].type) && (-- atl.nt) == 0)
360 		freemem(atl.t);
361 	if (atl.nt == 0) {
362 		error(aCPP_ l, "void assertion in #unassert");
363 		return ret;
364 	}
365 	for (i = 0; i < a->nbval && cmp_token_list(&atl, a->val + i); i ++);
366 	if (i != a->nbval) {
367 		/* we have it, undefine it */
368 		del_token_fifo(a->val + i);
369 		if (i < (a->nbval - 1))
370 			mmvwo(a->val + i, a->val + i + 1, (a->nbval - i - 1)
371 				* sizeof(struct token_fifo));
372 		if ((-- a->nbval) == 0) freemem(a->val);
373 		if (emit_assertions) {
374 			fprintf(emit_output, "#unassert %s(",
375 				HASH_ITEM_NAME(a));
376 			print_token_fifo(aCPP_ &atl);
377 			fputs(")\n", emit_output);
378 		}
379 	}
380 	ret = 0;
381 	goto handle_unassert_finish;
382 
383 handle_unassert_trunc:
384 	error(aCPP_ l, "unfinished #unassert");
385 handle_unassert_finish:
386 	if (atl.nt) del_token_fifo(&atl);
387 	return ret;
388 handle_unassert_warp:
389 	while (!next_token(aCPP_ ls) && ls->ctok->type != NEWLINE);
390 	return ret;
391 }
392 
393 /*
394  * Add the given assertion (as string).
395  */
make_assertion(pCPP_ char * aval)396 int make_assertion(pCPP_ char *aval)
397 {
398 	struct lexer_state lls;
399 	size_t n = strlen(aval) + 1;
400 	char *c = sdup(aval);
401 	int ret;
402 
403 	*(c + n - 1) = '\n';
404 	init_buf_lexer_state(&lls, 0);
405 	lls.flags = DEFAULT_LEXER_FLAGS;
406 	lls.input = 0;
407 	lls.input_string = (unsigned char *)c;
408 	lls.pbuf = 0;
409 	lls.ebuf = n;
410 	lls.line = -1;
411 	ret = handle_assert(aCPP_ &lls);
412 	freemem(c);
413 	free_lexer_state(&lls);
414 	return ret;
415 }
416 
417 /*
418  * Remove the given assertion (as string).
419  */
destroy_assertion(pCPP_ char * aval)420 int destroy_assertion(pCPP_ char *aval)
421 {
422 	struct lexer_state lls;
423 	size_t n = strlen(aval) + 1;
424 	char *c = sdup(aval);
425 	int ret;
426 
427 	*(c + n - 1) = '\n';
428 	init_buf_lexer_state(&lls, 0);
429 	lls.flags = DEFAULT_LEXER_FLAGS;
430 	lls.input = 0;
431 	lls.input_string = (unsigned char *)c;
432 	lls.pbuf = 0;
433 	lls.ebuf = n;
434 	lls.line = -1;
435 	ret = handle_unassert(aCPP_ &lls);
436 	freemem(c);
437 	free_lexer_state(&lls);
438 	return ret;
439 }
440 
441 /*
442  * erase the assertion table
443  */
wipe_assertions(pCPP)444 void wipe_assertions(pCPP)
445 {
446 	if (assertions_init_done) HTT_kill(&assertions);
447 	assertions_init_done = 0;
448 }
449 
450 /*
451  * initialize the assertion table
452  */
init_assertions(pCPP)453 void init_assertions(pCPP)
454 {
455 	wipe_assertions(aCPP);
456 	HTT_init(&assertions, del_assertion _aCLONE(clone_assertion));
457 	assertions_init_done = 1;
458 }
459 
460 /*
461  * retrieve an assertion from the hash table
462  */
get_assertion(pCPP_ char * name)463 struct assert *get_assertion(pCPP_ char *name)
464 {
465 	return HTT_get(&assertions, name);
466 }
467 
468 /*
469  * print already defined assertions
470  */
print_assertions(pCPP)471 void print_assertions(pCPP)
472 {
473 #ifdef UCPP_REENTRANT
474 	HTT_scan_arg(&assertions, print_assert, aCPP);
475 #else
476 	HTT_scan(&assertions, print_assert);
477 #endif
478 }
479