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