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