xref: /illumos-gate/usr/src/cmd/sh/macro.c (revision d583b39bfb4e2571d3e41097c5c357ffe353ad45)
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 
38 static unsigned char	quote;	/* used locally */
39 static unsigned char	quoted;	/* used locally */
40 static int getch();
41 static void comsubst(int);
42 static void flush(int);
43 
44 static void
45 copyto(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 
136 static void
137 skipto(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 
165 static
166 int getch(endch, trimflag)
167 unsigned char	endch;
168 int 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 */
175 retry:
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 
411 unsigned char *
412 macro(as)
413 unsigned 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 */
446 int savpipe = -1;
447 
448 static void
449 comsubst(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 
574 void
575 subst(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 
622 static void
623 flush(int ot)
624 {
625 	write(ot, stakbot, staktop - stakbot);
626 	if (flags & execpr)
627 		write(output, stakbot, staktop - stakbot);
628 	staktop = stakbot;
629 }
630