1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1986-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
18*4887Schin *                                                                      *
19*4887Schin ***********************************************************************/
20*4887Schin #pragma prototyped
21*4887Schin /*
22*4887Schin  * Glenn Fowler
23*4887Schin  * AT&T Research
24*4887Schin  *
25*4887Schin  * preprocessor macro call
26*4887Schin  */
27*4887Schin 
28*4887Schin #include "pplib.h"
29*4887Schin 
30*4887Schin #include <ctype.h>
31*4887Schin 
32*4887Schin /*
33*4887Schin  * call a macro by pushing its value on the input stream
34*4887Schin  * only the macro token itself has been consumed
35*4887Schin  * -1 returned if macro disabled
36*4887Schin  *  0 returned if tok==0 and sym->mac->value to be copied to output by caller
37*4887Schin  *  1 returned if value pushed on input
38*4887Schin  */
39*4887Schin 
40*4887Schin int
41*4887Schin ppcall(register struct ppsymbol* sym, int tok)
42*4887Schin {
43*4887Schin 	register int			c;
44*4887Schin 	register char*			p;
45*4887Schin 	register char*			q;
46*4887Schin 	register struct ppmacro*	mac;
47*4887Schin 	int				n;
48*4887Schin 	int				m;
49*4887Schin 	int				ret;
50*4887Schin 	int				old_hidden;
51*4887Schin 	int				last_line;
52*4887Schin 	long				old_state;
53*4887Schin 	char*				last_file;
54*4887Schin 	char*				old_token;
55*4887Schin 	struct ppmacstk*		mp;
56*4887Schin 	struct ppinstk*			old_in;
57*4887Schin 	struct ppinstk*			kp;
58*4887Schin 	struct pptuple*			tp;
59*4887Schin 
60*4887Schin 	ret = -1;
61*4887Schin 	sym->flags |= SYM_NOTICED;
62*4887Schin 	if (mac = sym->macro)
63*4887Schin 	{
64*4887Schin 		count(macro);
65*4887Schin 		if ((sym->flags & SYM_PREDICATE) && (pp.state & (CONDITIONAL|WARN)) == (CONDITIONAL|WARN))
66*4887Schin 			error(1, "%s: macro definition overrides assertion: use #%s ...", sym->name, sym->name);
67*4887Schin 		if (sym->flags & SYM_DISABLED)
68*4887Schin #if COMPATIBLE
69*4887Schin 			if ((pp.state & (COMPATIBILITY|TRANSITION)) != COMPATIBILITY || !mac->arity)
70*4887Schin #endif
71*4887Schin 		{
72*4887Schin 			pp.mode |= MARKMACRO;
73*4887Schin #if COMPATIBLE
74*4887Schin 			if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
75*4887Schin 				error(1, "%s: macro recursion inhibited", sym->name);
76*4887Schin #endif
77*4887Schin 			goto disable;
78*4887Schin 		}
79*4887Schin 		if ((sym->flags & SYM_PREDEFINED) && !(pp.mode & (HOSTED|INACTIVE)))
80*4887Schin 		{
81*4887Schin #if COMPATIBLE
82*4887Schin 			if (*sym->name != '_' && !(pp.state & COMPATIBILITY))
83*4887Schin #else
84*4887Schin 			if (*sym->name != '_')
85*4887Schin #endif
86*4887Schin 			{
87*4887Schin 				if (pp.state & STRICT)
88*4887Schin 				{
89*4887Schin 					error(1, "%s: obsolete predefined symbol expansion disabled", sym->name);
90*4887Schin 					goto disable;
91*4887Schin 				}
92*4887Schin 				error(1, "%s: obsolete predefined symbol expanded%s", sym->name, (pp.state & DIRECTIVE) ? "" : " outside of directive");
93*4887Schin 			}
94*4887Schin 			else if (!(pp.state & DIRECTIVE) && mac->value && (ppisdig(*mac->value) || *mac->value == '#'))
95*4887Schin 				error(1, "%s: predefined symbol expanded outside of directive", sym->name);
96*4887Schin 		}
97*4887Schin 		debug((-5, "macro %s = %s", sym->name, mac->value));
98*4887Schin 		if (pp.macref)
99*4887Schin 			(*pp.macref)(sym, error_info.file, error_info.line, (pp.state & CONDITIONAL) ? REF_IF : REF_NORMAL, 0L);
100*4887Schin 		if (tp = mac->tuple)
101*4887Schin 		{
102*4887Schin 			old_state = pp.state;
103*4887Schin 			pp.state |= DEFINITION|NOSPACE;
104*4887Schin 			old_token = pp.token;
105*4887Schin 			n = 2 * MAXTOKEN;
106*4887Schin 			pp.token = p = oldof(0, char, 0, n);
107*4887Schin 			q = p + MAXTOKEN;
108*4887Schin 			*pp.token++ = ' ';
109*4887Schin 			old_hidden = pp.hidden;
110*4887Schin 			while (c = pplex())
111*4887Schin 			{
112*4887Schin 				if (c == '\n')
113*4887Schin 				{
114*4887Schin 					pp.hidden++;
115*4887Schin 					pp.state |= HIDDEN|NEWLINE;
116*4887Schin 					old_state |= HIDDEN|NEWLINE;
117*4887Schin 					error_info.line++;
118*4887Schin 				}
119*4887Schin 				else if (c == '#')
120*4887Schin 				{
121*4887Schin 					ungetchr(c);
122*4887Schin 					break;
123*4887Schin 				}
124*4887Schin 				else
125*4887Schin 				{
126*4887Schin 					for (;;)
127*4887Schin 					{
128*4887Schin 						if (streq(pp.token, tp->token))
129*4887Schin 						{
130*4887Schin 							if (!(tp = tp->match))
131*4887Schin 								break;
132*4887Schin 							if (!tp->nomatch)
133*4887Schin 							{
134*4887Schin 								free(p);
135*4887Schin 								pp.state = old_state;
136*4887Schin 								pp.token = old_token;
137*4887Schin 								PUSH_TUPLE(sym, tp->token);
138*4887Schin 								ret = 1;
139*4887Schin 								goto disable;
140*4887Schin 							}
141*4887Schin 						}
142*4887Schin 						else if (!(tp = tp->nomatch))
143*4887Schin 							break;
144*4887Schin 					}
145*4887Schin 					if (!tp)
146*4887Schin 					{
147*4887Schin 						pp.token = pp.toknxt;
148*4887Schin 						break;
149*4887Schin 					}
150*4887Schin 				}
151*4887Schin 				if ((pp.token = pp.toknxt) > q)
152*4887Schin 				{
153*4887Schin 					c = pp.token - p;
154*4887Schin 					p = newof(p, char, n += MAXTOKEN, 0);
155*4887Schin 					q = p + n - MAXTOKEN;
156*4887Schin 					pp.token = p + c;
157*4887Schin 				}
158*4887Schin 				*pp.token++ = ' ';
159*4887Schin 			}
160*4887Schin 			if (pp.token > p && *(pp.token - 1) == ' ')
161*4887Schin 				pp.token--;
162*4887Schin 			if (pp.hidden != old_hidden)
163*4887Schin 				*pp.token++ = '\n';
164*4887Schin 			else
165*4887Schin 				*pp.token++ = ' ';
166*4887Schin 			*pp.token = 0;
167*4887Schin 			pp.state = old_state;
168*4887Schin 			pp.token = old_token;
169*4887Schin 			if (*p)
170*4887Schin 				PUSH_RESCAN(p);
171*4887Schin 			else
172*4887Schin 				free(p);
173*4887Schin 			if (!mac->value)
174*4887Schin 				goto disable;
175*4887Schin 		}
176*4887Schin 		if (sym->flags & SYM_FUNCTION)
177*4887Schin 		{
178*4887Schin 			/*
179*4887Schin 			 * a quick and dirty '(' peek to avoid possibly
180*4887Schin 			 * inappropriate ungetchr()'s below
181*4887Schin 			 */
182*4887Schin 
183*4887Schin 			for (p = pp.in->nextchr; isspace(*p); p++);
184*4887Schin 			if ((c = *p) != '(' && c != '/' && c != 0 && c != MARK)
185*4887Schin 				goto disable;
186*4887Schin 			old_token = pp.token;
187*4887Schin 			mp = pp.macp->next;
188*4887Schin 			if ((pp.token = (char*)&mp->arg[mac->arity + 1]) > pp.maxmac)
189*4887Schin 				error(3, "%s: too many nested function-like macros", sym->name);
190*4887Schin 			old_hidden = pp.hidden;
191*4887Schin 			old_state = pp.state;
192*4887Schin 			pp.state |= DEFINITION|FILEPOP|NOSPACE;
193*4887Schin 			while ((c = pplex()) == '\n')
194*4887Schin 			{
195*4887Schin 				pp.hidden++;
196*4887Schin 				pp.state |= HIDDEN|NEWLINE;
197*4887Schin 				old_state |= HIDDEN|NEWLINE;
198*4887Schin 				error_info.line++;
199*4887Schin 			}
200*4887Schin 			if (c != '(')
201*4887Schin 			{
202*4887Schin 				pp.state = old_state;
203*4887Schin 				if (c)
204*4887Schin 				{
205*4887Schin 					p = pp.toknxt;
206*4887Schin 					while (p > pp.token)
207*4887Schin 						ungetchr(*--p);
208*4887Schin #if COMPATIBLE
209*4887Schin 					if ((pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
210*4887Schin 						error(1, "%s: macro arguments omitted", sym->name);
211*4887Schin #endif
212*4887Schin 					if (c == T_ID && !(pp.state & HIDDEN))
213*4887Schin 						ungetchr(' ');
214*4887Schin 				}
215*4887Schin 				if (pp.hidden != old_hidden)
216*4887Schin 				{
217*4887Schin 					ungetchr('\n');
218*4887Schin 					error_info.line--;
219*4887Schin 					if (pp.hidden && !--pp.hidden)
220*4887Schin 						pp.state &= ~HIDDEN;
221*4887Schin 				}
222*4887Schin 				pp.token = old_token;
223*4887Schin 				goto disable;
224*4887Schin 			}
225*4887Schin 			pp.state = old_state;
226*4887Schin 
227*4887Schin 			/*
228*4887Schin 			 * arg[i][-1] is an extra char for each actual i
229*4887Schin 			 * for a possible ungetchr('"') during IN_QUOTE
230*4887Schin 			 * arg[i][-1]==0 if arg i need not be expanded
231*4887Schin 			 * arg[0][-2] holds the actual arg count
232*4887Schin 			 */
233*4887Schin 
234*4887Schin 			c = 0;
235*4887Schin 			m = 0;
236*4887Schin 			n = 0;
237*4887Schin 			mp = pp.macp->next;
238*4887Schin 			p = pp.token = (char*)&mp->arg[mac->arity + 1];
239*4887Schin 			pp.state |= COLLECTING|NOEXPAND;
240*4887Schin 			pp.state &= ~FILEPOP;
241*4887Schin 			sym->flags |= SYM_ACTIVE;
242*4887Schin 			old_in = pp.in;
243*4887Schin 			last_line = error_info.line;
244*4887Schin 			last_file = error_info.file;
245*4887Schin 			mp->line = error_info.line;
246*4887Schin #if MACKEYARGS
247*4887Schin 			if (pp.option & KEYARGS)
248*4887Schin 			{
249*4887Schin 				for (c = 0; c < mac->arity; c++)
250*4887Schin 					mp->arg[c] = mac->args.key[c].value + 1;
251*4887Schin 				mp->arg[0]++;
252*4887Schin 			}
253*4887Schin 			else
254*4887Schin #endif
255*4887Schin 			{
256*4887Schin 				*++p = ' ';
257*4887Schin 				mp->arg[0] = ++p;
258*4887Schin 			}
259*4887Schin #if MACKEYARGS
260*4887Schin 		keyarg:
261*4887Schin 			if (pp.option & KEYARGS)
262*4887Schin 			{
263*4887Schin 				pp.state |= NOSPACE;
264*4887Schin 				switch (pplex())
265*4887Schin 				{
266*4887Schin 				case T_ID:
267*4887Schin 					break;
268*4887Schin 				case ')':	/* no actual key args */
269*4887Schin 					if (!(pp.state & NOEXPAND))
270*4887Schin 						pp.state |= NOEXPAND;
271*4887Schin 					for (c = 0; c < mac->arity; c++)
272*4887Schin 						mp->arg[c][-1] = 0;
273*4887Schin 					c = 0;
274*4887Schin 					goto endactuals;
275*4887Schin 				default:
276*4887Schin 					error(3, "%s: invalid keyword macro argument", pp.token);
277*4887Schin 					break;
278*4887Schin 				}
279*4887Schin 				for (c = 0; c < mac->arity; c++)
280*4887Schin 					if (streq(pp.token, mac->args.key[c].name)) break;
281*4887Schin 				if (c >= mac->arity)
282*4887Schin 					error(2, "%s: invalid macro argument keyword", pp.token);
283*4887Schin 				if (pplex() != '=')
284*4887Schin 					error(2, "= expected in keyword macro argument");
285*4887Schin 				pp.state &= ~NOSPACE;
286*4887Schin 				if (!c)
287*4887Schin 					p++;
288*4887Schin 				pp.token = mp->arg[c] = ++p;
289*4887Schin 			}
290*4887Schin #endif
291*4887Schin 			for (;;)
292*4887Schin 			{
293*4887Schin 				if ((pp.mactop = pp.token = p) >= pp.maxmac)
294*4887Schin 					error(3, "%s: too many nested function-like macros", sym->name);
295*4887Schin 				switch (pplex())
296*4887Schin 				{
297*4887Schin 				case '(':
298*4887Schin 					n++;
299*4887Schin 					break;
300*4887Schin 				case ')':
301*4887Schin 					if (!n--)
302*4887Schin 					{
303*4887Schin 						if (p > mp->arg[c] && *(p - 1) == ' ')
304*4887Schin 							p--;
305*4887Schin 						if (p > mp->arg[c] && *(p - 1) == '\\')
306*4887Schin 						{
307*4887Schin 							for (q = mp->arg[c]; q < p; q++)
308*4887Schin 								if (*q == '\\')
309*4887Schin 									q++;
310*4887Schin 							if (q > p)
311*4887Schin 								*p++ = '\\';
312*4887Schin 						}
313*4887Schin #if MACKEYARGS
314*4887Schin 						*p = 0;
315*4887Schin 						m++;
316*4887Schin #endif
317*4887Schin 						goto endactuals;
318*4887Schin 					}
319*4887Schin 					break;
320*4887Schin 				case ',':
321*4887Schin 					if (!n && (m++, (c < mac->arity - 1 || !(sym->flags & SYM_VARIADIC))))
322*4887Schin 					{
323*4887Schin 						if (p > mp->arg[c] && *(p - 1) == ' ')
324*4887Schin 							p--;
325*4887Schin 						*p++ = 0;
326*4887Schin 						if (!(pp.state & NOEXPAND))
327*4887Schin 							pp.state |= NOEXPAND;
328*4887Schin 						else
329*4887Schin 							mp->arg[c][-1] = 0;
330*4887Schin #if MACKEYARGS
331*4887Schin 						if (pp.option & KEYARGS)
332*4887Schin 						{
333*4887Schin 							pp.token = p + 1;
334*4887Schin 							goto keyarg;
335*4887Schin 						}
336*4887Schin #endif
337*4887Schin 						{
338*4887Schin 							if ((pp.state & STRICT) && p == mp->arg[c])
339*4887Schin 								error(1, "%s: macro call argument %d is null", sym->name, c + 1);
340*4887Schin 							if (c < mac->arity)
341*4887Schin 								c++;
342*4887Schin 							*p++ = ' ';
343*4887Schin 						}
344*4887Schin 						pp.toknxt = mp->arg[c] = p;
345*4887Schin 					}
346*4887Schin 					break;
347*4887Schin 				case 0:
348*4887Schin 					if (pp.in == old_in)
349*4887Schin 						kp = 0;
350*4887Schin 					else
351*4887Schin 						for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
352*4887Schin 					if (!kp)
353*4887Schin 					{
354*4887Schin 						error(
355*4887Schin #if COMPATIBLE
356*4887Schin 							(pp.state & COMPATIBILITY) ? 3 :
357*4887Schin #endif
358*4887Schin 							2, "%s: %s in macro argument list", sym->name, pptokchr(0));
359*4887Schin 						goto endactuals;
360*4887Schin 					}
361*4887Schin 					continue;
362*4887Schin 				case '\n':
363*4887Schin 					pp.state |= HIDDEN;
364*4887Schin 					error_info.line++;
365*4887Schin 					pp.hidden++;
366*4887Schin 					/*FALLTHROUGH*/
367*4887Schin 				case ' ':
368*4887Schin 					if (p > mp->arg[c] && *(p - 1) != ' ') *p++ = ' ';
369*4887Schin 					continue;
370*4887Schin 				}
371*4887Schin 				p = pp.toknxt;
372*4887Schin 				if (error_info.line != last_line)
373*4887Schin 				{
374*4887Schin 					SETLINE(p, error_info.line);
375*4887Schin 					last_line = error_info.line;
376*4887Schin 				}
377*4887Schin 				if (error_info.file != last_file)
378*4887Schin 				{
379*4887Schin 					SETFILE(p, error_info.file);
380*4887Schin 					last_file = error_info.file;
381*4887Schin 				}
382*4887Schin 			}
383*4887Schin  endactuals:
384*4887Schin 			if (pp.state & NOEXPAND)
385*4887Schin 				mp->arg[c][-1] = 0;
386*4887Schin 			pp.token = old_token;
387*4887Schin 			if (pp.in != old_in)
388*4887Schin 			{
389*4887Schin 				for (kp = pp.in; kp && kp != old_in; kp = kp->prev);
390*4887Schin 				if (kp)
391*4887Schin 					error(2, "%s: macro call starts and ends in different files", sym->name);
392*4887Schin 			}
393*4887Schin 			pp.state &= ~(COLLECTING|FILEPOP|NOEXPAND);
394*4887Schin 			sym->flags &= ~SYM_ACTIVE;
395*4887Schin #if MACKEYARGS
396*4887Schin 			if (!(pp.option & KEYARGS))
397*4887Schin #endif
398*4887Schin 			{
399*4887Schin 				if (p > mp->arg[0] && ++m || (sym->flags & SYM_VARIADIC))
400*4887Schin 					c++;
401*4887Schin 				if (c != mac->arity && !(sym->flags & SYM_EMPTY))
402*4887Schin 				{
403*4887Schin 					n = mac->arity;
404*4887Schin 					if (!(sym->flags & SYM_VARIADIC))
405*4887Schin 						error(1, "%s: %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
406*4887Schin 					else if (c < --n)
407*4887Schin 						error(1, "%s: at least %d actual argument%s expected", sym->name, n, n == 1 ? "" : "s");
408*4887Schin #if COMPATIBLE
409*4887Schin 					if (!c && (pp.state & (COMPATIBILITY|STRICT)) == (COMPATIBILITY|STRICT))
410*4887Schin 						goto disable;
411*4887Schin #endif
412*4887Schin 				}
413*4887Schin 				if (!c)
414*4887Schin 					++c;
415*4887Schin 				while (c < mac->arity)
416*4887Schin 					mp->arg[c++] = (char*)"\0" + 1;
417*4887Schin 			}
418*4887Schin 			mp->arg[0][-2] = m;
419*4887Schin 			*p++ = 0;
420*4887Schin 			nextframe(mp, p);
421*4887Schin 			count(function);
422*4887Schin 		}
423*4887Schin 		if (!tok && (sym->flags & SYM_NOEXPAND))
424*4887Schin 		{
425*4887Schin 			if (sym->flags & SYM_FUNCTION)
426*4887Schin 				popframe(mp);
427*4887Schin 			ret = !mac->size;
428*4887Schin 		}
429*4887Schin 		else if (!(pp.state & HEADER) || (pp.option & HEADEREXPANDALL)  || pp.in->type != IN_COPY)
430*4887Schin 		{
431*4887Schin 			if (sym->flags & SYM_MULTILINE)
432*4887Schin 				PUSH_MULTILINE(sym);
433*4887Schin 			else
434*4887Schin 				PUSH_MACRO(sym);
435*4887Schin 			ret = 1;
436*4887Schin 		}
437*4887Schin 	}
438*4887Schin  disable:
439*4887Schin 	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))
440*4887Schin 	{
441*4887Schin 		struct ppinstk*	inp;
442*4887Schin 
443*4887Schin 		for (inp = pp.in; inp->type != IN_FILE && inp->prev; inp = inp->prev);
444*4887Schin 		sfsprintf(pp.hidebuf, MAXTOKEN, "_%d_%s_hIDe", inp->index, sym->name);
445*4887Schin 		PUSH_STRING(pp.hidebuf);
446*4887Schin 		ret = 1;
447*4887Schin 	}
448*4887Schin 	pp.state &= ~NEWLINE;
449*4887Schin 	pp.in->flags |= IN_tokens;
450*4887Schin 	count(token);
451*4887Schin 	return ret;
452*4887Schin }
453