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 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 
37 void
ppbuiltin(void)38 ppbuiltin(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