Mercurial > ~dholland > hg > tradcpp > index.cgi
annotate macro.c @ 85:c91dc1315745
Don't expand macros inside comments.
author | David A. Holland |
---|---|
date | Mon, 10 Jun 2013 21:36:24 -0400 |
parents | 7e4723d34248 |
children | 2b153df78214 |
rev | line source |
---|---|
30 | 1 /*- |
2 * Copyright (c) 2010 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 | |
33 | 30 #include <stdint.h> |
17 | 31 #include <stdlib.h> |
32 #include <string.h> | |
33 | |
34 #include "array.h" | |
35 #include "mode.h" | |
36 #include "place.h" | |
37 #include "macro.h" | |
19 | 38 #include "output.h" |
39 | |
40 struct expansionitem { | |
41 bool isstring; | |
42 union { | |
43 char *string; | |
44 unsigned param; | |
45 }; | |
46 }; | |
47
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
42
diff
changeset
|
47 DECLARRAY(expansionitem, static __unused); |
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
42
diff
changeset
|
48 DEFARRAY(expansionitem, static); |
17 | 49 |
50 struct macro { | |
51 struct place defplace; | |
52 struct place expansionplace; | |
53 unsigned hash; | |
54 char *name; | |
18 | 55 bool hasparams; |
56 struct stringarray params; | |
19 | 57 struct expansionitemarray expansion; |
58 bool inuse; | |
17 | 59 }; |
47
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
42
diff
changeset
|
60 DECLARRAY(macro, static __unused); |
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
42
diff
changeset
|
61 DEFARRAY(macro, static); |
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
42
diff
changeset
|
62 DECLARRAY(macroarray, static __unused); |
2e25e55dba6b
Fix inline usage as per the version in dholland-make2.
David A. Holland
parents:
42
diff
changeset
|
63 DEFARRAY(macroarray, static); |
17 | 64 |
65 static struct macroarrayarray macros; | |
66 static unsigned total_macros; | |
67 static unsigned hashmask; | |
68 | |
69 //////////////////////////////////////////////////////////// | |
70 // macro structure ops | |
71 | |
72 static | |
19 | 73 struct expansionitem * |
74 expansionitem_create_string(const char *string) | |
75 { | |
76 struct expansionitem *ei; | |
77 | |
78 ei = domalloc(sizeof(*ei)); | |
79 ei->isstring = true; | |
80 ei->string = dostrdup(string); | |
81 return ei; | |
82 } | |
83 | |
84 static | |
85 struct expansionitem * | |
86 expansionitem_create_stringlen(const char *string, size_t len) | |
87 { | |
88 struct expansionitem *ei; | |
89 | |
90 ei = domalloc(sizeof(*ei)); | |
91 ei->isstring = true; | |
92 ei->string = dostrndup(string, len); | |
93 return ei; | |
94 } | |
95 | |
96 static | |
97 struct expansionitem * | |
98 expansionitem_create_param(unsigned param) | |
99 { | |
100 struct expansionitem *ei; | |
101 | |
102 ei = domalloc(sizeof(*ei)); | |
103 ei->isstring = false; | |
104 ei->param = param; | |
105 return ei; | |
106 } | |
107 | |
108 static | |
109 void | |
110 expansionitem_destroy(struct expansionitem *ei) | |
111 { | |
112 if (ei->isstring) { | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
113 dostrfree(ei->string); |
19 | 114 } |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
115 dofree(ei, sizeof(*ei)); |
19 | 116 } |
117 | |
118 static | |
119 bool | |
120 expansionitem_eq(const struct expansionitem *ei1, | |
121 const struct expansionitem *ei2) | |
122 { | |
123 if (ei1->isstring != ei2->isstring) { | |
124 return false; | |
125 } | |
126 if (ei1->isstring) { | |
127 if (strcmp(ei1->string, ei2->string) != 0) { | |
128 return false; | |
129 } | |
130 } else { | |
131 if (ei1->param != ei2->param) { | |
132 return false; | |
133 } | |
134 } | |
135 return true; | |
136 } | |
137 | |
138 static | |
17 | 139 struct macro * |
18 | 140 macro_create(struct place *p1, const char *name, unsigned hash, |
19 | 141 struct place *p2) |
17 | 142 { |
143 struct macro *m; | |
144 | |
145 m = domalloc(sizeof(*m)); | |
146 m->defplace = *p1; | |
147 m->expansionplace = *p2; | |
148 m->hash = hash; | |
149 m->name = dostrdup(name); | |
18 | 150 m->hasparams = false; |
151 stringarray_init(&m->params); | |
19 | 152 expansionitemarray_init(&m->expansion); |
153 m->inuse = false; | |
17 | 154 return m; |
155 } | |
156 | |
19 | 157 DESTROYALL_ARRAY(expansionitem, ); |
158 | |
17 | 159 static |
160 void | |
161 macro_destroy(struct macro *m) | |
162 { | |
19 | 163 expansionitemarray_destroyall(&m->expansion); |
164 expansionitemarray_cleanup(&m->expansion); | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
165 dostrfree(m->name); |
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
166 dofree(m, sizeof(*m)); |
17 | 167 } |
168 | |
18 | 169 static |
170 bool | |
171 macro_eq(const struct macro *m1, const struct macro *m2) | |
172 { | |
173 unsigned num1, num2, i; | |
19 | 174 struct expansionitem *ei1, *ei2; |
18 | 175 const char *p1, *p2; |
176 | |
177 if (strcmp(m1->name, m2->name) != 0) { | |
178 return false; | |
179 } | |
180 | |
181 if (m1->hasparams != m2->hasparams) { | |
182 return false; | |
183 } | |
184 | |
19 | 185 num1 = expansionitemarray_num(&m1->expansion); |
186 num2 = expansionitemarray_num(&m2->expansion); | |
187 if (num1 != num2) { | |
18 | 188 return false; |
189 } | |
190 | |
19 | 191 for (i=0; i<num1; i++) { |
192 ei1 = expansionitemarray_get(&m1->expansion, i); | |
193 ei2 = expansionitemarray_get(&m2->expansion, i); | |
194 if (!expansionitem_eq(ei1, ei2)) { | |
195 return false; | |
196 } | |
197 } | |
198 | |
18 | 199 num1 = stringarray_num(&m1->params); |
200 num2 = stringarray_num(&m2->params); | |
201 if (num1 != num2) { | |
202 return false; | |
203 } | |
204 | |
205 for (i=0; i<num1; i++) { | |
206 p1 = stringarray_get(&m1->params, i); | |
207 p2 = stringarray_get(&m2->params, i); | |
208 if (strcmp(p1, p2) != 0) { | |
209 return false; | |
210 } | |
211 } | |
212 return true; | |
213 } | |
214 | |
17 | 215 //////////////////////////////////////////////////////////// |
216 // macro table | |
217 | |
218 /* | |
219 * Unless I've screwed up, this is something called Fletcher's Checksum | |
220 * that showed up in Dr. Dobbs in, according to my notes, May 1992. The | |
221 * implementation is new. | |
222 */ | |
223 static | |
224 unsigned | |
19 | 225 hashfunc(const char *s, size_t len) |
17 | 226 { |
227 uint16_t x1, x2, a; | |
19 | 228 size_t i; |
17 | 229 |
230 x1 = (uint16_t) (len >> 16); | |
231 x2 = (uint16_t) (len); | |
232 if (x1==0) { | |
233 x1++; | |
234 } | |
235 if (x2==0) { | |
236 x2++; | |
237 } | |
238 | |
239 for (i=0; i<len; i+=2) { | |
240 if (i==len-1) { | |
241 a = (unsigned char)s[i]; | |
242 /* don't run off the end of the array */ | |
243 } | |
244 else { | |
245 a = (unsigned char)s[i] + | |
246 ((uint16_t)(unsigned char)s[i+1] << 8); | |
247 } | |
248 x1 += a; | |
249 if (x1 < a) { | |
250 x1++; | |
251 } | |
252 x2 += x1; | |
253 if (x2 < x1) { | |
254 x2++; | |
255 } | |
256 } | |
257 | |
258 x1 ^= 0xffff; | |
259 x2 ^= 0xffff; | |
260 return ((uint32_t)x2)*65535U + x1; | |
261 } | |
262 | |
263 static | |
264 void | |
265 macrotable_init(void) | |
266 { | |
267 unsigned i; | |
268 | |
269 macroarrayarray_init(¯os); | |
270 macroarrayarray_setsize(¯os, 4); | |
271 for (i=0; i<4; i++) { | |
272 macroarrayarray_set(¯os, i, NULL); | |
273 } | |
274 total_macros = 0; | |
275 hashmask = 0x3; | |
276 } | |
277 | |
278 DESTROYALL_ARRAY(macro, ); | |
279 | |
280 static | |
281 void | |
282 macrotable_cleanup(void) | |
283 { | |
284 struct macroarray *bucket; | |
285 unsigned numbuckets, i; | |
286 | |
287 numbuckets = macroarrayarray_num(¯os); | |
288 for (i=0; i<numbuckets; i++) { | |
289 bucket = macroarrayarray_get(¯os, i); | |
28 | 290 if (bucket != NULL) { |
291 macroarray_destroyall(bucket); | |
292 macroarray_destroy(bucket); | |
293 } | |
17 | 294 } |
295 macroarrayarray_setsize(¯os, 0); | |
296 macroarrayarray_cleanup(¯os); | |
297 } | |
298 | |
299 static | |
300 struct macro * | |
19 | 301 macrotable_findlen(const char *name, size_t len, bool remove) |
17 | 302 { |
303 unsigned hash; | |
304 struct macroarray *bucket; | |
305 struct macro *m, *m2; | |
306 unsigned i, num; | |
19 | 307 size_t mlen; |
17 | 308 |
19 | 309 hash = hashfunc(name, len); |
17 | 310 bucket = macroarrayarray_get(¯os, hash & hashmask); |
311 if (bucket == NULL) { | |
312 return NULL; | |
313 } | |
314 num = macroarray_num(bucket); | |
315 for (i=0; i<num; i++) { | |
316 m = macroarray_get(bucket, i); | |
317 if (hash != m->hash) { | |
318 continue; | |
319 } | |
19 | 320 mlen = strlen(m->name); |
321 if (len == mlen && !memcmp(name, m->name, len)) { | |
17 | 322 if (remove) { |
323 if (i < num-1) { | |
324 m2 = macroarray_get(bucket, num-1); | |
325 macroarray_set(bucket, i, m2); | |
326 } | |
327 macroarray_setsize(bucket, num-1); | |
328 total_macros--; | |
329 } | |
330 return m; | |
331 } | |
332 } | |
333 return NULL; | |
334 } | |
335 | |
336 static | |
19 | 337 struct macro * |
338 macrotable_find(const char *name, bool remove) | |
339 { | |
340 return macrotable_findlen(name, strlen(name), remove); | |
341 } | |
342 | |
343 static | |
17 | 344 void |
345 macrotable_rehash(void) | |
346 { | |
347 struct macroarray *newbucket, *oldbucket; | |
348 struct macro *m; | |
349 unsigned newmask, tossbit; | |
350 unsigned numbuckets, i; | |
351 unsigned oldnum, j, k; | |
352 | |
353 numbuckets = macroarrayarray_num(¯os); | |
354 macroarrayarray_setsize(¯os, numbuckets*2); | |
355 | |
356 assert(hashmask == numbuckets - 1); | |
357 newmask = (hashmask << 1) | 1U; | |
74 | 358 tossbit = newmask & ~hashmask; |
17 | 359 hashmask = newmask; |
360 | |
361 for (i=0; i<numbuckets; i++) { | |
362 newbucket = NULL; | |
363 oldbucket = macroarrayarray_get(¯os, i); | |
74 | 364 if (oldbucket == NULL) { |
365 macroarrayarray_set(¯os, numbuckets + i, NULL); | |
366 continue; | |
367 } | |
17 | 368 oldnum = macroarray_num(oldbucket); |
369 for (j=0; j<oldnum; j++) { | |
370 m = macroarray_get(oldbucket, j); | |
371 if (m->hash & tossbit) { | |
372 if (newbucket == NULL) { | |
373 newbucket = macroarray_create(); | |
374 } | |
375 macroarray_set(oldbucket, j, NULL); | |
376 macroarray_add(newbucket, m, NULL); | |
377 } | |
378 } | |
379 for (j=k=0; j<oldnum; j++) { | |
380 m = macroarray_get(oldbucket, j); | |
74 | 381 if (m != NULL) { |
382 if (k < j) { | |
383 macroarray_set(oldbucket, k, m); | |
384 } | |
385 k++; | |
17 | 386 } |
387 } | |
388 macroarray_setsize(oldbucket, k); | |
389 macroarrayarray_set(¯os, numbuckets + i, newbucket); | |
390 } | |
391 } | |
392 | |
393 static | |
394 void | |
395 macrotable_add(struct macro *m) | |
396 { | |
397 unsigned hash; | |
398 struct macroarray *bucket; | |
399 unsigned numbuckets; | |
400 | |
401 numbuckets = macroarrayarray_num(¯os); | |
402 if (total_macros > 0 && total_macros / numbuckets > 9) { | |
403 macrotable_rehash(); | |
404 } | |
405 | |
19 | 406 hash = hashfunc(m->name, strlen(m->name)); |
17 | 407 bucket = macroarrayarray_get(¯os, hash & hashmask); |
408 if (bucket == NULL) { | |
409 bucket = macroarray_create(); | |
410 macroarrayarray_set(¯os, hash & hashmask, bucket); | |
411 } | |
412 macroarray_add(bucket, m, NULL); | |
413 total_macros++; | |
414 } | |
415 | |
416 //////////////////////////////////////////////////////////// | |
417 // external macro definition interface | |
418 | |
18 | 419 static |
420 struct macro * | |
421 macro_define_common_start(struct place *p1, const char *macro, | |
19 | 422 struct place *p2) |
17 | 423 { |
424 struct macro *m; | |
19 | 425 unsigned hash; |
17 | 426 |
18 | 427 if (!is_identifier(macro)) { |
428 complain(p1, "Invalid macro name %s", macro); | |
429 complain_fail(); | |
430 } | |
431 | |
19 | 432 hash = hashfunc(macro, strlen(macro)); |
433 m = macro_create(p1, macro, hash, p2); | |
18 | 434 return m; |
435 } | |
436 | |
437 static | |
438 void | |
439 macro_define_common_end(struct macro *m) | |
440 { | |
441 struct macro *oldm; | |
442 bool ok; | |
443 | |
444 oldm = macrotable_find(m->name, false); | |
445 if (oldm != NULL) { | |
446 ok = macro_eq(m, oldm); | |
447 if (ok) { | |
84
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
448 /* in traditional cpp this is silent */ |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
449 //complain(&m->defplace, |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
450 // "Warning: redefinition of %s", m->name); |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
451 //complain(&oldm->defplace, |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
452 // "Previous definition was here"); |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
453 //if (mode.werror) { |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
454 // complain_fail(); |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
455 //} |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
456 } else { |
18 | 457 complain(&m->defplace, |
84
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
458 "Warning: non-identical redefinition of %s", |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
459 m->name); |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
460 complain(&oldm->defplace, |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
461 "Previous definition was here"); |
7e4723d34248
Complain only about non-identical redefinitions of macros.
David A. Holland
parents:
74
diff
changeset
|
462 /* in traditional cpp this is not fatal */ |
17 | 463 if (mode.werror) { |
464 complain_fail(); | |
465 } | |
466 } | |
18 | 467 macro_destroy(m); |
17 | 468 return; |
469 } | |
18 | 470 macrotable_add(m); |
471 } | |
17 | 472 |
18 | 473 static |
474 void | |
475 macro_parse_parameters(struct macro *m, struct place *p, const char *params) | |
476 { | |
477 size_t len; | |
478 const char *s; | |
479 char *param; | |
480 | |
481 while (params != NULL) { | |
482 len = strspn(params, ws); | |
483 params += len; | |
484 p->column += len; | |
485 s = strchr(params, ','); | |
486 if (s) { | |
487 len = s-params; | |
488 param = dostrndup(params, len); | |
489 s++; | |
490 } else { | |
491 len = strlen(params); | |
492 param = dostrndup(params, len); | |
493 } | |
494 notrailingws(param, strlen(param)); | |
495 if (!is_identifier(param)) { | |
496 complain(p, "Invalid macro parameter name %s", param); | |
497 complain_fail(); | |
498 } else { | |
499 stringarray_add(&m->params, param, NULL); | |
500 } | |
501 params = s; | |
502 p->column += len; | |
503 } | |
504 } | |
505 | |
19 | 506 static |
507 bool | |
508 isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret) | |
509 { | |
510 unsigned num, i; | |
511 const char *param; | |
512 | |
513 num = stringarray_num(&m->params); | |
514 for (i=0; i<num; i++) { | |
515 param = stringarray_get(&m->params, i); | |
27 | 516 if (strlen(param) == len && !memcmp(name, param, len)) { |
19 | 517 *num_ret = i; |
518 return true; | |
519 } | |
520 } | |
521 return false; | |
522 } | |
523 | |
524 static | |
525 void | |
526 macro_parse_expansion(struct macro *m, const char *buf) | |
527 { | |
528 size_t blockstart, wordstart, pos; | |
529 struct expansionitem *ei; | |
530 unsigned param; | |
531 | |
532 pos = blockstart = 0; | |
533 while (buf[pos] != '\0') { | |
534 pos += strspn(buf+pos, ws); | |
535 if (strchr(alnum, buf[pos])) { | |
536 wordstart = pos; | |
537 pos += strspn(buf+pos, alnum); | |
538 if (isparam(m, buf+wordstart, pos-wordstart, ¶m)) { | |
539 if (pos > blockstart) { | |
540 ei = expansionitem_create_stringlen( | |
541 buf + blockstart, | |
27 | 542 wordstart - blockstart); |
19 | 543 expansionitemarray_add(&m->expansion, |
544 ei, NULL); | |
545 } | |
546 ei = expansionitem_create_param(param); | |
547 expansionitemarray_add(&m->expansion, ei,NULL); | |
548 blockstart = pos; | |
549 continue; | |
550 } | |
551 continue; | |
552 } | |
553 pos++; | |
554 } | |
555 if (pos > blockstart) { | |
556 ei = expansionitem_create_stringlen(buf + blockstart, | |
25 | 557 pos - blockstart); |
19 | 558 expansionitemarray_add(&m->expansion, ei, NULL); |
559 } | |
560 } | |
561 | |
18 | 562 void |
563 macro_define_plain(struct place *p1, const char *macro, | |
564 struct place *p2, const char *expansion) | |
565 { | |
566 struct macro *m; | |
19 | 567 struct expansionitem *ei; |
18 | 568 |
19 | 569 m = macro_define_common_start(p1, macro, p2); |
570 ei = expansionitem_create_string(expansion); | |
571 expansionitemarray_add(&m->expansion, ei, NULL); | |
18 | 572 macro_define_common_end(m); |
573 } | |
574 | |
575 void | |
576 macro_define_params(struct place *p1, const char *macro, | |
577 struct place *p2, const char *params, | |
578 struct place *p3, const char *expansion) | |
579 { | |
580 struct macro *m; | |
581 | |
19 | 582 m = macro_define_common_start(p1, macro, p3); |
25 | 583 m->hasparams = true; |
18 | 584 macro_parse_parameters(m, p2, params); |
19 | 585 macro_parse_expansion(m, expansion); |
18 | 586 macro_define_common_end(m); |
17 | 587 } |
588 | |
589 void | |
590 macro_undef(const char *macro) | |
591 { | |
592 struct macro *m; | |
593 | |
594 m = macrotable_find(macro, true); | |
595 if (m) { | |
596 macro_destroy(m); | |
597 } | |
598 } | |
599 | |
600 bool | |
601 macro_isdefined(const char *macro) | |
602 { | |
603 struct macro *m; | |
604 | |
605 m = macrotable_find(macro, false); | |
606 return m != NULL; | |
607 } | |
608 | |
609 //////////////////////////////////////////////////////////// | |
610 // macro expansion | |
611 | |
19 | 612 struct expstate { |
613 bool honordefined; | |
614 enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state; | |
615 struct macro *curmacro; | |
616 struct stringarray args; | |
617 unsigned argparens; | |
618 | |
619 bool tobuf; | |
620 char *buf; | |
621 size_t bufpos, bufmax; | |
622 }; | |
623 | |
624 static struct expstate mainstate; | |
625 | |
626 static void doexpand(struct expstate *es, struct place *p, | |
627 char *buf, size_t len); | |
628 | |
629 static | |
630 void | |
631 expstate_init(struct expstate *es, bool tobuf, bool honordefined) | |
632 { | |
633 es->honordefined = honordefined; | |
634 es->state = ES_NORMAL; | |
635 es->curmacro = NULL; | |
636 stringarray_init(&es->args); | |
637 es->argparens = 0; | |
638 es->tobuf = tobuf; | |
639 es->buf = NULL; | |
640 es->bufpos = 0; | |
641 es->bufmax = 0; | |
642 } | |
643 | |
644 static | |
645 void | |
646 expstate_cleanup(struct expstate *es) | |
647 { | |
648 assert(es->state == ES_NORMAL); | |
649 stringarray_cleanup(&es->args); | |
650 if (es->buf) { | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
651 dofree(es->buf, es->bufmax); |
19 | 652 } |
653 } | |
654 | |
655 static | |
656 void | |
657 expstate_destroyargs(struct expstate *es) | |
658 { | |
659 unsigned i, num; | |
660 | |
661 num = stringarray_num(&es->args); | |
662 for (i=0; i<num; i++) { | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
663 dostrfree(stringarray_get(&es->args, i)); |
19 | 664 } |
665 stringarray_setsize(&es->args, 0); | |
666 } | |
667 | |
668 static | |
669 void | |
21 | 670 expand_send(struct expstate *es, struct place *p, const char *buf, size_t len) |
19 | 671 { |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
672 size_t oldmax; |
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
673 |
19 | 674 if (es->tobuf) { |
38
b156910b59b2
Wrap free() in dofree() to allow instrumenting it for debugging.
David A. Holland
parents:
33
diff
changeset
|
675 assert(es->bufpos <= es->bufmax); |
19 | 676 if (es->bufpos + len > es->bufmax) { |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
677 oldmax = es->bufmax; |
19 | 678 if (es->bufmax == 0) { |
679 es->bufmax = 64; | |
680 } | |
681 while (es->bufpos + len > es->bufmax) { | |
682 es->bufmax *= 2; | |
683 } | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
684 es->buf = dorealloc(es->buf, oldmax, es->bufmax); |
19 | 685 } |
686 memcpy(es->buf + es->bufpos, buf, len); | |
687 es->bufpos += len; | |
38
b156910b59b2
Wrap free() in dofree() to allow instrumenting it for debugging.
David A. Holland
parents:
33
diff
changeset
|
688 assert(es->bufpos <= es->bufmax); |
19 | 689 } else { |
21 | 690 output(p, buf, len); |
19 | 691 } |
692 } | |
693 | |
694 static | |
695 void | |
21 | 696 expand_send_eof(struct expstate *es, struct place *p) |
19 | 697 { |
698 if (es->tobuf) { | |
21 | 699 expand_send(es, p, "", 1); |
19 | 700 es->bufpos--; |
701 } else { | |
702 output_eof(); | |
703 } | |
704 } | |
705 | |
706 static | |
707 void | |
708 expand_newarg(struct expstate *es, char *buf, size_t len) | |
709 { | |
710 char *text; | |
711 | |
712 text = dostrndup(buf, len); | |
713 stringarray_add(&es->args, text, NULL); | |
714 } | |
715 | |
716 static | |
717 void | |
718 expand_appendarg(struct expstate *es, char *buf, size_t len) | |
719 { | |
720 unsigned num; | |
721 char *text; | |
722 size_t oldlen; | |
723 | |
724 num = stringarray_num(&es->args); | |
725 assert(num > 0); | |
726 | |
727 text = stringarray_get(&es->args, num - 1); | |
728 oldlen = strlen(text); | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
729 text = dorealloc(text, oldlen + 1, oldlen + len + 1); |
19 | 730 memcpy(text + oldlen, buf, len); |
731 text[oldlen+len] = '\0'; | |
732 stringarray_set(&es->args, num - 1, text); | |
733 } | |
734 | |
735 static | |
736 char * | |
28 | 737 expand_substitute(struct place *p, struct expstate *es) |
19 | 738 { |
739 struct expansionitem *ei; | |
740 unsigned i, num; | |
741 size_t len; | |
742 char *arg; | |
743 char *ret; | |
28 | 744 unsigned numargs, numparams; |
745 | |
746 numargs = stringarray_num(&es->args); | |
747 numparams = stringarray_num(&es->curmacro->params); | |
748 | |
749 if (numargs == 0 && numparams == 1) { | |
750 /* no arguments <=> one empty argument */ | |
751 stringarray_add(&es->args, dostrdup(""), NULL); | |
752 numargs++; | |
753 } | |
754 if (numargs != numparams) { | |
755 complain(p, "Wrong number of arguments for macro %s; " | |
756 "found %u, expected %u", | |
757 es->curmacro->name, numargs, numparams); | |
758 complain_fail(); | |
759 while (numargs < numparams) { | |
760 stringarray_add(&es->args, dostrdup(""), NULL); | |
761 numargs++; | |
762 } | |
763 } | |
19 | 764 |
765 len = 0; | |
766 num = expansionitemarray_num(&es->curmacro->expansion); | |
767 for (i=0; i<num; i++) { | |
768 ei = expansionitemarray_get(&es->curmacro->expansion, i); | |
769 if (ei->isstring) { | |
770 len += strlen(ei->string); | |
771 } else { | |
772 arg = stringarray_get(&es->args, ei->param); | |
773 len += strlen(arg); | |
774 } | |
775 } | |
776 | |
777 ret = domalloc(len+1); | |
778 *ret = '\0'; | |
779 for (i=0; i<num; i++) { | |
780 ei = expansionitemarray_get(&es->curmacro->expansion, i); | |
781 if (ei->isstring) { | |
782 strcat(ret, ei->string); | |
783 } else { | |
784 arg = stringarray_get(&es->args, ei->param); | |
785 strcat(ret, arg); | |
786 } | |
787 } | |
788 | |
789 return ret; | |
790 } | |
791 | |
792 static | |
793 void | |
794 expand_domacro(struct expstate *es, struct place *p) | |
795 { | |
796 struct macro *m; | |
797 char *newbuf, *newbuf2; | |
798 | |
799 if (es->curmacro == NULL) { | |
800 /* defined() */ | |
801 if (stringarray_num(&es->args) != 1) { | |
802 complain(p, "Too many arguments for defined()"); | |
803 complain_fail(); | |
21 | 804 expand_send(es, p, "0", 1); |
19 | 805 return; |
806 } | |
807 m = macrotable_find(stringarray_get(&es->args, 0), false); | |
21 | 808 expand_send(es, p, (m != NULL) ? "1" : "0", 1); |
25 | 809 expstate_destroyargs(es); |
19 | 810 return; |
811 } | |
812 | |
813 assert(es->curmacro->inuse == false); | |
814 es->curmacro->inuse = true; | |
815 | |
28 | 816 newbuf = expand_substitute(p, es); |
19 | 817 newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false); |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
818 dostrfree(newbuf); |
25 | 819 expstate_destroyargs(es); |
19 | 820 doexpand(es, p, newbuf2, strlen(newbuf2)); |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
821 dostrfree(newbuf2); |
19 | 822 |
823 es->curmacro->inuse = false; | |
824 } | |
825 | |
826 static | |
827 void | |
828 expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len) | |
829 { | |
830 switch (es->state) { | |
831 case ES_NORMAL: | |
21 | 832 expand_send(es, p, buf, len); |
19 | 833 break; |
834 case ES_WANTLPAREN: | |
835 break; | |
836 case ES_NOARG: | |
837 break; | |
838 case ES_HAVEARG: | |
839 expand_appendarg(es, buf, len); | |
840 break; | |
841 } | |
842 } | |
843 | |
844 static | |
845 void | |
846 expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len) | |
847 { | |
848 struct macro *m; | |
849 struct expansionitem *ei; | |
850 char *newbuf; | |
17 | 851 |
19 | 852 switch (es->state) { |
853 case ES_NORMAL: | |
854 if (es->honordefined && | |
855 len == 7 && !memcmp(buf, "defined", 7)) { | |
856 es->curmacro = NULL; | |
857 es->state = ES_WANTLPAREN; | |
858 break; | |
859 } | |
860 m = macrotable_findlen(buf, len, false); | |
42
ad7763329eba
Don't crash if a macro tries to expand itself recursively.
David A. Holland
parents:
40
diff
changeset
|
861 if (m == NULL || m->inuse) { |
21 | 862 expand_send(es, p, buf, len); |
19 | 863 } else if (!m->hasparams) { |
864 m->inuse = true; | |
865 assert(expansionitemarray_num(&m->expansion) == 1); | |
866 ei = expansionitemarray_get(&m->expansion, 0); | |
867 assert(ei->isstring); | |
868 newbuf = macroexpand(p, ei->string, | |
869 strlen(ei->string), false); | |
870 doexpand(es, p, newbuf, strlen(newbuf)); | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
871 dostrfree(newbuf); |
19 | 872 m->inuse = false; |
873 } else { | |
874 es->curmacro = m; | |
875 es->state = ES_WANTLPAREN; | |
876 } | |
877 break; | |
878 case ES_WANTLPAREN: | |
879 if (es->curmacro != NULL) { | |
880 complain(p, "Expected arguments for macro %s", | |
881 es->curmacro->name); | |
882 complain_fail(); | |
883 } else { | |
884 /* "defined foo" means "defined(foo)" */ | |
885 expand_newarg(es, buf, len); | |
886 es->state = ES_NORMAL; | |
887 expand_domacro(es, p); | |
888 break; | |
889 } | |
890 break; | |
891 case ES_NOARG: | |
892 expand_newarg(es, buf, len); | |
893 es->state = ES_HAVEARG; | |
894 break; | |
895 case ES_HAVEARG: | |
896 expand_appendarg(es, buf, len); | |
897 break; | |
898 } | |
899 } | |
900 | |
901 static | |
902 void | |
903 expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len) | |
904 { | |
905 switch (es->state) { | |
906 case ES_NORMAL: | |
21 | 907 expand_send(es, p, buf, len); |
19 | 908 break; |
909 case ES_WANTLPAREN: | |
910 es->state = ES_NOARG; | |
911 break; | |
912 case ES_NOARG: | |
913 expand_newarg(es, buf, len); | |
914 es->state = ES_HAVEARG; | |
915 es->argparens++; | |
916 break; | |
917 case ES_HAVEARG: | |
918 expand_appendarg(es, buf, len); | |
919 es->argparens++; | |
920 break; | |
921 } | |
922 } | |
923 | |
924 static | |
925 void | |
926 expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len) | |
927 { | |
928 switch (es->state) { | |
929 case ES_NORMAL: | |
21 | 930 expand_send(es, p, buf, len); |
19 | 931 break; |
932 case ES_WANTLPAREN: | |
933 if (es->curmacro) { | |
934 complain(p, "Expected arguments for macro %s", | |
935 es->curmacro->name); | |
936 } else { | |
937 complain(p, "Expected arguments for defined()"); | |
938 } | |
939 complain_fail(); | |
940 break; | |
941 case ES_NOARG: | |
942 assert(es->argparens == 0); | |
943 es->state = ES_NORMAL; | |
944 expand_domacro(es, p); | |
945 break; | |
946 case ES_HAVEARG: | |
947 if (es->argparens > 0) { | |
948 es->argparens--; | |
949 expand_appendarg(es, buf, len); | |
950 } else { | |
951 es->state = ES_NORMAL; | |
952 expand_domacro(es, p); | |
953 } | |
954 break; | |
955 } | |
956 } | |
957 | |
958 static | |
959 void | |
960 expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len) | |
961 { | |
962 switch (es->state) { | |
963 case ES_NORMAL: | |
21 | 964 expand_send(es, p, buf, len); |
19 | 965 break; |
966 case ES_WANTLPAREN: | |
967 if (es->curmacro) { | |
968 complain(p, "Expected arguments for macro %s", | |
969 es->curmacro->name); | |
970 } else { | |
971 complain(p, "Expected arguments for defined()"); | |
972 } | |
973 complain_fail(); | |
974 break; | |
975 case ES_NOARG: | |
976 assert(es->argparens == 0); | |
977 expand_newarg(es, buf, 0); | |
978 break; | |
979 case ES_HAVEARG: | |
980 if (es->argparens > 0) { | |
981 expand_appendarg(es, buf, len); | |
982 } else { | |
983 es->state = ES_NOARG; | |
984 } | |
985 break; | |
986 } | |
987 } | |
988 | |
989 static | |
990 void | |
991 expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len) | |
992 { | |
993 switch (es->state) { | |
994 case ES_NORMAL: | |
21 | 995 expand_send(es, p, buf, len); |
19 | 996 break; |
997 case ES_WANTLPAREN: | |
998 if (es->curmacro) { | |
999 complain(p, "Expected arguments for macro %s", | |
1000 es->curmacro->name); | |
1001 } else { | |
1002 complain(p, "Expected arguments for defined()"); | |
1003 } | |
1004 complain_fail(); | |
1005 break; | |
1006 case ES_NOARG: | |
1007 expand_newarg(es, buf, len); | |
1008 es->state = ES_HAVEARG; | |
1009 break; | |
1010 case ES_HAVEARG: | |
1011 expand_appendarg(es, buf, len); | |
1012 break; | |
1013 } | |
1014 } | |
1015 | |
1016 static | |
1017 void | |
1018 expand_got_eof(struct expstate *es, struct place *p) | |
1019 { | |
1020 switch (es->state) { | |
1021 case ES_NORMAL: | |
21 | 1022 expand_send_eof(es, p); |
19 | 1023 break; |
1024 case ES_WANTLPAREN: | |
1025 if (es->curmacro) { | |
1026 complain(p, "Expected arguments for macro %s", | |
1027 es->curmacro->name); | |
1028 } else { | |
1029 complain(p, "Expected arguments for defined()"); | |
1030 } | |
1031 complain_fail(); | |
1032 break; | |
1033 case ES_NOARG: | |
1034 case ES_HAVEARG: | |
1035 if (es->curmacro) { | |
1036 complain(p, "Unclosed argument list for macro %s", | |
1037 es->curmacro->name); | |
1038 } else { | |
1039 complain(p, "Unclosed argument list for defined()"); | |
1040 } | |
1041 complain_fail(); | |
1042 expstate_destroyargs(es); | |
1043 break; | |
1044 } | |
1045 es->state = ES_NORMAL; | |
1046 es->curmacro = NULL; | |
1047 es->argparens = 0; | |
1048 } | |
1049 | |
1050 static | |
1051 void | |
1052 doexpand(struct expstate *es, struct place *p, char *buf, size_t len) | |
1053 { | |
85 | 1054 char *s; |
19 | 1055 size_t x; |
85 | 1056 bool inquote = false; |
19 | 1057 |
1058 while (len > 0) { | |
1059 x = strspn(buf, ws); | |
28 | 1060 if (x > len) { |
1061 /* XXX gross, need strnspn */ | |
1062 x = len; | |
1063 } | |
1064 | |
19 | 1065 if (x > 0) { |
1066 expand_got_ws(es, p, buf, x); | |
1067 buf += x; | |
1068 len -= x; | |
28 | 1069 continue; |
19 | 1070 } |
1071 | |
1072 x = strspn(buf, alnum); | |
28 | 1073 if (x > len) { |
1074 /* XXX gross, need strnspn */ | |
1075 x = len; | |
1076 } | |
1077 | |
19 | 1078 if (x > 0) { |
1079 expand_got_word(es, p, buf, x); | |
1080 buf += x; | |
1081 len -= x; | |
1082 continue; | |
1083 } | |
1084 | |
85 | 1085 if (!inquote && len > 1 && buf[0] == '/' && buf[1] == '*') { |
1086 s = strstr(buf, "*/"); | |
1087 if (s) { | |
1088 x = s - buf; | |
1089 } else { | |
1090 x = len; | |
1091 } | |
1092 expand_got_ws(es, p, buf, x); | |
1093 buf += x; | |
1094 len -= x; | |
1095 continue; | |
1096 } | |
1097 | |
19 | 1098 if (buf[0] == '(') { |
1099 expand_got_lparen(es, p, buf, 1); | |
1100 buf++; | |
1101 len--; | |
1102 continue; | |
1103 } | |
1104 | |
1105 if (buf[0] == ')') { | |
1106 expand_got_rparen(es, p, buf, 1); | |
1107 buf++; | |
1108 len--; | |
1109 continue; | |
1110 } | |
1111 | |
1112 if (buf[0] == ',') { | |
1113 expand_got_comma(es, p, buf, 1); | |
1114 buf++; | |
1115 len--; | |
1116 continue; | |
1117 } | |
1118 | |
85 | 1119 if (len > 1 && buf[0] == '\\' && buf[1] == '"') { |
1120 expand_got_other(es, p, buf, 2); | |
1121 buf += 2; | |
1122 len -= 2; | |
1123 continue; | |
1124 } | |
1125 if (buf[0] == '"') { | |
1126 inquote = !inquote; | |
1127 } | |
1128 | |
19 | 1129 expand_got_other(es, p, buf, 1); |
1130 buf++; | |
1131 len--; | |
1132 } | |
1133 } | |
1134 | |
1135 char * | |
1136 macroexpand(struct place *p, char *buf, size_t len, bool honordefined) | |
1137 { | |
1138 struct expstate es; | |
1139 char *ret; | |
1140 | |
1141 expstate_init(&es, true, honordefined); | |
1142 doexpand(&es, p, buf, len); | |
1143 expand_got_eof(&es, p); | |
40 | 1144 |
1145 /* trim to fit, so the malloc debugging won't complain */ | |
1146 es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1); | |
1147 | |
19 | 1148 ret = es.buf; |
1149 es.buf = NULL; | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
1150 es.bufpos = es.bufmax = 0; |
40 | 1151 |
19 | 1152 expstate_cleanup(&es); |
1153 | |
1154 return ret; | |
1155 } | |
1156 | |
1157 void | |
1158 macro_sendline(struct place *p, char *buf, size_t len) | |
1159 { | |
1160 doexpand(&mainstate, p, buf, len); | |
21 | 1161 output(p, "\n", 1); |
19 | 1162 } |
1163 | |
1164 void | |
1165 macro_sendeof(struct place *p) | |
1166 { | |
1167 expand_got_eof(&mainstate, p); | |
1168 } | |
17 | 1169 |
1170 //////////////////////////////////////////////////////////// | |
1171 // module initialization | |
1172 | |
1173 void | |
1174 macros_init(void) | |
1175 { | |
1176 macrotable_init(); | |
19 | 1177 expstate_init(&mainstate, false, false); |
17 | 1178 } |
1179 | |
1180 void | |
1181 macros_cleanup(void) | |
1182 { | |
19 | 1183 expstate_cleanup(&mainstate); |
17 | 1184 macrotable_cleanup(); |
1185 } |