xref: /onnv-gate/usr/src/lib/libpp/common/ppcall.c (revision 10898:1883b621b3ea)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*10898Sroland.mainz@nrubsig.org *          Copyright (c) 1986-2009 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.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
ppcall(register struct ppsymbol * sym,int tok)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;
54*10898Sroland.mainz@nrubsig.org 	char*				old_next;
554887Schin 	char*				old_token;
564887Schin 	struct ppmacstk*		mp;
574887Schin 	struct ppinstk*			old_in;
584887Schin 	struct ppinstk*			kp;
594887Schin 	struct pptuple*			tp;
604887Schin 
614887Schin 	ret = -1;
624887Schin 	sym->flags |= SYM_NOTICED;
634887Schin 	if (mac = sym->macro)
644887Schin 	{
654887Schin 		count(macro);
664887Schin 		if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN))
674887Schin 			error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name);
684887Schin 		if (sym->flags & SYM_DISABLED)
694887Schin #if COMPATIBLE
704887Schin 			if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity)
714887Schin #endif
724887Schin 		{
734887Schin 			pp.mode |= MARKMACRO;
744887Schin #if COMPATIBLE
754887Schin 			if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
764887Schin 				error(1, "%s: macro recursion inhibited", sym->name);
774887Schin #endif
784887Schin 			goto disable;
794887Schin 		}
804887Schin 		if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE)))
814887Schin 		{
824887Schin #if COMPATIBLE
834887Schin 			if (*sym->name != '_' && !(pp.state & COMPATIBILITY))
844887Schin #else
854887Schin 			if (*sym->name != '_')
864887Schin #endif
874887Schin 			{
884887Schin 				if (pp.state & STRICT)
894887Schin 				{
904887Schin 					error(1, "%s: obsolete predefined symbol expansion disabled", sym->name);
914887Schin 					goto disable;
924887Schin 				}
934887Schin 				error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive");
944887Schin 			}
954887Schin 			else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#'))
964887Schin 				error(1, "%s: predefined symbol expanded outside of directive", sym->name);
974887Schin 		}
984887Schin 		debug((-5, "macro %s = %s", sym->name, mac->value));
994887Schin 		if (pp.macref)
1004887Schin 			(*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L);
1014887Schin 		if (tp = mac->tuple)
1024887Schin 		{
1034887Schin 			old_state = pp.state;
1044887Schin 			pp.state |= DEFINITION|NOSPACE;
1054887Schin 			old_token = pp.token;
1064887Schin 			n = 2 * MAXTOKEN;
1074887Schin 			pp.token = p = oldof(0, char, 0, n);
1084887Schin 			q = p + MAXTOKEN;
1094887Schin 			*pp.token++ = ' ';
1104887Schin 			old_hidden = pp.hidden;
1114887Schin 			while (c = pplex())
1124887Schin 			{
1134887Schin 				if (c == '\n')
1144887Schin 				{
1154887Schin 					pp.hidden++;
1164887Schin 					pp.state |= HIDDEN|NEWLINE;
1174887Schin 					old_state |= HIDDEN|NEWLINE;
1184887Schin 					error_info.line++;
1194887Schin 				}
1204887Schin 				else if (c == '#')
1214887Schin 				{
1224887Schin 					ungetchr(c);
1234887Schin 					break;
1244887Schin 				}
1254887Schin 				else
1264887Schin 				{
1274887Schin 					for (;;)
1284887Schin 					{
1294887Schin 						if (streq(pp.token, tp->token))
1304887Schin 						{
1314887Schin 							if (!(tp = tp->match))
1324887Schin 								break;
1334887Schin 							if (!tp->nomatch)
1344887Schin 							{
1354887Schin 								free(p);
1364887Schin 								pp.state = old_state;
1374887Schin 								pp.token = old_token;
1384887Schin 								PUSH_TUPLE(sym, tp->token);
1394887Schin 								ret = 1;
1404887Schin 								goto disable;
1414887Schin 							}
1424887Schin 						}
1434887Schin 						else if (!(tp = tp->nomatch))
1444887Schin 							break;
1454887Schin 					}
1464887Schin 					if (!tp)
1474887Schin 					{
1484887Schin 						pp.token = pp.toknxt;
1494887Schin 						break;
1504887Schin 					}
1514887Schin 				}
1524887Schin 				if ((pp.token = pp.toknxt) > q)
1534887Schin 				{
1544887Schin 					c = pp.token - p;
1554887Schin 					p = newof(p, char, n += MAXTOKEN, 0);
1564887Schin 					q = p + n - MAXTOKEN;
1574887Schin 					pp.token = p + c;
1584887Schin 				}
1594887Schin 				*pp.token++ = ' ';
1604887Schin 			}
1614887Schin 			if (pp.token > p && *(pp.token - 1) == ' ')
1624887Schin 				pp.token--;
1634887Schin 			if (pp.hidden != old_hidden)
1644887Schin 				*pp.token++ = '\n';
1654887Schin 			else
1664887Schin 				*pp.token++ = ' ';
1674887Schin 			*pp.token = 0;
1684887Schin 			pp.state = old_state;
1694887Schin 			pp.token = old_token;
1704887Schin 			if (*p)
1714887Schin 				PUSH_RESCAN(p);
1724887Schin 			else
1734887Schin 				free(p);
1744887Schin 			if (!mac->value)
1754887Schin 				goto disable;
1764887Schin 		}
1774887Schin 		if (sym->flags & SYM_FUNCTION)
1784887Schin 		{
1794887Schin 			/*
1804887Schin 			 * a quick and dirty '(' peek to avoid possibly
1814887Schin 			 * inappropriate ungetchr()'s below
1824887Schin 			 */
1834887Schin 
1844887Schin 			for (p = pp.in->nextchr; isspace(*p); p++);
1854887Schin 			if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK)
1864887Schin 				goto disable;
187*10898Sroland.mainz@nrubsig.org 			old_next = (c == MARK) ? pp.in->nextchr : NiL;
1884887Schin 			old_token = pp.token;
1894887Schin 			mp = pp.macp->next;
1904887Schin 			if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac)
1914887Schin 				error(3, "%s: too many nested function-like macros", sym->name);
1924887Schin 			old_hidden = pp.hidden;
1934887Schin 			old_state = pp.state;
1944887Schin 			pp.state |= DEFINITION|FILEPOP|NOSPACE;
1954887Schin 			while ((c = pplex()) == '\n')
1964887Schin 			{
1974887Schin 				pp.hidden++;
1984887Schin 				pp.state |= HIDDEN|NEWLINE;
1994887Schin 				old_state |= HIDDEN|NEWLINE;
2004887Schin 				error_info.line++;
2014887Schin 			}
2024887Schin 			if (c != '(')
2034887Schin 			{
2044887Schin 				pp.state = old_state;
205*10898Sroland.mainz@nrubsig.org 				if (old_next)
206*10898Sroland.mainz@nrubsig.org 					pp.in->nextchr = old_next;
207*10898Sroland.mainz@nrubsig.org 				else
2084887Schin 				{
209*10898Sroland.mainz@nrubsig.org 					if (c)
210*10898Sroland.mainz@nrubsig.org 					{
211*10898Sroland.mainz@nrubsig.org 						p = pp.toknxt;
212*10898Sroland.mainz@nrubsig.org 						while (p > pp.token)
213*10898Sroland.mainz@nrubsig.org 							ungetchr(*--p);
2144887Schin #if COMPATIBLE
215*10898Sroland.mainz@nrubsig.org 						if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
216*10898Sroland.mainz@nrubsig.org 							error(1, "%s: macro arguments omitted", sym->name);
2174887Schin #endif
218*10898Sroland.mainz@nrubsig.org 						if (c == T_ID && !(pp.state & HIDDEN))
219*10898Sroland.mainz@nrubsig.org 							ungetchr(' ');
220*10898Sroland.mainz@nrubsig.org 					}
221*10898Sroland.mainz@nrubsig.org 					if (pp.hidden != old_hidden)
222*10898Sroland.mainz@nrubsig.org 					{
223*10898Sroland.mainz@nrubsig.org 						ungetchr('\n');
224*10898Sroland.mainz@nrubsig.org 						error_info.line--;
225*10898Sroland.mainz@nrubsig.org 						if (pp.hidden && !--pp.hidden)
226*10898Sroland.mainz@nrubsig.org 							pp.state &= ~HIDDEN;
227*10898Sroland.mainz@nrubsig.org 					}
2284887Schin 				}
2294887Schin 				pp.token = old_token;
2304887Schin 				goto disable;
2314887Schin 			}
2324887Schin 			pp.state = old_state;
2334887Schin 
2344887Schin 			/*
2354887Schin 			 * arg[i][-1] is an extra char for each actual i
2364887Schin 			 * for a possible ungetchr('"') during IN_QUOTE
2374887Schin 			 * arg[i][-1]==0 if arg i need not be expanded
2384887Schin 			 * arg[0][-2] holds the actual arg count
2394887Schin 			 */
2404887Schin 
2414887Schin 			c = 0;
2424887Schin 			m = 0;
2434887Schin 			n = 0;
2444887Schin 			mp = pp.macp->next;
2454887Schin 			p = pp.token = (char*)&mp->arg[mac->arity + 1];
2464887Schin 			pp.state |= COLLECTING|NOEXPAND;
2474887Schin 			pp.state &= ~FILEPOP;
2484887Schin 			sym->flags |= SYM_ACTIVE;
2494887Schin 			old_in = pp.in;
2504887Schin 			last_line = error_info.line;
2514887Schin 			last_file = error_info.file;
2524887Schin 			mp->line = error_info.line;
2534887Schin #if MACKEYARGS
2544887Schin 			if (pp.option & KEYARGS)
2554887Schin 			{
2564887Schin 				for (c = 0; c < mac->arity; c++)
2574887Schin 					mp->arg[c] = mac->args.key[c].value + 1;
2584887Schin 				mp->arg[0]++;
2594887Schin 			}
2604887Schin 			else
2614887Schin #endif
2624887Schin 			{
2634887Schin 				*++p = ' ';
2644887Schin 				mp->arg[0] = ++p;
2654887Schin 			}
2664887Schin #if MACKEYARGS
2674887Schin 		keyarg:
2684887Schin 			if (pp.option & KEYARGS)
2694887Schin 			{
2704887Schin 				pp.state |= NOSPACE;
2714887Schin 				switch (pplex())
2724887Schin 				{
2734887Schin 				case T_ID:
2744887Schin 					break;
2754887Schin 				case ')':	/* no actual key args */
2764887Schin 					if (!(pp.state & NOEXPAND))
2774887Schin 						pp.state |= NOEXPAND;
2784887Schin 					for (c = 0; c < mac->arity; c++)
2794887Schin 						mp->arg[c][-1] = 0;
2804887Schin 					c = 0;
2814887Schin 					goto endactuals;
2824887Schin 				default:
2834887Schin 					error(3, "%s: invalid keyword macro argument", pp.token);
2844887Schin 					break;
2854887Schin 				}
2864887Schin 				for (c = 0; c < mac->arity; c++)
2874887Schin 					if (streq(pp.token, mac->args.key[c].name)) break;
2884887Schin 				if (c >= mac->arity)
2894887Schin 					error(2, "%s: invalid macro argument keyword", pp.token);
2904887Schin 				if (pplex() != '=')
2914887Schin 					error(2, "= expected in keyword macro argument");
2924887Schin 				pp.state &= ~NOSPACE;
2934887Schin 				if (!c)
2944887Schin 					p++;
2954887Schin 				pp.token = mp->arg[c] = ++p;
2964887Schin 			}
2974887Schin #endif
2984887Schin 			for (;;)
2994887Schin 			{
3004887Schin 				if ((pp.mactop = pp.token = p) >= pp.maxmac)
3014887Schin 					error(3, "%s: too many nested function-like macros", sym->name);
3024887Schin 				switch (pplex())
3034887Schin 				{
3044887Schin 				case '(':
3054887Schin 					n++;
3064887Schin 					break;
3074887Schin 				case ')':
3084887Schin 					if (!n--)
3094887Schin 					{
3104887Schin 						if (p > mp->arg[c] && *(p - 1) == ' ')
3114887Schin 							p--;
3124887Schin 						if (p > mp->arg[c] && *(p - 1) == '\\')
3134887Schin 						{
3144887Schin 							for (q = mp->arg[c]; q < p; q++)
3154887Schin 								if (*q == '\\')
3164887Schin 									q++;
3174887Schin 							if (q > p)
3184887Schin 								*p++ = '\\';
3194887Schin 						}
3204887Schin #if MACKEYARGS
3214887Schin 						*p = 0;
3224887Schin 						m++;
3234887Schin #endif
3244887Schin 						goto endactuals;
3254887Schin 					}
3264887Schin 					break;
3274887Schin 				case ',':
3284887Schin 					if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC))))
3294887Schin 					{
3304887Schin 						if (p > mp->arg[c] && *(p - 1) == ' ')
3314887Schin 							p--;
3324887Schin 						*p++ = 0;
3334887Schin 						if (!(pp.state & NOEXPAND))
3344887Schin 							pp.state |= NOEXPAND;
3354887Schin 						else
3364887Schin 							mp->arg[c][-1] = 0;
3374887Schin #if MACKEYARGS
3384887Schin 						if (pp.option & KEYARGS)
3394887Schin 						{
3404887Schin 							pp.token = p + 1;
3414887Schin 							goto keyarg;
3424887Schin 						}
3434887Schin #endif
3444887Schin 						{
3454887Schin 							if ((pp.state & STRICT) && p == mp->arg[c])
3464887Schin 								error(1, "%s: macro call argument %d is null", sym->name, c + 1);
3474887Schin 							if (c < mac->arity)
3484887Schin 								c++;
3494887Schin 							*p++ = ' ';
3504887Schin 						}
3514887Schin 						pp.toknxt = mp->arg[c] = p;
3524887Schin 					}
3534887Schin 					break;
3544887Schin 				case 0:
3554887Schin 					if (pp.in == old_in)
3564887Schin 						kp = 0;
3574887Schin 					else
3584887Schin 						for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
3594887Schin 					if (!kp)
3604887Schin 					{
3614887Schin 						error(
3624887Schin #if COMPATIBLE
3634887Schin 							(pp.state & COMPATIBILITY) ? 3 :
3644887Schin #endif
3654887Schin 							2, "%s: %s in macro argument list", sym->name, pptokchr(0));
3664887Schin 						goto endactuals;
3674887Schin 					}
3684887Schin 					continue;
3694887Schin 				case '\n':
3704887Schin 					pp.state |= HIDDEN;
3714887Schin 					error_info.line++;
3724887Schin 					pp.hidden++;
3734887Schin 					/*FALLTHROUGH*/
3744887Schin 				case ' ':
3754887Schin 					if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' ';
3764887Schin 					continue;
3774887Schin 				}
3784887Schin 				p = pp.toknxt;
3794887Schin 				if (error_info.line != last_line)
3804887Schin 				{
3814887Schin 					SETLINE(p, error_info.line);
3824887Schin 					last_line = error_info.line;
3834887Schin 				}
3844887Schin 				if (error_info.file != last_file)
3854887Schin 				{
3864887Schin 					SETFILE(p, error_info.file);
3874887Schin 					last_file = error_info.file;
3884887Schin 				}
3894887Schin 			}
3904887Schin  endactuals:
3914887Schin 			if (pp.state & NOEXPAND)
3924887Schin 				mp->arg[c][-1] = 0;
3934887Schin 			pp.token = old_token;
3944887Schin 			if (pp.in != old_in)
3954887Schin 			{
3964887Schin 				for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
3974887Schin 				if (kp)
3984887Schin 					error(2, "%s: macro call starts and ends in different files", sym->name);
3994887Schin 			}
4004887Schin 			pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND);
4014887Schin 			sym->flags &= ~SYM_ACTIVE;
4024887Schin #if MACKEYARGS
4034887Schin 			if (!(pp.option & KEYARGS))
4044887Schin #endif
4054887Schin 			{
4064887Schin 				if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC))
4074887Schin 					c++;
4084887Schin 				if (c != mac->arity && !(sym->flags & SYM_EMPTY))
4094887Schin 				{
4104887Schin 					n = mac->arity;
4114887Schin 					if (!(sym->flags & SYM_VARIADIC))
4124887Schin 						error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
4134887Schin 					else if (c < --n)
4144887Schin 						error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
4154887Schin #if COMPATIBLE
4164887Schin 					if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
4174887Schin 						goto disable;
4184887Schin #endif
4194887Schin 				}
4204887Schin 				if (!c)
4214887Schin 					++c;
4224887Schin 				while (c < mac->arity)
4234887Schin 					mp->arg[c++] = (char*)"\0" + 1;
4244887Schin 			}
4254887Schin 			mp->arg[0][-2] = m;
4264887Schin 			*p++ = 0;
4274887Schin 			nextframe(mp, p);
4284887Schin 			count(function);
4294887Schin 		}
4304887Schin 		if (!tok && (sym->flags & SYM_NOEXPAND))
4314887Schin 		{
4324887Schin 			if (sym->flags & SYM_FUNCTION)
4334887Schin 				popframe(mp);
4344887Schin 			ret = !mac->size;
4354887Schin 		}
4364887Schin 		else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL)  || pp.in->type != IN_COPY)
4374887Schin 		{
4384887Schin 			if (sym->flags & SYM_MULTILINE)
4394887Schin 				PUSH_MULTILINE(sym);
4404887Schin 			else
4414887Schin 				PUSH_MACRO(sym);
4424887Schin 			ret = 1;
4434887Schin 		}
4444887Schin 	}
4454887Schin  disable:
4464887Schin 	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))
4474887Schin 	{
4484887Schin 		struct ppinstk*	inp;
4494887Schin 
4504887Schin 		for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev);
4514887Schin 		sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name);
4524887Schin 		PUSH_STRING(pp.hidebuf);
4534887Schin 		ret = 1;
4544887Schin 	}
4554887Schin 	pp.state &= ~NEWLINE;
4564887Schin 	pp.in->flags |= IN_tokens;
4574887Schin 	count(token);
4584887Schin 	return ret;
4594887Schin }
460