1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1984-2012 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  * make file read routines
26  */
27 
28 #include "make.h"
29 
30 /*
31  * read the base and global rules
32  */
33 
34 static void
readrules(void)35 readrules(void)
36 {
37 	register char*		s;
38 	register Sfio_t*	tmp;
39 	register List_t*	p;
40 
41 	state.global = 1;
42 
43 	/*
44 	 * read the base rules
45 	 */
46 
47 	if (!(s = state.rules))
48 		state.rules = null;
49 	else if (*s)
50 	{
51 		tmp = sfstropen();
52 		edit(tmp, s, KEEP, KEEP, external.object);
53 		readfile(sfstruse(tmp), COMP_BASE|(state.explicitrules ? COMP_RULES : 0), NiL);
54 		edit(tmp, s, DELETE, KEEP, DELETE);
55 		state.rules = strdup(sfstruse(tmp));
56 		sfstrclose(tmp);
57 	}
58 	setvar(external.rules, state.rules, 0)->property |= V_compiled;
59 
60 	/*
61 	 * read the explicit global makefiles
62 	 *
63 	 * NOTE: internal.tmplist is used to handle the effects
64 	 *	 of load() on internal list pointers
65 	 */
66 
67 	if (p = internal.globalfiles->prereqs)
68 	{
69 		for (p = internal.tmplist->prereqs = listcopy(p); p; p = p->next)
70 			readfile(p->rule->name, COMP_GLOBAL, NiL);
71 		freelist(internal.tmplist->prereqs);
72 		internal.tmplist->prereqs = 0;
73 	}
74 	state.global = 0;
75 }
76 
77 /*
78  * read a file given an open file pointer
79  */
80 
81 static void
readfp(Sfio_t * sp,register Rule_t * r,int type)82 readfp(Sfio_t* sp, register Rule_t* r, int type)
83 {
84 	register char*	s;
85 	register char*	t;
86 	int		n;
87 	int		needrules;
88 	int		preprocess;
89 	int		splice;
90 	char*		name;
91 	char*		b;
92 	char*		e;
93 	char*		objfile;
94 	Rule_t*		x;
95 	Sfio_t*		fp;
96 
97 	objfile = 0;
98 	name = r->name;
99 	if (!state.makefile)
100 	{
101 		/*
102 		 * set up the related file names
103 		 */
104 
105 		fp = sfstropen();
106 		setvar(external.file, r->name, 0)->property |= V_compiled;
107 		edit(fp, r->name, DELETE, KEEP, KEEP);
108 		state.makefile = strdup(sfstruse(fp));
109 		sfstrclose(fp);
110 		objfile = objectfile();
111 	}
112 	needrules = !state.base && !state.rules;
113 
114 	/*
115 	 * load if object file
116 	 */
117 
118 	if (loadable(sp, r, 0))
119 	{
120 		if (!state.base && !state.global && !state.list)
121 			error(3, "%s: explicit make object files must be global", r->name);
122 		if (!state.rules)
123 			readrules();
124 		message((-2, "loading %sobject file %s", state.global ? "global " : null, r->name));
125 		if (load(sp, r->name, 0, 0) > 0)
126 		{
127 			sfclose(sp);
128 			return;
129 		}
130 		error(3, "%s: must be recompiled", name);
131 	}
132 
133 	/*
134 	 * check object corresponding to file
135 	 */
136 
137 	if (state.global || !state.forceread && (!(type & COMP_FILE) || needrules))
138 	{
139 		fp = sfstropen();
140 		if (!objfile)
141 		{
142 			edit(fp, r->name, DELETE, KEEP, external.object);
143 			objfile = sfstruse(fp);
144 		}
145 		state.init++;
146 		x = bindfile(NiL, objfile, 0);
147 		state.init--;
148 		sfstrclose(fp);
149 		if (!x || !x->time)
150 			/* ignore */;
151 		else if (x == r)
152 			error(3, "%s must be recompiled", r->name);
153 		else if (fp = sfopen(NiL, s = x->name, "br"))
154 		{
155 			if (needrules)
156 				x->dynamic |= D_built;
157 			if (loadable(fp, x, 1))
158 			{
159 				if (needrules)
160 				{
161 					if (state.rules && !state.explicitrules)
162 					{
163 						edit(internal.tmp, state.rules, DELETE, KEEP, DELETE);
164 						edit(internal.wrk, b = getval(external.rules, VAL_PRIMARY), DELETE, KEEP, DELETE);
165 						if (strcmp(sfstruse(internal.tmp), sfstruse(internal.wrk)))
166 						{
167 							error(state.exec || state.mam.out ? -1 : 1, "%s: base rules changed to %s", sfstrbase(internal.tmp), sfstrbase(internal.wrk));
168 							state.rules = b;
169 							state.forceread = 1;
170 							needrules = 1;
171 						}
172 					}
173 					if (!state.forceread)
174 					{
175 						needrules = 0;
176 						readrules();
177 					}
178 				}
179 				if (!state.forceread)
180 				{
181 					message((-2, "loading %s file %s", state.global ? "global" : "object", s));
182 					n = load(fp, s, 1, 0);
183 					if (n > 0)
184 					{
185 						sfclose(fp);
186 						sfclose(sp);
187 						return;
188 					}
189 				}
190 				r = getrule(name);
191 			}
192 			sfclose(fp);
193 			if (state.global)
194 				error(1, "%s: reading%s", r->name, state.forceread ? " -- should be compiled before local makefiles" : null);
195 			else if (state.writeobject)
196 				error(state.exec || state.mam.out ? -1 : 1, "%s: recompiling", s);
197 		}
198 	}
199 
200 	/*
201 	 * at this point we have to read it
202 	 * if its the first makefile then the
203 	 * base rules must be determined and loaded
204 	 * along with the global rules before the parse
205 	 */
206 
207 	preprocess = state.preprocess;
208 	if (!state.global)
209 	{
210 		/*
211 		 * first check for and apply makefile converter
212 		 */
213 
214 		s = 0;
215 		if (*(t = getval(external.convert, VAL_PRIMARY)))
216 		{
217 			char*	u;
218 			char*	v;
219 			Sfio_t*	exp;
220 
221 			exp = sfstropen();
222 			if (e = strchr(r->name, '/'))
223 				e++;
224 			else
225 				e = r->name;
226 			b = tokopen(t, 1);
227 			while ((t = tokread(b)) && (t = colonlist(exp, t, 0, ' ')))
228 			{
229 				u = tokopen(t, 0);
230 				while ((v = tokread(u)) && !streq(e, v));
231 				tokclose(u);
232 				if (!(s = tokread(b)))
233 				{
234 					error(2, "%s: %s: no action for file", external.convert, t);
235 					break;
236 				}
237 				if (v)
238 				{
239 					s = getarg((e = t = strdup(s), &e), NiL);
240 					break;
241 				}
242 				s = 0;
243 			}
244 			tokclose(b);
245 			sfstrclose(exp);
246 		}
247 		if (s)
248 		{
249 			message((-2, "converting %s using \"%s\"", r->name, s));
250 			sfclose(sp);
251 			if (!(sp = fapply(internal.internal, null, r->name, s, CO_ALWAYS|CO_LOCAL|CO_URGENT)))
252 				error(3, "%s: error in makefile converter \"%s\"", r->name, s);
253 			free(t);
254 			preprocess = -1;
255 		}
256 		if (needrules)
257 		{
258 			if ((s = sfreserve(sp, 0, 0)) && (n = sfvalue(sp)) >= 0)
259 			{
260 				int	c;
261 				int	d;
262 				int	old;
263 
264 				if (n > 0)
265 				{
266 					if (n > MAXNAME)
267 						n = MAXNAME;
268 					else
269 						n--;
270 				}
271 
272 				/*
273 				 * quick makefile type check while
274 				 * checking for base rules
275 				 */
276 
277 				old = 0;
278 				splice = 0;
279 				b = s;
280 				c = *(s + n);
281 				*(s + n) = 0;
282 				for (;;)
283 				{
284 					if (e = strchr(s, '\n'))
285 						*e = 0;
286 					else if (c != '\n')
287 						break;
288 					if (splice)
289 						/* skip */;
290 					else if (*s == SALT)
291 					{
292 						while (isspace(*++s));
293 						for (t = s; isalnum(*t); t++);
294 						d = *t;
295 						*t = 0;
296 						if (strneq(s, "rules", 5))
297 						{
298 							if (*t = d)
299 								t++;
300 							while (*t == ' ' || *t == '\t')
301 								t++;
302 							rules(*t == '/' && *(t + 1) == '*' ? null : t);
303 							break;
304 						}
305 						else if (!strmatch(s, "assert|comment|define|elif|else|endif|endmac|error|ident|if|ifdef|ifndef|include|line|macdef|pragma|unassert|undef|warning"))
306 							old = 1;
307 						else if (!preprocess)
308 							preprocess = 1;
309 						*t = d;
310 					}
311 					else if (*s == '<' && *(s + 1) == '<')
312 					{
313 						old = preprocess = 0;
314 						break;
315 					}
316 					else
317 					{
318 						while (isspace(*s))
319 							s++;
320 						if (strneq(s, "rules", 5))
321 						{
322 							for (s += 5; *s == ' ' || *s == '\t'; s++);
323 							rules(*s == '/' && *(s + 1) == '*' ? null : s);
324 							old = 0;
325 							break;
326 						}
327 						else if (strneq(s, ".SOURCE", 7) && (*(s + 7) == '.' || *(s + 7) == ':' || isspace(*(s + 7))))
328 						{
329 							old = 0;
330 							break;
331 						}
332 						else
333 						{
334 							d = ':';
335 							while (*s)
336 							{
337 								if (*s == '/' && *(s + 1) == '*' && (*(s + 2) == '*' || isspace(*(s + 2)) || !*(s + 2)))
338 									break;
339 								else if (*s == d)
340 								{
341 									if (*++s == d)
342 										s++;
343 									else if (isalnum(*s))
344 									{
345 										while (isalnum(*s))
346 											s++;
347 										if (*s == d)
348 											break;
349 									}
350 									d = 0;
351 								}
352 								while (*s && *s != d && !isspace(*s))
353 									s++;
354 								while (isspace(*s))
355 									s++;
356 							}
357 							if (*s)
358 							{
359 								old = 0;
360 								break;
361 							}
362 						}
363 					}
364 					if (!(s = e))
365 						break;
366 					splice = e > b && *(e - 1) == '\\';
367 					*s++ = '\n';
368 				}
369 				if (e)
370 					*e = '\n';
371 				*(b + n) = c;
372 				if (old)
373 					punt(1);
374 			}
375 			if (!state.rules)
376 				state.rules = getval(external.rules, VAL_PRIMARY);
377 			readrules();
378 			r = getrule(name);
379 		}
380 	}
381 
382 	/*
383 	 * check for obsolete makefile preprocessor
384 	 */
385 
386 	if (preprocess > 0)
387 	{
388 		s = "$(MAKEPP) $(MAKEPPFLAGS) $(>)";
389 		message((-2, "preprocessing %s using \"%s\"", r->name, s));
390 		sfclose(sp);
391 		if (!(sp = fapply(internal.internal, null, r->name, s, CO_ALWAYS|CO_LOCAL|CO_URGENT)))
392 			error(3, "%s: error in makefile preprocessor \"%s\"", r->name, s);
393 	}
394 
395 	/*
396 	 * parse the file
397 	 */
398 
399 	if (state.base)
400 	{
401 		if (!state.compile)
402 			state.compile = RECOMPILE;
403 		state.global = 1;
404 	}
405 	n = state.reading;
406 	state.reading = 1;
407 	parse(sp, NiL, r->name, NiL);
408 	sfclose(sp);
409 	state.reading = n;
410 	if (!state.compile && !state.global)
411 		state.compile = RECOMPILE;
412 	if ((state.questionable & 0x00000400) || !state.global)
413 		state.forceread = 1;
414 }
415 
416 /*
417  * read a makefile
418  */
419 
420 int
readfile(register char * file,int type,char * filter)421 readfile(register char* file, int type, char* filter)
422 {
423 	register Rule_t*	r;
424 	Sfio_t*			rfp;
425 	Stat_t			st;
426 
427 	if (streq(file, "-") && (file = "/dev/null") || isdynamic(file))
428 	{
429 		rfp = sfstropen();
430 		expand(rfp, file);
431 		state.init++;
432 		file = makerule(sfstruse(rfp))->name;
433 		state.init--;
434 		sfstrclose(rfp);
435 	}
436 	state.init++;
437 	r = bindfile(NiL, file, BIND_MAKEFILE|BIND_RULE);
438 	state.init--;
439 	if (r && (r->time || strneq(r->name, "/dev/", 5) && !rstat(r->name, &st, 0)))
440 	{
441 		compref(r, type);
442 		r->dynamic |= D_scanned;
443 		file = r->name;
444 		if (rfp = filter ? fapply(internal.internal, null, file, filter, CO_ALWAYS|CO_LOCAL|CO_URGENT) : rsfopen(file))
445 		{
446 			if (state.mam.dynamic || state.mam.regress)
447 				mampush(state.mam.out, r, P_force);
448 			if (state.user)
449 			{
450 				r->status = EXISTS;
451 				parse(rfp, NiL, file, NiL);
452 				sfclose(rfp);
453 			}
454 			else
455 				readfp(rfp, r, type);
456 			if (state.mam.dynamic || state.mam.regress)
457 				mampop(state.mam.out, r, 0);
458 			if ((type & COMP_BASE) && r->uname)
459 			{
460 				oldname(r);
461 				r->dynamic &= ~D_bound;
462 			}
463 			if (state.pushed)
464 			{
465 				state.pushed = 0;
466 				state.global = state.push_global;
467 				state.user = state.push_user;
468 			}
469 			return(1);
470 		}
471 		if ((type & COMP_DONTCARE) || (r->property & P_dontcare))
472 		{
473 			r->property |= P_dontcare;
474 			return(0);
475 		}
476 	}
477 	if (!(type & COMP_DONTCARE))
478 		error((type & COMP_INCLUDE) ? 2 : 3, "%s: cannot read%s", file, (type & COMP_INCLUDE) ? " include file" : (type & COMP_GLOBAL) ? " global rules" : (type & COMP_BASE) ? " base rules" : null);
479 	else if ((type & COMP_INCLUDE) && error_info.line)
480 		compref(r ? r : makerule(file), type);
481 	return(0);
482 }
483