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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * 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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 #if defined(sun)
32 #pragma ident	"@(#)macro.c	1.16	06/10/09 SMI"
33 #endif
34 
35 #include "defs.h"
36 
37 /*
38  * Copyright 2008-2021 J. Schilling
39  *
40  * @(#)macro.c	1.102 21/07/21 2008-2021 J. Schilling
41  */
42 #ifndef lint
43 static	UConst char sccsid[] =
44 	"@(#)macro.c	1.102 21/07/21 2008-2021 J. Schilling";
45 #endif
46 
47 /*
48  * UNIX shell
49  */
50 
51 #ifdef	SCHILY_INCLUDES
52 #include	"sym.h"
53 #include	<schily/param.h>
54 #include	<schily/wait.h>
55 #else
56 #include	"sym.h"
57 #include	<sys/param.h>
58 #include	<wait.h>
59 #endif
60 
61 #ifndef	MAXPATHLEN
62 #define	MAXPATHLEN	1024
63 #endif
64 
65 #define	no_pipe	(int *)0
66 
67 		int	macflag;
68 static unsigned char	quote;	/* used locally */
69 static unsigned char	quoted;	/* used locally */
70 
71 #define	COM_BACKQUOTE	0	/* `command`  type command substitution */
72 #define	COM_DOL_PAREN	1	/* $(command) type command substitution */
73 
74 static void	copyto		__PR((unsigned char endch, int trimflag));
75 static void	skipto		__PR((unsigned char endch));
76 #ifdef	DO_DOT_SH_PARAMS
77 static unsigned char *shvar	__PR((unsigned char *v));
78 #endif
79 static unsigned int dolname	__PR((unsigned char **argpp,
80 					unsigned int c, unsigned int addc));
81 static int	getch		__PR((unsigned char endch, int trimflag));
82 	unsigned char *macro	__PR((unsigned char *as));
83 	unsigned char *_macro	__PR((unsigned char *as));
84 static void	comsubst	__PR((int, int));
85 	void	subst		__PR((int in, int ot));
86 static void	flush		__PR((int));
87 
88 #ifdef	DO_SUBSTRING
89 static int	mbschars	__PR((unsigned char *v));
90 static unsigned char	*prefsubstr __PR((unsigned char *v, unsigned char *pat,
91 					int largest));
92 static unsigned char	*mbdecr	__PR((unsigned char *s, unsigned char *sp));
93 static int		suffsubstr __PR((unsigned char *v, unsigned char *pat,
94 					int largest));
95 #ifdef	__needed__
96 static Uchar	*globesc	__PR((Uchar *argp));
97 #endif
98 #endif
99 static void	sizecpy		__PR((int vsize, Uchar *v, int trimflag));
100 static Uchar	*trimcpy	__PR((Uchar *argp, int trimflag));
101 
102 #ifdef	PROTOTYPES
103 static void
copyto(unsigned char endch,int trimflag)104 copyto(unsigned char endch, int trimflag)
105 #else
106 static void
107 copyto(endch, trimflag)
108 	unsigned char	endch;
109 	int		trimflag;
110 #endif
111 /* trimflag - flag to check if argument will be trimmed */
112 {
113 	unsigned int	c;
114 	unsigned int	d;
115 	unsigned char *pc;
116 
117 	while ((c = getch(endch, trimflag)) != endch && c) {
118 		if (quote) {
119 			if (c == '\\') { /* don't interpret next character */
120 				GROWSTAKTOP();
121 				pushstak(c);
122 				d = readwc();
123 				if (!escchar(d)) {
124 					/*
125 					 * both \ and following
126 					 * character are quoted if next
127 					 * character is not $, `, ", or \
128 					 */
129 					GROWSTAKTOP();
130 					pushstak('\\');
131 					GROWSTAKTOP();
132 					pushstak('\\');
133 				}
134 				pc = readw(d);
135 				/*
136 				 * d might be NULL
137 				 * Even if d is NULL, we have to save it
138 				 */
139 				if (*pc) {
140 					while (*pc) {
141 						GROWSTAKTOP();
142 						pushstak(*pc++);
143 					}
144 				} else {
145 					GROWSTAKTOP();
146 					pushstak(*pc);
147 				}
148 			} else { /* push escapes onto stack to quote chars */
149 				pc = readw(c);
150 				GROWSTAKTOP();
151 				pushstak('\\');
152 				while (*pc) {
153 					GROWSTAKTOP();
154 					pushstak(*pc++);
155 				}
156 			}
157 		} else if (c == '\\') {
158 			c = readwc(); /* get character to be escaped */
159 			GROWSTAKTOP();
160 			pushstak('\\');
161 			pc = readw(c);
162 			/* c might be NULL */
163 			/* Evenif c is NULL, we have to save it */
164 			if (*pc) {
165 				while (*pc) {
166 					GROWSTAKTOP();
167 					pushstak(*pc++);
168 				}
169 			} else {
170 				GROWSTAKTOP();
171 				pushstak(*pc);
172 			}
173 		} else {
174 			pc = readw(c);
175 			while (*pc) {
176 				GROWSTAKTOP();
177 				pushstak(*pc++);
178 			}
179 		}
180 	}
181 	GROWSTAKTOP();
182 	zerostak();	/* Add nul byte */
183 	if (c != endch)
184 		error(badsub);
185 }
186 
187 #ifdef	PROTOTYPES
188 static void
skipto(unsigned char endch)189 skipto(unsigned char endch)
190 #else
191 static void
192 skipto(endch)
193 	unsigned char	endch;
194 #endif
195 {
196 	/*
197 	 * skip chars up to }
198 	 */
199 	unsigned int	c;
200 
201 	while ((c = readwc()) != '\0' && c != endch) {
202 		switch (c) {
203 		case SQUOTE:
204 			skipto(SQUOTE);
205 			break;
206 
207 		case DQUOTE:
208 			skipto(DQUOTE);
209 			break;
210 
211 		case DOLLAR:
212 			if ((c = readwc()) == BRACE)
213 				skipto('}');
214 			else if (c == SQUOTE || c == DQUOTE)
215 				skipto(c);
216 			else if (c == 0)
217 				goto out;
218 			break;
219 
220 		case ESCAPE:
221 			if (!(c = readwc()))
222 				goto out;
223 		}
224 	}
225 out:
226 	if (c != endch)
227 		error(badsub);
228 }
229 
230 /*
231  * Expand special shell variables ${.sh.xxx}.
232  */
233 #ifdef	DO_DOT_SH_PARAMS
234 static unsigned char *
shvar(v)235 shvar(v)
236 	unsigned char	*v;
237 {
238 	if (eq(v, "status")) {			/* exit status */
239 		sitos(retex.ex_status);
240 		v = numbuf;
241 	} else if (eq(v, "termsig")) {		/* kill signame */
242 		numbuf[0] = '\0';
243 		sig2str(retex.ex_status, (char *)numbuf);
244 		v = numbuf;
245 		if (numbuf[0] == '\0')
246 			strcpy((char *)numbuf, "UNKNOWN");
247 	} else if (eq(v, "code")) {		/* exit code (reason) */
248 		itos(retex.ex_code);
249 		v = numbuf;
250 	} else if (eq(v, "codename")) {		/* text for above */
251 		v = UC code2str(retex.ex_code);
252 	} else if (eq(v, "pid")) {		/* exited pid */
253 		itos(retex.ex_pid);
254 		v = numbuf;
255 	} else if (eq(v, "signo")) {		/* SIGCHLD or trapsig */
256 		itos(retex.ex_signo);
257 		v = numbuf;
258 	} else if (eq(v, "signame")) {		/* text for above */
259 		sig2str(retex.ex_signo, (char *)numbuf);
260 		v = numbuf;
261 	} else if (eq(v, "shell")) {		/* Shell implementation name */
262 		v = UC shname;
263 	} else if (eq(v, "version")) {		/* Shell version */
264 		v = UC shvers;
265 	} else if (eq(v, "path")) {		/* Shell path */
266 		static unsigned char *shpath = NULL;
267 		char		pbuf[MAXPATHLEN + 1];
268 		unsigned char	*pname;
269 #ifdef	HAVE_GETEXECNAME
270 		const char	*exname = getexecname();
271 #else
272 			char	*exname = getexecpath();
273 #endif
274 		if (shpath)
275 			return (shpath);
276 		if (exname == NULL)
277 			return (NULL);
278 		if (*exname == '/') {
279 			pname = UC exname;
280 		} else {
281 #ifdef	HAVE_REALPATH
282 			pname = UC realpath(exname, pbuf);
283 #else
284 			pname = UC abspath(exname, pbuf, sizeof (pbuf));
285 #endif
286 		}
287 		if (pname) {
288 			/*
289 			 * NUMBUFLEN is sufficient in most cases.
290 			 * We cannot do this on stak as we need to
291 			 * keep the current stak segment open.
292 			 */
293 			if (length(pname) > NUMBUFLEN) {
294 				shpath = pname = make(pname);
295 			} else {
296 				movstr(pname, numbuf);
297 				return (numbuf);
298 			}
299 		}
300 #ifndef	HAVE_GETEXECNAME
301 		libc_free(exname);
302 #endif
303 		v = UC pname;
304 	} else {
305 		return (NULL);
306 	}
307 	return (v);
308 }
309 #endif
310 
311 /*
312  * Collect the parameter name.
313  * If "addc" is null, collect a normal parameter name,
314  * else "addc" is an additional permitted character.
315  * This is typically '.' for the ".sh.xxx" parameters.
316  * Returns the first non-matching character to allow the rest
317  * of the parser to recover.
318  */
319 static unsigned int
dolname(argpp,c,addc)320 dolname(argpp, c, addc)
321 	unsigned char	**argpp;
322 	unsigned int	c;
323 	unsigned int	addc;
324 {
325 	unsigned char	*argp;
326 
327 	argp = (unsigned char *)relstak();
328 	while (alphanum(c) || (addc && c == addc)) {
329 		GROWSTAKTOP();
330 		pushstak(c);
331 		c = readwc();
332 	}
333 	GROWSTAKTOP();
334 	zerostak();
335 	*argpp = argp;		/* Return start offset against stakbot */
336 	return (c);
337 }
338 
339 #ifdef	PROTOTYPES
340 static int
getch(unsigned char endch,int trimflag)341 getch(unsigned char endch, int trimflag)
342 #else
343 static int
344 getch(endch, trimflag)
345 	unsigned char	endch;
346 	/*
347 	 * flag to check if an argument is going to be trimmed, here document
348 	 * output is never trimmed
349 	 */
350 	int	trimflag;
351 #endif
352 {
353 	unsigned int	d;
354 	/*
355 	 * atflag to check if $@ has already been seen within double quotes
356 	 */
357 	int atflag = 0;
358 retry:
359 	d = readwc();
360 	if (!subchar(d))
361 		return (d);
362 
363 	if (d == DOLLAR) {
364 		unsigned int c;
365 
366 		if ((c = readwc(), dolchar(c))) {
367 			struct namnod *n = (struct namnod *)NIL;
368 			int		dolg = 0;
369 #ifdef	DO_U_DOLAT_NOFAIL
370 			int		isg = 0;
371 #endif
372 #ifdef	DO_SUBSTRING
373 			int		largest = 0;
374 #endif
375 			int		vsize = -1;
376 			BOOL		bra;
377 			BOOL		nulflg;
378 			unsigned char	*argp, *v = NULL;
379 			unsigned char		idb[2];
380 			unsigned char		*id = idb;
381 			unsigned char	oquote;
382 
383 			*id = '\0';
384 
385 			if ((bra = (c == BRACE)) != FALSE)
386 				c = readwc();
387 #ifdef	DO_SUBSTRING
388 getname:
389 #endif
390 			if (letter(c)) {	/* valid parameter name */
391 
392 				c = dolname(&argp, c, 0);
393 				n = lookup(absstak(argp));
394 				setstak(argp);
395 #ifndef	DO_POSIX_UNSET
396 				if (n->namflg & N_FUNCTN) {
397 					error(badsub);
398 					return (EOF);
399 				}
400 #endif
401 #ifdef	DO_LINENO
402 				if (n == &linenonod) {
403 					v = linenoval();
404 				} else
405 #endif
406 					v = n->namval;
407 				id = (unsigned char *)n->namid;
408 				peekc = c | MARK;
409 			} else if (digchar(c)) {	/* astchar or digits */
410 				*id = c;
411 				idb[1] = 0;
412 				if (astchar(c))	{	/* '*' or '@' */
413 					if (c == '@' && !atflag && quote) {
414 						quoted--;
415 						atflag = 1;
416 					}
417 #ifdef	DO_U_DOLAT_NOFAIL
418 					isg = dolg = 1;
419 #else
420 					dolg = 1;
421 #endif
422 					c = 1;
423 				} else if (digit(c)) {
424 					c -= '0';
425 #ifdef	DO_POSIX_PARAM
426 					if (bra) {
427 						int	dd;
428 						int	overflow = 0;
429 						int	maxmult = INT_MAX / 10;
430 
431 						while ((dd = readwc(),
432 							digit(dd))) {
433 							dd -= '0';
434 							if (c > maxmult)
435 								overflow = 1;
436 							c *= 10;
437 							if (INT_MAX - c < dd)
438 								overflow = 1;
439 							c += dd;
440 						}
441 						peekc = dd | MARK;
442 						if (overflow)
443 							c = INT_MAX;
444 					}
445 #endif
446 				}
447 				/*
448 				 * Double cast is needed to work around a
449 				 * GCC bug.
450 				 */
451 				v = ((c == 0) ?
452 					cmdadr :
453 					(dolc > 0 && c <= dolc) ?
454 					dolv[c] :
455 					(unsigned char *)(Intptr_t)(dolg = 0));
456 			} else if (c == '$') {
457 				v = pidadr;
458 			} else if (c == '!') {
459 				v = pcsadr;
460 			} else if (c == '#') {
461 #ifdef	DO_SUBSTRING
462 				if (bra == 1) {
463 					c = readwc();
464 					if (c == ':' ||
465 					    c == '-' ||
466 					    c == '+' ||
467 					    c == '?' ||
468 					    c == '=') {
469 						/*
470 						 * Check for corner case ${#?}
471 						 */
472 						if (c == '?') {
473 							int nc = readwc();
474 
475 							peekc = nc|MARK;
476 							if (nc == '}') {
477 								bra++;
478 								goto getname;
479 							}
480 						}
481 						itos(dolc);
482 						v = numbuf;
483 						goto docolon;
484 					} else if (c != '}') {
485 						bra++;
486 						goto getname;
487 					} else {
488 						bra = 0;
489 					}
490 				}
491 #endif
492 				itos(dolc);
493 				v = numbuf;
494 			} else if (c == '?') {
495 #ifdef	DO_SIGNED_EXIT
496 				sitos(retval);
497 #else
498 				itos(retval);
499 #endif
500 				v = numbuf;
501 #ifdef	DO_DOL_SLASH
502 			} else if (c == '/') {
503 				if (retex.ex_code == CLD_EXITED) {
504 					sitos(retex.ex_status);
505 					v = numbuf;
506 				} else if (retex.ex_code == C_NOEXEC ||
507 					    retex.ex_code == C_NOTFOUND) {
508 					v = UC code2str(retex.ex_code);
509 				} else {
510 					sig2str(retex.ex_status,
511 						    (char *)numbuf);
512 					v = numbuf;
513 				}
514 #endif
515 			} else if (c == '-') {
516 				v = flagadr;
517 #ifdef	DO_DOT_SH_PARAMS
518 			} else if (bra && c == '.') {
519 				unsigned char	*shv;
520 
521 				c = dolname(&argp, c, '.');
522 				v = absstak(argp);	/* Variable name */
523 				if (v[0] == '.' &&
524 				    v[1] == 's' &&
525 				    v[2] == 'h' &&
526 				    v[3] == '.' &&
527 					(shv = shvar(&v[4])) != NULL) {
528 					v = shv;
529 				} else {
530 					v = NULL;
531 				}
532 				setstak(argp);
533 				peekc = c | MARK;
534 #endif
535 			} else if (bra) {
536 				error(badsub);
537 				return (EOF);
538 			} else {
539 				goto retry;
540 			}
541 			c = readwc();
542 #ifdef	DO_SUBSTRING
543 docolon:
544 #endif
545 			if (c == ':' && bra == 1) { /* null and unset fix */
546 				nulflg = 1;
547 				c = readwc();	/* c now holds char past ':' */
548 			} else {
549 				nulflg = 0;
550 			}
551 			if (!defchar(c) && bra) {	/* check "}=-+?#%" */
552 				error(badsub);
553 				return (EOF);
554 			}
555 			argp = 0;
556 			if (bra) {		/* ${...} */
557 				/*
558 				 * This place is probably the wrong place to
559 				 * mark the word as expanded, but before, we
560 				 * did not mark a substitution to word in
561 				 * ${var-word} if "var" is unset.
562 				 */
563 				macflag |= M_PARM;
564 
565 				if (c != '}') {	/* word follows parameter */
566 					/*
567 					 * Collect word or null string depending
568 					 * on parameter value and setchar(c).
569 					 */
570 					argp = (unsigned char *)relstak();
571 					if ((v == 0 ||
572 					    (nulflg && *v == 0)) ^
573 					    (setchar(c))) {
574 						int	ntrim = trimflag;
575 
576 						oquote = quote;
577 #ifdef	DO_SUBSTRING
578 						if (c == '#' || c == '%') {
579 							unsigned int	nc;
580 
581 #ifdef	__needed__
582 							/*
583 							 * See globesc() below.
584 							 */
585 							ntrim = 0;
586 #endif
587 							nc = readwc();
588 							if (nc == c) {
589 								largest++;
590 							} else {
591 								peekc = nc|MARK;
592 							}
593 							quote = 0;
594 						} else {
595 							unsigned int	nc;
596 
597 							nc = readwc();
598 							if (nc == DQUOTE)
599 								quote = 0;
600 							peekc = nc|MARK;
601 						}
602 #endif
603 						copyto('}', ntrim);
604 						quote = oquote;
605 					} else {
606 						skipto('}');
607 					}
608 					argp = absstak(argp);
609 				}
610 			} else {
611 				peekc = c | MARK;
612 				c = 0;
613 			}
614 #ifdef	DO_SUBSTRING
615 			if (bra > 1) {
616 				int	l = 0;
617 
618 				if (c != '}' || argp) {
619 					error(badsub);
620 					return (EOF);
621 				}
622 				if (v)
623 					l = mbschars(v);
624 				itos(l);
625 				v = numbuf;
626 			}
627 			if ((c == '#' || c == '%')) {
628 				if (v) {
629 					UIntptr_t	b = relstakp(argp);
630 
631 					if (dolg) {
632 						error(badsub);
633 						return (EOF);
634 					}
635 #ifdef	__needed__
636 					if (quote) {
637 						/*
638 						 * This is a copy that we may
639 						 * shrink.
640 						 */
641 						trim(argp);
642 					}
643 #endif
644 
645 					/*
646 					 * Treat double quotes in glob pattern.
647 					 */
648 #ifdef	__needed__
649 					/*
650 					 * See ntrim = 0 above.
651 					 */
652 					argp = globesc(argp);
653 #endif
654 					if (c == '#') {
655 						v = prefsubstr(v, argp,
656 								largest);
657 					} else {
658 						vsize = suffsubstr(v, argp,
659 								largest);
660 					}
661 					setstak(b);
662 				} else {
663 					/*
664 					 * Clear to let it fail later with
665 					 * an unset error with set -u.
666 					 */
667 					argp = 0;
668 				}
669 			}
670 #endif
671 			if (v && (!nulflg || *v)) {
672 				/*
673 				 * Parameter is not unset and
674 				 * either non-null or the ':' is not present.
675 				 */
676 
677 				if (c != '+') {
678 					Uchar sep = ' ';
679 
680 #ifdef	DO_IFS_SEP
681 					if (ifsnod.namval)
682 						sep = *ifsnod.namval;
683 #endif
684 					/*
685 					 * Substitute parameter value
686 					 */
687 					(void) mbtowc(NULL, NULL, 0);
688 					for (;;) {
689 						if (*v == 0 && quote) {
690 							GROWSTAKTOP();
691 							pushstak('\\');
692 							GROWSTAKTOP();
693 							pushstak('\0');
694 						} else {
695 							macflag |= M_PARM;
696 							sizecpy(vsize, v,
697 								trimflag);
698 						}
699 
700 						if (dolg == 0 ||
701 						    (++dolg > dolc)) {
702 							break;
703 						} else {
704 							/*
705 							 * $* and $@ expansion
706 							 */
707 							v = dolv[dolg];
708 							if (*id == '*' &&
709 							    quote) {
710 								/*
711 								 * push quoted
712 								 * separator so
713 								 * that "$*"
714 								 * will not be
715 								 * broken into
716 								 * separate
717 								 * arguments.
718 								 */
719 								if (!sep)
720 								    continue;
721 								GROWSTAKTOP();
722 								pushstak('\\');
723 								GROWSTAKTOP();
724 								pushstak(sep);
725 							} else {
726 								if (*id == '@')
727 								    macflag |=
728 									M_DOLAT;
729 								GROWSTAKTOP();
730 								pushstak(' ');
731 							}
732 						}
733 					}
734 				}
735 			} else if (argp) {
736 				if (c == '?') {
737 					if (trimflag)
738 						trim(argp);
739 					failed(id, *argp ? (const char *)argp :
740 					    badparam);
741 					return (EOF);
742 				} else if (c == '=') {
743 					if (n) {
744 						int slength = staktop - stakbot;
745 						UIntptr_t aoff = argp - stakbot;
746 						unsigned char *savp = fixstak();
747 						struct ionod *iosav = iotemp;
748 						unsigned char *newargp;
749 
750 						/*
751 						 * The malloc()-based stak.c
752 						 * will relocate the last item
753 						 * if we call fixstak();
754 						 */
755 						argp = savp + aoff;
756 
757 						/*
758 						 * copy word onto stack, trim
759 						 * it, and then do assignment
760 						 */
761 						usestak();
762 						argp = trimcpy(argp, trimflag);
763 						newargp = fixstak();
764 						assign(n, newargp);
765 						tdystak(savp, iosav);
766 						(void) memcpystak(stakbot,
767 								    savp,
768 								    slength);
769 						staktop = stakbot + slength;
770 					} else {
771 						error(badsub);
772 						return (EOF);
773 					}
774 				}
775 #ifdef	DO_U_DOLAT_NOFAIL
776 			} else if ((flags & setflg) && isg == 0) {
777 #else
778 			} else if (flags & setflg) {
779 #endif
780 				failed(id, unset);
781 				return (EOF);
782 			}
783 			goto retry;
784 #ifdef		DO_DOL_PAREN
785 		} else if (c == '(' && (macflag & M_NOCOMSUBST) == 0) {
786 			comsubst(trimflag, COM_DOL_PAREN);
787 			goto retry;
788 #endif
789 		} else {
790 			peekc = c | MARK;
791 		}
792 	} else if (d == endch) {
793 		return (d);
794 	} else if (d == SQUOTE) {
795 		if (macflag & M_NOCOMSUBST)
796 			return (d);
797 		comsubst(trimflag, COM_BACKQUOTE);
798 		goto retry;
799 	} else if (d == DQUOTE && trimflag) {
800 		if (!quote) {
801 			atflag = 0;
802 			quoted++;
803 		}
804 		quote ^= QUOTE;
805 		goto retry;
806 	}
807 	return (d);
808 }
809 
810 unsigned char *
macro(as)811 macro(as)
812 	unsigned char	*as;
813 {
814 	macflag &= ~M_SPLIT;
815 	(void) _macro(as);
816 	return (fixstak());
817 }
818 
819 unsigned char *
_macro(as)820 _macro(as)
821 	unsigned char	*as;
822 {
823 	/*
824 	 * Strip "" and do $ substitution
825 	 * Leaves result on top of stack
826 	 */
827 	BOOL	savqu = quoted;
828 	unsigned char	savq = quote;
829 	UIntptr_t	b = relstak();
830 	struct filehdr	fb;
831 
832 	fb.fsiz = 1;	/* It's a filehdr not a fileblk */
833 	push((struct fileblk *)&fb);
834 	estabf(as);
835 	usestak();
836 	quote = 0;
837 	quoted = 0;
838 	copyto(0, 1);
839 	pop();
840 	if (quoted && (stakbot == staktop)) {
841 		GROWSTAKTOP();
842 		pushstak('\\');
843 		GROWSTAKTOP();
844 		pushstak('\0');
845 		zerostak();
846 /*
847  * above is the fix for *'.c' bug
848  */
849 	}
850 	quote = savq;
851 	quoted = savqu;
852 	return (absstak(b));
853 }
854 /* Save file descriptor for command substitution */
855 int savpipe = -1;
856 
857 static void
comsubst(trimflag,type)858 comsubst(trimflag, type)
859 	int	trimflag;
860 	int	type;
861 /* trimflag - used to determine if argument will later be trimmed */
862 {
863 	/*
864 	 * command substn
865 	 */
866 	struct fileblk	cb;
867 	unsigned int	d;
868 	int strlngth = staktop - stakbot;
869 	unsigned char *oldstaktop;
870 	unsigned char *savptr = fixstak();
871 	struct ionod *iosav = iotemp;
872 	struct ionod *fiosav = fiotemp;
873 	int		oiof = 0;
874 	int		ofiof = 0;
875 	unsigned char	*pc;
876 	struct trenod	*tc = NULL;
877 	int		omacflag = macflag;
878 
879 	if (iosav) {
880 		oiof = iosav->iofile;
881 		iosav->iofile |= IOBARRIER;
882 	}
883 	if (fiosav) {
884 		ofiof = fiosav->iofile;
885 		fiosav->iofile |= IOBARRIER;
886 	}
887 
888 	if (type == COM_BACKQUOTE) {  /* `command`  type command substitution */
889 
890 	usestak();
891 	while ((d = readwc()) != SQUOTE && d) {
892 		if (d == '\\') {
893 			d = readwc();
894 			if (!escchar(d) || (d == '"' && !quote)) {
895 				/*
896 				 * trim quotes for `, \, or " if
897 				 * command substitution is within
898 				 * double quotes
899 				 */
900 				GROWSTAKTOP();
901 				pushstak('\\');
902 			}
903 		}
904 		pc = readw(d);
905 		/* d might be NULL */
906 		if (*pc) {
907 			while (*pc) {
908 				GROWSTAKTOP();
909 				pushstak(*pc++);
910 			}
911 		} else {
912 			GROWSTAKTOP();
913 			pushstak(*pc);
914 		}
915 	}
916 	{
917 		unsigned char	*argc;
918 
919 		argc = fixstak();
920 		push(&cb);
921 		estabf(argc);  /* read from string */
922 		tc = cmd(EOFSYM, MTFLG | NLFLG | SEMIFLG);
923 	}
924 	}
925 #ifdef	DO_DOL_PAREN
926 	else {	/* $(command) type command substitution */
927 		d = readwc();
928 		if (d == '(') {		/* This is an arithmetic expression */
929 			Intmax_t	i;
930 			unsigned char	*argc;
931 			struct argnod	*arg = (struct argnod *)locstak();
932 			unsigned char	*argp = arg->argval;
933 			int		err;
934 
935 			/*
936 			 * savptr holds strlngth bytes in a saved area
937 			 * containing the already parsed part of the string.
938 			 * Use match_arith() to find the end of the expresssion
939 			 * and call macro() with this string to expand
940 			 * variables and embedded command substitutions.
941 			 * NOTE: match_arith() expects the string to be part
942 			 * of struct argnod, so argp must not start at stakbot.
943 			 */
944 			*argp = '\0';
945 			staktop = match_arith(argp);
946 			arg = (struct argnod *)stakbot;
947 			argc = staktop-1;
948 			argp = arg->argval;
949 			while (argc > argp && *(--argc) != ')')
950 				;
951 			*argc = 0;
952 			arg = (struct argnod *)fixstak();
953 			argc = arg->argval;
954 			argc = macro(&argc[2]);
955 			(void) memcpystak(stakbot, savptr, strlngth);
956 			staktop = stakbot + strlngth;
957 			/*
958 			 * First implementation: just return the expanded string
959 			 */
960 			i = strexpr(argc, &err);
961 			if (err == 0) {
962 				staktop = movstrstak(&numbuf[slltos(i)],
963 								staktop);
964 			} else {
965 				exitval = err;
966 			}
967 			macflag = omacflag | M_ARITH;
968 			if (iosav)
969 				iosav->iofile = oiof;
970 			if (fiosav)
971 				fiosav->iofile = ofiof;
972 			return;
973 		}
974 		peekc = d | MARK;
975 		tc = cmd(')', MTFLG | NLFLG | SEMIFLG);
976 	}
977 #endif
978 	{
979 		struct trenod	*t;
980 		int		pv[2];
981 #ifdef	DO_POSIX_E
982 		int		oret = retval;
983 		struct excode	oex;
984 
985 		oex = retex;
986 #endif
987 
988 		/*
989 		 * this is done like this so that the pipe
990 		 * is open only when needed
991 		 */
992 		t = makefork(FPOU|STDOUT_FILENO, tc);
993 		chkpipe(pv);
994 		savpipe = pv[OTPIPE];
995 #ifdef	DO_DOL_PAREN
996 		if (type != COM_BACKQUOTE) {
997 			push(&cb);
998 			estabf(NULL);
999 		}
1000 #endif
1001 		initf(pv[INPIPE]); /* read from pipe */
1002 #ifdef	PARSE_DEBUG
1003 		prtree(t, "Command Substitution: ");
1004 #endif
1005 		/*
1006 		 * The SVr4 version of the shell did not allocate a job
1007 		 * slot when XEC_NOSTOP was specified. Since we use vfork()
1008 		 * and optiomized pipes (-DDO_PIPE_PARENT) we also need to
1009 		 * specify XEC_ALLOCJOB to avoid that we overwrite the
1010 		 * existing job slot with command substitution.
1011 		 */
1012 		execute(t, XEC_NOSTOP|XEC_ALLOCJOB, (int)(flags & errflg),
1013 			no_pipe, pv);
1014 #ifdef	DO_POSIX_E
1015 		retval = oret;	/* Restore old retval for $? */
1016 		retex = oex;
1017 #endif
1018 		close(pv[OTPIPE]);
1019 		savpipe = -1;
1020 	}
1021 #if 0
1022 	/*
1023 	 * Do we really need to call this here?
1024 	 * execute() calls it already and if we leave this call here, we may
1025 	 * end up with accessing already free()d memory later.
1026 	 *
1027 	 * Calling tdystak() would free only the space that was allocated by
1028 	 * the parser (cmd()) and this seems to be of limited size. Given that
1029 	 * comsubst() is called as part of another command execution, it seems
1030 	 * that the lifetime of that space is not significantly enhanced.
1031 	 */
1032 	tdystak(savptr, iosav);
1033 #endif
1034 	(void) memcpystak(stakbot, savptr, strlngth);
1035 	oldstaktop = staktop = stakbot + strlngth;
1036 	while ((d = readwc()) != '\0') {
1037 		if (quote || (d == '\\' && trimflag)) {
1038 			unsigned char *rest;
1039 			/*
1040 			 * quote output from command subst. if within double
1041 			 * quotes or backslash part of output
1042 			 */
1043 			rest = readw(d);
1044 			GROWSTAKTOP();
1045 			pushstak('\\');
1046 			while ((d = *rest++) != '\0') {
1047 			/* Pick up all of multibyte character */
1048 				GROWSTAKTOP();
1049 				pushstak(d);
1050 			}
1051 		} else {
1052 			pc = readw(d);
1053 			while (*pc) {
1054 				GROWSTAKTOP();
1055 				pushstak(*pc++);
1056 			}
1057 		}
1058 	}
1059 	{
1060 		extern pid_t parent;
1061 		int	rc;
1062 		int	ret = 0;
1063 		int	wstatus;
1064 		int	wcode;
1065 
1066 		while ((ret = wait_status(parent,
1067 				&wcode, &wstatus,
1068 				(WEXITED|WTRAPPED))) != parent) {
1069 			/* break out if waitid(2) has failed */
1070 			if (ret == -1)
1071 				break;
1072 		}
1073 		rc = wstatus;
1074 		if ((flags2 & fullexitcodeflg) == 0)
1075 			rc &= 0xFF; /* As dumb as with historic wait */
1076 		if (wcode == CLD_KILLED || wcode == CLD_DUMPED)
1077 			rc |= SIGFLG;
1078 #ifdef	DO_EXIT_MODFIX
1079 		else if (wstatus != 0 && rc == 0)
1080 			rc = SIGFLG;	/* Use special value 128 */
1081 #endif
1082 #ifndef	DO_POSIX_E
1083 		if (rc && (flags & errflg))
1084 			exitsh(rc);
1085 #endif
1086 		exitval = rc;
1087 		ex.ex_status = wstatus;
1088 		ex.ex_code = wcode;
1089 		ex.ex_pid = parent;
1090 		flags |= eflag;
1091 #ifndef	DO_POSIX_E
1092 		exitset();	/* Set retval from exitval for $? */
1093 #endif
1094 	}
1095 	if (iosav)
1096 		iosav->iofile = oiof;
1097 	if (fiosav)
1098 		fiosav->iofile = ofiof;
1099 	while (oldstaktop != staktop) {
1100 		/*
1101 		 * strip off trailing newlines from command substitution only
1102 		 */
1103 		if ((*--staktop) != NL) {
1104 			++staktop;
1105 			break;
1106 		} else if (quote)
1107 			staktop--; /* skip past backslashes if quoting */
1108 	}
1109 	pop();
1110 
1111 	macflag = omacflag | M_COMMAND;
1112 }
1113 
1114 #define	CPYSIZ	512
1115 
1116 void
subst(in,ot)1117 subst(in, ot)
1118 	int	in;
1119 	int	ot;
1120 {
1121 	unsigned int	c;
1122 	struct fileblk	fb;
1123 	int	count = CPYSIZ;
1124 	unsigned char	*pc;
1125 #ifdef	DO_POSIX_HERE
1126 	int	oquote = quote;
1127 #endif
1128 
1129 	push(&fb);
1130 	initf(in);
1131 	/*
1132 	 * DQUOTE used to stop it from quoting
1133 	 */
1134 #ifdef	DO_POSIX_HERE
1135 	quote = 0;
1136 #endif
1137 	while ((c = (getch(DQUOTE, 0))) != '\0') {
1138 		/*
1139 		 * read characters from here document and interpret them
1140 		 */
1141 		if (c == '\\') {
1142 			c = readwc();
1143 			/*
1144 			 * check if character in here document is escaped
1145 			 */
1146 			if (!escchar(c) || c == '"') {
1147 				GROWSTAKTOP();
1148 				pushstak('\\');
1149 			}
1150 		}
1151 		pc = readw(c);
1152 		/* c might be NULL */
1153 		if (*pc) {
1154 			while (*pc) {
1155 				GROWSTAKTOP();
1156 				pushstak(*pc++);
1157 			}
1158 		} else {
1159 			GROWSTAKTOP();
1160 			pushstak(*pc);
1161 		}
1162 		if (--count == 0) {
1163 			flush(ot);
1164 			count = CPYSIZ;
1165 		}
1166 	}
1167 #ifdef	DO_POSIX_HERE
1168 	quote = oquote;
1169 #endif
1170 	flush(ot);
1171 	pop();
1172 }
1173 
1174 static void
flush(ot)1175 flush(ot)
1176 	int	ot;
1177 {
1178 	write(ot, stakbot, staktop - stakbot);
1179 	if (flags & execpr)
1180 		write(output, stakbot, staktop - stakbot);
1181 	staktop = stakbot;
1182 }
1183 
1184 #ifdef	DO_SUBSTRING
1185 static int
mbschars(v)1186 mbschars(v)
1187 	unsigned char	*v;
1188 {
1189 	register unsigned char	*s = v;
1190 		wchar_t		wc;
1191 		int		len;
1192 		int		chars = 0;
1193 
1194 	while (*s) {
1195 		if ((len = mbtowc(&wc, (char *)s, MB_LEN_MAX)) <= 0) {
1196 			(void) mbtowc(NULL, NULL, 0);
1197 			len = 1;
1198 		}
1199 		s += len;
1200 		chars++;
1201 	}
1202 	return (chars);
1203 }
1204 
1205 /*
1206  * Prefix substring matcher for ${var#pat} and ${var##pat}
1207  *
1208  * Returns pointer to first non-matching character in the string.
1209  */
1210 static unsigned char *
prefsubstr(v,pat,largest)1211 prefsubstr(v, pat, largest)
1212 	unsigned char	*v;		/* The data value to check	*/
1213 	unsigned char	*pat;		/* The pattern to match against */
1214 	int		largest;	/* Whether to match largest str	*/
1215 {
1216 	register unsigned char	*s = v;
1217 	register unsigned char	*r = v;
1218 	register unsigned int	c;
1219 		wchar_t		wc;
1220 		int		len;
1221 
1222 	do {
1223 		c = *s;
1224 		*s = '\0';
1225 		if (gmatch(C v, C pat)) {
1226 			if (!largest) {
1227 				*s = c;
1228 				return (s);
1229 			}
1230 			r = s;
1231 		}
1232 		*s = c;
1233 
1234 		if ((len = mbtowc(&wc, (char *)s, MB_LEN_MAX)) <= 0) {
1235 			(void) mbtowc(NULL, NULL, 0);
1236 			len = 1;
1237 		}
1238 		s += len;
1239 	} while (c);
1240 	return (r);
1241 }
1242 
1243 /*
1244  * Return a pointer to the last multi byte character before "sp".
1245  * Currently always called after gmatch() and thus from an intact mbstate.
1246  */
1247 static unsigned char *
mbdecr(s,sp)1248 mbdecr(s, sp)
1249 	unsigned char	*s;
1250 	unsigned char	*sp;
1251 {
1252 	wchar_t		wc;
1253 	int		len;
1254 
1255 	while (s < sp) {
1256 		if ((len = mbtowc(&wc, (char *)s, MB_LEN_MAX)) <= 0) {
1257 			(void) mbtowc(NULL, NULL, 0);
1258 			len = 1;
1259 		}
1260 		if ((s + len) >= sp)
1261 			return (s);
1262 		s += len;
1263 	}
1264 	return (sp-1);
1265 }
1266 
1267 /*
1268  * Suffix substring matcher for ${var%pat} and ${var%%pat}
1269  *
1270  * Returns size of non-matching initial part in the string.
1271  */
1272 static int
suffsubstr(v,pat,largest)1273 suffsubstr(v, pat, largest)
1274 	unsigned char	*v;		/* The data value to check	*/
1275 	unsigned char	*pat;		/* The pattern to match against */
1276 	int		largest;	/* Whether to match largest str	*/
1277 {
1278 	register unsigned char	*s = v;
1279 	register int		size = strlen(C v);
1280 
1281 	s += size;
1282 	while (s >= v) {
1283 		if (gmatch(C s, C pat)) {
1284 			size = s - v;
1285 			if (!largest)
1286 				break;
1287 		}
1288 		s = mbdecr(v, s);
1289 	}
1290 	return (size);
1291 }
1292 
1293 /*
1294  * Convert prefix and suffix pattern into something that is
1295  * accepted by glob().
1296  */
1297 #ifdef	__needed__
1298 static Uchar *
globesc(argp)1299 globesc(argp)
1300 	Uchar	*argp;
1301 {
1302 	int		c;
1303 	int		escflag = FALSE;
1304 	UIntptr_t	b = relstak();
1305 
1306 	pushstak('\0');			/* Terminate current argp */
1307 	(void) mbtowc(NULL, NULL, 0);
1308 	while ((c = *argp) != '\0') {
1309 		wchar_t		wc;
1310 		int		len;
1311 
1312 		if ((len = mbtowc(&wc, C argp, MB_LEN_MAX)) <= 0) {
1313 			(void) mbtowc(NULL, NULL, 0);
1314 			len = 1;
1315 		}
1316 		if (c == '"') {
1317 			escflag = !escflag;
1318 			argp += len;
1319 			continue;
1320 		}
1321 		if (escflag) {
1322 			switch (c) {
1323 
1324 			case '\\':
1325 			case '*':
1326 			case '?':
1327 			case '[':
1328 				GROWSTAKTOP();
1329 				pushstak('\\');
1330 				break;
1331 			default:
1332 				;
1333 			}
1334 		}
1335 		while (len-- > 0) {
1336 			GROWSTAKTOP();
1337 			pushstak(*argp++);
1338 		}
1339 	}
1340 	zerostak();
1341 	staktop = (absstak(b));
1342 	return (&staktop[1]);	/* Point past first added null byte */
1343 }
1344 #endif
1345 #endif
1346 
1347 /*
1348  * If vsize is >= 0, copy vsize characters, else copy all.
1349  * The mbstate is reset from our caller, thus we do not need to
1350  * call mbtowc(NULL, NULL, 0)
1351  */
1352 static void
sizecpy(vsize,v,trimflag)1353 sizecpy(vsize, v, trimflag)
1354 	int	vsize;
1355 	Uchar	*v;
1356 	int	trimflag;
1357 {
1358 	int	c;
1359 
1360 	while (vsize && (c = *v) != '\0') {
1361 		wchar_t	wc;
1362 		int	clength;
1363 
1364 		if ((clength = mbtowc(&wc, (char *)v, MB_LEN_MAX)) <= 0) {
1365 			(void) mbtowc(NULL, NULL, 0);
1366 			clength = 1;
1367 		}
1368 		if (quote || (c == '\\' && trimflag)) {
1369 			GROWSTAKTOP();
1370 			pushstak('\\');
1371 		}
1372 		while (clength-- > 0) {
1373 			GROWSTAKTOP();
1374 			pushstak(*v++);
1375 		}
1376 		if (vsize > 0)
1377 			vsize--;
1378 	}
1379 }
1380 
1381 static Uchar *
trimcpy(argp,trimflag)1382 trimcpy(argp, trimflag)
1383 	Uchar	*argp;
1384 	int	trimflag;
1385 {
1386 	int	c;
1387 
1388 	(void) mbtowc(NULL, NULL, 0);
1389 	while ((c = *argp) != '\0') {
1390 		wchar_t		wc;
1391 		int		len;
1392 
1393 		if ((len = mbtowc(&wc, C argp, MB_LEN_MAX)) <= 0) {
1394 			(void) mbtowc(NULL, NULL, 0);
1395 			len = 1;
1396 		}
1397 		if (c == '\\' && trimflag) {
1398 			argp++;
1399 			if (*argp == 0) {
1400 				argp++;
1401 				continue;
1402 			}
1403 			if ((len = mbtowc(&wc, C argp, MB_LEN_MAX)) <= 0) {
1404 				(void) mbtowc(NULL, NULL, 0);
1405 				len = 1;
1406 			}
1407 		}
1408 		while (len-- > 0) {
1409 			GROWSTAKTOP();
1410 			pushstak(*argp++);
1411 		}
1412 	}
1413 	return (argp);
1414 }
1415