xref: /trunk/main/soltools/cpp/_macro.c (revision 001da7d5)
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
dodefine(Tokenrow * trp)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
doadefine(Tokenrow * trp,int type)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
expandrow(Tokenrow * trp,char * flag)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
expand(Tokenrow * trp,Nlist * np,MacroValidatorList * pValidators)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
gatherargs(Tokenrow * trp,Tokenrow ** atr,int * narg)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
substargs(Nlist * np,Tokenrow * rtr,Tokenrow ** atr)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
doconcat(Tokenrow * trp)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
lookuparg(Nlist * mac,Token * tp)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 *
stringify(Tokenrow * vp)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
builtin(Tokenrow * trp,int biname)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 
779