Mercurial > ~dholland > hg > tradcpp > index.cgi
annotate macro.c @ 64:f50b4ea6cbfe
Prune single-line comments from (most) directive lines.
Also, don't pass the string length to the directive processing
functions, as half of them weren't honoring it. Instead, ensure that
the directive line is terminated at the place the directive processing
functions should stop looking at it.
author | David A. Holland |
---|---|
date | Sun, 31 Mar 2013 02:04:56 -0400 |
parents | 2e25e55dba6b |
children | bbbf71859a21 |
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; | |
358 tossbit = newmask && ~hashmask; | |
359 hashmask = newmask; | |
360 | |
361 for (i=0; i<numbuckets; i++) { | |
362 newbucket = NULL; | |
363 oldbucket = macroarrayarray_get(¯os, i); | |
364 oldnum = macroarray_num(oldbucket); | |
365 for (j=0; j<oldnum; j++) { | |
366 m = macroarray_get(oldbucket, j); | |
367 if (m->hash & tossbit) { | |
368 if (newbucket == NULL) { | |
369 newbucket = macroarray_create(); | |
370 } | |
371 macroarray_set(oldbucket, j, NULL); | |
372 macroarray_add(newbucket, m, NULL); | |
373 } | |
374 } | |
375 for (j=k=0; j<oldnum; j++) { | |
376 m = macroarray_get(oldbucket, j); | |
377 if (m != NULL && k < j) { | |
378 macroarray_set(oldbucket, k++, m); | |
379 } | |
380 } | |
381 macroarray_setsize(oldbucket, k); | |
382 macroarrayarray_set(¯os, numbuckets + i, newbucket); | |
383 } | |
384 } | |
385 | |
386 static | |
387 void | |
388 macrotable_add(struct macro *m) | |
389 { | |
390 unsigned hash; | |
391 struct macroarray *bucket; | |
392 unsigned numbuckets; | |
393 | |
394 numbuckets = macroarrayarray_num(¯os); | |
395 if (total_macros > 0 && total_macros / numbuckets > 9) { | |
396 macrotable_rehash(); | |
397 } | |
398 | |
19 | 399 hash = hashfunc(m->name, strlen(m->name)); |
17 | 400 bucket = macroarrayarray_get(¯os, hash & hashmask); |
401 if (bucket == NULL) { | |
402 bucket = macroarray_create(); | |
403 macroarrayarray_set(¯os, hash & hashmask, bucket); | |
404 } | |
405 macroarray_add(bucket, m, NULL); | |
406 total_macros++; | |
407 } | |
408 | |
409 //////////////////////////////////////////////////////////// | |
410 // external macro definition interface | |
411 | |
18 | 412 static |
413 struct macro * | |
414 macro_define_common_start(struct place *p1, const char *macro, | |
19 | 415 struct place *p2) |
17 | 416 { |
417 struct macro *m; | |
19 | 418 unsigned hash; |
17 | 419 |
18 | 420 if (!is_identifier(macro)) { |
421 complain(p1, "Invalid macro name %s", macro); | |
422 complain_fail(); | |
423 } | |
424 | |
19 | 425 hash = hashfunc(macro, strlen(macro)); |
426 m = macro_create(p1, macro, hash, p2); | |
18 | 427 return m; |
428 } | |
429 | |
430 static | |
431 void | |
432 macro_define_common_end(struct macro *m) | |
433 { | |
434 struct macro *oldm; | |
435 bool ok; | |
436 | |
437 oldm = macrotable_find(m->name, false); | |
438 if (oldm != NULL) { | |
439 ok = macro_eq(m, oldm); | |
440 if (ok) { | |
441 complain(&m->defplace, | |
442 "Warning: redefinition of %s", m->name); | |
17 | 443 if (mode.werror) { |
444 complain_fail(); | |
445 } | |
18 | 446 } else { |
447 complain(&m->defplace, | |
448 "Redefinition of %s is not identical", | |
449 m->name); | |
450 complain_fail(); | |
17 | 451 } |
18 | 452 complain(&oldm->defplace, "Previous definition was here"); |
453 macro_destroy(m); | |
17 | 454 return; |
455 } | |
18 | 456 macrotable_add(m); |
457 } | |
17 | 458 |
18 | 459 static |
460 void | |
461 macro_parse_parameters(struct macro *m, struct place *p, const char *params) | |
462 { | |
463 size_t len; | |
464 const char *s; | |
465 char *param; | |
466 | |
467 while (params != NULL) { | |
468 len = strspn(params, ws); | |
469 params += len; | |
470 p->column += len; | |
471 s = strchr(params, ','); | |
472 if (s) { | |
473 len = s-params; | |
474 param = dostrndup(params, len); | |
475 s++; | |
476 } else { | |
477 len = strlen(params); | |
478 param = dostrndup(params, len); | |
479 } | |
480 notrailingws(param, strlen(param)); | |
481 if (!is_identifier(param)) { | |
482 complain(p, "Invalid macro parameter name %s", param); | |
483 complain_fail(); | |
484 } else { | |
485 stringarray_add(&m->params, param, NULL); | |
486 } | |
487 params = s; | |
488 p->column += len; | |
489 } | |
490 } | |
491 | |
19 | 492 static |
493 bool | |
494 isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret) | |
495 { | |
496 unsigned num, i; | |
497 const char *param; | |
498 | |
499 num = stringarray_num(&m->params); | |
500 for (i=0; i<num; i++) { | |
501 param = stringarray_get(&m->params, i); | |
27 | 502 if (strlen(param) == len && !memcmp(name, param, len)) { |
19 | 503 *num_ret = i; |
504 return true; | |
505 } | |
506 } | |
507 return false; | |
508 } | |
509 | |
510 static | |
511 void | |
512 macro_parse_expansion(struct macro *m, const char *buf) | |
513 { | |
514 size_t blockstart, wordstart, pos; | |
515 struct expansionitem *ei; | |
516 unsigned param; | |
517 | |
518 pos = blockstart = 0; | |
519 while (buf[pos] != '\0') { | |
520 pos += strspn(buf+pos, ws); | |
521 if (strchr(alnum, buf[pos])) { | |
522 wordstart = pos; | |
523 pos += strspn(buf+pos, alnum); | |
524 if (isparam(m, buf+wordstart, pos-wordstart, ¶m)) { | |
525 if (pos > blockstart) { | |
526 ei = expansionitem_create_stringlen( | |
527 buf + blockstart, | |
27 | 528 wordstart - blockstart); |
19 | 529 expansionitemarray_add(&m->expansion, |
530 ei, NULL); | |
531 } | |
532 ei = expansionitem_create_param(param); | |
533 expansionitemarray_add(&m->expansion, ei,NULL); | |
534 blockstart = pos; | |
535 continue; | |
536 } | |
537 continue; | |
538 } | |
539 pos++; | |
540 } | |
541 if (pos > blockstart) { | |
542 ei = expansionitem_create_stringlen(buf + blockstart, | |
25 | 543 pos - blockstart); |
19 | 544 expansionitemarray_add(&m->expansion, ei, NULL); |
545 } | |
546 } | |
547 | |
18 | 548 void |
549 macro_define_plain(struct place *p1, const char *macro, | |
550 struct place *p2, const char *expansion) | |
551 { | |
552 struct macro *m; | |
19 | 553 struct expansionitem *ei; |
18 | 554 |
19 | 555 m = macro_define_common_start(p1, macro, p2); |
556 ei = expansionitem_create_string(expansion); | |
557 expansionitemarray_add(&m->expansion, ei, NULL); | |
18 | 558 macro_define_common_end(m); |
559 } | |
560 | |
561 void | |
562 macro_define_params(struct place *p1, const char *macro, | |
563 struct place *p2, const char *params, | |
564 struct place *p3, const char *expansion) | |
565 { | |
566 struct macro *m; | |
567 | |
19 | 568 m = macro_define_common_start(p1, macro, p3); |
25 | 569 m->hasparams = true; |
18 | 570 macro_parse_parameters(m, p2, params); |
19 | 571 macro_parse_expansion(m, expansion); |
18 | 572 macro_define_common_end(m); |
17 | 573 } |
574 | |
575 void | |
576 macro_undef(const char *macro) | |
577 { | |
578 struct macro *m; | |
579 | |
580 m = macrotable_find(macro, true); | |
581 if (m) { | |
582 macro_destroy(m); | |
583 } | |
584 } | |
585 | |
586 bool | |
587 macro_isdefined(const char *macro) | |
588 { | |
589 struct macro *m; | |
590 | |
591 m = macrotable_find(macro, false); | |
592 return m != NULL; | |
593 } | |
594 | |
595 //////////////////////////////////////////////////////////// | |
596 // macro expansion | |
597 | |
19 | 598 struct expstate { |
599 bool honordefined; | |
600 enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state; | |
601 struct macro *curmacro; | |
602 struct stringarray args; | |
603 unsigned argparens; | |
604 | |
605 bool tobuf; | |
606 char *buf; | |
607 size_t bufpos, bufmax; | |
608 }; | |
609 | |
610 static struct expstate mainstate; | |
611 | |
612 static void doexpand(struct expstate *es, struct place *p, | |
613 char *buf, size_t len); | |
614 | |
615 static | |
616 void | |
617 expstate_init(struct expstate *es, bool tobuf, bool honordefined) | |
618 { | |
619 es->honordefined = honordefined; | |
620 es->state = ES_NORMAL; | |
621 es->curmacro = NULL; | |
622 stringarray_init(&es->args); | |
623 es->argparens = 0; | |
624 es->tobuf = tobuf; | |
625 es->buf = NULL; | |
626 es->bufpos = 0; | |
627 es->bufmax = 0; | |
628 } | |
629 | |
630 static | |
631 void | |
632 expstate_cleanup(struct expstate *es) | |
633 { | |
634 assert(es->state == ES_NORMAL); | |
635 stringarray_cleanup(&es->args); | |
636 if (es->buf) { | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
637 dofree(es->buf, es->bufmax); |
19 | 638 } |
639 } | |
640 | |
641 static | |
642 void | |
643 expstate_destroyargs(struct expstate *es) | |
644 { | |
645 unsigned i, num; | |
646 | |
647 num = stringarray_num(&es->args); | |
648 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
|
649 dostrfree(stringarray_get(&es->args, i)); |
19 | 650 } |
651 stringarray_setsize(&es->args, 0); | |
652 } | |
653 | |
654 static | |
655 void | |
21 | 656 expand_send(struct expstate *es, struct place *p, const char *buf, size_t len) |
19 | 657 { |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
658 size_t oldmax; |
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
659 |
19 | 660 if (es->tobuf) { |
38
b156910b59b2
Wrap free() in dofree() to allow instrumenting it for debugging.
David A. Holland
parents:
33
diff
changeset
|
661 assert(es->bufpos <= es->bufmax); |
19 | 662 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
|
663 oldmax = es->bufmax; |
19 | 664 if (es->bufmax == 0) { |
665 es->bufmax = 64; | |
666 } | |
667 while (es->bufpos + len > es->bufmax) { | |
668 es->bufmax *= 2; | |
669 } | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
670 es->buf = dorealloc(es->buf, oldmax, es->bufmax); |
19 | 671 } |
672 memcpy(es->buf + es->bufpos, buf, len); | |
673 es->bufpos += len; | |
38
b156910b59b2
Wrap free() in dofree() to allow instrumenting it for debugging.
David A. Holland
parents:
33
diff
changeset
|
674 assert(es->bufpos <= es->bufmax); |
19 | 675 } else { |
21 | 676 output(p, buf, len); |
19 | 677 } |
678 } | |
679 | |
680 static | |
681 void | |
21 | 682 expand_send_eof(struct expstate *es, struct place *p) |
19 | 683 { |
684 if (es->tobuf) { | |
21 | 685 expand_send(es, p, "", 1); |
19 | 686 es->bufpos--; |
687 } else { | |
688 output_eof(); | |
689 } | |
690 } | |
691 | |
692 static | |
693 void | |
694 expand_newarg(struct expstate *es, char *buf, size_t len) | |
695 { | |
696 char *text; | |
697 | |
698 text = dostrndup(buf, len); | |
699 stringarray_add(&es->args, text, NULL); | |
700 } | |
701 | |
702 static | |
703 void | |
704 expand_appendarg(struct expstate *es, char *buf, size_t len) | |
705 { | |
706 unsigned num; | |
707 char *text; | |
708 size_t oldlen; | |
709 | |
710 num = stringarray_num(&es->args); | |
711 assert(num > 0); | |
712 | |
713 text = stringarray_get(&es->args, num - 1); | |
714 oldlen = strlen(text); | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
715 text = dorealloc(text, oldlen + 1, oldlen + len + 1); |
19 | 716 memcpy(text + oldlen, buf, len); |
717 text[oldlen+len] = '\0'; | |
718 stringarray_set(&es->args, num - 1, text); | |
719 } | |
720 | |
721 static | |
722 char * | |
28 | 723 expand_substitute(struct place *p, struct expstate *es) |
19 | 724 { |
725 struct expansionitem *ei; | |
726 unsigned i, num; | |
727 size_t len; | |
728 char *arg; | |
729 char *ret; | |
28 | 730 unsigned numargs, numparams; |
731 | |
732 numargs = stringarray_num(&es->args); | |
733 numparams = stringarray_num(&es->curmacro->params); | |
734 | |
735 if (numargs == 0 && numparams == 1) { | |
736 /* no arguments <=> one empty argument */ | |
737 stringarray_add(&es->args, dostrdup(""), NULL); | |
738 numargs++; | |
739 } | |
740 if (numargs != numparams) { | |
741 complain(p, "Wrong number of arguments for macro %s; " | |
742 "found %u, expected %u", | |
743 es->curmacro->name, numargs, numparams); | |
744 complain_fail(); | |
745 while (numargs < numparams) { | |
746 stringarray_add(&es->args, dostrdup(""), NULL); | |
747 numargs++; | |
748 } | |
749 } | |
19 | 750 |
751 len = 0; | |
752 num = expansionitemarray_num(&es->curmacro->expansion); | |
753 for (i=0; i<num; i++) { | |
754 ei = expansionitemarray_get(&es->curmacro->expansion, i); | |
755 if (ei->isstring) { | |
756 len += strlen(ei->string); | |
757 } else { | |
758 arg = stringarray_get(&es->args, ei->param); | |
759 len += strlen(arg); | |
760 } | |
761 } | |
762 | |
763 ret = domalloc(len+1); | |
764 *ret = '\0'; | |
765 for (i=0; i<num; i++) { | |
766 ei = expansionitemarray_get(&es->curmacro->expansion, i); | |
767 if (ei->isstring) { | |
768 strcat(ret, ei->string); | |
769 } else { | |
770 arg = stringarray_get(&es->args, ei->param); | |
771 strcat(ret, arg); | |
772 } | |
773 } | |
774 | |
775 return ret; | |
776 } | |
777 | |
778 static | |
779 void | |
780 expand_domacro(struct expstate *es, struct place *p) | |
781 { | |
782 struct macro *m; | |
783 char *newbuf, *newbuf2; | |
784 | |
785 if (es->curmacro == NULL) { | |
786 /* defined() */ | |
787 if (stringarray_num(&es->args) != 1) { | |
788 complain(p, "Too many arguments for defined()"); | |
789 complain_fail(); | |
21 | 790 expand_send(es, p, "0", 1); |
19 | 791 return; |
792 } | |
793 m = macrotable_find(stringarray_get(&es->args, 0), false); | |
21 | 794 expand_send(es, p, (m != NULL) ? "1" : "0", 1); |
25 | 795 expstate_destroyargs(es); |
19 | 796 return; |
797 } | |
798 | |
799 assert(es->curmacro->inuse == false); | |
800 es->curmacro->inuse = true; | |
801 | |
28 | 802 newbuf = expand_substitute(p, es); |
19 | 803 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
|
804 dostrfree(newbuf); |
25 | 805 expstate_destroyargs(es); |
19 | 806 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
|
807 dostrfree(newbuf2); |
19 | 808 |
809 es->curmacro->inuse = false; | |
810 } | |
811 | |
812 static | |
813 void | |
814 expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len) | |
815 { | |
816 switch (es->state) { | |
817 case ES_NORMAL: | |
21 | 818 expand_send(es, p, buf, len); |
19 | 819 break; |
820 case ES_WANTLPAREN: | |
821 break; | |
822 case ES_NOARG: | |
823 break; | |
824 case ES_HAVEARG: | |
825 expand_appendarg(es, buf, len); | |
826 break; | |
827 } | |
828 } | |
829 | |
830 static | |
831 void | |
832 expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len) | |
833 { | |
834 struct macro *m; | |
835 struct expansionitem *ei; | |
836 char *newbuf; | |
17 | 837 |
19 | 838 switch (es->state) { |
839 case ES_NORMAL: | |
840 if (es->honordefined && | |
841 len == 7 && !memcmp(buf, "defined", 7)) { | |
842 es->curmacro = NULL; | |
843 es->state = ES_WANTLPAREN; | |
844 break; | |
845 } | |
846 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
|
847 if (m == NULL || m->inuse) { |
21 | 848 expand_send(es, p, buf, len); |
19 | 849 } else if (!m->hasparams) { |
850 m->inuse = true; | |
851 assert(expansionitemarray_num(&m->expansion) == 1); | |
852 ei = expansionitemarray_get(&m->expansion, 0); | |
853 assert(ei->isstring); | |
854 newbuf = macroexpand(p, ei->string, | |
855 strlen(ei->string), false); | |
856 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
|
857 dostrfree(newbuf); |
19 | 858 m->inuse = false; |
859 } else { | |
860 es->curmacro = m; | |
861 es->state = ES_WANTLPAREN; | |
862 } | |
863 break; | |
864 case ES_WANTLPAREN: | |
865 if (es->curmacro != NULL) { | |
866 complain(p, "Expected arguments for macro %s", | |
867 es->curmacro->name); | |
868 complain_fail(); | |
869 } else { | |
870 /* "defined foo" means "defined(foo)" */ | |
871 expand_newarg(es, buf, len); | |
872 es->state = ES_NORMAL; | |
873 expand_domacro(es, p); | |
874 break; | |
875 } | |
876 break; | |
877 case ES_NOARG: | |
878 expand_newarg(es, buf, len); | |
879 es->state = ES_HAVEARG; | |
880 break; | |
881 case ES_HAVEARG: | |
882 expand_appendarg(es, buf, len); | |
883 break; | |
884 } | |
885 } | |
886 | |
887 static | |
888 void | |
889 expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len) | |
890 { | |
891 switch (es->state) { | |
892 case ES_NORMAL: | |
21 | 893 expand_send(es, p, buf, len); |
19 | 894 break; |
895 case ES_WANTLPAREN: | |
896 es->state = ES_NOARG; | |
897 break; | |
898 case ES_NOARG: | |
899 expand_newarg(es, buf, len); | |
900 es->state = ES_HAVEARG; | |
901 es->argparens++; | |
902 break; | |
903 case ES_HAVEARG: | |
904 expand_appendarg(es, buf, len); | |
905 es->argparens++; | |
906 break; | |
907 } | |
908 } | |
909 | |
910 static | |
911 void | |
912 expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len) | |
913 { | |
914 switch (es->state) { | |
915 case ES_NORMAL: | |
21 | 916 expand_send(es, p, buf, len); |
19 | 917 break; |
918 case ES_WANTLPAREN: | |
919 if (es->curmacro) { | |
920 complain(p, "Expected arguments for macro %s", | |
921 es->curmacro->name); | |
922 } else { | |
923 complain(p, "Expected arguments for defined()"); | |
924 } | |
925 complain_fail(); | |
926 break; | |
927 case ES_NOARG: | |
928 assert(es->argparens == 0); | |
929 es->state = ES_NORMAL; | |
930 expand_domacro(es, p); | |
931 break; | |
932 case ES_HAVEARG: | |
933 if (es->argparens > 0) { | |
934 es->argparens--; | |
935 expand_appendarg(es, buf, len); | |
936 } else { | |
937 es->state = ES_NORMAL; | |
938 expand_domacro(es, p); | |
939 } | |
940 break; | |
941 } | |
942 } | |
943 | |
944 static | |
945 void | |
946 expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len) | |
947 { | |
948 switch (es->state) { | |
949 case ES_NORMAL: | |
21 | 950 expand_send(es, p, buf, len); |
19 | 951 break; |
952 case ES_WANTLPAREN: | |
953 if (es->curmacro) { | |
954 complain(p, "Expected arguments for macro %s", | |
955 es->curmacro->name); | |
956 } else { | |
957 complain(p, "Expected arguments for defined()"); | |
958 } | |
959 complain_fail(); | |
960 break; | |
961 case ES_NOARG: | |
962 assert(es->argparens == 0); | |
963 expand_newarg(es, buf, 0); | |
964 break; | |
965 case ES_HAVEARG: | |
966 if (es->argparens > 0) { | |
967 expand_appendarg(es, buf, len); | |
968 } else { | |
969 es->state = ES_NOARG; | |
970 } | |
971 break; | |
972 } | |
973 } | |
974 | |
975 static | |
976 void | |
977 expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len) | |
978 { | |
979 switch (es->state) { | |
980 case ES_NORMAL: | |
21 | 981 expand_send(es, p, buf, len); |
19 | 982 break; |
983 case ES_WANTLPAREN: | |
984 if (es->curmacro) { | |
985 complain(p, "Expected arguments for macro %s", | |
986 es->curmacro->name); | |
987 } else { | |
988 complain(p, "Expected arguments for defined()"); | |
989 } | |
990 complain_fail(); | |
991 break; | |
992 case ES_NOARG: | |
993 expand_newarg(es, buf, len); | |
994 es->state = ES_HAVEARG; | |
995 break; | |
996 case ES_HAVEARG: | |
997 expand_appendarg(es, buf, len); | |
998 break; | |
999 } | |
1000 } | |
1001 | |
1002 static | |
1003 void | |
1004 expand_got_eof(struct expstate *es, struct place *p) | |
1005 { | |
1006 switch (es->state) { | |
1007 case ES_NORMAL: | |
21 | 1008 expand_send_eof(es, p); |
19 | 1009 break; |
1010 case ES_WANTLPAREN: | |
1011 if (es->curmacro) { | |
1012 complain(p, "Expected arguments for macro %s", | |
1013 es->curmacro->name); | |
1014 } else { | |
1015 complain(p, "Expected arguments for defined()"); | |
1016 } | |
1017 complain_fail(); | |
1018 break; | |
1019 case ES_NOARG: | |
1020 case ES_HAVEARG: | |
1021 if (es->curmacro) { | |
1022 complain(p, "Unclosed argument list for macro %s", | |
1023 es->curmacro->name); | |
1024 } else { | |
1025 complain(p, "Unclosed argument list for defined()"); | |
1026 } | |
1027 complain_fail(); | |
1028 expstate_destroyargs(es); | |
1029 break; | |
1030 } | |
1031 es->state = ES_NORMAL; | |
1032 es->curmacro = NULL; | |
1033 es->argparens = 0; | |
1034 } | |
1035 | |
1036 static | |
1037 void | |
1038 doexpand(struct expstate *es, struct place *p, char *buf, size_t len) | |
1039 { | |
1040 size_t x; | |
1041 | |
1042 while (len > 0) { | |
1043 x = strspn(buf, ws); | |
28 | 1044 if (x > len) { |
1045 /* XXX gross, need strnspn */ | |
1046 x = len; | |
1047 } | |
1048 | |
19 | 1049 if (x > 0) { |
1050 expand_got_ws(es, p, buf, x); | |
1051 buf += x; | |
1052 len -= x; | |
28 | 1053 continue; |
19 | 1054 } |
1055 | |
1056 x = strspn(buf, alnum); | |
28 | 1057 if (x > len) { |
1058 /* XXX gross, need strnspn */ | |
1059 x = len; | |
1060 } | |
1061 | |
19 | 1062 if (x > 0) { |
1063 expand_got_word(es, p, buf, x); | |
1064 buf += x; | |
1065 len -= x; | |
1066 continue; | |
1067 } | |
1068 | |
1069 if (buf[0] == '(') { | |
1070 expand_got_lparen(es, p, buf, 1); | |
1071 buf++; | |
1072 len--; | |
1073 continue; | |
1074 } | |
1075 | |
1076 if (buf[0] == ')') { | |
1077 expand_got_rparen(es, p, buf, 1); | |
1078 buf++; | |
1079 len--; | |
1080 continue; | |
1081 } | |
1082 | |
1083 if (buf[0] == ',') { | |
1084 expand_got_comma(es, p, buf, 1); | |
1085 buf++; | |
1086 len--; | |
1087 continue; | |
1088 } | |
1089 | |
1090 expand_got_other(es, p, buf, 1); | |
1091 buf++; | |
1092 len--; | |
1093 } | |
1094 } | |
1095 | |
1096 char * | |
1097 macroexpand(struct place *p, char *buf, size_t len, bool honordefined) | |
1098 { | |
1099 struct expstate es; | |
1100 char *ret; | |
1101 | |
1102 expstate_init(&es, true, honordefined); | |
1103 doexpand(&es, p, buf, len); | |
1104 expand_got_eof(&es, p); | |
40 | 1105 |
1106 /* trim to fit, so the malloc debugging won't complain */ | |
1107 es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1); | |
1108 | |
19 | 1109 ret = es.buf; |
1110 es.buf = NULL; | |
39
337110e7240a
Pass the size to free; it makes debug checking easier.
David A. Holland
parents:
38
diff
changeset
|
1111 es.bufpos = es.bufmax = 0; |
40 | 1112 |
19 | 1113 expstate_cleanup(&es); |
1114 | |
1115 return ret; | |
1116 } | |
1117 | |
1118 void | |
1119 macro_sendline(struct place *p, char *buf, size_t len) | |
1120 { | |
1121 doexpand(&mainstate, p, buf, len); | |
21 | 1122 output(p, "\n", 1); |
19 | 1123 } |
1124 | |
1125 void | |
1126 macro_sendeof(struct place *p) | |
1127 { | |
1128 expand_got_eof(&mainstate, p); | |
1129 } | |
17 | 1130 |
1131 //////////////////////////////////////////////////////////// | |
1132 // module initialization | |
1133 | |
1134 void | |
1135 macros_init(void) | |
1136 { | |
1137 macrotable_init(); | |
19 | 1138 expstate_init(&mainstate, false, false); |
17 | 1139 } |
1140 | |
1141 void | |
1142 macros_cleanup(void) | |
1143 { | |
19 | 1144 expstate_cleanup(&mainstate); |
17 | 1145 macrotable_cleanup(); |
1146 } |