210598013338edcb5e0fbd9e4774c5a38a17d95d
[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 <stdlib.h>
32 #include <string.h>
33
34 #include "array.h"
35 #include "mode.h"
36 #include "place.h"
37 #include "macro.h"
38 #include "output.h"
39
40 struct expansionitem {
41         bool isstring;
42         union {
43                 char *string;
44                 unsigned param;
45         };
46 };
47 DECLARRAY(expansionitem, static UNUSED);
48 DEFARRAY(expansionitem, static);
49
50 struct macro {
51         struct place defplace;
52         struct place expansionplace;
53         unsigned hash;
54         char *name;
55         bool hasparams;
56         struct stringarray params;
57         struct expansionitemarray expansion;
58         bool inuse;
59 };
60 DECLARRAY(macro, static UNUSED);
61 DEFARRAY(macro, static);
62 DECLARRAY(macroarray, static UNUSED);
63 DEFARRAY(macroarray, static);
64
65 static struct macroarrayarray macros;
66 static unsigned total_macros;
67 static unsigned hashmask;
68
69 ////////////////////////////////////////////////////////////
70 // macro structure ops
71
72 static
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) {
113                 dostrfree(ei->string);
114         }
115         dofree(ei, sizeof(*ei));
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
139 struct macro *
140 macro_create(struct place *p1, const char *name, unsigned hash,
141              struct place *p2)
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);
150         m->hasparams = false;
151         stringarray_init(&m->params);
152         expansionitemarray_init(&m->expansion);
153         m->inuse = false;
154         return m;
155 }
156
157 DESTROYALL_ARRAY(expansionitem, );
158
159 static
160 void
161 macro_destroy(struct macro *m)
162 {
163         expansionitemarray_destroyall(&m->expansion);
164         expansionitemarray_cleanup(&m->expansion);
165         dostrfree(m->name);
166         dofree(m, sizeof(*m));
167 }
168
169 static
170 bool
171 macro_eq(const struct macro *m1, const struct macro *m2)
172 {
173         unsigned num1, num2, i;
174         struct expansionitem *ei1, *ei2;
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
185         num1 = expansionitemarray_num(&m1->expansion);
186         num2 = expansionitemarray_num(&m2->expansion);
187         if (num1 != num2) {
188                 return false;
189         }
190
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
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
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
225 hashfunc(const char *s, size_t len)
226 {
227         uint16_t x1, x2, a;
228         size_t i;
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(&macros);
270         macroarrayarray_setsize(&macros, 4);
271         for (i=0; i<4; i++) {
272                 macroarrayarray_set(&macros, 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(&macros);
288         for (i=0; i<numbuckets; i++) {
289                 bucket = macroarrayarray_get(&macros, i);
290                 if (bucket != NULL) {
291                         macroarray_destroyall(bucket);
292                         macroarray_destroy(bucket);
293                 }
294         }
295         macroarrayarray_setsize(&macros, 0);
296         macroarrayarray_cleanup(&macros);
297 }
298
299 static
300 struct macro *
301 macrotable_findlen(const char *name, size_t len, bool remove)
302 {
303         unsigned hash;
304         struct macroarray *bucket;
305         struct macro *m, *m2;
306         unsigned i, num;
307         size_t mlen;
308
309         hash = hashfunc(name, len);
310         bucket = macroarrayarray_get(&macros, 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                 }
320                 mlen = strlen(m->name);
321                 if (len == mlen && !memcmp(name, m->name, len)) {
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
337 struct macro *
338 macrotable_find(const char *name, bool remove)
339 {
340         return macrotable_findlen(name, strlen(name), remove);
341 }
342
343 static
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(&macros);
354         macroarrayarray_setsize(&macros, 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(&macros, i);
364                 if (oldbucket == NULL) {
365                         macroarrayarray_set(&macros, numbuckets + i, NULL);
366                         continue;
367                 }
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);
381                         if (m != NULL) {
382                                 if (k < j) {
383                                         macroarray_set(oldbucket, k, m);
384                                 }
385                                 k++;
386                         }
387                 }
388                 macroarray_setsize(oldbucket, k);
389                 macroarrayarray_set(&macros, 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(&macros);
402         if (total_macros > 0 && total_macros / numbuckets > 9) {
403                 macrotable_rehash();
404         }
405
406         hash = hashfunc(m->name, strlen(m->name));
407         bucket = macroarrayarray_get(&macros, hash & hashmask);
408         if (bucket == NULL) {
409                 bucket = macroarray_create();
410                 macroarrayarray_set(&macros, hash & hashmask, bucket);
411         }
412         macroarray_add(bucket, m, NULL);
413         total_macros++;
414 }
415
416 ////////////////////////////////////////////////////////////
417 // external macro definition interface
418
419 static
420 struct macro *
421 macro_define_common_start(struct place *p1, const char *macro,
422                           struct place *p2)
423 {
424         struct macro *m;
425         unsigned hash;
426
427         if (!is_identifier(macro)) {
428                 complain(p1, "Invalid macro name %s", macro);
429                 complain_fail();
430         }
431
432         hash = hashfunc(macro, strlen(macro));
433         m = macro_create(p1, macro, hash, p2);
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) {
448                         /* in traditional cpp this is silent */
449                         //complain(&m->defplace,
450                         //       "Warning: redefinition of %s", m->name);
451                         //complain(&oldm->defplace,
452                         //       "Previous definition was here");
453                         //if (mode.werror) {
454                         //      complain_fail();
455                         //}
456                 } else {
457                         complain(&m->defplace,
458                                  "Warning: non-identical redefinition of %s",
459                                  m->name);
460                         complain(&oldm->defplace,
461                                  "Previous definition was here");
462                         /* in traditional cpp this is not fatal */
463                         if (mode.werror) {
464                                 complain_fail();
465                         }
466                 }
467                 macro_destroy(m);
468                 return;
469         }
470         macrotable_add(m);
471 }
472
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
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);
516                 if (strlen(param) == len && !memcmp(name, param, len)) {
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, &param)) {
539                                 if (wordstart > blockstart) {
540                                         ei = expansionitem_create_stringlen(
541                                                 buf + blockstart,
542                                                 wordstart - blockstart);
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,
557                                                     pos - blockstart);
558                 expansionitemarray_add(&m->expansion, ei, NULL);
559         }
560 }
561
562 void
563 macro_define_plain(struct place *p1, const char *macro,
564                    struct place *p2, const char *expansion)
565 {
566         struct macro *m;
567         struct expansionitem *ei;
568
569         m = macro_define_common_start(p1, macro, p2);
570         ei = expansionitem_create_string(expansion);
571         expansionitemarray_add(&m->expansion, ei, NULL);
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
582         m = macro_define_common_start(p1, macro, p3);
583         m->hasparams = true;
584         macro_parse_parameters(m, p2, params);
585         macro_parse_expansion(m, expansion);
586         macro_define_common_end(m);
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
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) {
651                 dofree(es->buf, es->bufmax);
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++) {
663                 dostrfree(stringarray_get(&es->args, i));
664         }
665         stringarray_setsize(&es->args, 0);
666 }
667
668 static
669 void
670 expand_send(struct expstate *es, struct place *p, const char *buf, size_t len)
671 {
672         size_t oldmax;
673
674         if (es->tobuf) {
675                 assert(es->bufpos <= es->bufmax);
676                 if (es->bufpos + len > es->bufmax) {
677                         oldmax = es->bufmax;
678                         if (es->bufmax == 0) {
679                                 es->bufmax = 64;
680                         }
681                         while (es->bufpos + len > es->bufmax) {
682                                 es->bufmax *= 2;
683                         }
684                         es->buf = dorealloc(es->buf, oldmax, es->bufmax);
685                 }
686                 memcpy(es->buf + es->bufpos, buf, len);
687                 es->bufpos += len;
688                 assert(es->bufpos <= es->bufmax);
689         } else {
690                 output(p, buf, len);
691         }
692 }
693
694 static
695 void
696 expand_send_eof(struct expstate *es, struct place *p)
697 {
698         if (es->tobuf) {
699                 expand_send(es, p, "", 1);
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);
729         text = dorealloc(text, oldlen + 1, oldlen + len + 1);
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 *
737 expand_substitute(struct place *p, struct expstate *es)
738 {
739         struct expansionitem *ei;
740         unsigned i, num;
741         size_t len;
742         char *arg;
743         char *ret;
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         }
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();
804                         expand_send(es, p, "0", 1);
805                         return;
806                 }
807                 m = macrotable_find(stringarray_get(&es->args, 0), false);
808                 expand_send(es, p, (m != NULL) ? "1" : "0", 1);
809                 expstate_destroyargs(es);
810                 return;
811         }
812
813         assert(es->curmacro->inuse == false);
814         es->curmacro->inuse = true;
815
816         newbuf = expand_substitute(p, es);
817         newbuf2 = macroexpand(p, newbuf, strlen(newbuf), false);
818         dostrfree(newbuf);
819         expstate_destroyargs(es);
820         doexpand(es, p, newbuf2, strlen(newbuf2));
821         dostrfree(newbuf2);
822
823         es->curmacro->inuse = false;
824 }
825
826 /*
827  * The traditional behavior if a function-like macro appears without
828  * arguments is to pretend it isn't a macro; that is, just emit its
829  * name.
830  */
831 static
832 void
833 expand_missingargs(struct expstate *es, struct place *p, bool needspace)
834 {
835         if (es->curmacro == NULL) {
836                 /* defined */
837                 expand_send(es, p, "defined", 7);
838                 return;
839         }
840         expand_send(es, p, es->curmacro->name, strlen(es->curmacro->name));
841         /* send a space in case we ate whitespace after the macro name */
842         if (needspace) {
843                 expand_send(es, p, " ", 1);
844         }
845 }
846
847 static
848 void
849 expand_got_ws(struct expstate *es, struct place *p, char *buf, size_t len)
850 {
851         switch (es->state) {
852             case ES_NORMAL:
853                 expand_send(es, p, buf, len);
854                 break;
855             case ES_WANTLPAREN:
856                 break;
857             case ES_NOARG:
858                 expand_newarg(es, buf, len);
859                 es->state = ES_HAVEARG;
860                 break;
861             case ES_HAVEARG:
862                 expand_appendarg(es, buf, len);
863                 break;
864         }
865 }
866
867 static
868 void
869 expand_got_word(struct expstate *es, struct place *p, char *buf, size_t len)
870 {
871         struct macro *m;
872         struct expansionitem *ei;
873         char *newbuf;
874
875         switch (es->state) {
876             case ES_NORMAL:
877                 if (es->honordefined &&
878                     len == 7 && !memcmp(buf, "defined", 7)) {
879                         es->curmacro = NULL;
880                         es->state = ES_WANTLPAREN;
881                         break;
882                 }
883                 m = macrotable_findlen(buf, len, false);
884                 if (m == NULL || m->inuse) {
885                         expand_send(es, p, buf, len);
886                 } else if (!m->hasparams) {
887                         m->inuse = true;
888                         assert(expansionitemarray_num(&m->expansion) == 1);
889                         ei = expansionitemarray_get(&m->expansion, 0);
890                         assert(ei->isstring);
891                         newbuf = macroexpand(p, ei->string,
892                                              strlen(ei->string), false);
893                         doexpand(es, p, newbuf, strlen(newbuf));
894                         dostrfree(newbuf);
895                         m->inuse = false;
896                 } else {
897                         es->curmacro = m;
898                         es->state = ES_WANTLPAREN;
899                 }
900                 break;
901             case ES_WANTLPAREN:
902                 if (es->curmacro != NULL) {
903                         expand_missingargs(es, p, true);
904                         es->state = ES_NORMAL;
905                         /* try again */
906                         expand_got_word(es, p, buf, len);
907                 } else {
908                         /* "defined foo" means "defined(foo)" */
909                         expand_newarg(es, buf, len);
910                         es->state = ES_NORMAL;
911                         expand_domacro(es, p);
912                 }
913                 break;
914             case ES_NOARG:
915                 expand_newarg(es, buf, len);
916                 es->state = ES_HAVEARG;
917                 break;
918             case ES_HAVEARG:
919                 expand_appendarg(es, buf, len);
920                 break;
921         }
922 }
923
924 static
925 void
926 expand_got_lparen(struct expstate *es, struct place *p, char *buf, size_t len)
927 {
928         switch (es->state) {
929             case ES_NORMAL:
930                 expand_send(es, p, buf, len);
931                 break;
932             case ES_WANTLPAREN:
933                 es->state = ES_NOARG;
934                 break;
935             case ES_NOARG:
936                 expand_newarg(es, buf, len);
937                 es->state = ES_HAVEARG;
938                 es->argparens++;
939                 break;
940             case ES_HAVEARG:
941                 expand_appendarg(es, buf, len);
942                 es->argparens++;
943                 break;
944         }
945 }
946
947 static
948 void
949 expand_got_rparen(struct expstate *es, struct place *p, char *buf, size_t len)
950 {
951         switch (es->state) {
952             case ES_NORMAL:
953                 expand_send(es, p, buf, len);
954                 break;
955             case ES_WANTLPAREN:
956                 expand_missingargs(es, p, false);
957                 es->state = ES_NORMAL;
958                 /* try again */
959                 expand_got_rparen(es, p, buf, len);
960                 break;
961             case ES_NOARG:
962                 assert(es->argparens == 0);
963                 if (stringarray_num(&es->args) > 0) {
964                         /* we are after a comma; enter an empty argument */
965                         expand_newarg(es, buf, 0);
966                 }
967                 es->state = ES_NORMAL;
968                 expand_domacro(es, p);
969                 break;
970             case ES_HAVEARG:
971                 if (es->argparens > 0) {
972                         es->argparens--;
973                         expand_appendarg(es, buf, len);
974                 } else {
975                         es->state = ES_NORMAL;
976                         expand_domacro(es, p);
977                 }
978                 break;
979         }
980 }
981
982 static
983 void
984 expand_got_comma(struct expstate *es, struct place *p, char *buf, size_t len)
985 {
986         switch (es->state) {
987             case ES_NORMAL:
988                 expand_send(es, p, buf, len);
989                 break;
990             case ES_WANTLPAREN:
991                 expand_missingargs(es, p, false);
992                 es->state = ES_NORMAL;
993                 /* try again */
994                 expand_got_comma(es, p, buf, len);
995                 break;
996             case ES_NOARG:
997                 assert(es->argparens == 0);
998                 expand_newarg(es, buf, 0);
999                 break;
1000             case ES_HAVEARG:
1001                 if (es->argparens > 0) {
1002                         expand_appendarg(es, buf, len);
1003                 } else {
1004                         es->state = ES_NOARG;
1005                 }
1006                 break;
1007         }
1008 }
1009
1010 static
1011 void
1012 expand_got_other(struct expstate *es, struct place *p, char *buf, size_t len)
1013 {
1014         switch (es->state) {
1015             case ES_NORMAL:
1016                 expand_send(es, p, buf, len);
1017                 break;
1018             case ES_WANTLPAREN:
1019                 expand_missingargs(es, p, false);
1020                 es->state = ES_NORMAL;
1021                 /* try again */
1022                 expand_got_other(es, p, buf, len);
1023                 break;
1024             case ES_NOARG:
1025                 expand_newarg(es, buf, len);
1026                 es->state = ES_HAVEARG;
1027                 break;
1028             case ES_HAVEARG:
1029                 expand_appendarg(es, buf, len);
1030                 break;
1031         }
1032 }
1033
1034 static
1035 void
1036 expand_got_eof(struct expstate *es, struct place *p)
1037 {
1038         switch (es->state) {
1039             case ES_NORMAL:
1040                 break;
1041             case ES_WANTLPAREN:
1042                 expand_missingargs(es, p, false);
1043                 break;
1044             case ES_NOARG:
1045             case ES_HAVEARG:
1046                 if (es->curmacro) {
1047                         complain(p, "Unclosed argument list for macro %s",
1048                                  es->curmacro->name);
1049                 } else {
1050                         complain(p, "Unclosed argument list for defined()");
1051                 }
1052                 complain_fail();
1053                 expstate_destroyargs(es);
1054                 break;
1055         }
1056         expand_send_eof(es, p);
1057         es->state = ES_NORMAL;
1058         es->curmacro = NULL;
1059         es->argparens = 0;
1060 }
1061
1062 static
1063 void
1064 doexpand(struct expstate *es, struct place *p, char *buf, size_t len)
1065 {
1066         char *s;
1067         size_t x;
1068         bool inquote = false;
1069         char quote = '\0';
1070
1071         while (len > 0) {
1072                 x = strspn(buf, ws);
1073                 if (x > len) {
1074                         /* XXX gross, need strnspn */
1075                         x = len;
1076                 }
1077
1078                 if (x > 0) {
1079                         expand_got_ws(es, p, buf, x);
1080                         buf += x;
1081                         len -= x;
1082                         continue;
1083                 }
1084
1085                 x = strspn(buf, alnum);
1086                 if (x > len) {
1087                         /* XXX gross, need strnspn */
1088                         x = len;
1089                 }
1090
1091                 if (!inquote && x > 0) {
1092                         expand_got_word(es, p, buf, x);
1093                         buf += x;
1094                         len -= x;
1095                         continue;
1096                 }
1097
1098                 if (!inquote && len > 1 && buf[0] == '/' && buf[1] == '*') {
1099                         s = strstr(buf, "*/");
1100                         if (s) {
1101                                 x = s - buf;
1102                         } else {
1103                                 x = len;
1104                         }
1105                         expand_got_ws(es, p, buf, x);
1106                         buf += x;
1107                         len -= x;
1108                         continue;
1109                 }
1110
1111                 if (!inquote && buf[0] == '(') {
1112                         expand_got_lparen(es, p, buf, 1);
1113                         buf++;
1114                         len--;
1115                         continue;
1116                 }
1117
1118                 if (!inquote && buf[0] == ')') {
1119                         expand_got_rparen(es, p, buf, 1);
1120                         buf++;
1121                         len--;
1122                         continue;
1123                 }
1124
1125                 if (!inquote && buf[0] == ',') {
1126                         expand_got_comma(es, p, buf, 1);
1127                         buf++;
1128                         len--;
1129                         continue;
1130                 }
1131
1132                 if (len > 1 && buf[0] == '\\' &&
1133                     (buf[1] == '"' || buf[1] == '\'')) {
1134                         expand_got_other(es, p, buf, 2);
1135                         buf += 2;
1136                         len -= 2;
1137                         continue;
1138                 }
1139                 if (!inquote && (buf[0] == '"' || buf[0] == '\'')) {
1140                         inquote = true;
1141                         quote = buf[0];
1142                 } else if (inquote && buf[0] == quote) {
1143                         inquote = false;
1144                 }
1145
1146                 expand_got_other(es, p, buf, 1);
1147                 buf++;
1148                 len--;
1149         }
1150 }
1151
1152 char *
1153 macroexpand(struct place *p, char *buf, size_t len, bool honordefined)
1154 {
1155         struct expstate es;
1156         char *ret;
1157
1158         expstate_init(&es, true, honordefined);
1159         doexpand(&es, p, buf, len);
1160         expand_got_eof(&es, p);
1161
1162         /* trim to fit, so the malloc debugging won't complain */
1163         es.buf = dorealloc(es.buf, es.bufmax, strlen(es.buf) + 1);
1164
1165         ret = es.buf;
1166         es.buf = NULL;
1167         es.bufpos = es.bufmax = 0;
1168
1169         expstate_cleanup(&es);
1170
1171         return ret;
1172 }
1173
1174 void
1175 macro_sendline(struct place *p, char *buf, size_t len)
1176 {
1177         doexpand(&mainstate, p, buf, len);
1178         output(p, "\n", 1);
1179 }
1180
1181 void
1182 macro_sendeof(struct place *p)
1183 {
1184         expand_got_eof(&mainstate, p);
1185 }
1186
1187 ////////////////////////////////////////////////////////////
1188 // module initialization
1189
1190 void
1191 macros_init(void)
1192 {
1193         macrotable_init();
1194         expstate_init(&mainstate, false, false);
1195 }
1196
1197 void
1198 macros_cleanup(void)
1199 {
1200         expstate_cleanup(&mainstate);
1201         macrotable_cleanup();
1202 }