14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1986-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                                                                      *
194887Schin ***********************************************************************/
204887Schin #pragma prototyped
214887Schin /*
224887Schin  * Glenn Fowler
234887Schin  * AT&T Research
244887Schin  *
254887Schin  * preprocessor macro call
264887Schin  */
274887Schin 
284887Schin #include "pplib.h"
294887Schin 
304887Schin #include <ctype.h>
314887Schin 
324887Schin /*
334887Schin  * call a macro by pushing its value on the input stream
344887Schin  * only the macro token itself has been consumed
354887Schin  * -1 returned if macro disabled
364887Schin  *  0 returned if tok==0 and sym->mac->value to be copied to output by caller
374887Schin  *  1 returned if value pushed on input
384887Schin  */
394887Schin 
404887Schin int
414887Schin ppcall(register struct ppsymbol* sym, int tok)
424887Schin {
434887Schin 	register int			c;
444887Schin 	register char*			p;
454887Schin 	register char*			q;
464887Schin 	register struct ppmacro*	mac;
474887Schin 	int				n;
484887Schin 	int				m;
494887Schin 	int				ret;
504887Schin 	int				old_hidden;
514887Schin 	int				last_line;
524887Schin 	long				old_state;
534887Schin 	char*				last_file;
544887Schin 	char*				old_token;
554887Schin 	struct ppmacstk*		mp;
564887Schin 	struct ppinstk*			old_in;
574887Schin 	struct ppinstk*			kp;
584887Schin 	struct pptuple*			tp;
594887Schin 
604887Schin 	ret = -1;
614887Schin 	sym->flags |= SYM_NOTICED;
624887Schin 	if (mac = sym->macro)
634887Schin 	{
644887Schin 		count(macro);
654887Schin 		if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN))
664887Schin 			error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name);
674887Schin 		if (sym->flags & SYM_DISABLED)
684887Schin #if COMPATIBLE
694887Schin 			if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity)
704887Schin #endif
714887Schin 		{
724887Schin 			pp.mode |= MARKMACRO;
734887Schin #if COMPATIBLE
744887Schin 			if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
754887Schin 				error(1, "%s: macro recursion inhibited", sym->name);
764887Schin #endif
774887Schin 			goto disable;
784887Schin 		}
794887Schin 		if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE)))
804887Schin 		{
814887Schin #if COMPATIBLE
824887Schin 			if (*sym->name != '_' && !(pp.state & COMPATIBILITY))
834887Schin #else
844887Schin 			if (*sym->name != '_')
854887Schin #endif
864887Schin 			{
874887Schin 				if (pp.state & STRICT)
884887Schin 				{
894887Schin 					error(1, "%s: obsolete predefined symbol expansion disabled", sym->name);
904887Schin 					goto disable;
914887Schin 				}
924887Schin 				error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive");
934887Schin 			}
944887Schin 			else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#'))
954887Schin 				error(1, "%s: predefined symbol expanded outside of directive", sym->name);
964887Schin 		}
974887Schin 		debug((-5, "macro %s = %s", sym->name, mac->value));
984887Schin 		if (pp.macref)
994887Schin 			(*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L);
1004887Schin 		if (tp = mac->tuple)
1014887Schin 		{
1024887Schin 			old_state = pp.state;
1034887Schin 			pp.state |= DEFINITION|NOSPACE;
1044887Schin 			old_token = pp.token;
1054887Schin 			n = 2 * MAXTOKEN;
1064887Schin 			pp.token = p = oldof(0, char, 0, n);
1074887Schin 			q = p + MAXTOKEN;
1084887Schin 			*pp.token++ = ' ';
1094887Schin 			old_hidden = pp.hidden;
1104887Schin 			while (c = pplex())
1114887Schin 			{
1124887Schin 				if (c == '\n')
1134887Schin 				{
1144887Schin 					pp.hidden++;
1154887Schin 					pp.state |= HIDDEN|NEWLINE;
1164887Schin 					old_state |= HIDDEN|NEWLINE;
1174887Schin 					error_info.line++;
1184887Schin 				}
1194887Schin 				else if (c == '#')
1204887Schin 				{
1214887Schin 					ungetchr(c);
1224887Schin 					break;
1234887Schin 				}
1244887Schin 				else
1254887Schin 				{
1264887Schin 					for (;;)
1274887Schin 					{
1284887Schin 						if (streq(pp.token, tp->token))
1294887Schin 						{
1304887Schin 							if (!(tp = tp->match))
1314887Schin 								break;
1324887Schin 							if (!tp->nomatch)
1334887Schin 							{
1344887Schin 								free(p);
1354887Schin 								pp.state = old_state;
1364887Schin 								pp.token = old_token;
1374887Schin 								PUSH_TUPLE(sym, tp->token);
1384887Schin 								ret = 1;
1394887Schin 								goto disable;
1404887Schin 							}
1414887Schin 						}
1424887Schin 						else if (!(tp = tp->nomatch))
1434887Schin 							break;
1444887Schin 					}
1454887Schin 					if (!tp)
1464887Schin 					{
1474887Schin 						pp.token = pp.toknxt;
1484887Schin 						break;
1494887Schin 					}
1504887Schin 				}
1514887Schin 				if ((pp.token = pp.toknxt) > q)
1524887Schin 				{
1534887Schin 					c = pp.token - p;
1544887Schin 					p = newof(p, char, n += MAXTOKEN, 0);
1554887Schin 					q = p + n - MAXTOKEN;
1564887Schin 					pp.token = p + c;
1574887Schin 				}
1584887Schin 				*pp.token++ = ' ';
1594887Schin 			}
1604887Schin 			if (pp.token > p && *(pp.token - 1) == ' ')
1614887Schin 				pp.token--;
1624887Schin 			if (pp.hidden != old_hidden)
1634887Schin 				*pp.token++ = '\n';
1644887Schin 			else
1654887Schin 				*pp.token++ = ' ';
1664887Schin 			*pp.token = 0;
1674887Schin 			pp.state = old_state;
1684887Schin 			pp.token = old_token;
1694887Schin 			if (*p)
1704887Schin 				PUSH_RESCAN(p);
1714887Schin 			else
1724887Schin 				free(p);
1734887Schin 			if (!mac->value)
1744887Schin 				goto disable;
1754887Schin 		}
1764887Schin 		if (sym->flags & SYM_FUNCTION)
1774887Schin 		{
1784887Schin 			/*
1794887Schin 			 * a quick and dirty '(' peek to avoid possibly
1804887Schin 			 * inappropriate ungetchr()'s below
1814887Schin 			 */
1824887Schin 
1834887Schin 			for (p = pp.in->nextchr; isspace(*p); p++);
1844887Schin 			if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK)
1854887Schin 				goto disable;
1864887Schin 			old_token = pp.token;
1874887Schin 			mp = pp.macp->next;
1884887Schin 			if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac)
1894887Schin 				error(3, "%s: too many nested function-like macros", sym->name);
1904887Schin 			old_hidden = pp.hidden;
1914887Schin 			old_state = pp.state;
1924887Schin 			pp.state |= DEFINITION|FILEPOP|NOSPACE;
1934887Schin 			while ((c = pplex()) == '\n')
1944887Schin 			{
1954887Schin 				pp.hidden++;
1964887Schin 				pp.state |= HIDDEN|NEWLINE;
1974887Schin 				old_state |= HIDDEN|NEWLINE;
1984887Schin 				error_info.line++;
1994887Schin 			}
2004887Schin 			if (c != '(')
2014887Schin 			{
2024887Schin 				pp.state = old_state;
2034887Schin 				if (c)
2044887Schin 				{
2054887Schin 					p = pp.toknxt;
2064887Schin 					while (p > pp.token)
2074887Schin 						ungetchr(*--p);
2084887Schin #if COMPATIBLE
2094887Schin 					if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
2104887Schin 						error(1, "%s: macro arguments omitted", sym->name);
2114887Schin #endif
2124887Schin 					if (c == T_ID && !(pp.state & HIDDEN))
2134887Schin 						ungetchr(' ');
2144887Schin 				}
2154887Schin 				if (pp.hidden != old_hidden)
2164887Schin 				{
2174887Schin 					ungetchr('\n');
2184887Schin 					error_info.line--;
2194887Schin 					if (pp.hidden && !--pp.hidden)
2204887Schin 						pp.state &= ~HIDDEN;
2214887Schin 				}
2224887Schin 				pp.token = old_token;
2234887Schin 				goto disable;
2244887Schin 			}
2254887Schin 			pp.state = old_state;
2264887Schin 
2274887Schin 			/*
2284887Schin 			 * arg[i][-1] is an extra char for each actual i
2294887Schin 			 * for a possible ungetchr('"') during IN_QUOTE
2304887Schin 			 * arg[i][-1]==0 if arg i need not be expanded
2314887Schin 			 * arg[0][-2] holds the actual arg count
2324887Schin 			 */
2334887Schin 
2344887Schin 			c = 0;
2354887Schin 			m = 0;
2364887Schin 			n = 0;
2374887Schin 			mp = pp.macp->next;
2384887Schin 			p = pp.token = (char*)&mp->arg[mac->arity + 1];
2394887Schin 			pp.state |= COLLECTING|NOEXPAND;
2404887Schin 			pp.state &= ~FILEPOP;
2414887Schin 			sym->flags |= SYM_ACTIVE;
2424887Schin 			old_in = pp.in;
2434887Schin 			last_line = error_info.line;
2444887Schin 			last_file = error_info.file;
2454887Schin 			mp->line = error_info.line;
2464887Schin #if MACKEYARGS
2474887Schin 			if (pp.option & KEYARGS)
2484887Schin 			{
2494887Schin 				for (c = 0; c < mac->arity; c++)
2504887Schin 					mp->arg[c] = mac->args.key[c].value + 1;
2514887Schin 				mp->arg[0]++;
2524887Schin 			}
2534887Schin 			else
2544887Schin #endif
2554887Schin 			{
2564887Schin 				*++p = ' ';
2574887Schin 				mp->arg[0] = ++p;
2584887Schin 			}
2594887Schin #if MACKEYARGS
2604887Schin 		keyarg:
2614887Schin 			if (pp.option & KEYARGS)
2624887Schin 			{
2634887Schin 				pp.state |= NOSPACE;
2644887Schin 				switch (pplex())
2654887Schin 				{
2664887Schin 				case T_ID:
2674887Schin 					break;
2684887Schin 				case ')':	/* no actual key args */
2694887Schin 					if (!(pp.state & NOEXPAND))
2704887Schin 						pp.state |= NOEXPAND;
2714887Schin 					for (c = 0; c < mac->arity; c++)
2724887Schin 						mp->arg[c][-1] = 0;
2734887Schin 					c = 0;
2744887Schin 					goto endactuals;
2754887Schin 				default:
2764887Schin 					error(3, "%s: invalid keyword macro argument", pp.token);
2774887Schin 					break;
2784887Schin 				}
2794887Schin 				for (c = 0; c < mac->arity; c++)
2804887Schin 					if (streq(pp.token, mac->args.key[c].name)) break;
2814887Schin 				if (c >= mac->arity)
2824887Schin 					error(2, "%s: invalid macro argument keyword", pp.token);
2834887Schin 				if (pplex() != '=')
2844887Schin 					error(2, "= expected in keyword macro argument");
2854887Schin 				pp.state &= ~NOSPACE;
2864887Schin 				if (!c)
2874887Schin 					p++;
2884887Schin 				pp.token = mp->arg[c] = ++p;
2894887Schin 			}
2904887Schin #endif
2914887Schin 			for (;;)
2924887Schin 			{
2934887Schin 				if ((pp.mactop = pp.token = p) >= pp.maxmac)
2944887Schin 					error(3, "%s: too many nested function-like macros", sym->name);
2954887Schin 				switch (pplex())
2964887Schin 				{
2974887Schin 				case '(':
2984887Schin 					n++;
2994887Schin 					break;
3004887Schin 				case ')':
3014887Schin 					if (!n--)
3024887Schin 					{
3034887Schin 						if (p > mp->arg[c] && *(p - 1) == ' ')
3044887Schin 							p--;
3054887Schin 						if (p > mp->arg[c] && *(p - 1) == '\\')
3064887Schin 						{
3074887Schin 							for (q = mp->arg[c]; q < p; q++)
3084887Schin 								if (*q == '\\')
3094887Schin 									q++;
3104887Schin 							if (q > p)
3114887Schin 								*p++ = '\\';
3124887Schin 						}
3134887Schin #if MACKEYARGS
3144887Schin 						*p = 0;
3154887Schin 						m++;
3164887Schin #endif
3174887Schin 						goto endactuals;
3184887Schin 					}
3194887Schin 					break;
3204887Schin 				case ',':
3214887Schin 					if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC))))
3224887Schin 					{
3234887Schin 						if (p > mp->arg[c] && *(p - 1) == ' ')
3244887Schin 							p--;
3254887Schin 						*p++ = 0;
3264887Schin 						if (!(pp.state & NOEXPAND))
3274887Schin 							pp.state |= NOEXPAND;
3284887Schin 						else
3294887Schin 							mp->arg[c][-1] = 0;
3304887Schin #if MACKEYARGS
3314887Schin 						if (pp.option & KEYARGS)
3324887Schin 						{
3334887Schin 							pp.token = p + 1;
3344887Schin 							goto keyarg;
3354887Schin 						}
3364887Schin #endif
3374887Schin 						{
3384887Schin 							if ((pp.state & STRICT) && p == mp->arg[c])
3394887Schin 								error(1, "%s: macro call argument %d is null", sym->name, c + 1);
3404887Schin 							if (c < mac->arity)
3414887Schin 								c++;
3424887Schin 							*p++ = ' ';
3434887Schin 						}
3444887Schin 						pp.toknxt = mp->arg[c] = p;
3454887Schin 					}
3464887Schin 					break;
3474887Schin 				case 0:
3484887Schin 					if (pp.in == old_in)
3494887Schin 						kp = 0;
3504887Schin 					else
3514887Schin 						for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
3524887Schin 					if (!kp)
3534887Schin 					{
3544887Schin 						error(
3554887Schin #if COMPATIBLE
3564887Schin 							(pp.state & COMPATIBILITY) ? 3 :
3574887Schin #endif
3584887Schin 							2, "%s: %s in macro argument list", sym->name, pptokchr(0));
3594887Schin 						goto endactuals;
3604887Schin 					}
3614887Schin 					continue;
3624887Schin 				case '\n':
3634887Schin 					pp.state |= HIDDEN;
3644887Schin 					error_info.line++;
3654887Schin 					pp.hidden++;
3664887Schin 					/*FALLTHROUGH*/
3674887Schin 				case ' ':
3684887Schin 					if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' ';
3694887Schin 					continue;
3704887Schin 				}
3714887Schin 				p = pp.toknxt;
3724887Schin 				if (error_info.line != last_line)
3734887Schin 				{
3744887Schin 					SETLINE(p, error_info.line);
3754887Schin 					last_line = error_info.line;
3764887Schin 				}
3774887Schin 				if (error_info.file != last_file)
3784887Schin 				{
3794887Schin 					SETFILE(p, error_info.file);
3804887Schin 					last_file = error_info.file;
3814887Schin 				}
3824887Schin 			}
3834887Schin  endactuals:
3844887Schin 			if (pp.state & NOEXPAND)
3854887Schin 				mp->arg[c][-1] = 0;
3864887Schin 			pp.token = old_token;
3874887Schin 			if (pp.in != old_in)
3884887Schin 			{
3894887Schin 				for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
3904887Schin 				if (kp)
3914887Schin 					error(2, "%s: macro call starts and ends in different files", sym->name);
3924887Schin 			}
3934887Schin 			pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND);
3944887Schin 			sym->flags &= ~SYM_ACTIVE;
3954887Schin #if MACKEYARGS
3964887Schin 			if (!(pp.option & KEYARGS))
3974887Schin #endif
3984887Schin 			{
3994887Schin 				if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC))
4004887Schin 					c++;
4014887Schin 				if (c != mac->arity && !(sym->flags & SYM_EMPTY))
4024887Schin 				{
4034887Schin 					n = mac->arity;
4044887Schin 					if (!(sym->flags & SYM_VARIADIC))
4054887Schin 						error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
4064887Schin 					else if (c < --n)
4074887Schin 						error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
4084887Schin #if COMPATIBLE
4094887Schin 					if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
4104887Schin 						goto disable;
4114887Schin #endif
4124887Schin 				}
4134887Schin 				if (!c)
4144887Schin 					++c;
4154887Schin 				while (c < mac->arity)
4164887Schin 					mp->arg[c++] = (char*)"\0" + 1;
4174887Schin 			}
4184887Schin 			mp->arg[0][-2] = m;
4194887Schin 			*p++ = 0;
4204887Schin 			nextframe(mp, p);
4214887Schin 			count(function);
4224887Schin 		}
4234887Schin 		if (!tok && (sym->flags & SYM_NOEXPAND))
4244887Schin 		{
4254887Schin 			if (sym->flags & SYM_FUNCTION)
4264887Schin 				popframe(mp);
4274887Schin 			ret = !mac->size;
4284887Schin 		}
4294887Schin 		else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL)  || pp.in->type != IN_COPY)
4304887Schin 		{
4314887Schin 			if (sym->flags & SYM_MULTILINE)
4324887Schin 				PUSH_MULTILINE(sym);
4334887Schin 			else
4344887Schin 				PUSH_MACRO(sym);
4354887Schin 			ret = 1;
4364887Schin 		}
4374887Schin 	}
4384887Schin  disable:
4394887Schin 	if (ret < 0 && sym->hidden && !(pp.mode & EXPOSE) && !(pp.state & HEADER) && (pp.in->type == IN_FILE || pp.in->type == IN_MACRO || pp.in->type == IN_EXPAND))
4404887Schin 	{
4414887Schin 		struct ppinstk*	inp;
4424887Schin 
4434887Schin 		for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev);
4444887Schin 		sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name);
4454887Schin 		PUSH_STRING(pp.hidebuf);
4464887Schin 		ret = 1;
4474887Schin 	}
4484887Schin 	pp.state &= ~NEWLINE;
4494887Schin 	pp.in->flags |= IN_tokens;
4504887Schin 	count(token);
4514887Schin 	return ret;
4524887Schin }
453