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