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/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved  	*/
28
29#pragma ident	"%Z%%M%	%I%	%E% SMI"
30/*
31 * UNIX shell
32 */
33
34#include	"defs.h"
35#include	"sym.h"
36#include	<wait.h>
37
38static unsigned char	quote;	/* used locally */
39static unsigned char	quoted;	/* used locally */
40static int getch();
41static void comsubst(int);
42static void flush(int);
43
44static void
45copyto(unsigned char endch, int trimflag)
46/* trimflag - flag to check if argument will be trimmed */
47{
48	unsigned int	c;
49	unsigned int 	d;
50	unsigned char *pc;
51
52	while ((c = getch(endch, trimflag)) != endch && c)
53		if (quote) {
54			if(c == '\\') { /* don't interpret next character */
55				if (staktop >= brkend)
56					growstak(staktop);
57				pushstak(c);
58				d = readwc();
59				if(!escchar(d)) { /* both \ and following
60						     character are quoted if next
61						     character is not $, `, ", or \*/
62					if (staktop >= brkend)
63						growstak(staktop);
64					pushstak('\\');
65					if (staktop >= brkend)
66						growstak(staktop);
67					pushstak('\\');
68					pc = readw(d);
69					/* push entire multibyte char */
70					while(*pc) {
71						if (staktop >= brkend)
72							growstak(staktop);
73						pushstak(*pc++);
74					}
75				} else {
76					pc = readw(d);
77					/* d might be NULL */
78					/* Evenif d is NULL, we have to save it */
79					if (*pc) {
80						while (*pc) {
81							if (staktop >= brkend)
82								growstak(staktop);
83							pushstak(*pc++);
84						}
85					} else {
86						if (staktop >= brkend)
87							growstak(staktop);
88						pushstak(*pc);
89					}
90				}
91			} else { /* push escapes onto stack to quote characters */
92				pc = readw(c);
93				if (staktop >= brkend)
94					growstak(staktop);
95				pushstak('\\');
96				while(*pc) {
97					if (staktop >= brkend)
98						growstak(staktop);
99					pushstak(*pc++);
100				}
101			}
102		} else if(c == '\\') {
103			c = readwc(); /* get character to be escaped */
104			if (staktop >= brkend)
105				growstak(staktop);
106			pushstak('\\');
107			pc = readw(c);
108			/* c might be NULL */
109			/* Evenif c is NULL, we have to save it */
110			if (*pc) {
111				while (*pc) {
112					if (staktop >= brkend)
113						growstak(staktop);
114					pushstak(*pc++);
115				}
116			} else {
117				if (staktop >= brkend)
118					growstak(staktop);
119				pushstak(*pc);
120			}
121		} else {
122			pc = readw(c);
123			while (*pc) {
124				if (staktop >= brkend)
125					growstak(staktop);
126				pushstak(*pc++);
127			}
128		}
129	if (staktop >= brkend)
130			growstak(staktop);
131	zerostak();
132	if (c != endch)
133		error(badsub);
134}
135
136static void
137skipto(unsigned char endch)
138{
139	/*
140	 * skip chars up to }
141	 */
142	unsigned int	c;
143
144	while ((c = readwc()) && c != endch)
145	{
146		switch (c)
147		{
148		case SQUOTE:
149			skipto(SQUOTE);
150			break;
151
152		case DQUOTE:
153			skipto(DQUOTE);
154			break;
155
156		case DOLLAR:
157			if (readwc() == BRACE)
158				skipto('}');
159		}
160	}
161	if (c != endch)
162		error(badsub);
163}
164
165static
166int getch(endch, trimflag)
167unsigned char	endch;
168int trimflag; /* flag to check if an argument is going to be trimmed, here document
169		 output is never trimmed
170	 */
171{
172	unsigned int	d;
173	int atflag;  /* flag to check if $@ has already been seen within double
174		        quotes */
175retry:
176	d = readwc();
177	if (!subchar(d))
178		return(d);
179
180	if (d == DOLLAR)
181	{
182		unsigned int c;
183
184		if ((c = readwc(), dolchar(c)))
185		{
186			struct namnod *n = (struct namnod *)NIL;
187			int		dolg = 0;
188			BOOL		bra;
189			BOOL		nulflg;
190			unsigned char	*argp, *v;
191			unsigned char		idb[2];
192			unsigned char		*id = idb;
193
194			if (bra = (c == BRACE))
195				c = readwc();
196			if (letter(c))
197			{
198				argp = (unsigned char *)relstak();
199				while (alphanum(c))
200				{
201					if (staktop >= brkend)
202						growstak(staktop);
203					pushstak(c);
204					c = readwc();
205				}
206				if (staktop >= brkend)
207					growstak(staktop);
208				zerostak();
209				n = lookup(absstak(argp));
210				setstak(argp);
211				if (n->namflg & N_FUNCTN)
212					error(badsub);
213				v = n->namval;
214				id = (unsigned char *)n->namid;
215				peekc = c | MARK;
216			}
217			else if (digchar(c))
218			{
219				*id = c;
220				idb[1] = 0;
221				if (astchar(c))
222				{
223					if(c == '@' && !atflag && quote) {
224						quoted--;
225						atflag = 1;
226					}
227					dolg = 1;
228					c = '1';
229				}
230				c -= '0';
231				v = ((c == 0) ? cmdadr : ((int)c <= dolc) ? dolv[c] : (unsigned char *)(dolg = 0));
232			}
233			else if (c == '$')
234				v = pidadr;
235			else if (c == '!')
236				v = pcsadr;
237			else if (c == '#')
238			{
239				itos(dolc);
240				v = numbuf;
241			}
242			else if (c == '?')
243			{
244				itos(retval);
245				v = numbuf;
246			}
247			else if (c == '-')
248				v = flagadr;
249			else if (bra)
250				error(badsub);
251			else
252				goto retry;
253			c = readwc();
254			if (c == ':' && bra)	/* null and unset fix */
255			{
256				nulflg = 1;
257				c = readwc();
258			}
259			else
260				nulflg = 0;
261			if (!defchar(c) && bra)
262				error(badsub);
263			argp = 0;
264			if (bra)
265			{
266				if (c != '}')
267				{
268					argp = (unsigned char *)relstak();
269					if ((v == 0 || (nulflg && *v == 0)) ^ (setchar(c)))
270						copyto('}', trimflag);
271					else
272						skipto('}');
273					argp = absstak(argp);
274				}
275			}
276			else
277			{
278				peekc = c | MARK;
279				c = 0;
280			}
281			if (v && (!nulflg || *v))
282			{
283
284				if (c != '+')
285				{
286					for (;;)
287					{
288						if (*v == 0 && quote) {
289							if (staktop >= brkend)
290								growstak(staktop);
291							pushstak('\\');
292							if (staktop >= brkend)
293								growstak(staktop);
294							pushstak('\0');
295						} else {
296							while (c = *v) {
297								wchar_t 	wc;
298								int 	length;
299								if ((length = mbtowc(&wc, (char *)v, MB_LEN_MAX)) <= 0)
300									length = 1;
301
302								if(quote || (c == '\\' && trimflag)) {
303									if (staktop >= brkend)
304										growstak(staktop);
305									pushstak('\\');
306								}
307								while(length-- > 0) {
308									if (staktop >= brkend)
309										growstak(staktop);
310									pushstak(*v++);
311								}
312							}
313						}
314
315						if (dolg == 0 || (++dolg > dolc))
316							break;
317						else /* $* and $@ expansion */
318						{
319							v = dolv[dolg];
320							if(*id == '*' && quote) {
321/* push quoted space so that " $* " will not be broken into separate arguments */
322								if (staktop >= brkend)
323									growstak(staktop);
324								pushstak('\\');
325							}
326							if (staktop >= brkend)
327								growstak(staktop);
328							pushstak(' ');
329						}
330					}
331				}
332			}
333			else if (argp)
334			{
335				if (c == '?') {
336					if(trimflag)
337						trim(argp);
338					failed(id, *argp ? (const char *)argp :
339					    badparam);
340				}
341				else if (c == '=')
342				{
343					if (n)
344					{
345						int strlngth = staktop - stakbot;
346						unsigned char *savptr = fixstak();
347						unsigned char *newargp;
348					/*
349					 * copy word onto stack, trim it, and then
350					 * do assignment
351					 */
352						usestak();
353						while(c = *argp) {
354							wchar_t 	wc;
355							int 		len;
356
357							if ((len = mbtowc(&wc, (char *)argp, MB_LEN_MAX)) <= 0)
358								len = 1;
359
360							if(c == '\\' && trimflag) {
361								argp++;
362								if (*argp == 0) {
363									argp++;
364									continue;
365								}
366								if ((len = mbtowc(&wc, (char *)argp, MB_LEN_MAX)) <= 0)
367									len = 1;
368							}
369							while(len-- > 0) {
370								if (staktop >= brkend)
371									growstak(staktop);
372								pushstak(*argp++);
373							}
374						}
375						newargp = fixstak();
376						assign(n, newargp);
377						tdystak(savptr);
378						(void) memcpystak(stakbot, savptr, strlngth);
379						staktop = stakbot + strlngth;
380					}
381					else
382						error(badsub);
383				}
384			}
385			else if (flags & setflg)
386				failed(id, unset);
387			goto retry;
388		}
389		else
390			peekc = c | MARK;
391	}
392	else if (d == endch)
393		return(d);
394	else if (d == SQUOTE)
395	{
396		comsubst(trimflag);
397		goto retry;
398	}
399	else if (d == DQUOTE && trimflag)
400	{
401		if(!quote) {
402			atflag = 0;
403			quoted++;
404		}
405		quote ^= QUOTE;
406		goto retry;
407	}
408	return(d);
409}
410
411unsigned char *
412macro(as)
413unsigned char	*as;
414{
415	/*
416	 * Strip "" and do $ substitution
417	 * Leaves result on top of stack
418	 */
419	BOOL	savqu = quoted;
420	unsigned char	savq = quote;
421	struct filehdr	fb;
422
423	push(&fb);
424	estabf(as);
425	usestak();
426	quote = 0;
427	quoted = 0;
428	copyto(0, 1);
429	pop();
430	if (quoted && (stakbot == staktop)) {
431		if (staktop >= brkend)
432			growstak(staktop);
433		pushstak('\\');
434		if (staktop >= brkend)
435			growstak(staktop);
436		pushstak('\0');
437/*
438 * above is the fix for *'.c' bug
439 */
440	}
441	quote = savq;
442	quoted = savqu;
443	return(fixstak());
444}
445/* Save file descriptor for command substitution */
446int savpipe = -1;
447
448static void
449comsubst(int trimflag)
450/* trimflag - used to determine if argument will later be trimmed */
451{
452	/*
453	 * command substn
454	 */
455	struct fileblk	cb;
456	unsigned int	d;
457	int strlngth = staktop - stakbot;
458	unsigned char *oldstaktop;
459	unsigned char *savptr = fixstak();
460	unsigned char	*pc;
461
462	usestak();
463	while ((d = readwc()) != SQUOTE && d) {
464		if(d == '\\') {
465			d = readwc();
466			if(!escchar(d) || (d == '"' && !quote)) {
467		/* trim quotes for `, \, or " if command substitution is within
468		   double quotes */
469				if (staktop >= brkend)
470					growstak(staktop);
471				pushstak('\\');
472			}
473		}
474		pc = readw(d);
475		/* d might be NULL */
476		if (*pc) {
477			while (*pc) {
478				if (staktop >= brkend)
479					growstak(staktop);
480				pushstak(*pc++);
481			}
482		} else {
483			if (staktop >= brkend)
484				growstak(staktop);
485			pushstak(*pc);
486		}
487	}
488	{
489		unsigned char	*argc;
490
491		argc = fixstak();
492		push(&cb);
493		estabf(argc);  /* read from string */
494	}
495	{
496		struct trenod	*t;
497		int		pv[2];
498
499		/*
500		 * this is done like this so that the pipe
501		 * is open only when needed
502		 */
503	 	t = makefork(FPOU, cmd(EOFSYM, MTFLG | NLFLG ));
504		chkpipe(pv);
505		savpipe = pv[OTPIPE];
506		initf(pv[INPIPE]); /* read from pipe */
507		execute(t, XEC_NOSTOP, (int)(flags & errflg), 0, pv);
508		close(pv[OTPIPE]);
509		savpipe = -1;
510	}
511	tdystak(savptr);
512	(void) memcpystak(stakbot, savptr, strlngth);
513	oldstaktop = staktop = stakbot + strlngth;
514	while (d = readwc()) {
515		if(quote || (d == '\\' && trimflag)) {
516			unsigned char *rest;
517			/* quote output from command subst. if within double
518			   quotes or backslash part of output */
519			rest = readw(d);
520			if (staktop >= brkend)
521				growstak(staktop);
522			pushstak('\\');
523			while(d = *rest++) {
524			/* Pick up all of multibyte character */
525				if (staktop >= brkend)
526					growstak(staktop);
527				pushstak(d);
528			}
529		}
530		else {
531			pc = readw(d);
532			while (*pc) {
533				if (staktop >= brkend)
534					growstak(staktop);
535				pushstak(*pc++);
536			}
537		}
538	}
539	{
540		extern pid_t parent;
541		int stat;
542		int rc;
543		int	ret = 0;
544
545		while ((ret = waitpid(parent,&stat,0)) != parent) {
546			/* break out if waitpid(2) has failed */
547			if (ret == -1)
548				break;
549		}
550		if (WIFEXITED(stat))
551			rc = WEXITSTATUS(stat);
552		else
553			rc = (WTERMSIG(stat) | SIGFLG);
554		if (rc && (flags & errflg))
555			exitsh(rc);
556		exitval = rc;
557		flags |= eflag;
558		exitset();
559	}
560	while (oldstaktop != staktop)
561	{ /* strip off trailing newlines from command substitution only */
562		if ((*--staktop) != NL)
563		{
564			++staktop;
565			break;
566		} else if(quote)
567			staktop--; /* skip past backslashes if quoting */
568	}
569	pop();
570}
571
572#define CPYSIZ	512
573
574void
575subst(int in, int ot)
576{
577	unsigned int	c;
578	struct fileblk	fb;
579	int	count = CPYSIZ;
580	unsigned char	*pc;
581
582	push(&fb);
583	initf(in);
584	/*
585	 * DQUOTE used to stop it from quoting
586	 */
587	while (c = (getch(DQUOTE, 0))) /* read characters from here document
588				       and interpret them */
589	{
590		if(c == '\\') {
591			c = readwc(); /* check if character in here document is
592					escaped */
593			if(!escchar(c) || c == '"') {
594				if (staktop >= brkend)
595					growstak(staktop);
596				pushstak('\\');
597			}
598		}
599		pc = readw(c);
600		/* c might be NULL */
601		if (*pc) {
602			while (*pc) {
603				if (staktop >= brkend)
604					growstak(staktop);
605				pushstak(*pc++);
606			}
607		} else {
608			if (staktop >= brkend)
609				growstak(staktop);
610			pushstak(*pc);
611		}
612		if (--count == 0)
613		{
614			flush(ot);
615			count = CPYSIZ;
616		}
617	}
618	flush(ot);
619	pop();
620}
621
622static void
623flush(int ot)
624{
625	write(ot, stakbot, staktop - stakbot);
626	if (flags & execpr)
627		write(output, stakbot, staktop - stakbot);
628	staktop = stakbot;
629}
630