xref: /trunk/main/soltools/cpp/_macro.c (revision 3f2293a34d640799d3a646809239d04f5e6f46e0)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 #ifdef _MSC_VER
23 #   define _POSIX_
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef __hpux
29 #   define _HPUX_SOURCE
30 #endif
31 #if defined(__IBMC__) || defined(__EMX__)
32 #   define PATH_MAX _MAX_PATH
33 #endif
34 #include <limits.h>
35 
36 #include "cpp.h"
37 
38 #define NCONCAT 16384
39 
40 /*
41  * do a macro definition.  tp points to the name being defined in the line
42  */
43 void
44     dodefine(Tokenrow * trp)
45 {
46     Token *tp;
47     Nlist *np;
48     Source *s;
49     Tokenrow *def, *args;
50     static uchar location[(PATH_MAX + 8) * NINC], *cp;
51 
52     tp = trp->tp + 1;
53     if (tp >= trp->lp || tp->type != NAME)
54     {
55         error(ERROR, "#defined token is not a name");
56         return;
57     }
58     np = lookup(tp, 1);
59     if (np->flag & ISUNCHANGE)
60     {
61         error(ERROR, "#defined token %t can't be redefined", tp);
62         return;
63     }
64     /* collect arguments */
65     tp += 1;
66     args = NULL;
67     if (tp < trp->lp && tp->type == LP && tp->wslen == 0)
68     {
69         /* macro with args */
70         int narg = 0;
71 
72         tp += 1;
73         args = new(Tokenrow);
74         maketokenrow(2, args);
75         if (tp->type != RP)
76         {
77             int err = 0;
78 
79             for (;;)
80             {
81                 Token *atp;
82 
83                 if (tp->type != NAME)
84                 {
85                     err++;
86                     break;
87                 }
88                 if (narg >= args->max)
89                     growtokenrow(args);
90                 for (atp = args->bp; atp < args->lp; atp++)
91                     if (atp->len == tp->len
92                         && strncmp((char *) atp->t, (char *) tp->t, tp->len) == 0)
93                         error(ERROR, "Duplicate macro argument");
94                 *args->lp++ = *tp;
95                 narg++;
96                 tp += 1;
97                 if (tp->type == RP)
98                     break;
99                 if (tp->type != COMMA)
100                 {
101                     err++;
102                     break;
103                 }
104                 tp += 1;
105             }
106             if (err)
107             {
108                 error(ERROR, "Syntax error in macro parameters");
109                 return;
110             }
111         }
112         tp += 1;
113     }
114     trp->tp = tp;
115     if (((trp->lp) - 1)->type == NL)
116         trp->lp -= 1;
117     def = normtokenrow(trp);
118     if (np->flag & ISDEFINED)
119     {
120         if (comparetokens(def, np->vp)
121             || (np->ap == NULL) != (args == NULL)
122             || (np->ap && comparetokens(args, np->ap)))
123         {
124             if ( np->loc )
125                 error(ERROR,
126                     "Macro redefinition of %t (already defined at %s)",
127                     trp->bp + 2, np->loc);
128             else
129                 error(ERROR,
130                     "Macro redefinition of %t (already defined at %s)",
131                     trp->bp + 2, "commandline" );
132         }
133     }
134     if (args)
135     {
136         Tokenrow *tap;
137 
138         tap = normtokenrow(args);
139         dofree(args->bp);
140         dofree(args);
141         args = tap;
142     }
143     np->ap = args;
144     np->vp = def;
145     np->flag |= ISDEFINED;
146 
147     /* build location string of macro definition */
148     for (cp = location, s = cursource; s; s = s->next)
149         if (*s->filename)
150         {
151             if (cp != location)
152                 *cp++ = ' ';
153             sprintf((char *)cp, "%s:%d", s->filename, s->line);
154             cp += strlen((char *)cp);
155         }
156 
157     np->loc = newstring(location, strlen((char *)location), 0);
158 
159     if (Mflag)
160     {
161         if (np->ap)
162             error(INFO, "Macro definition of %s(%r) [%r]", np->name, np->ap, np->vp);
163         else
164             error(INFO, "Macro definition of %s [%r]", np->name, np->vp);
165     }
166 }
167 
168 /*
169  * Definition received via -D or -U
170  */
171 void
172     doadefine(Tokenrow * trp, int type)
173 {
174     Nlist *np;
175     static uchar onestr[2] = "1";
176     static Token onetoken[1] = {{NUMBER, 0, 0, 1, onestr, 0}};
177     static Tokenrow onetr = {onetoken, onetoken, onetoken + 1, 1};
178 
179     trp->tp = trp->bp;
180     if (type == 'U')
181     {
182         if (trp->lp - trp->tp != 2 || trp->tp->type != NAME)
183             goto syntax;
184         if ((np = lookup(trp->tp, 0)) == NULL)
185             return;
186         np->flag &= ~ISDEFINED;
187         return;
188     }
189 
190     if (type == 'A')
191     {
192         if (trp->tp >= trp->lp || trp->tp->type != NAME)
193             goto syntax;
194         trp->tp->type = ARCHITECTURE;
195         np = lookup(trp->tp, 1);
196         np->flag |= ISARCHITECTURE;
197         trp->tp += 1;
198         if (trp->tp >= trp->lp || trp->tp->type == END)
199         {
200             np->vp = &onetr;
201             return;
202         }
203         else
204             error(FATAL, "Illegal -A argument %r", trp);
205     }
206 
207     if (trp->tp >= trp->lp || trp->tp->type != NAME)
208         goto syntax;
209     np = lookup(trp->tp, 1);
210     np->flag |= ISDEFINED;
211     trp->tp += 1;
212     if (trp->tp >= trp->lp || trp->tp->type == END)
213     {
214         np->vp = &onetr;
215         return;
216     }
217     if (trp->tp->type != ASGN)
218         goto syntax;
219     trp->tp += 1;
220     if ((trp->lp - 1)->type == END)
221         trp->lp -= 1;
222     np->vp = normtokenrow(trp);
223     return;
224 syntax:
225     error(FATAL, "Illegal -D or -U argument %r", trp);
226 }
227 
228 
229 
230 /*
231  * Do macro expansion in a row of tokens.
232  * Flag is NULL if more input can be gathered.
233  */
234 void
235     expandrow(Tokenrow * trp, char *flag)
236 {
237     Token * tp;
238     Nlist * np;
239 
240     MacroValidatorList  validators;
241     mvl_init(&validators);
242     /* Sets all token-identifiers to 0 because tokens may not be initialised (never use C!) */
243     tokenrow_zeroTokenIdentifiers(trp);
244 
245     if (flag)
246         setsource(flag, -1, -1, "", 0);
247     for (tp = trp->tp; tp < trp->lp;)
248     {
249         mvl_check(&validators, tp);
250 
251         if (tp->type != NAME
252             || quicklook(tp->t[0], tp->len > 1 ? tp->t[1] : 0) == 0
253             || (np = lookup(tp, 0)) == NULL
254             || (np->flag & (ISDEFINED | ISMAC)) == 0
255             || (np->flag & ISACTIVE) != 0)
256         {
257             tp++;
258             continue;
259         }
260         trp->tp = tp;
261         if (np->val == KDEFINED)
262         {
263             tp->type = DEFINED;
264             if ((tp + 1) < trp->lp && (tp + 1)->type == NAME)
265                 (tp + 1)->type = NAME1;
266             else
267                 if ((tp + 3) < trp->lp && (tp + 1)->type == LP
268                     && (tp + 2)->type == NAME && (tp + 3)->type == RP)
269                     (tp + 2)->type = NAME1;
270                 else
271                     error(ERROR, "Incorrect syntax for `defined'");
272             tp++;
273             continue;
274         }
275         else
276             if (np->val == KMACHINE)
277             {
278                 if (((tp - 1) >= trp->bp) && ((tp - 1)->type == SHARP))
279                 {
280                     tp->type = ARCHITECTURE;
281                     if ((tp + 1) < trp->lp && (tp + 1)->type == NAME)
282                         (tp + 1)->type = NAME2;
283                     else
284                         if ((tp + 3) < trp->lp && (tp + 1)->type == LP
285                             && (tp + 2)->type == NAME && (tp + 3)->type == RP)
286                             (tp + 2)->type = NAME2;
287                         else
288                             error(ERROR, "Incorrect syntax for `#machine'");
289                 }
290                 tp++;
291                 continue;
292             }
293 
294         if (np->flag & ISMAC)
295             builtin(trp, np->val);
296         else
297             expand(trp, np, &validators);
298         tp = trp->tp;
299     }   // end for
300     if (flag)
301         unsetsource();
302 
303     mvl_destruct(&validators);
304 }
305 
306 /*
307  * Expand the macro whose name is np, at token trp->tp, in the tokenrow.
308  * Return trp->tp at the first token next to be expanded
309  * (ordinarily the beginning of the expansion)
310  * I.e.: the same position as before!
311  * Only one expansion is performed, then we return to the expandrow()
312  * loop and start at same position.
313  */
314 void
315     expand(Tokenrow * trp, Nlist * np, MacroValidatorList * pValidators)
316 {
317 //  Token * pOldNextTp;
318     Tokenrow ntr;
319     int ntokc, narg, i;
320     Tokenrow *atr[NARG + 1];
321 
322     if (Mflag == 2)
323     {
324         if (np->ap)
325             error(INFO, "Macro expansion of %t with %s(%r)", trp->tp, np->name, np->ap);
326         else
327             error(INFO, "Macro expansion of %t with %s", trp->tp, np->name);
328     }
329 
330     copytokenrow(&ntr, np->vp);         /* copy macro value */
331     if (np->ap == NULL)                 /* parameterless */
332         ntokc = 1;
333     else
334     {
335         ntokc = gatherargs(trp, atr, &narg);
336         if (narg < 0)
337         {                               /* not actually a call (no '(') */
338             trp->tp++;
339             return;
340         }
341         if (narg != rowlen(np->ap))
342         {
343             error(ERROR, "Disagreement in number of macro arguments");
344             trp->tp += ntokc;
345             return;
346         }
347 
348         /** If gatherargs passed a macro validating token, this token
349             must become valid here.
350             trp->tp+0 was checked in expandrow(), so we dont need to do it
351             again here:
352         */
353         for (i = 1; i < ntokc; i++)
354         {
355             mvl_check(pValidators,trp->tp+i);
356         }
357 
358         substargs(np, &ntr, atr);       /* put args into replacement */
359         for (i = 0; i < narg; i++)
360         {
361             dofree(atr[i]->bp);
362             dofree(atr[i]);
363         }
364     }
365 
366 /* old
367     np->flag |= ISACTIVE;
368 */
369 
370 /* rh
371 */
372     doconcat(&ntr);                     /* execute ## operators */
373     ntr.tp = ntr.bp;
374     makespace(&ntr, trp->tp);
375 
376 /* old
377 //  expandrow(&ntr, "<expand>");
378 //  insertrow(trp, ntokc, &ntr);
379 //  dofree(ntr.bp);
380 //  np->flag &= ~ISACTIVE;
381 */
382 
383 /* NP
384         // Replace macro by its value:
385 */
386 //  pOldNextTp = trp->tp+ntokc;
387     tokenrow_zeroTokenIdentifiers(&ntr);
388     insertrow(trp, ntokc, &ntr);
389         /* Reassign old macro validators:
390         */
391 //  mvl_move(pValidators, trp->tp - pOldNextTp);
392 
393         /* add validator for just invalidated macro:
394         */
395     np->flag |= ISACTIVE;
396     if (trp->tp != trp->lp)
397     {   /* tp is a valid pointer: */
398         mvl_add(pValidators,np,trp->tp);
399     }
400     else
401     {   /* tp is == lp, therefore does not point to valid memory: */
402         mvl_add(pValidators,np,0);
403     }
404         /* reset trp->tp to original position:
405         */
406     trp->tp -= ntr.lp - ntr.bp;         /* so the result will be tested for macros from the same position again */
407 
408     dofree(ntr.bp);
409 
410     return;
411 }
412 
413 /*
414  * Gather an arglist, starting in trp with tp pointing at the macro name.
415  * Return total number of tokens passed, stash number of args found.
416  * trp->tp is not changed relative to the tokenrow.
417  */
418 int
419     gatherargs(Tokenrow * trp, Tokenrow ** atr, int *narg)
420 {
421     int parens = 1;
422     int ntok = 0;
423     Token *bp, *lp;
424     Tokenrow ttr;
425     int ntokp;
426     int needspace;
427 
428     *narg = -1;                         /* means that there is no macro
429                                          * call */
430     /* look for the ( */
431     for (;;)
432     {
433         trp->tp++;
434         ntok++;
435         if (trp->tp >= trp->lp)
436         {
437             gettokens(trp, 0);
438             if ((trp->lp - 1)->type == END)
439             {
440                 trp->lp -= 1;
441                 trp->tp -= ntok;
442                 return ntok;
443             }
444         }
445         if (trp->tp->type == LP)
446             break;
447         if (trp->tp->type != NL)
448             return ntok;
449     }
450     *narg = 0;
451     ntok++;
452     ntokp = ntok;
453     trp->tp++;
454     /* search for the terminating ), possibly extending the row */
455     needspace = 0;
456     while (parens > 0)
457     {
458         if (trp->tp >= trp->lp)
459             gettokens(trp, 0);
460         if (needspace)
461         {
462             needspace = 0;
463             /* makespace(trp); [rh] */
464         }
465         if (trp->tp->type == END)
466         {
467             trp->lp -= 1;
468             trp->tp -= ntok;
469             error(ERROR, "EOF in macro arglist");
470             return ntok;
471         }
472         if (trp->tp->type == NL)
473         {
474             trp->tp += 1;
475             adjustrow(trp, -1);
476             trp->tp -= 1;
477             /* makespace(trp); [rh] */
478             needspace = 1;
479             continue;
480         }
481         if (trp->tp->type == LP)
482             parens++;
483         else
484             if (trp->tp->type == RP)
485                 parens--;
486         trp->tp++;
487         ntok++;
488     }
489     trp->tp -= ntok;
490     /* Now trp->tp won't move underneath us */
491     lp = bp = trp->tp + ntokp;
492     for (; parens >= 0; lp++)
493     {
494         if (lp->type == LP)
495         {
496             parens++;
497             continue;
498         }
499         if (lp->type == RP)
500             parens--;
501         if (lp->type == DSHARP)
502             lp->type = DSHARP1;         /* ## not special in arg */
503         if ((lp->type == COMMA && parens == 0) ||
504                 ( parens < 0 && ((lp - 1)->type != LP)))
505         {
506             if (*narg >= NARG - 1)
507                 error(FATAL, "Sorry, too many macro arguments");
508             ttr.bp = ttr.tp = bp;
509             ttr.lp = lp;
510             atr[(*narg)++] = normtokenrow(&ttr);
511             bp = lp + 1;
512         }
513     }
514     return ntok;
515 }
516 
517 /*
518  * substitute the argument list into the replacement string
519  *  This would be simple except for ## and #
520  */
521 void
522     substargs(Nlist * np, Tokenrow * rtr, Tokenrow ** atr)
523 {
524     Tokenrow tatr;
525     Token *tp;
526     int ntok, argno;
527 
528     for (rtr->tp = rtr->bp; rtr->tp < rtr->lp;)
529     {
530         if (rtr->tp->type == SHARP)
531         {                               /* string operator */
532             tp = rtr->tp;
533             rtr->tp += 1;
534             if ((argno = lookuparg(np, rtr->tp)) < 0)
535             {
536                 error(ERROR, "# not followed by macro parameter");
537                 continue;
538             }
539             ntok = 1 + (rtr->tp - tp);
540             rtr->tp = tp;
541             insertrow(rtr, ntok, stringify(atr[argno]));
542             continue;
543         }
544         if (rtr->tp->type == NAME
545             && (argno = lookuparg(np, rtr->tp)) >= 0)
546         {
547             if (((rtr->tp + 1) < rtr->lp && (rtr->tp + 1)->type == DSHARP)
548                 || (rtr->tp != rtr->bp  && (rtr->tp - 1)->type == DSHARP))
549             {
550                 copytokenrow(&tatr, atr[argno]);
551                 makespace(&tatr, rtr->tp);
552                 insertrow(rtr, 1, &tatr);
553                 dofree(tatr.bp);
554             }
555             else
556             {
557                 copytokenrow(&tatr, atr[argno]);
558                 makespace(&tatr, rtr->tp);
559                 expandrow(&tatr, "<macro>");
560                 insertrow(rtr, 1, &tatr);
561                 dofree(tatr.bp);
562             }
563             continue;
564         }
565         rtr->tp++;
566     }
567 }
568 
569 /*
570  * Evaluate the ## operators in a tokenrow
571  */
572 void
573     doconcat(Tokenrow * trp)
574 {
575     Token *ltp, *ntp;
576     Tokenrow ntr;
577     int len;
578 
579     for (trp->tp = trp->bp; trp->tp < trp->lp; trp->tp++)
580     {
581         if (trp->tp->type == DSHARP1)
582             trp->tp->type = DSHARP;
583         else
584             if (trp->tp->type == DSHARP)
585             {
586                 int  i;
587                 char tt[NCONCAT];
588 
589                 ltp = trp->tp - 1;
590                 ntp = trp->tp + 1;
591 
592                 if (ltp < trp->bp || ntp >= trp->lp)
593                 {
594                     error(ERROR, "## occurs at border of replacement");
595                     continue;
596                 }
597 
598                 ntp = ltp;
599                 i   = 1;
600                 len = 0;
601 
602                 do
603                 {
604                     if (len + ntp->len + ntp->wslen > sizeof(tt))
605                     {
606                         error(ERROR, "## string concatination buffer overrun");
607                         break;
608                     }
609 
610                     if (ntp != trp->tp + 1)
611                     {
612                         strncpy((char *) tt + len, (char *) ntp->t - ntp->wslen,
613                                 ntp->len + ntp->wslen);
614                         len += ntp->len + ntp->wslen;
615                     }
616                     else    // Leerzeichen um ## herum entfernen:
617                     {
618                         strncpy((char *) tt + len, (char *) ntp->t, ntp->len);
619                         len += ntp->len;
620                     }
621 
622                     ntp = trp->tp + i;
623                     i++;
624                 }
625                 while (ntp < trp->lp);
626 
627                 tt[len] = '\0';
628                 setsource("<##>", -1, -1, tt, 0);
629                 maketokenrow(3, &ntr);
630                 gettokens(&ntr, 1);
631                 unsetsource();
632                 if (ntr.bp->type == UNCLASS)
633                     error(WARNING, "Bad token %r produced by ##", &ntr);
634                 while ((ntr.lp-1)->len == 0 && ntr.lp != ntr.bp)
635                     ntr.lp--;
636 
637                 doconcat(&ntr);
638                 trp->tp = ltp;
639                 makespace(&ntr, ltp);
640                 insertrow(trp, ntp - ltp, &ntr);
641                 dofree(ntr.bp);
642                 trp->tp--;
643             }
644     }
645 }
646 
647 /*
648  * tp is a potential parameter name of macro mac;
649  * look it up in mac's arglist, and if found, return the
650  * corresponding index in the argname array.  Return -1 if not found.
651  */
652 int
653     lookuparg(Nlist * mac, Token * tp)
654 {
655     Token *ap;
656 
657     if (tp->type != NAME || mac->ap == NULL)
658         return -1;
659     for (ap = mac->ap->bp; ap < mac->ap->lp; ap++)
660     {
661         if (ap->len == tp->len && strncmp((char *) ap->t, (char *) tp->t, ap->len) == 0)
662             return ap - mac->ap->bp;
663     }
664     return -1;
665 }
666 
667 /*
668  * Return a quoted version of the tokenrow (from # arg)
669  */
670 #define STRLEN  512
671 Tokenrow *
672     stringify(Tokenrow * vp)
673 {
674     static Token t = {STRING, 0, 0, 0, NULL, 0};
675     static Tokenrow tr = {&t, &t, &t + 1, 1};
676     Token *tp;
677     uchar s[STRLEN];
678     uchar *sp = s, *cp;
679     int i, instring;
680 
681     *sp++ = '"';
682     for (tp = vp->bp; tp < vp->lp; tp++)
683     {
684         instring = tp->type == STRING || tp->type == CCON;
685         if (sp + 2 * tp->len + tp->wslen  >= &s[STRLEN - 10])
686         {
687             error(ERROR, "Stringified macro arg is too long");
688             break;
689         }
690 
691         // Change by np 31.10.2001, #93725 - begin
692         if ( tp->wslen > 0 )
693         *sp++ = ' ';
694         // change end.
695 
696         for (i = 0, cp = tp->t; (unsigned int)i < tp->len; i++)
697         {
698             if (instring && (*cp == '"' || *cp == '\\'))
699                 *sp++ = '\\';
700             *sp++ = *cp++;
701         }
702     }
703     *sp++ = '"';
704     *sp = '\0';
705     sp = s;
706     t.len = strlen((char *) sp);
707     t.t = newstring(sp, t.len, 0);
708     return &tr;
709 }
710 
711 /*
712  * expand a builtin name
713  */
714 void
715     builtin(Tokenrow * trp, int biname)
716 {
717     char *op;
718     Token *tp;
719     Source *s;
720 
721     tp = trp->tp;
722     trp->tp++;
723     /* need to find the real source */
724     s = cursource;
725     while (s && s->fd == -1)
726         s = s->next;
727     if (s == NULL)
728         s = cursource;
729     /* most are strings */
730     tp->type = STRING;
731     if (tp->wslen)
732     {
733         *outptr++ = ' ';
734         tp->wslen = 1;
735     }
736     op = outptr;
737     *op++ = '"';
738     switch (biname)
739     {
740 
741         case KLINENO:
742             tp->type = NUMBER;
743             op = outnum(op - 1, s->line);
744             break;
745 
746         case KFILE:
747             {
748                 char *src = s->filename;
749 
750                 while ((*op++ = *src++) != 0)
751                     if (src[-1] == '\\')
752                         *op++ = '\\';
753                 op--;
754                 break;
755             }
756 
757         case KDATE:
758             strncpy(op, curtime + 4, 7);
759             strncpy(op + 7, curtime + 20, 4);
760             op += 11;
761             break;
762 
763         case KTIME:
764             strncpy(op, curtime + 11, 8);
765             op += 8;
766             break;
767 
768         default:
769             error(ERROR, "cpp botch: unknown internal macro");
770             return;
771     }
772     if (tp->type == STRING)
773         *op++ = '"';
774     tp->t = (uchar *) outptr;
775     tp->len = op - outptr;
776     outptr = op;
777 }
778