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