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