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