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