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