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