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