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 builtin macro support
26*4887Schin  */
27*4887Schin 
28*4887Schin #include "pplib.h"
29*4887Schin 
30*4887Schin #include <times.h>
31*4887Schin 
32*4887Schin /*
33*4887Schin  * process a #(...) builtin macro call
34*4887Schin  * `#(' has already been seen
35*4887Schin  */
36*4887Schin 
37*4887Schin void
38*4887Schin ppbuiltin(void)
39*4887Schin {
40*4887Schin 	register int		c;
41*4887Schin 	register char*		p;
42*4887Schin 	register char*		a;
43*4887Schin 
44*4887Schin 	int			n;
45*4887Schin 	int			op;
46*4887Schin 	char*			token;
47*4887Schin 	char*			t;
48*4887Schin 	long			number;
49*4887Schin 	struct ppinstk*		in;
50*4887Schin 	struct pplist*		list;
51*4887Schin 	struct ppsymbol*	sym;
52*4887Schin 	Sfio_t*			sp;
53*4887Schin 
54*4887Schin 	number = pp.state;
55*4887Schin 	pp.state |= DISABLE|FILEPOP|NOSPACE;
56*4887Schin 	token = pp.token;
57*4887Schin 	p = pp.token = pp.tmpbuf;
58*4887Schin 	*(a = pp.args) = 0;
59*4887Schin 	if ((c = pplex()) != T_ID)
60*4887Schin 	{
61*4887Schin 		error(2, "%s: #(<identifier>...) expected", p);
62*4887Schin 		*p = 0;
63*4887Schin 	}
64*4887Schin 	switch (op = (int)hashget(pp.strtab, p))
65*4887Schin 	{
66*4887Schin 	case V_DEFAULT:
67*4887Schin 		n = 0;
68*4887Schin 		p = pp.token = pp.valbuf;
69*4887Schin 		if ((c = pplex()) == ',')
70*4887Schin 		{
71*4887Schin 			op = -1;
72*4887Schin 			c = pplex();
73*4887Schin 		}
74*4887Schin 		pp.state &= ~NOSPACE;
75*4887Schin 		for (;;)
76*4887Schin 		{
77*4887Schin 			if (!c)
78*4887Schin 			{
79*4887Schin 				error(2, "%s in #(...) argument", pptokchr(c));
80*4887Schin 				break;
81*4887Schin 			}
82*4887Schin 			if (c == '(') n++;
83*4887Schin 			else if (c == ')' && !n--) break;
84*4887Schin 			else if (c == ',' && !n && op > 0) op = 0;
85*4887Schin 			if (op) pp.token = pp.toknxt;
86*4887Schin 			c = pplex();
87*4887Schin 		}
88*4887Schin 		*pp.token = 0;
89*4887Schin 		pp.token = token;
90*4887Schin 		pp.state = number;
91*4887Schin 		break;
92*4887Schin 	case V_EMPTY:
93*4887Schin 		p = pp.valbuf;
94*4887Schin 		if ((c = pplex()) == ')') *p = '1';
95*4887Schin 		else
96*4887Schin 		{
97*4887Schin 			*p = '0';
98*4887Schin 			n = 0;
99*4887Schin 			for (;;)
100*4887Schin 			{
101*4887Schin 				if (!c)
102*4887Schin 				{
103*4887Schin 					error(2, "%s in #(...) argument", pptokchr(c));
104*4887Schin 					break;
105*4887Schin 				}
106*4887Schin 				if (c == '(') n++;
107*4887Schin 				else if (c == ')' && !n--) break;
108*4887Schin 				c = pplex();
109*4887Schin 			}
110*4887Schin 		}
111*4887Schin 		*(p + 1) = 0;
112*4887Schin 		pp.token = token;
113*4887Schin 		pp.state = number;
114*4887Schin 		break;
115*4887Schin 	case V_ITERATE:
116*4887Schin 		n = 0;
117*4887Schin 		pp.token = pp.valbuf;
118*4887Schin 		if ((c = pplex()) != T_ID || !(sym = ppsymref(pp.symtab, pp.token)) || !sym->macro || sym->macro->arity != 1 || (c = pplex()) != ',')
119*4887Schin 		{
120*4887Schin 			error(2, "#(%s <macro(x)>, ...) expected", p);
121*4887Schin 			for (;;)
122*4887Schin 			{
123*4887Schin 				if (!c)
124*4887Schin 				{
125*4887Schin 					error(2, "%s in #(...) argument", pptokchr(c));
126*4887Schin 					break;
127*4887Schin 				}
128*4887Schin 				if (c == '(') n++;
129*4887Schin 				else if (c == ')' && !n--) break;
130*4887Schin 				c = pplex();
131*4887Schin 			}
132*4887Schin 			*pp.valbuf = 0;
133*4887Schin 		}
134*4887Schin 		else while (c != ')')
135*4887Schin 		{
136*4887Schin 			p = pp.token;
137*4887Schin 			if (pp.token > pp.valbuf) *pp.token++ = ' ';
138*4887Schin 			STRCOPY(pp.token, sym->name, a);
139*4887Schin 			*pp.token++ = '(';
140*4887Schin 			if (!c || !(c = pplex()))
141*4887Schin 			{
142*4887Schin 				pp.token = p;
143*4887Schin 				error(2, "%s in #(...) argument", pptokchr(c));
144*4887Schin 				break;
145*4887Schin 			}
146*4887Schin 			pp.state &= ~NOSPACE;
147*4887Schin 			while (c)
148*4887Schin 			{
149*4887Schin 				if (c == '(') n++;
150*4887Schin 				else if (c == ')' && !n--) break;
151*4887Schin 				else if (c == ',' && !n) break;
152*4887Schin 				pp.token = pp.toknxt;
153*4887Schin 				c = pplex();
154*4887Schin 			}
155*4887Schin 			*pp.token++ = ')';
156*4887Schin 			pp.state |= NOSPACE;
157*4887Schin 		}
158*4887Schin 		p = pp.valbuf;
159*4887Schin 		pp.token = token;
160*4887Schin 		pp.state = number;
161*4887Schin 		break;
162*4887Schin 	default:
163*4887Schin 		pp.token = token;
164*4887Schin 		while (c != ')')
165*4887Schin 		{
166*4887Schin 			if (!c)
167*4887Schin 			{
168*4887Schin 				error(2, "%s in #(...) argument", pptokchr(c));
169*4887Schin 				break;
170*4887Schin 			}
171*4887Schin 			if ((c = pplex()) == T_ID && !*a)
172*4887Schin 				strcpy(a, pp.token);
173*4887Schin 		}
174*4887Schin 		pp.state = number;
175*4887Schin 		switch (op)
176*4887Schin 		{
177*4887Schin 		case V_ARGC:
178*4887Schin 			c = -1;
179*4887Schin 			for (in = pp.in; in; in = in->prev)
180*4887Schin 				if ((in->type == IN_MACRO || in->type == IN_MULTILINE) && (in->symbol->flags & SYM_FUNCTION))
181*4887Schin 				{
182*4887Schin 					c = *((unsigned char*)(pp.macp->arg[0] - 2));
183*4887Schin 					break;
184*4887Schin 				}
185*4887Schin 			sfsprintf(p = pp.valbuf, MAXTOKEN, "%d", c);
186*4887Schin 			break;
187*4887Schin 		case V_BASE:
188*4887Schin 			p = (a = strrchr(error_info.file, '/')) ? a + 1 : error_info.file;
189*4887Schin 			break;
190*4887Schin 		case V_DATE:
191*4887Schin 			if (!(p = pp.date))
192*4887Schin 			{
193*4887Schin 				time_t	tm;
194*4887Schin 
195*4887Schin 				time(&tm);
196*4887Schin 				a = p = ctime(&tm) + 4;
197*4887Schin 				*(p + 20) = 0;
198*4887Schin 				for (p += 7; *p = *(p + 9); p++);
199*4887Schin 				pp.date = p = strdup(a);
200*4887Schin 			}
201*4887Schin 			break;
202*4887Schin 		case V_FILE:
203*4887Schin 			p = error_info.file;
204*4887Schin 			break;
205*4887Schin 		case V_LINE:
206*4887Schin 			sfsprintf(p = pp.valbuf, MAXTOKEN, "%d", error_info.line);
207*4887Schin 			break;
208*4887Schin 		case V_PATH:
209*4887Schin 			p = pp.path;
210*4887Schin 			break;
211*4887Schin 		case V_SOURCE:
212*4887Schin 			p = error_info.file;
213*4887Schin 			for (in = pp.in; in->prev; in = in->prev)
214*4887Schin 				if (in->prev->type == IN_FILE && in->file)
215*4887Schin 					p = in->file;
216*4887Schin 			break;
217*4887Schin 		case V_STDC:
218*4887Schin 			p = pp.valbuf;
219*4887Schin 			p[0] = ((pp.state & (COMPATIBILITY|TRANSITION)) || (pp.mode & (HOSTED|HOSTEDTRANSITION)) == (HOSTED|HOSTEDTRANSITION)) ? '0' : '1';
220*4887Schin 			p[1] = 0;
221*4887Schin 			break;
222*4887Schin 		case V_TIME:
223*4887Schin 			if (!(p = pp.time))
224*4887Schin 			{
225*4887Schin 				time_t	tm;
226*4887Schin 
227*4887Schin 				time(&tm);
228*4887Schin 				p = ctime(&tm) + 11;
229*4887Schin 				*(p + 8) = 0;
230*4887Schin 				pp.time = p = strdup(p);
231*4887Schin 			}
232*4887Schin 			break;
233*4887Schin 		case V_VERSION:
234*4887Schin 			p = (char*)pp.version;
235*4887Schin 			break;
236*4887Schin 		case V_DIRECTIVE:
237*4887Schin 			pp.state |= NEWLINE;
238*4887Schin 			pp.mode |= RELAX;
239*4887Schin 			strcpy(p = pp.valbuf, "#");
240*4887Schin 			break;
241*4887Schin 		case V_GETENV:
242*4887Schin 			if (!(p = getenv(a))) p = "";
243*4887Schin 			break;
244*4887Schin 		case V_GETMAC:
245*4887Schin 			p = (sym = pprefmac(a, REF_NORMAL)) ? sym->macro->value : "";
246*4887Schin 			break;
247*4887Schin 		case V_GETOPT:
248*4887Schin 			sfsprintf(p = pp.valbuf, MAXTOKEN, "%ld", ppoption(a));
249*4887Schin 			break;
250*4887Schin 		case V_GETPRD:
251*4887Schin 			p = (list = (struct pplist*)hashget(pp.prdtab, a)) ? list->value : "";
252*4887Schin 			break;
253*4887Schin 		case V__PRAGMA:
254*4887Schin 			if ((c = pplex()) == '(')
255*4887Schin 			{
256*4887Schin 				number = pp.state;
257*4887Schin 				pp.state |= NOSPACE|STRIP;
258*4887Schin 				c = pplex();
259*4887Schin 				pp.state = number;
260*4887Schin 				if (c == T_STRING || c == T_WSTRING)
261*4887Schin 				{
262*4887Schin 					if (!(sp = sfstropen()))
263*4887Schin 						error(3, "temporary buffer allocation error");
264*4887Schin 					sfprintf(sp, "#%s %s\n", dirname(PRAGMA), pp.token);
265*4887Schin 					a = sfstruse(sp);
266*4887Schin 					if ((c = pplex()) == ')')
267*4887Schin 					{
268*4887Schin 						pp.state |= NEWLINE;
269*4887Schin 						PUSH_BUFFER(p, a, 1);
270*4887Schin 					}
271*4887Schin 					sfstrclose(sp);
272*4887Schin 				}
273*4887Schin 			}
274*4887Schin 			if (c != ')')
275*4887Schin 				error(2, "%s: (\"...\") expected", p);
276*4887Schin 			return;
277*4887Schin 		case V_FUNCTION:
278*4887Schin 
279*4887Schin #define BACK(a,p)	((a>p)?*--a:(number++?0:((p=pp.outbuf+PPBUFSIZ),(a=pp.outbuf+2*PPBUFSIZ),*--a)))
280*4887Schin #define PEEK(a,p)	((a>p)?*(a-1):(number?0:*(pp.outbuf+2*PPBUFSIZ-1)))
281*4887Schin 
282*4887Schin 			number = pp.outbuf == pp.outb;
283*4887Schin 			a = pp.outp;
284*4887Schin 			p = pp.outb;
285*4887Schin 			op = 0;
286*4887Schin 			while (c = BACK(a, p))
287*4887Schin 			{
288*4887Schin 				if (c == '"' || c == '\'')
289*4887Schin 				{
290*4887Schin 					op = 0;
291*4887Schin 					while ((n = BACK(a, p)) && n != c || PEEK(a, p) == '\\');
292*4887Schin 				}
293*4887Schin 				else if (c == '\n')
294*4887Schin 				{
295*4887Schin 					token = a;
296*4887Schin 					while (c = BACK(a, p))
297*4887Schin 						if (c == '\n')
298*4887Schin 						{
299*4887Schin 							a = token;
300*4887Schin 							break;
301*4887Schin 						}
302*4887Schin 						else if (c == '#' && PEEK(a, p) == '\n')
303*4887Schin 							break;
304*4887Schin 				}
305*4887Schin 				else if (c == ' ')
306*4887Schin 					/*ignore*/;
307*4887Schin 				else if (c == '{') /* '}' */
308*4887Schin 					op = 1;
309*4887Schin 				else if (op == 1)
310*4887Schin 				{
311*4887Schin 					if (c == ')')
312*4887Schin 					{
313*4887Schin 						op = 2;
314*4887Schin 						n = 1;
315*4887Schin 					}
316*4887Schin 					else
317*4887Schin 						op = 0;
318*4887Schin 				}
319*4887Schin 				else if (op == 2)
320*4887Schin 				{
321*4887Schin 					if (c == ')')
322*4887Schin 						n++;
323*4887Schin 					else if (c == '(' && !--n)
324*4887Schin 						op = 3;
325*4887Schin 				}
326*4887Schin 				else if (op == 3)
327*4887Schin 				{
328*4887Schin 					if (ppisidig(c))
329*4887Schin 					{
330*4887Schin 						for (t = p, token = a; ppisidig(PEEK(a, p)); a--);
331*4887Schin 						for (p = pp.valbuf + 1; a <= token; *p++ = *a++);
332*4887Schin 						*p = 0;
333*4887Schin 						p = pp.valbuf + 1;
334*4887Schin 						if (streq(p, "for") || streq(p, "if") || streq(p, "switch") || streq(p, "while"))
335*4887Schin 						{
336*4887Schin 							op = 0;
337*4887Schin 							p = t;
338*4887Schin 							continue;
339*4887Schin 						}
340*4887Schin 					}
341*4887Schin 					else
342*4887Schin 						op = 0;
343*4887Schin 					break;
344*4887Schin 				}
345*4887Schin 			}
346*4887Schin 			if (op == 3)
347*4887Schin 				strncpy(pp.funbuf, p, sizeof(pp.funbuf) - 1);
348*4887Schin 			else if (*pp.funbuf)
349*4887Schin 				p = pp.funbuf;
350*4887Schin 			else
351*4887Schin 				p = "__FUNCTION__";
352*4887Schin 			break;
353*4887Schin 		default:
354*4887Schin 			if (pp.builtin && (a = (*pp.builtin)(pp.valbuf, p, a)))
355*4887Schin 				p = a;
356*4887Schin 			break;
357*4887Schin 		}
358*4887Schin 		break;
359*4887Schin 	}
360*4887Schin 	if (strchr(p, MARK))
361*4887Schin 	{
362*4887Schin 		a = pp.tmpbuf;
363*4887Schin 		strcpy(a, p);
364*4887Schin 		c = p != pp.valbuf;
365*4887Schin 		p = pp.valbuf + c;
366*4887Schin 		for (;;)
367*4887Schin 		{
368*4887Schin 			if (p < pp.valbuf + MAXTOKEN - 2)
369*4887Schin 				switch (*p++ = *a++)
370*4887Schin 				{
371*4887Schin 				case 0:
372*4887Schin 					break;
373*4887Schin 				case MARK:
374*4887Schin 					*p++ = MARK;
375*4887Schin 					/*FALLTHROUGH*/
376*4887Schin 				default:
377*4887Schin 					continue;
378*4887Schin 				}
379*4887Schin 			break;
380*4887Schin 		}
381*4887Schin 		p = pp.valbuf + c;
382*4887Schin 	}
383*4887Schin 	if (p == pp.valbuf)
384*4887Schin 		PUSH_STRING(p);
385*4887Schin 	else
386*4887Schin 	{
387*4887Schin 		if (p == pp.valbuf + 1)
388*4887Schin 			*pp.valbuf = '"';
389*4887Schin 		else
390*4887Schin 		{
391*4887Schin 			if (strlen(p) > MAXTOKEN - 2)
392*4887Schin 				error(1, "%-.16s: builtin value truncated", p);
393*4887Schin 			sfsprintf(pp.valbuf, MAXTOKEN, "\"%-.*s", MAXTOKEN - 2, p);
394*4887Schin 		}
395*4887Schin 		PUSH_QUOTE(pp.valbuf, 1);
396*4887Schin 	}
397*4887Schin }
398