tradcpp: upgrade to 0.5.3
[oweals/cde.git] / cde / util / tradcpp / macro.c
1 /*-
2  * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by David A. Holland.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "union.h"
36 #include "array.h"
37 #include "mode.h"
38 #include "place.h"
39 #include "macro.h"
40 #include "output.h"
41
42 struct expansionitem {
43         enum { EI_STRING, EI_PARAM, EI_FILE, EI_LINE } itemtype;
44         union {
45                 char *ei_string;                /* for EI_STRING */
46                 unsigned ei_param;              /* for EI_PARAM */
47         } UN;
48 };
49 DECLARRAY(expansionitem, static UNUSED);
50 DEFARRAY(expansionitem, static);
51
52 #ifdef NEED_UNION_ACCESSORS
53 #define ei_string un.ei_string
54 #define ei_param un.ei_param
55 #endif
56
57
58 struct macro {
59         struct place defplace;
60         struct place expansionplace;
61         unsigned hash;
62         char *name;
63         bool hasparams;
64         struct stringarray params;
65         struct expansionitemarray expansion;
66         bool inuse;
67 };
68 DECLARRAY(macro, static UNUSED);
69 DEFARRAY(macro, static);
70 DECLARRAY(macroarray, static UNUSED);
71 DEFARRAY(macroarray, static);
72
73 static struct macroarrayarray macros;
74 static unsigned total_macros;
75 static unsigned hashmask;
76
77 ////////////////////////////////////////////////////////////
78 // macro structure ops
79
80 static
81 struct expansionitem *
82 expansionitem_create_string(const char *string)
83 {
84         struct expansionitem *ei;
85
86         ei = domalloc(sizeof(*ei));
87         ei->itemtype = EI_STRING;
88         ei->ei_string = dostrdup(string);
89         return ei;
90 }
91
92 static
93 struct expansionitem *
94 expansionitem_create_stringlen(const char *string, size_t len)
95 {
96         struct expansionitem *ei;
97
98         ei = domalloc(sizeof(*ei));
99         ei->itemtype = EI_STRING;
100         ei->ei_string = dostrndup(string, len);
101         return ei;
102 }
103
104 static
105 struct expansionitem *
106 expansionitem_create_param(unsigned param)
107 {
108         struct expansionitem *ei;
109
110         ei = domalloc(sizeof(*ei));
111         ei->itemtype = EI_PARAM;
112         ei->ei_param = param;
113         return ei;
114 }
115
116 static
117 struct expansionitem *
118 expansionitem_create_file(void)
119 {
120         struct expansionitem *ei;
121
122         ei = domalloc(sizeof(*ei));
123         ei->itemtype = EI_FILE;
124         return ei;
125 }
126
127 static
128 struct expansionitem *
129 expansionitem_create_line(void)
130 {
131         struct expansionitem *ei;
132
133         ei = domalloc(sizeof(*ei));
134         ei->itemtype = EI_LINE;
135         return ei;
136 }
137
138 static
139 void
140 expansionitem_destroy(struct expansionitem *ei)
141 {
142         switch (ei->itemtype) {
143             case EI_STRING:
144                 dostrfree(ei->ei_string);
145                 break;
146             case EI_PARAM:
147             case EI_FILE:
148             case EI_LINE:
149                 break;
150         }
151         dofree(ei, sizeof(*ei));
152 }
153
154 static
155 bool
156 expansionitem_eq(const struct expansionitem *ei1,
157                  const struct expansionitem *ei2)
158 {
159         if (ei1->itemtype != ei2->itemtype) {
160                 return false;
161         }
162         switch (ei1->itemtype) {
163             case EI_STRING:
164                 if (strcmp(ei1->ei_string, ei2->ei_string) != 0) {
165                         return false;
166                 }
167                 break;
168             case EI_PARAM:
169                 if (ei1->ei_param != ei2->ei_param) {
170                         return false;
171                 }
172                 break;
173             case EI_FILE:
174             case EI_LINE:
175                 break;
176         }
177         return true;
178 }
179
180 static
181 struct macro *
182 macro_create(struct place *p1, const char *name, unsigned hash,
183              struct place *p2)
184 {
185         struct macro *m;
186
187         m = domalloc(sizeof(*m));
188         m->defplace = *p1;
189         m->expansionplace = *p2;
190         m->hash = hash;
191         m->name = dostrdup(name);
192         m->hasparams = false;
193         stringarray_init(&m->params);
194         expansionitemarray_init(&m->expansion);
195         m->inuse = false;
196         return m;
197 }
198
199 DESTROYALL_ARRAY(expansionitem, );
200
201 static
202 void
203 macro_destroy(struct macro *m)
204 {
205         expansionitemarray_destroyall(&m->expansion);
206         expansionitemarray_cleanup(&m->expansion);
207         dostrfree(m->name);
208         dofree(m, sizeof(*m));
209 }
210
211 static
212 bool
213 macro_eq(const struct macro *m1, const struct macro *m2)
214 {
215         unsigned num1, num2, i;
216         struct expansionitem *ei1, *ei2;
217         const char *p1, *p2;
218
219         if (strcmp(m1->name, m2->name) != 0) {
220                 return false;
221         }
222
223         if (m1->hasparams != m2->hasparams) {
224                 return false;
225         }
226
227         num1 = expansionitemarray_num(&m1->expansion);
228         num2 = expansionitemarray_num(&m2->expansion);
229         if (num1 != num2) {
230                 return false;
231         }
232
233         for (i=0; i<num1; i++) {
234                 ei1 = expansionitemarray_get(&m1->expansion, i);
235                 ei2 = expansionitemarray_get(&m2->expansion, i);
236                 if (!expansionitem_eq(ei1, ei2)) {
237                         return false;
238                 }
239         }
240
241         num1 = stringarray_num(&m1->params);
242         num2 = stringarray_num(&m2->params);
243         if (num1 != num2) {
244                 return false;
245         }
246
247         for (i=0; i<num1; i++) {
248                 p1 = stringarray_get(&m1->params, i);
249                 p2 = stringarray_get(&m2->params, i);
250                 if (strcmp(p1, p2) != 0) {
251                         return false;
252                 }
253         }
254         return true;
255 }
256
257 ////////////////////////////////////////////////////////////
258 // macro table
259
260 /*
261  * Unless I've screwed up, this is something called Fletcher's Checksum
262  * that showed up in Dr. Dobbs in, according to my notes, May 1992. The
263  * implementation is new.
264  */
265 static
266 unsigned
267 hashfunc(const char *s, size_t len)
268 {
269         uint16_t x1, x2, a;
270         size_t i;
271
272         x1 = (uint16_t) (len >> 16);
273         x2 = (uint16_t) (len);
274         if (x1==0) {
275                 x1++;
276         }
277         if (x2==0) {
278                 x2++;
279         }
280
281         for (i=0; i<len; i+=2) {
282                 if (i==len-1) {
283                         a = (unsigned char)s[i];
284                         /* don't run off the end of the array */
285                 }
286                 else {
287                         a = (unsigned char)s[i] +
288                                 ((uint16_t)(unsigned char)s[i+1] << 8);
289                 }
290                 x1 += a;
291                 if (x1 < a) {
292                         x1++;
293                 }
294                 x2 += x1;
295                 if (x2 < x1) {
296                         x2++;
297                 }
298         }
299
300         x1 ^= 0xffff;
301         x2 ^= 0xffff;
302         return ((uint32_t)x2)*65535U + x1;
303 }
304
305 static
306 void
307 macrotable_init(void)
308 {
309         unsigned i;
310
311         macroarrayarray_init(&macros);
312         macroarrayarray_setsize(&macros, 4);
313         for (i=0; i<4; i++) {
314                 macroarrayarray_set(&macros, i, NULL);
315         }
316         total_macros = 0;
317         hashmask = 0x3;
318 }
319
320 DESTROYALL_ARRAY(macro, );
321
322 static
323 void
324 macrotable_cleanup(void)
325 {
326         struct macroarray *bucket;
327         unsigned numbuckets, i;
328
329         numbuckets = macroarrayarray_num(&macros);
330         for (i=0; i<numbuckets; i++) {
331                 bucket = macroarrayarray_get(&macros, i);
332                 if (bucket != NULL) {
333                         macroarray_destroyall(bucket);
334                         macroarray_destroy(bucket);
335                 }
336         }
337         macroarrayarray_setsize(&macros, 0);
338         macroarrayarray_cleanup(&macros);
339 }
340
341 static
342 struct macro *
343 macrotable_findlen(const char *name, size_t len, bool remove_it)
344 {
345         unsigned hash;
346         struct macroarray *bucket;
347         struct macro *m, *m2;
348         unsigned i, num;
349         size_t mlen;
350
351         hash = hashfunc(name, len);
352         bucket = macroarrayarray_get(&macros, hash & hashmask);
353         if (bucket == NULL) {
354                 return NULL;
355         }
356         num = macroarray_num(bucket);
357         for (i=0; i<num; i++) {
358                 m = macroarray_get(bucket, i);
359                 if (hash != m->hash) {
360                         continue;
361                 }
362                 mlen = strlen(m->name);
363                 if (len == mlen && !memcmp(name, m->name, len)) {
364                         if (remove_it) {
365                                 if (i < num-1) {
366                                         m2 = macroarray_get(bucket, num-1);
367                                         macroarray_set(bucket, i, m2);
368                                 }
369                                 macroarray_setsize(bucket, num-1);
370                                 total_macros--;
371                         }
372                         return m;
373                 }
374         }
375         return NULL;
376 }
377
378 static
379 struct macro *
380 macrotable_find(const char *name, bool remove_it)
381 {
382         return macrotable_findlen(name, strlen(name), remove_it);
383 }
384
385 static
386 void
387 macrotable_rehash(void)
388 {
389         struct macroarray *newbucket, *oldbucket;
390         struct macro *m;
391         unsigned newmask, tossbit;
392         unsigned numbuckets, i;
393         unsigned oldnum, j, k;
394
395         numbuckets = macroarrayarray_num(&macros);
396         macroarrayarray_setsize(&macros, numbuckets*2);
397
398         assert(hashmask == numbuckets - 1);
399         newmask = (hashmask << 1) | 1U;
400         tossbit = newmask & ~hashmask;
401         hashmask = newmask;
402
403         for (i=0; i<numbuckets; i++) {
404                 newbucket = NULL;
405                 oldbucket = macroarrayarray_get(&macros, i);
406                 if (oldbucket == NULL) {
407                         macroarrayarray_set(&macros, numbuckets + i, NULL);
408                         continue;
409                 }
410                 oldnum = macroarray_num(oldbucket);
411                 for (j=0; j<oldnum; j++) {
412                         m = macroarray_get(oldbucket, j);
413                         if (m->hash & tossbit) {
414                                 if (newbucket == NULL) {
415                                         newbucket = macroarray_create();
416                                 }
417                                 macroarray_set(oldbucket, j, NULL);
418                                 macroarray_add(newbucket, m, NULL);
419                         }
420                 }
421                 for (j=k=0; j<oldnum; j++) {
422                         m = macroarray_get(oldbucket, j);
423                         if (m != NULL) {
424                                 if (k < j) {
425                                         macroarray_set(oldbucket, k, m);
426                                 }
427                                 k++;
428                         }
429                 }
430                 macroarray_setsize(oldbucket, k);
431                 macroarrayarray_set(&macros, numbuckets + i, newbucket);
432         }
433 }
434
435 static
436 void
437 macrotable_add(struct macro *m)
438 {
439         unsigned hash;
440         struct macroarray *bucket;
441         unsigned numbuckets;
442
443         numbuckets = macroarrayarray_num(&macros);
444         if (total_macros > 0 && total_macros / numbuckets > 9) {
445                 macrotable_rehash();
446         }
447
448         hash = hashfunc(m->name, strlen(m->name));
449         bucket = macroarrayarray_get(&macros, hash & hashmask);
450         if (bucket == NULL) {
451                 bucket = macroarray_create();
452                 macroarrayarray_set(&macros, hash & hashmask, bucket);
453         }
454         macroarray_add(bucket, m, NULL);
455         total_macros++;
456 }
457
458 ////////////////////////////////////////////////////////////
459 // external macro definition interface
460
461 static
462 struct macro *
463 macro_define_common_start(struct place *p1, const char *macro,
464                           struct place *p2)
465 {
466         struct macro *m;
467         unsigned hash;
468
469         if (!is_identifier(macro)) {
470                 complain(p1, "Invalid macro name %s", macro);
471                 complain_fail();
472         }
473
474         hash = hashfunc(macro, strlen(macro));
475         m = macro_create(p1, macro, hash, p2);
476         return m;
477 }
478
479 static
480 void
481 macro_define_common_end(struct macro *m)
482 {
483         struct macro *oldm;
484         bool ok;
485
486         oldm = macrotable_find(m->name, false);
487         if (oldm != NULL) {
488                 ok = macro_eq(m, oldm);
489                 if (ok) {
490                         /* in traditional cpp this is silent */
491                         //complain(&m->defplace,
492                         //       "Warning: redefinition of %s", m->name);
493                         //complain(&oldm->defplace,
494                         //       "Previous definition was here");
495                         //if (mode.werror) {
496                         //      complain_fail();
497                         //}
498                 } else {
499                         complain(&m->defplace,
500                                  "Warning: non-identical redefinition of %s",
501                                  m->name);
502                         complain(&oldm->defplace,
503                                  "Previous definition was here");
504                         /* in traditional cpp this is not fatal */
505                         if (mode.werror) {
506                                 complain_fail();
507                         }
508                 }
509                 macro_destroy(m);
510                 return;
511         }
512         macrotable_add(m);
513 }
514
515 static
516 void
517 macro_parse_parameters(struct macro *m, struct place *p, const char *params)
518 {
519         size_t len;
520         const char *s;
521         char *param;
522
523         while (params != NULL) {
524                 len = strspn(params, ws);
525                 params += len;
526                 place_addcolumns(p, len);
527                 s = strchr(params, ',');
528                 if (s) {
529                         len = s-params;
530                         param = dostrndup(params, len);
531                         s++;
532                 } else {
533                         len = strlen(params);
534                         param = dostrndup(params, len);
535                 }
536                 notrailingws(param, strlen(param));
537                 if (!is_identifier(param)) {
538                         complain(p, "Invalid macro parameter name %s", param);
539                         complain_fail();
540                 } else {
541                         stringarray_add(&m->params, param, NULL);
542                 }
543                 params = s;
544                 place_addcolumns(p, len);
545         }
546 }
547
548 static
549 bool
550 isparam(struct macro *m, const char *name, size_t len, unsigned *num_ret)
551 {
552         unsigned num, i;
553         const char *param;
554
555         num = stringarray_num(&m->params);
556         for (i=0; i<num; i++) {
557                 param = stringarray_get(&m->params, i);
558                 if (strlen(param) == len && !memcmp(name, param, len)) {
559                         *num_ret = i;
560                         return true;
561                 }
562         }
563         return false;
564 }
565
566 static
567 void
568 macro_parse_expansion(struct macro *m, const char *buf)
569 {
570         size_t blockstart, wordstart, pos;
571         struct expansionitem *ei;
572         unsigned param;
573
574         pos = blockstart = 0;
575         while (buf[pos] != '\0') {
576                 pos += strspn(buf+pos, ws);
577                 if (strchr(alnum, buf[pos])) {
578                         wordstart = pos;
579                         pos += strspn(buf+pos, alnum);
580                         if (isparam(m, buf+wordstart, pos-wordstart, &param)) {
581                                 if (wordstart > blockstart) {
582                                         ei = expansionitem_create_stringlen(
583                                                 buf + blockstart,
584                                                 wordstart - blockstart);
585                                         expansionitemarray_add(&m->expansion,
586                                                                ei, NULL);
587                                 }
588                                 ei = expansionitem_create_param(param);
589                                 expansionitemarray_add(&m->expansion, ei,NULL);
590                                 blockstart = pos;
591                                 continue;
592                         }
593                         continue;
594                 }
595                 pos++;
596         }
597         if (pos > blockstart) {
598                 ei = expansionitem_create_stringlen(buf + blockstart,
599                                                     pos - blockstart);
600                 expansionitemarray_add(&m->expansion, ei, NULL);
601         }
602 }
603
604 void
605 macro_define_plain(struct place *p1, const char *macro,
606                    struct place *p2, const char *expansion)
607 {
608         struct macro *m;
609         struct expansionitem *ei;
610
611         m = macro_define_common_start(p1, macro, p2);
612         ei = expansionitem_create_string(expansion);
613         expansionitemarray_add(&m->expansion, ei, NULL);
614         macro_define_common_end(m);
615 }
616
617 void
618 macro_define_params(struct place *p1, const char *macro,
619                     struct place *p2, const char *params,
620                     struct place *p3, const char *expansion)
621 {
622         struct macro *m;
623
624         m = macro_define_common_start(p1, macro, p3);
625         m->hasparams = true;
626         macro_parse_parameters(m, p2, params);
627         macro_parse_expansion(m, expansion);
628         macro_define_common_end(m);
629 }
630
631 void
632 macro_define_magic(struct place *p, const char *macro)
633 {
634         struct macro *m;
635         struct expansionitem *ei;
636
637         m = macro_define_common_start(p, macro, p);
638         if (!strcmp(macro, "__FILE__")) {
639                 ei = expansionitem_create_file();
640         }
641         else {
642                 assert(!strcmp(macro, "__LINE__"));
643                 ei = expansionitem_create_line();
644         }
645         expansionitemarray_add(&m->expansion, ei, NULL);
646         macro_define_common_end(m);
647 }
648
649 void
650 macro_undef(const char *macro)
651 {
652         struct macro *m;
653
654         m = macrotable_find(macro, true);
655         if (m) {
656                 macro_destroy(m);
657         }
658 }
659
660 bool
661 macro_isdefined(const char *macro)
662 {
663         struct macro *m;
664
665         m = macrotable_find(macro, false);
666         return m != NULL;
667 }
668
669 ////////////////////////////////////////////////////////////
670 // macro expansion
671
672 struct expstate {
673         bool honordefined;
674         enum { ES_NORMAL, ES_WANTLPAREN, ES_NOARG, ES_HAVEARG } state;
675         struct macro *curmacro;
676         struct stringarray args;
677         unsigned argparens;
678
679         bool tobuf;
680         char *buf;
681         size_t bufpos, bufmax;
682 };
683
684 static struct expstate mainstate;
685
686 static void doexpand(struct expstate *es, struct place *p,
687                      const char *buf, size_t len);
688
689 static
690 void
691 expstate_init(struct expstate *es, bool tobuf, bool honordefined)
692 {
693         es->honordefined = honordefined;
694         es->state = ES_NORMAL;
695         es->curmacro = NULL;
696         stringarray_init(&es->args);
697         es->argparens = 0;
698         es->tobuf = tobuf;
699         es->buf = NULL;
700         es->bufpos = 0;
701         es->bufmax = 0;
702 }
703
704 static
705 void
706 expstate_cleanup(struct expstate *es)
707 {
708         assert(es->state == ES_NORMAL);
709         stringarray_cleanup(&es->args);
710         if (es->buf) {
711                 dofree(es->buf, es->bufmax);
712         }
713 }
714
715 static
716 void
717 expstate_destroyargs(struct expstate *es)
718 {
719         unsigned i, num;
720
721         num = stringarray_num(&es->args);
722         for (i=0; i<num; i++) {
723                 dostrfree(stringarray_get(&es->args, i));
724         }
725         stringarray_setsize(&es->args, 0);
726 }
727
728 static
729 void
730 expand_send(struct expstate *es, struct place *p, const char *buf, size_t len)
731 {
732         size_t oldmax;
733
734         if (es->tobuf) {
735                 assert(es->bufpos <= es->bufmax);
736                 if (es->bufpos + len > es->bufmax) {
737                         oldmax = es->bufmax;
738                         if (es->bufmax == 0) {
739                                 es->bufmax = 64;
740                         }
741                         while (es->bufpos + len > es->bufmax) {
742                                 es->bufmax *= 2;
743                         }
744                         es->buf = dorealloc(es->buf, oldmax, es->bufmax);
745                 }
746                 memcpy(es->buf + es->bufpos, buf, len);
747                 es->bufpos += len;
748                 assert(es->bufpos <= es->bufmax);
749         } else {
750                 output(p, buf, len);
751         }
752 }
753
754 static
755 void
756 expand_send_eof(struct expstate *es, struct place *p)
757 {
758         if (es->tobuf) {
759                 expand_send(es, p, "", 1);
760                 es->bufpos--;
761         } else {
762                 output_eof();
763         }
764 }
765
766 static
767 void
768 expand_newarg(struct expstate *es, const char *buf, size_t len)
769 {
770         char *text;
771
772         text = dostrndup(buf, len);
773         stringarray_add(&es->args, text, NULL);
774 }
775
776 static
777 void
778 expand_appendarg(struct expstate *es, const char *buf, size_t len)
779 {
780         unsigned num;
781         char *text;
782         size_t oldlen;
783
784         num = stringarray_num(&es->args);
785         assert(num > 0);
786
787         text = stringarray_get(&es->args, num - 1);
788         oldlen = strlen(text);
789         text = dorealloc(text, oldlen + 1, oldlen + len + 1);
790         memcpy(text + oldlen, buf, len);
791         text[oldlen+len] = '\0';
792         stringarray_set(&es->args, num - 1, text);
793 }
794
795 static
796 char *
797 expand_substitute(struct place *p, struct expstate *es)
798 {
799         struct expansionitem *ei;
800         unsigned i, num;
801         size_t len;
802         char *arg;
803         char *ret;
804         unsigned numargs, numparams;
805         char numbuf[64];
806
807         numargs = stringarray_num(&es->args);
808         numparams = stringarray_num(&es->curmacro->params);
809
810         if (numargs == 0 && numparams == 1) {
811                 /* no arguments <=> one empty argument */
812                 stringarray_add(&es->args, dostrdup(""), NULL);
813                 numargs++;
814         }
815         if (numargs != numparams) {
816                 complain(p, "Wrong number of arguments for macro %s; "
817                          "found %u, expected %u",
818                          es->curmacro->name, numargs, numparams);
819                 complain_fail();
820                 while (numargs < numparams) {
821                         stringarray_add(&es->args, dostrdup(""), NULL);
822                         numargs++;
823                 }
824         }
825
826         len = 0;
827         num = expansionitemarray_num(&es->curmacro->expansion);
828         for (i=0; i<num; i++) {
829                 ei = expansionitemarray_get(&es->curmacro->expansion, i);
830                 switch (ei->itemtype) {
831                     case EI_STRING:
832                         len += strlen(ei->ei_string);
833                         break;
834                     case EI_PARAM:
835                         arg = stringarray_get(&es->args, ei->ei_param);
836                         len += strlen(arg);
837                         break;
838                     case EI_FILE:
839                         len += strlen(place_getname(p)) + 2;
840                         break;
841                     case EI_LINE:
842                         len += snprintf(numbuf, sizeof(numbuf), "%u", p->line);
843                         break;
844                 }
845         }
846
847         ret = domalloc(len+1);
848         *ret = '\0';
849         for (i=0; i<num; i++) {
850                 ei = expansionitemarray_get(&es->curmacro->expansion, i);
851                 switch (ei->itemtype) {
852                     case EI_STRING:
853                         strcat(ret, ei->ei_string);
854                         break;
855                     case EI_PARAM:
856                         arg = stringarray_get(&es->args, ei->ei_param);
857                         strcat(ret, arg);
858                         break;
859                     case EI_FILE:
860                         strcat(ret, "\"");
861                         strcat(ret, place_getname(p));
862                         strcat(ret, "\"");
863                         break;
864                     case EI_LINE:
865                         snprintf(numbuf, sizeof(numbuf), "%u", p->line);
866                         strcat(ret, numbuf);
867                         break;
868                 }
869         }
870
871         return ret;
872 }
873
874 static
875 void
876 expand_domacro(struct expstate *es, struct place *p)
877 {
878         struct macro *m;
879         const char *name, *val;
880         char *newbuf, *newbuf2;
881
882         if (es->curmacro == NULL) {
883                 /* defined() */
884                 if (stringarray_num(&es->args) != 1) {
885                         complain(p, "Too many arguments for defined()");
886                         complain_fail();
887                         expand_send(es, p, "0", 1);
888                         return;
889                 }
890                 name = stringarray_get(&es->args, 0);
891                 m = macrotable_find(name, false);
892                 val = (m != NULL) ? "1" : "0";
893                 debuglog(p, "defined(%s): %s", name, val); 
894                 expand_send(es, p, val, 1);
895                 expstate_destroyargs(es);
896                 return;
897         }
898
899         m = es->curmacro;
900         assert(m->inuse == false);
901         m->inuse = true;
902
903         debuglog(p, "Expanding macro %s", m->name);
904         newbuf = expand_substitute(p, es);
905         debuglog(p, "Substituting for %s: %s", m->name, newbuf);
906
907         newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false);
908         dostrfree(newbuf);
909         expstate_destroyargs(es);
910         debuglog(p, "Complete expansion for %s: %s", m->name, newbuf2);
911
912         doexpand(es, p, newbuf2, strlen(newbuf2));
913         dostrfree(newbuf2);
914
915         m->inuse = false;
916 }
917
918 /*
919  * The traditional behavior if a function-like macro appears without
920  * arguments is to pretend it isn't a macro; that is, just emit its
921  * name.
922  */
923 static
924 void
925 expand_missingargs(struct expstate *es, struct place *p, bool needspace)
926 {
927         if (es->curmacro == NULL) {
928                 /* defined */
929                 expand_send(es, p, "defined", 7);
930                 return;
931         }
932         expand_send(es, p, es->curmacro->name, strlen(es->curmacro->name));
933         /* send a space in case we ate whitespace after the macro name */
934         if (needspace) {
935                 expand_send(es, p, " ", 1);
936         }
937 }
938
939 static
940 void
941 expand_got_ws(struct expstate *es, struct place *p,
942               const char *buf, size_t len)
943 {
944         switch (es->state) {
945             case ES_NORMAL:
946                 expand_send(es, p, buf, len);
947                 break;
948             case ES_WANTLPAREN:
949                 /* XXX notyet */
950                 //expand_send(es, p, buf, len);
951                 break;
952             case ES_NOARG:
953                 expand_newarg(es, buf, len);
954                 es->state = ES_HAVEARG;
955                 break;
956             case ES_HAVEARG:
957                 expand_appendarg(es, buf, len);
958                 break;
959         }
960 }
961
962 static
963 void
964 expand_got_word(struct expstate *es, struct place *p,
965                 const char *buf, size_t len)
966 {
967         struct macro *m;
968
969         switch (es->state) {
970             case ES_NORMAL:
971                 if (es->honordefined &&
972                     len == 7 && !memcmp(buf, "defined", 7)) {
973                         es->curmacro = NULL;
974                         es->state = ES_WANTLPAREN;
975                         break;
976                 }
977                 m = macrotable_findlen(buf, len, false);
978                 if (m == NULL || m->inuse) {
979                         expand_send(es, p, buf, len);
980                 } else if (!m->hasparams) {
981                         es->curmacro = m;
982                         expand_domacro(es, p);
983                 } else {
984                         es->curmacro = m;
985                         es->state = ES_WANTLPAREN;
986                 }
987                 break;
988             case ES_WANTLPAREN:
989                 if (es->curmacro != NULL) {
990                         expand_missingargs(es, p, true);
991                         es->state = ES_NORMAL;
992                         /* try again */
993                         expand_got_word(es, p, buf, len);
994                 } else {
995                         /* "defined foo" means "defined(foo)" */
996                         expand_newarg(es, buf, len);
997                         es->state = ES_NORMAL;
998                         expand_domacro(es, p);
999                 }
1000                 break;
1001             case ES_NOARG:
1002                 expand_newarg(es, buf, len);
1003                 es->state = ES_HAVEARG;
1004                 break;
1005             case ES_HAVEARG:
1006                 expand_appendarg(es, buf, len);
1007                 break;
1008         }
1009 }
1010
1011 static
1012 void
1013 expand_got_lparen(struct expstate *es, struct place *p,
1014                   const char *buf, size_t len)
1015 {
1016         switch (es->state) {
1017             case ES_NORMAL:
1018                 expand_send(es, p, buf, len);
1019                 break;
1020             case ES_WANTLPAREN:
1021                 es->state = ES_NOARG;
1022                 break;
1023             case ES_NOARG:
1024                 expand_newarg(es, buf, len);
1025                 es->state = ES_HAVEARG;
1026                 es->argparens++;
1027                 break;
1028             case ES_HAVEARG:
1029                 expand_appendarg(es, buf, len);
1030                 es->argparens++;
1031                 break;
1032         }
1033 }
1034
1035 static
1036 void
1037 expand_got_rparen(struct expstate *es, struct place *p,
1038                   const char *buf, size_t len)
1039 {
1040         switch (es->state) {
1041             case ES_NORMAL:
1042                 expand_send(es, p, buf, len);
1043                 break;
1044             case ES_WANTLPAREN:
1045                 expand_missingargs(es, p, false);
1046                 es->state = ES_NORMAL;
1047                 /* try again */
1048                 expand_got_rparen(es, p, buf, len);
1049                 break;
1050             case ES_NOARG:
1051                 assert(es->argparens == 0);
1052                 if (stringarray_num(&es->args) > 0) {
1053                         /* we are after a comma; enter an empty argument */
1054                         expand_newarg(es, buf, 0);
1055                 }
1056                 es->state = ES_NORMAL;
1057                 expand_domacro(es, p);
1058                 break;
1059             case ES_HAVEARG:
1060                 if (es->argparens > 0) {
1061                         es->argparens--;
1062                         expand_appendarg(es, buf, len);
1063                 } else {
1064                         es->state = ES_NORMAL;
1065                         expand_domacro(es, p);
1066                 }
1067                 break;
1068         }
1069 }
1070
1071 static
1072 void
1073 expand_got_comma(struct expstate *es, struct place *p,
1074                  const char *buf, size_t len)
1075 {
1076         switch (es->state) {
1077             case ES_NORMAL:
1078                 expand_send(es, p, buf, len);
1079                 break;
1080             case ES_WANTLPAREN:
1081                 expand_missingargs(es, p, false);
1082                 es->state = ES_NORMAL;
1083                 /* try again */
1084                 expand_got_comma(es, p, buf, len);
1085                 break;
1086             case ES_NOARG:
1087                 assert(es->argparens == 0);
1088                 expand_newarg(es, buf, 0);
1089                 break;
1090             case ES_HAVEARG:
1091                 if (es->argparens > 0) {
1092                         expand_appendarg(es, buf, len);
1093                 } else {
1094                         es->state = ES_NOARG;
1095                 }
1096                 break;
1097         }
1098 }
1099
1100 static
1101 void
1102 expand_got_other(struct expstate *es, struct place *p,
1103                  const char *buf, size_t len)
1104 {
1105         switch (es->state) {
1106             case ES_NORMAL:
1107                 expand_send(es, p, buf, len);
1108                 break;
1109             case ES_WANTLPAREN:
1110                 expand_missingargs(es, p, false);
1111                 es->state = ES_NORMAL;
1112                 /* try again */
1113                 expand_got_other(es, p, buf, len);
1114                 break;
1115             case ES_NOARG:
1116                 expand_newarg(es, buf, len);
1117                 es->state = ES_HAVEARG;
1118                 break;
1119             case ES_HAVEARG:
1120                 expand_appendarg(es, buf, len);
1121                 break;
1122         }
1123 }
1124
1125 static
1126 void
1127 expand_got_eof(struct expstate *es, struct place *p)
1128 {
1129         switch (es->state) {
1130             case ES_NORMAL:
1131                 break;
1132             case ES_WANTLPAREN:
1133                 expand_missingargs(es, p, false);
1134                 break;
1135             case ES_NOARG:
1136             case ES_HAVEARG:
1137                 if (es->curmacro) {
1138                         complain(p, "Unclosed argument list for macro %s",
1139                                  es->curmacro->name);
1140                 } else {
1141                         complain(p, "Unclosed argument list for defined()");
1142                 }
1143                 complain_fail();
1144                 expstate_destroyargs(es);
1145                 break;
1146         }
1147         expand_send_eof(es, p);
1148         es->state = ES_NORMAL;
1149         es->curmacro = NULL;
1150         es->argparens = 0;
1151 }
1152
1153 static
1154 void
1155 doexpand(struct expstate *es, struct place *p, const char *buf, size_t len)
1156 {
1157         char *s;
1158         size_t x;
1159         bool inquote = false;
1160         char quote = '\0';
1161
1162         while (len > 0) {
1163                 x = strspn(buf, ws);
1164                 if (x > len) {
1165                         /* XXX gross, need strnspn */
1166                         x = len;
1167                 }
1168
1169                 if (x > 0) {
1170                         expand_got_ws(es, p, buf, x);
1171                         buf += x;
1172                         len -= x;
1173                         continue;
1174                 }
1175
1176                 x = strspn(buf, alnum);
1177                 if (x > len) {
1178                         /* XXX gross, need strnspn */
1179                         x = len;
1180                 }
1181
1182                 if (!inquote && x > 0) {
1183                         expand_got_word(es, p, buf, x);
1184                         buf += x;
1185                         len -= x;
1186                         continue;
1187                 }
1188
1189                 if (!inquote && len > 1 && buf[0] == '/' && buf[1] == '*') {
1190                         s = strstr(buf, "*/");
1191                         if (s) {
1192                                 x = s - buf;
1193                         } else {
1194                                 x = len;
1195                         }
1196                         expand_got_ws(es, p, buf, x);
1197                         buf += x;
1198                         len -= x;
1199                         continue;
1200                 }
1201
1202                 if (!inquote && buf[0] == '(') {
1203                         expand_got_lparen(es, p, buf, 1);
1204                         buf++;
1205                         len--;
1206                         continue;
1207                 }
1208
1209                 if (!inquote && buf[0] == ')') {
1210                         expand_got_rparen(es, p, buf, 1);
1211                         buf++;
1212                         len--;
1213                         continue;
1214                 }
1215
1216                 if (!inquote && buf[0] == ',') {
1217                         expand_got_comma(es, p, buf, 1);
1218                         buf++;
1219                         len--;
1220                         continue;
1221                 }
1222
1223                 if (len > 1 && buf[0] == '\\' &&
1224                     (buf[1] == '"' || buf[1] == '\'')) {
1225                         expand_got_other(es, p, buf, 2);
1226                         buf += 2;
1227                         len -= 2;
1228                         continue;
1229                 }
1230                 if (!inquote && (buf[0] == '"' || buf[0] == '\'')) {
1231                         inquote = true;
1232                         quote = buf[0];
1233                 } else if (inquote && buf[0] == quote) {
1234                         inquote = false;
1235                 }
1236
1237                 expand_got_other(es, p, buf, 1);
1238                 buf++;
1239                 len--;
1240         }
1241 }
1242
1243 char *
1244 macroexpand(struct place *p, const char *buf, size_t len, bool honordefined)
1245 {
1246         struct expstate es;
1247         char *ret;
1248
1249         expstate_init(&es, true, honordefined);
1250         doexpand(&es, p, buf, len);
1251         expand_got_eof(&es, p);
1252
1253         /* trim to fit, so the malloc debugging won't complain */
1254         es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1);
1255
1256         ret = es.buf;
1257         es.buf = NULL;
1258         es.bufpos = es.bufmax = 0;
1259
1260         expstate_cleanup(&es);
1261
1262         return ret;
1263 }
1264
1265 void
1266 macro_sendline(struct place *p, const char *buf, size_t len)
1267 {
1268         doexpand(&mainstate, p, buf, len);
1269         switch (mainstate.state) {
1270             case ES_NORMAL:
1271                 /*
1272                  * If we were sent a blank line, don't emit a newline
1273                  * for it. This matches the prior behavior of tradcpp.
1274                  */
1275                 if (len > 0) {
1276                         output(p, "\n", 1);
1277                 }
1278                 break;
1279             case ES_WANTLPAREN:
1280             case ES_NOARG:
1281             case ES_HAVEARG:
1282                 /*
1283                  * Apparently to match gcc's -traditional behavior we
1284                  * need to emit a space for each newline that appears
1285                  * while processing macro args.
1286                  */
1287                 expand_got_ws(&mainstate, p, " ", 1);
1288                 break;
1289         }
1290 }
1291
1292 void
1293 macro_sendeof(struct place *p)
1294 {
1295         expand_got_eof(&mainstate, p);
1296 }
1297
1298 ////////////////////////////////////////////////////////////
1299 // module initialization
1300
1301 void
1302 macros_init(void)
1303 {
1304         macrotable_init();
1305         expstate_init(&mainstate, false, false);
1306 }
1307
1308 void
1309 macros_cleanup(void)
1310 {
1311         expstate_cleanup(&mainstate);
1312         macrotable_cleanup();
1313 }