1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27/*
28 *	macro.cc
29 *
30 *	Handle expansion of make macros
31 */
32
33/*
34 * Included files
35 */
36#include <mksh/dosys.h>		/* sh_command2string() */
37#include <mksh/i18n.h>		/* get_char_semantics_value() */
38#include <mksh/macro.h>
39#include <mksh/misc.h>		/* retmem() */
40#include <mksh/read.h>		/* get_next_block_fn() */
41
42#include <libintl.h>
43
44/*
45 * File table of contents
46 */
47static void	add_macro_to_global_list(Name macro_to_add);
48static void	expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd);
49
50static void	init_arch_macros(void);
51static void	init_mach_macros(void);
52static Boolean	init_arch_done = false;
53static Boolean	init_mach_done = false;
54
55
56long env_alloc_num = 0;
57long env_alloc_bytes = 0;
58
59/*
60 *	getvar(name)
61 *
62 *	Return expanded value of macro.
63 *
64 *	Return value:
65 *				The expanded value of the macro
66 *
67 *	Parameters:
68 *		name		The name of the macro we want the value for
69 *
70 *	Global variables used:
71 */
72Name
73getvar(register Name name)
74{
75	String_rec		destination;
76	wchar_t			buffer[STRING_BUFFER_LENGTH];
77	register Name		result;
78
79	if ((name == host_arch) || (name == target_arch)) {
80		if (!init_arch_done) {
81			init_arch_done = true;
82			init_arch_macros();
83		}
84	}
85	if ((name == host_mach) || (name == target_mach)) {
86		if (!init_mach_done) {
87			init_mach_done = true;
88			init_mach_macros();
89		}
90	}
91
92	INIT_STRING_FROM_STACK(destination, buffer);
93	expand_value(maybe_append_prop(name, macro_prop)->body.macro.value,
94		     &destination,
95		     false);
96	result = GETNAME(destination.buffer.start, FIND_LENGTH);
97	if (destination.free_after_use) {
98		retmem(destination.buffer.start);
99	}
100	return result;
101}
102
103/*
104 *	expand_value(value, destination, cmd)
105 *
106 *	Recursively expands all macros in the string value.
107 *	destination is where the expanded value should be appended.
108 *
109 *	Parameters:
110 *		value		The value we are expanding
111 *		destination	Where to deposit the expansion
112 *		cmd		If we are evaluating a command line we
113 *				turn \ quoting off
114 *
115 *	Global variables used:
116 */
117void
118expand_value(Name value, register String destination, Boolean cmd)
119{
120	Source_rec		sourceb;
121	register Source		source = &sourceb;
122	register wchar_t	*source_p = NULL;
123	register wchar_t	*source_end = NULL;
124	wchar_t			*block_start = NULL;
125	int			quote_seen = 0;
126
127	if (value == NULL) {
128		/*
129		 * Make sure to get a string allocated even if it
130		 * will be empty.
131		 */
132		MBSTOWCS(wcs_buffer, "");
133		append_string(wcs_buffer, destination, FIND_LENGTH);
134		destination->text.end = destination->text.p;
135		return;
136	}
137	if (!value->dollar) {
138		/*
139		 * If the value we are expanding does not contain
140		 * any $, we don't have to parse it.
141		 */
142		APPEND_NAME(value,
143			destination,
144			(int) value->hash.length
145		);
146		destination->text.end = destination->text.p;
147		return;
148	}
149
150	if (value->being_expanded) {
151		fatal_reader_mksh(gettext("Loop detected when expanding macro value `%s'"),
152			     value->string_mb);
153	}
154	value->being_expanded = true;
155	/* Setup the structure we read from */
156	Wstring vals(value);
157	sourceb.string.text.p = sourceb.string.buffer.start = wcsdup(vals.get_string());
158	sourceb.string.free_after_use = true;
159	sourceb.string.text.end =
160	  sourceb.string.buffer.end =
161	    sourceb.string.text.p + value->hash.length;
162	sourceb.previous = NULL;
163	sourceb.fd = -1;
164	sourceb.inp_buf =
165	  sourceb.inp_buf_ptr =
166	    sourceb.inp_buf_end = NULL;
167	sourceb.error_converting = false;
168	/* Lift some pointers from the struct to local register variables */
169	CACHE_SOURCE(0);
170/* We parse the string in segments */
171/* We read chars until we find a $, then we append what we have read so far */
172/* (since last $ processing) to the destination. When we find a $ we call */
173/* expand_macro() and let it expand that particular $ reference into dest */
174	block_start = source_p;
175	quote_seen = 0;
176	for (; 1; source_p++) {
177		switch (GET_CHAR()) {
178		case backslash_char:
179			/* Quote $ in macro value */
180			if (!cmd) {
181				quote_seen = ~quote_seen;
182			}
183			continue;
184		case dollar_char:
185			/* Save the plain string we found since */
186			/* start of string or previous $ */
187			if (quote_seen) {
188				append_string(block_start,
189					      destination,
190					      source_p - block_start - 1);
191				block_start = source_p;
192				break;
193			}
194			append_string(block_start,
195				      destination,
196				      source_p - block_start);
197			source->string.text.p = ++source_p;
198			UNCACHE_SOURCE();
199			/* Go expand the macro reference */
200			expand_macro(source, destination, sourceb.string.buffer.start, cmd);
201			CACHE_SOURCE(1);
202			block_start = source_p + 1;
203			break;
204		case nul_char:
205			/* The string ran out. Get some more */
206			append_string(block_start,
207				      destination,
208				      source_p - block_start);
209			GET_NEXT_BLOCK_NOCHK(source);
210			if (source == NULL) {
211				destination->text.end = destination->text.p;
212				value->being_expanded = false;
213				return;
214			}
215			if (source->error_converting) {
216				fatal_reader_mksh("Internal error: Invalid byte sequence in expand_value()");
217			}
218			block_start = source_p;
219			source_p--;
220			continue;
221		}
222		quote_seen = 0;
223	}
224	retmem(sourceb.string.buffer.start);
225}
226
227/*
228 *	expand_macro(source, destination, current_string, cmd)
229 *
230 *	Should be called with source->string.text.p pointing to
231 *	the first char after the $ that starts a macro reference.
232 *	source->string.text.p is returned pointing to the first char after
233 *	the macro name.
234 *	It will read the macro name, expanding any macros in it,
235 *	and get the value. The value is then expanded.
236 *	destination is a String that is filled in with the expanded macro.
237 *	It may be passed in referencing a buffer to expand the macro into.
238 * 	Note that most expansions are done on demand, e.g. right
239 *	before the command is executed and not while the file is
240 * 	being parsed.
241 *
242 *	Parameters:
243 *		source		The source block that references the string
244 *				to expand
245 *		destination	Where to put the result
246 *		current_string	The string we are expanding, for error msg
247 *		cmd		If we are evaluating a command line we
248 *				turn \ quoting off
249 *
250 *	Global variables used:
251 *		funny		Vector of semantic tags for characters
252 *		is_conditional	Set if a conditional macro is refd
253 *		make_word_mentioned Set if the word "MAKE" is mentioned
254 *		makefile_type	We deliver extra msg when reading makefiles
255 *		query		The Name "?", compared against
256 *		query_mentioned	Set if the word "?" is mentioned
257 */
258void
259expand_macro(register Source source, register String destination, wchar_t *current_string, Boolean cmd)
260{
261	static Name		make = (Name)NULL;
262	static wchar_t		colon_sh[4];
263	static wchar_t		colon_shell[7];
264	String_rec		string;
265	wchar_t			buffer[STRING_BUFFER_LENGTH];
266	register wchar_t	*source_p = source->string.text.p;
267	register wchar_t	*source_end = source->string.text.end;
268	register int		closer = 0;
269	wchar_t			*block_start = (wchar_t *)NULL;
270	int			quote_seen = 0;
271	register int		closer_level = 1;
272	Name			name = (Name)NULL;
273	wchar_t			*colon = (wchar_t *)NULL;
274	wchar_t			*percent = (wchar_t *)NULL;
275	wchar_t			*eq = (wchar_t *) NULL;
276	Property		macro = NULL;
277	wchar_t			*p = (wchar_t*)NULL;
278	String_rec		extracted;
279	wchar_t			extracted_string[MAXPATHLEN];
280	wchar_t			*left_head = NULL;
281	wchar_t			*left_tail = NULL;
282	wchar_t			*right_tail = NULL;
283	int			left_head_len = 0;
284	int			left_tail_len = 0;
285	int			tmp_len = 0;
286	wchar_t			*right_hand[128];
287	int			i = 0;
288	enum {
289		no_extract,
290		dir_extract,
291		file_extract
292	}                       extraction = no_extract;
293	enum {
294		no_replace,
295		suffix_replace,
296		pattern_replace,
297		sh_replace
298	}			replacement = no_replace;
299
300	if (make == NULL) {
301		MBSTOWCS(wcs_buffer, "MAKE");
302		make = GETNAME(wcs_buffer, FIND_LENGTH);
303
304		MBSTOWCS(colon_sh, ":sh");
305		MBSTOWCS(colon_shell, ":shell");
306	}
307
308	right_hand[0] = NULL;
309
310	/* First copy the (macro-expanded) macro name into string. */
311	INIT_STRING_FROM_STACK(string, buffer);
312recheck_first_char:
313	/* Check the first char of the macro name to figure out what to do. */
314	switch (GET_CHAR()) {
315	case nul_char:
316		GET_NEXT_BLOCK_NOCHK(source);
317		if (source == NULL) {
318			WCSTOMBS(mbs_buffer, current_string);
319			fatal_reader_mksh(gettext("'$' at end of string `%s'"),
320				     mbs_buffer);
321		}
322		if (source->error_converting) {
323			fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()");
324		}
325		goto recheck_first_char;
326	case parenleft_char:
327		/* Multi char name. */
328		closer = (int) parenright_char;
329		break;
330	case braceleft_char:
331		/* Multi char name. */
332		closer = (int) braceright_char;
333		break;
334	case newline_char:
335		fatal_reader_mksh(gettext("'$' at end of line"));
336	default:
337		/* Single char macro name. Just suck it up */
338		append_char(*source_p, &string);
339		source->string.text.p = source_p + 1;
340		goto get_macro_value;
341	}
342
343	/* Handle multi-char macro names */
344	block_start = ++source_p;
345	quote_seen = 0;
346	for (; 1; source_p++) {
347		switch (GET_CHAR()) {
348		case nul_char:
349			append_string(block_start,
350				      &string,
351				      source_p - block_start);
352			GET_NEXT_BLOCK_NOCHK(source);
353			if (source == NULL) {
354				if (current_string != NULL) {
355					WCSTOMBS(mbs_buffer, current_string);
356					fatal_reader_mksh(gettext("Unmatched `%c' in string `%s'"),
357						     closer ==
358						     (int) braceright_char ?
359						     (int) braceleft_char :
360						     (int) parenleft_char,
361						     mbs_buffer);
362				} else {
363					fatal_reader_mksh(gettext("Premature EOF"));
364				}
365			}
366			if (source->error_converting) {
367				fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()");
368			}
369			block_start = source_p;
370			source_p--;
371			continue;
372		case newline_char:
373			fatal_reader_mksh(gettext("Unmatched `%c' on line"),
374				     closer == (int) braceright_char ?
375				     (int) braceleft_char :
376				     (int) parenleft_char);
377		case backslash_char:
378			/* Quote dollar in macro value. */
379			if (!cmd) {
380				quote_seen = ~quote_seen;
381			}
382			continue;
383		case dollar_char:
384			/*
385			 * Macro names may reference macros.
386			 * This expands the value of such macros into the
387			 * macro name string.
388			 */
389			if (quote_seen) {
390				append_string(block_start,
391					      &string,
392					      source_p - block_start - 1);
393				block_start = source_p;
394				break;
395			}
396			append_string(block_start,
397				      &string,
398				      source_p - block_start);
399			source->string.text.p = ++source_p;
400			UNCACHE_SOURCE();
401			expand_macro(source, &string, current_string, cmd);
402			CACHE_SOURCE(0);
403			block_start = source_p;
404			source_p--;
405			break;
406		case parenleft_char:
407			/* Allow nested pairs of () in the macro name. */
408			if (closer == (int) parenright_char) {
409				closer_level++;
410			}
411			break;
412		case braceleft_char:
413			/* Allow nested pairs of {} in the macro name. */
414			if (closer == (int) braceright_char) {
415				closer_level++;
416			}
417			break;
418		case parenright_char:
419		case braceright_char:
420			/*
421			 * End of the name. Save the string in the macro
422			 * name string.
423			 */
424			if ((*source_p == closer) && (--closer_level <= 0)) {
425				source->string.text.p = source_p + 1;
426				append_string(block_start,
427					      &string,
428					      source_p - block_start);
429				goto get_macro_value;
430			}
431			break;
432		}
433		quote_seen = 0;
434	}
435	/*
436	 * We got the macro name. We now inspect it to see if it
437	 * specifies any translations of the value.
438	 */
439get_macro_value:
440	name = NULL;
441	/* First check if we have a $(@D) type translation. */
442	if ((get_char_semantics_value(string.buffer.start[0]) &
443	     (int) special_macro_sem) &&
444	    (string.text.p - string.buffer.start >= 2) &&
445	    ((string.buffer.start[1] == 'D') ||
446	     (string.buffer.start[1] == 'F'))) {
447		switch (string.buffer.start[1]) {
448		case 'D':
449			extraction = dir_extract;
450			break;
451		case 'F':
452			extraction = file_extract;
453			break;
454		default:
455			WCSTOMBS(mbs_buffer, string.buffer.start);
456			fatal_reader_mksh(gettext("Illegal macro reference `%s'"),
457				     mbs_buffer);
458		}
459		/* Internalize the macro name using the first char only. */
460		name = GETNAME(string.buffer.start, 1);
461		(void) wcscpy(string.buffer.start, string.buffer.start + 2);
462	}
463	/* Check for other kinds of translations. */
464	if ((colon = (wchar_t *) wcschr(string.buffer.start,
465				       (int) colon_char)) != NULL) {
466		/*
467		 * We have a $(FOO:.c=.o) type translation.
468		 * Get the name of the macro proper.
469		 */
470		if (name == NULL) {
471			name = GETNAME(string.buffer.start,
472				       colon - string.buffer.start);
473		}
474		/* Pickup all the translations. */
475		if (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell)) {
476			replacement = sh_replace;
477		} else if ((svr4) ||
478		           ((percent = (wchar_t *) wcschr(colon + 1,
479							 (int) percent_char)) == NULL)) {
480			while (colon != NULL) {
481				if ((eq = (wchar_t *) wcschr(colon + 1,
482							    (int) equal_char)) == NULL) {
483					fatal_reader_mksh(gettext("= missing from replacement macro reference"));
484				}
485				left_tail_len = eq - colon - 1;
486				if(left_tail) {
487					retmem(left_tail);
488				}
489				left_tail = ALLOC_WC(left_tail_len + 1);
490				(void) wcsncpy(left_tail,
491					      colon + 1,
492					      eq - colon - 1);
493				left_tail[eq - colon - 1] = (int) nul_char;
494				replacement = suffix_replace;
495				if ((colon = (wchar_t *) wcschr(eq + 1,
496							       (int) colon_char)) != NULL) {
497					tmp_len = colon - eq;
498					if(right_tail) {
499						retmem(right_tail);
500					}
501					right_tail = ALLOC_WC(tmp_len);
502					(void) wcsncpy(right_tail,
503						      eq + 1,
504						      colon - eq - 1);
505					right_tail[colon - eq - 1] =
506					  (int) nul_char;
507				} else {
508					if(right_tail) {
509						retmem(right_tail);
510					}
511					right_tail = ALLOC_WC(wcslen(eq) + 1);
512					(void) wcscpy(right_tail, eq + 1);
513				}
514			}
515		} else {
516			if ((eq = (wchar_t *) wcschr(colon + 1,
517						    (int) equal_char)) == NULL) {
518				fatal_reader_mksh(gettext("= missing from replacement macro reference"));
519			}
520			if ((percent = (wchar_t *) wcschr(colon + 1,
521							 (int) percent_char)) == NULL) {
522				fatal_reader_mksh(gettext("%% missing from replacement macro reference"));
523			}
524			if (eq < percent) {
525				fatal_reader_mksh(gettext("%% missing from replacement macro reference"));
526			}
527
528			if (percent > (colon + 1)) {
529				tmp_len = percent - colon;
530				if(left_head) {
531					retmem(left_head);
532				}
533				left_head = ALLOC_WC(tmp_len);
534				(void) wcsncpy(left_head,
535					      colon + 1,
536					      percent - colon - 1);
537				left_head[percent-colon-1] = (int) nul_char;
538				left_head_len = percent-colon-1;
539			} else {
540				left_head = NULL;
541				left_head_len = 0;
542			}
543
544			if (eq > percent+1) {
545				tmp_len = eq - percent;
546				if(left_tail) {
547					retmem(left_tail);
548				}
549				left_tail = ALLOC_WC(tmp_len);
550				(void) wcsncpy(left_tail,
551					      percent + 1,
552					      eq - percent - 1);
553				left_tail[eq-percent-1] = (int) nul_char;
554				left_tail_len = eq-percent-1;
555			} else {
556				left_tail = NULL;
557				left_tail_len = 0;
558			}
559
560			if ((percent = (wchar_t *) wcschr(++eq,
561							 (int) percent_char)) == NULL) {
562
563				right_hand[0] = ALLOC_WC(wcslen(eq) + 1);
564				right_hand[1] = NULL;
565				(void) wcscpy(right_hand[0], eq);
566			} else {
567				i = 0;
568				do {
569					right_hand[i] = ALLOC_WC(percent-eq+1);
570					(void) wcsncpy(right_hand[i],
571						      eq,
572						      percent - eq);
573					right_hand[i][percent-eq] =
574					  (int) nul_char;
575					if (i++ >= VSIZEOF(right_hand)) {
576						fatal_mksh(gettext("Too many %% in pattern"));
577					}
578					eq = percent + 1;
579					if (eq[0] == (int) nul_char) {
580						MBSTOWCS(wcs_buffer, "");
581						right_hand[i] = (wchar_t *) wcsdup(wcs_buffer);
582						i++;
583						break;
584					}
585				} while ((percent = (wchar_t *) wcschr(eq, (int) percent_char)) != NULL);
586				if (eq[0] != (int) nul_char) {
587					right_hand[i] = ALLOC_WC(wcslen(eq) + 1);
588					(void) wcscpy(right_hand[i], eq);
589					i++;
590				}
591				right_hand[i] = NULL;
592			}
593			replacement = pattern_replace;
594		}
595	}
596	if (name == NULL) {
597		/*
598		 * No translations found.
599		 * Use the whole string as the macro name.
600		 */
601		name = GETNAME(string.buffer.start,
602			       string.text.p - string.buffer.start);
603	}
604	if (string.free_after_use) {
605		retmem(string.buffer.start);
606	}
607	if (name == make) {
608		make_word_mentioned = true;
609	}
610	if (name == query) {
611		query_mentioned = true;
612	}
613	if ((name == host_arch) || (name == target_arch)) {
614		if (!init_arch_done) {
615			init_arch_done = true;
616			init_arch_macros();
617		}
618	}
619	if ((name == host_mach) || (name == target_mach)) {
620		if (!init_mach_done) {
621			init_mach_done = true;
622			init_mach_macros();
623		}
624	}
625	/* Get the macro value. */
626	macro = get_prop(name->prop, macro_prop);
627	if ((macro != NULL) && macro->body.macro.is_conditional) {
628		conditional_macro_used = true;
629		/*
630		 * Add this conditional macro to the beginning of the
631		 * global list.
632		 */
633		add_macro_to_global_list(name);
634		if (makefile_type == reading_makefile) {
635			warning_mksh(gettext("Conditional macro `%s' referenced in file `%ws', line %d"),
636					name->string_mb, file_being_read, line_number);
637		}
638	}
639	/* Macro name read and parsed. Expand the value. */
640	if ((macro == NULL) || (macro->body.macro.value == NULL)) {
641		/* If the value is empty, we just get out of here. */
642		goto exit;
643	}
644	if (replacement == sh_replace) {
645		/* If we should do a :sh transform, we expand the command
646		 * and process it.
647		 */
648		INIT_STRING_FROM_STACK(string, buffer);
649		/* Expand the value into a local string buffer and run cmd. */
650		expand_value_with_daemon(name, macro, &string, cmd);
651		sh_command2string(&string, destination);
652	} else if ((replacement != no_replace) || (extraction != no_extract)) {
653		/*
654		 * If there were any transforms specified in the macro
655		 * name, we deal with them here.
656		 */
657		INIT_STRING_FROM_STACK(string, buffer);
658		/* Expand the value into a local string buffer. */
659		expand_value_with_daemon(name, macro, &string, cmd);
660		/* Scan the expanded string. */
661		p = string.buffer.start;
662		while (*p != (int) nul_char) {
663			wchar_t		chr;
664
665			/*
666			 * First skip over any white space and append
667			 * that to the destination string.
668			 */
669			block_start = p;
670			while ((*p != (int) nul_char) && iswspace(*p)) {
671				p++;
672			}
673			append_string(block_start,
674				      destination,
675				      p - block_start);
676			/* Then find the end of the next word. */
677			block_start = p;
678			while ((*p != (int) nul_char) && !iswspace(*p)) {
679				p++;
680			}
681			/* If we cant find another word we are done */
682			if (block_start == p) {
683				break;
684			}
685			/* Then apply the transforms to the word */
686			INIT_STRING_FROM_STACK(extracted, extracted_string);
687			switch (extraction) {
688			case dir_extract:
689				/*
690				 * $(@D) type transform. Extract the
691				 * path from the word. Deliver "." if
692				 * none is found.
693				 */
694				if (p != NULL) {
695					chr = *p;
696					*p = (int) nul_char;
697				}
698				eq = (wchar_t *) wcsrchr(block_start, (int) slash_char);
699				if (p != NULL) {
700					*p = chr;
701				}
702				if ((eq == NULL) || (eq > p)) {
703					MBSTOWCS(wcs_buffer, ".");
704					append_string(wcs_buffer, &extracted, 1);
705				} else {
706					append_string(block_start,
707						      &extracted,
708						      eq - block_start);
709				}
710				break;
711			case file_extract:
712				/*
713				 * $(@F) type transform. Remove the path
714				 * from the word if any.
715				 */
716				if (p != NULL) {
717					chr = *p;
718					*p = (int) nul_char;
719				}
720				eq = (wchar_t *) wcsrchr(block_start, (int) slash_char);
721				if (p != NULL) {
722					*p = chr;
723				}
724				if ((eq == NULL) || (eq > p)) {
725					append_string(block_start,
726						      &extracted,
727						      p - block_start);
728				} else {
729					append_string(eq + 1,
730						      &extracted,
731						      p - eq - 1);
732				}
733				break;
734			case no_extract:
735				append_string(block_start,
736					      &extracted,
737					      p - block_start);
738				break;
739			}
740			switch (replacement) {
741			case suffix_replace:
742				/*
743				 * $(FOO:.o=.c) type transform.
744				 * Maybe replace the tail of the word.
745				 */
746				if (((extracted.text.p -
747				      extracted.buffer.start) >=
748				     left_tail_len) &&
749				    IS_WEQUALN(extracted.text.p - left_tail_len,
750					      left_tail,
751					      left_tail_len)) {
752					append_string(extracted.buffer.start,
753						      destination,
754						      (extracted.text.p -
755						       extracted.buffer.start)
756						      - left_tail_len);
757					append_string(right_tail,
758						      destination,
759						      FIND_LENGTH);
760				} else {
761					append_string(extracted.buffer.start,
762						      destination,
763						      FIND_LENGTH);
764				}
765				break;
766			case pattern_replace:
767				/* $(X:a%b=c%d) type transform. */
768				if (((extracted.text.p -
769				      extracted.buffer.start) >=
770				     left_head_len+left_tail_len) &&
771				    IS_WEQUALN(left_head,
772					      extracted.buffer.start,
773					      left_head_len) &&
774				    IS_WEQUALN(left_tail,
775					      extracted.text.p - left_tail_len,
776					      left_tail_len)) {
777					i = 0;
778					while (right_hand[i] != NULL) {
779						append_string(right_hand[i],
780							      destination,
781							      FIND_LENGTH);
782						i++;
783						if (right_hand[i] != NULL) {
784							append_string(extracted.buffer.
785								      start +
786								      left_head_len,
787								      destination,
788								      (extracted.text.p - extracted.buffer.start)-left_head_len-left_tail_len);
789						}
790					}
791				} else {
792					append_string(extracted.buffer.start,
793						      destination,
794						      FIND_LENGTH);
795				}
796				break;
797			case no_replace:
798				append_string(extracted.buffer.start,
799					      destination,
800					      FIND_LENGTH);
801				break;
802			case sh_replace:
803				break;
804			    }
805		}
806		if (string.free_after_use) {
807			retmem(string.buffer.start);
808		}
809	} else {
810		/*
811		 * This is for the case when the macro name did not
812		 * specify transforms.
813		 */
814		if (!strncmp(name->string_mb, "GET", 3)) {
815			dollarget_seen = true;
816		}
817		dollarless_flag = false;
818		if (!strncmp(name->string_mb, "<", 1) &&
819		    dollarget_seen) {
820			dollarless_flag = true;
821			dollarget_seen = false;
822		}
823		expand_value_with_daemon(name, macro, destination, cmd);
824	}
825exit:
826	if(left_tail) {
827		retmem(left_tail);
828	}
829	if(right_tail) {
830		retmem(right_tail);
831	}
832	if(left_head) {
833		retmem(left_head);
834	}
835	i = 0;
836	while (right_hand[i] != NULL) {
837		retmem(right_hand[i]);
838		i++;
839	}
840	*destination->text.p = (int) nul_char;
841	destination->text.end = destination->text.p;
842}
843
844static void
845add_macro_to_global_list(Name macro_to_add)
846{
847	Macro_list	new_macro;
848	Macro_list	macro_on_list;
849	char		*name_on_list = (char*)NULL;
850	char		*name_to_add = macro_to_add->string_mb;
851	char		*value_on_list = (char*)NULL;
852	const char	*value_to_add = (char*)NULL;
853
854	if (macro_to_add->prop->body.macro.value != NULL) {
855		value_to_add = macro_to_add->prop->body.macro.value->string_mb;
856	} else {
857		value_to_add = "";
858	}
859
860	/*
861	 * Check if this macro is already on list, if so, do nothing
862	 */
863	for (macro_on_list = cond_macro_list;
864	     macro_on_list != NULL;
865	     macro_on_list = macro_on_list->next) {
866
867		name_on_list = macro_on_list->macro_name;
868		value_on_list = macro_on_list->value;
869
870		if (IS_EQUAL(name_on_list, name_to_add)) {
871			if (IS_EQUAL(value_on_list, value_to_add)) {
872				return;
873			}
874		}
875	}
876	new_macro = (Macro_list) malloc(sizeof(Macro_list_rec));
877	new_macro->macro_name = strdup(name_to_add);
878	new_macro->value = strdup(value_to_add);
879	new_macro->next = cond_macro_list;
880	cond_macro_list = new_macro;
881}
882
883/*
884 *	init_arch_macros(void)
885 *
886 *	Set the magic macros TARGET_ARCH, HOST_ARCH,
887 *
888 *	Parameters:
889 *
890 *	Global variables used:
891 * 	                        host_arch   Property for magic macro HOST_ARCH
892 * 	                        target_arch Property for magic macro TARGET_ARCH
893 *
894 *	Return value:
895 *				The function does not return a value, but can
896 *				call fatal() in case of error.
897 */
898static void
899init_arch_macros(void)
900{
901	String_rec	result_string;
902	wchar_t		wc_buf[STRING_BUFFER_LENGTH];
903	char		mb_buf[STRING_BUFFER_LENGTH];
904	FILE		*pipe;
905	Name		value;
906	int		set_host, set_target;
907	const char	*mach_command = "/bin/mach";
908
909	set_host = (get_prop(host_arch->prop, macro_prop) == NULL);
910	set_target = (get_prop(target_arch->prop, macro_prop) == NULL);
911
912	if (set_host || set_target) {
913		INIT_STRING_FROM_STACK(result_string, wc_buf);
914		append_char((int) hyphen_char, &result_string);
915
916		if ((pipe = popen(mach_command, "r")) == NULL) {
917			fatal_mksh(gettext("Execute of %s failed"), mach_command);
918		}
919		while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) {
920			MBSTOWCS(wcs_buffer, mb_buf);
921			append_string(wcs_buffer, &result_string, wcslen(wcs_buffer));
922		}
923		if (pclose(pipe) != 0) {
924			fatal_mksh(gettext("Execute of %s failed"), mach_command);
925		}
926
927		value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start));
928
929		if (set_host) {
930			(void) setvar_daemon(host_arch, value, false, no_daemon, true, 0);
931		}
932		if (set_target) {
933			(void) setvar_daemon(target_arch, value, false, no_daemon, true, 0);
934		}
935	}
936}
937
938/*
939 *	init_mach_macros(void)
940 *
941 *	Set the magic macros TARGET_MACH, HOST_MACH,
942 *
943 *	Parameters:
944 *
945 *	Global variables used:
946 * 	                        host_mach   Property for magic macro HOST_MACH
947 * 	                        target_mach Property for magic macro TARGET_MACH
948 *
949 *	Return value:
950 *				The function does not return a value, but can
951 *				call fatal() in case of error.
952 */
953static void
954init_mach_macros(void)
955{
956	String_rec	result_string;
957	wchar_t		wc_buf[STRING_BUFFER_LENGTH];
958	char		mb_buf[STRING_BUFFER_LENGTH];
959	FILE		*pipe;
960	Name		value;
961	int		set_host, set_target;
962	const char	*arch_command = "/bin/arch";
963
964	set_host = (get_prop(host_mach->prop, macro_prop) == NULL);
965	set_target = (get_prop(target_mach->prop, macro_prop) == NULL);
966
967	if (set_host || set_target) {
968		INIT_STRING_FROM_STACK(result_string, wc_buf);
969		append_char((int) hyphen_char, &result_string);
970
971		if ((pipe = popen(arch_command, "r")) == NULL) {
972			fatal_mksh(gettext("Execute of %s failed"), arch_command);
973		}
974		while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) {
975			MBSTOWCS(wcs_buffer, mb_buf);
976			append_string(wcs_buffer, &result_string, wcslen(wcs_buffer));
977		}
978		if (pclose(pipe) != 0) {
979			fatal_mksh(gettext("Execute of %s failed"), arch_command);
980		}
981
982		value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start));
983
984		if (set_host) {
985			(void) setvar_daemon(host_mach, value, false, no_daemon, true, 0);
986		}
987		if (set_target) {
988			(void) setvar_daemon(target_mach, value, false, no_daemon, true, 0);
989		}
990	}
991}
992
993/*
994 *	expand_value_with_daemon(name, macro, destination, cmd)
995 *
996 *	Checks for daemons and then maybe calls expand_value().
997 *
998 *	Parameters:
999 *              name            Name of the macro  (Added by the NSE)
1000 *		macro		The property block with the value to expand
1001 *		destination	Where the result should be deposited
1002 *		cmd		If we are evaluating a command line we
1003 *				turn \ quoting off
1004 *
1005 *	Global variables used:
1006 */
1007static void
1008expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd)
1009{
1010	register Chain		chain;
1011
1012
1013	switch (macro->body.macro.daemon) {
1014	case no_daemon:
1015		if (!svr4 && !posix) {
1016			expand_value(macro->body.macro.value, destination, cmd);
1017		} else {
1018			if (dollarless_flag && tilde_rule) {
1019				expand_value(dollarless_value, destination, cmd);
1020				dollarless_flag = false;
1021				tilde_rule = false;
1022			} else {
1023				expand_value(macro->body.macro.value, destination, cmd);
1024			}
1025		}
1026		return;
1027	case chain_daemon:
1028		/* If this is a $? value we call the daemon to translate the */
1029		/* list of names to a string */
1030		for (chain = (Chain) macro->body.macro.value;
1031		     chain != NULL;
1032		     chain = chain->next) {
1033			APPEND_NAME(chain->name,
1034				      destination,
1035				      (int) chain->name->hash.length);
1036			if (chain->next != NULL) {
1037				append_char((int) space_char, destination);
1038			}
1039		}
1040		return;
1041	}
1042}
1043
1044/*
1045 * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value.
1046 */
1047char	*sunpro_dependencies_buf = NULL;
1048char	*sunpro_dependencies_oldbuf = NULL;
1049int	sunpro_dependencies_buf_size = 0;
1050
1051/*
1052 *	setvar_daemon(name, value, append, daemon, strip_trailing_spaces)
1053 *
1054 *	Set a macro value, possibly supplying a daemon to be used
1055 *	when referencing the value.
1056 *
1057 *	Return value:
1058 *				The property block with the new value
1059 *
1060 *	Parameters:
1061 *		name		Name of the macro to set
1062 *		value		The value to set
1063 *		append		Should we reset or append to the current value?
1064 *		daemon		Special treatment when reading the value
1065 *		strip_trailing_spaces from the end of value->string
1066 *		debug_level	Indicates how much tracing we should do
1067 *
1068 *	Global variables used:
1069 *		makefile_type	Used to check if we should enforce read only
1070 *		path_name	The Name "PATH", compared against
1071 *		virtual_root	The Name "VIRTUAL_ROOT", compared against
1072 *		vpath_defined	Set if the macro VPATH is set
1073 *		vpath_name	The Name "VPATH", compared against
1074 *		envvar		A list of environment vars with $ in value
1075 */
1076Property
1077setvar_daemon(register Name name, register Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level)
1078{
1079	register Property	macro = maybe_append_prop(name, macro_prop);
1080	register Property	macro_apx = get_prop(name->prop, macro_append_prop);
1081	int			length = 0;
1082	String_rec		destination;
1083	wchar_t			buffer[STRING_BUFFER_LENGTH];
1084	register Chain		chain;
1085	Name			val;
1086	wchar_t			*val_string = (wchar_t*)NULL;
1087	Wstring			wcb;
1088
1089
1090	if ((makefile_type != reading_nothing) &&
1091	    macro->body.macro.read_only) {
1092		return macro;
1093	}
1094	/* Strip spaces from the end of the value */
1095	if (daemon == no_daemon) {
1096		if(value != NULL) {
1097			wcb.init(value);
1098			length = wcb.length();
1099			val_string = wcb.get_string();
1100		}
1101		if ((length > 0) && iswspace(val_string[length-1])) {
1102			INIT_STRING_FROM_STACK(destination, buffer);
1103			buffer[0] = 0;
1104			append_string(val_string, &destination, length);
1105			if (strip_trailing_spaces) {
1106				while ((length > 0) &&
1107				       iswspace(destination.buffer.start[length-1])) {
1108					destination.buffer.start[--length] = 0;
1109				}
1110			}
1111			value = GETNAME(destination.buffer.start, FIND_LENGTH);
1112		}
1113	}
1114
1115	if(macro_apx != NULL) {
1116		val = macro_apx->body.macro_appendix.value;
1117	} else {
1118		val = macro->body.macro.value;
1119	}
1120
1121	if (append) {
1122		/*
1123		 * If we are appending, we just tack the new value after
1124		 * the old one with a space in between.
1125		 */
1126		INIT_STRING_FROM_STACK(destination, buffer);
1127		buffer[0] = 0;
1128		if ((macro != NULL) && (val != NULL)) {
1129			APPEND_NAME(val,
1130				      &destination,
1131				      (int) val->hash.length);
1132			if (value != NULL) {
1133				wcb.init(value);
1134				if(wcb.length() > 0) {
1135					MBTOWC(wcs_buffer, " ");
1136					append_char(wcs_buffer[0], &destination);
1137				}
1138			}
1139		}
1140		if (value != NULL) {
1141			APPEND_NAME(value,
1142				      &destination,
1143				      (int) value->hash.length);
1144		}
1145		value = GETNAME(destination.buffer.start, FIND_LENGTH);
1146		wcb.init(value);
1147		if (destination.free_after_use) {
1148			retmem(destination.buffer.start);
1149		}
1150	}
1151
1152	/* Debugging trace */
1153	if (debug_level > 1) {
1154		if (value != NULL) {
1155			switch (daemon) {
1156			case chain_daemon:
1157				(void) printf("%s =", name->string_mb);
1158				for (chain = (Chain) value;
1159				     chain != NULL;
1160				     chain = chain->next) {
1161					(void) printf(" %s", chain->name->string_mb);
1162				}
1163				(void) printf("\n");
1164				break;
1165			case no_daemon:
1166				(void) printf("%s= %s\n",
1167					      name->string_mb,
1168					      value->string_mb);
1169				break;
1170			}
1171		} else {
1172			(void) printf("%s =\n", name->string_mb);
1173		}
1174	}
1175	/* Set the new values in the macro property block */
1176/**/
1177	if(macro_apx != NULL) {
1178		macro_apx->body.macro_appendix.value = value;
1179		INIT_STRING_FROM_STACK(destination, buffer);
1180		buffer[0] = 0;
1181		if (value != NULL) {
1182			APPEND_NAME(value,
1183				      &destination,
1184				      (int) value->hash.length);
1185			if (macro_apx->body.macro_appendix.value_to_append != NULL) {
1186				MBTOWC(wcs_buffer, " ");
1187				append_char(wcs_buffer[0], &destination);
1188			}
1189		}
1190		if (macro_apx->body.macro_appendix.value_to_append != NULL) {
1191			APPEND_NAME(macro_apx->body.macro_appendix.value_to_append,
1192				      &destination,
1193				      (int) macro_apx->body.macro_appendix.value_to_append->hash.length);
1194		}
1195		value = GETNAME(destination.buffer.start, FIND_LENGTH);
1196		if (destination.free_after_use) {
1197			retmem(destination.buffer.start);
1198		}
1199	}
1200/**/
1201	macro->body.macro.value = value;
1202	macro->body.macro.daemon = daemon;
1203	/*
1204	 * If the user changes the VIRTUAL_ROOT, we need to flush
1205	 * the vroot package cache.
1206	 */
1207	if (name == path_name) {
1208		flush_path_cache();
1209	}
1210	if (name == virtual_root) {
1211		flush_vroot_cache();
1212	}
1213	/* If this sets the VPATH we remember that */
1214	if ((name == vpath_name) &&
1215	    (value != NULL) &&
1216	    (value->hash.length > 0)) {
1217		vpath_defined = true;
1218	}
1219	/*
1220	 * For environment variables we also set the
1221	 * environment value each time.
1222	 */
1223	if (macro->body.macro.exported) {
1224		static char	*env;
1225
1226		if (!reading_environment && (value != NULL)) {
1227			Envvar	p;
1228
1229			for (p = envvar; p != NULL; p = p->next) {
1230				if (p->name == name) {
1231					p->value = value;
1232					p->already_put = false;
1233					goto found_it;
1234				}
1235			}
1236			p = ALLOC(Envvar);
1237			p->name = name;
1238			p->value = value;
1239			p->next = envvar;
1240			p->env_string = NULL;
1241			p->already_put = false;
1242			envvar = p;
1243found_it:;
1244		} if (reading_environment || (value == NULL) || !value->dollar) {
1245			length = 2 + strlen(name->string_mb);
1246			if (value != NULL) {
1247				length += strlen(value->string_mb);
1248			}
1249			Property env_prop = maybe_append_prop(name, env_mem_prop);
1250			/*
1251			 * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value.
1252			 */
1253			if (!strncmp(name->string_mb, "SUNPRO_DEPENDENCIES", 19)) {
1254				if (length >= sunpro_dependencies_buf_size) {
1255					sunpro_dependencies_buf_size=length*2;
1256					if (sunpro_dependencies_buf_size < 4096)
1257						sunpro_dependencies_buf_size = 4096; // Default minimum size
1258					if (sunpro_dependencies_buf)
1259						sunpro_dependencies_oldbuf = sunpro_dependencies_buf;
1260					sunpro_dependencies_buf=getmem(sunpro_dependencies_buf_size);
1261				}
1262				env = sunpro_dependencies_buf;
1263			} else {
1264				env = getmem(length);
1265			}
1266			env_alloc_num++;
1267			env_alloc_bytes += length;
1268			(void) sprintf(env,
1269				       "%s=%s",
1270				       name->string_mb,
1271				       value == NULL ?
1272			                 "" : value->string_mb);
1273			(void) putenv(env);
1274			env_prop->body.env_mem.value = env;
1275			if (sunpro_dependencies_oldbuf) {
1276				/* Return old buffer */
1277				retmem_mb(sunpro_dependencies_oldbuf);
1278				sunpro_dependencies_oldbuf = NULL;
1279			}
1280		}
1281	}
1282	if (name == target_arch) {
1283		Name		ha = getvar(host_arch);
1284		Name		ta = getvar(target_arch);
1285		Name		vr = getvar(virtual_root);
1286		int		length;
1287		wchar_t		*new_value;
1288		wchar_t		*old_vr;
1289		Boolean		new_value_allocated = false;
1290
1291		Wstring		ha_str(ha);
1292		Wstring		ta_str(ta);
1293		Wstring		vr_str(vr);
1294
1295		wchar_t * wcb_ha = ha_str.get_string();
1296		wchar_t * wcb_ta = ta_str.get_string();
1297		wchar_t * wcb_vr = vr_str.get_string();
1298
1299		length = 32 +
1300		  wcslen(wcb_ha) +
1301		    wcslen(wcb_ta) +
1302		      wcslen(wcb_vr);
1303		old_vr = wcb_vr;
1304		MBSTOWCS(wcs_buffer, "/usr/arch/");
1305		if (IS_WEQUALN(old_vr,
1306			       wcs_buffer,
1307			       wcslen(wcs_buffer))) {
1308			old_vr = (wchar_t *) wcschr(old_vr, (int) colon_char) + 1;
1309		}
1310		if ( (ha == ta) || (wcslen(wcb_ta) == 0) ) {
1311			new_value = old_vr;
1312		} else {
1313			new_value = ALLOC_WC(length);
1314			new_value_allocated = true;
1315			WCSTOMBS(mbs_buffer, old_vr);
1316			(void) swprintf(new_value, length * SIZEOFWCHAR_T,
1317				        L"/usr/arch/%s/%s:%s",
1318				        ha->string_mb + 1,
1319				        ta->string_mb + 1,
1320				        mbs_buffer);
1321		}
1322		if (new_value[0] != 0) {
1323			(void) setvar_daemon(virtual_root,
1324					     GETNAME(new_value, FIND_LENGTH),
1325					     false,
1326					     no_daemon,
1327					     true,
1328					     debug_level);
1329		}
1330		if (new_value_allocated) {
1331			retmem(new_value);
1332		}
1333	}
1334	return macro;
1335}
1336