xref: /illumos-gate/usr/src/cmd/make/lib/mksh/macro.cc (revision e7afc443)
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  */
47 static void	add_macro_to_global_list(Name macro_to_add);
48 static void	expand_value_with_daemon(Name, Property macro, String destination, Boolean cmd);
49 
50 static void	init_arch_macros(void);
51 static void	init_mach_macros(void);
52 static Boolean	init_arch_done = false;
53 static Boolean	init_mach_done = false;
54 
55 
56 long env_alloc_num = 0;
57 long 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  */
72 Name
getvar(Name name)73 getvar(Name name)
74 {
75 	String_rec		destination;
76 	wchar_t			buffer[STRING_BUFFER_LENGTH];
77 	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  */
117 void
expand_value(Name value,String destination,Boolean cmd)118 expand_value(Name value, String destination, Boolean cmd)
119 {
120 	Source_rec		sourceb;
121 	Source		source = &sourceb;
122 	wchar_t	*source_p = NULL;
123 	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  */
258 void
expand_macro(Source source,String destination,wchar_t * current_string,Boolean cmd)259 expand_macro(Source source, 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 	wchar_t	*source_p = source->string.text.p;
267 	wchar_t	*source_end = source->string.text.end;
268 	int		closer = 0;
269 	wchar_t			*block_start = (wchar_t *)NULL;
270 	int			quote_seen = 0;
271 	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);
312 recheck_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 	 */
439 get_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 	}
825 exit:
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 
844 static void
add_macro_to_global_list(Name macro_to_add)845 add_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  */
898 static void
init_arch_macros(void)899 init_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  */
953 static void
init_mach_macros(void)954 init_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  */
1007 static void
expand_value_with_daemon(Name,Property macro,String destination,Boolean cmd)1008 expand_value_with_daemon(Name, Property macro, String destination, Boolean cmd)
1009 {
1010 	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  */
1047 char	*sunpro_dependencies_buf = NULL;
1048 char	*sunpro_dependencies_oldbuf = NULL;
1049 int	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  */
1076 Property
setvar_daemon(Name name,Name value,Boolean append,Daemon daemon,Boolean strip_trailing_spaces,short debug_level)1077 setvar_daemon(Name name, Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level)
1078 {
1079 	Property	macro = maybe_append_prop(name, macro_prop);
1080 	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 	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;
1243 found_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