xref: /titanic_44/usr/src/cmd/sh/macro.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
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 
43 static void
44 copyto(endch, trimflag)
45 int trimflag;  /* flag to check if argument will be trimmed */
46 register unsigned char	endch;
47 {
48 	register unsigned int	c;
49 	register unsigned int 	d;
50 	register 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
137 skipto(endch)
138 register unsigned char	endch;
139 {
140 	/*
141 	 * skip chars up to }
142 	 */
143 	register 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 	register 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 			register 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 								register 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 ? argp : (unsigned char *)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 							register 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 						memcpy(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 	register BOOL	savqu = quoted;
420 	register 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 comsubst(trimflag)
449 int trimflag; /* used to determine if argument will later be trimmed */
450 {
451 	/*
452 	 * command substn
453 	 */
454 	struct fileblk	cb;
455 	register unsigned int	d;
456 	int strlngth = staktop - stakbot;
457 	register 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 		register unsigned char	*argc;
489 
490 		argc = fixstak();
491 		push(&cb);
492 		estabf(argc);  /* read from string */
493 	}
494 	{
495 		register 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 	memcpy(stakbot, savptr, strlngth);
512 	oldstaktop = staktop = stakbot + strlngth;
513 	while (d = readwc()) {
514 		if(quote || (d == '\\' && trimflag)) {
515 			register 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 		register 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 subst(in, ot)
574 int	in, ot;
575 {
576 	register unsigned int	c;
577 	struct fileblk	fb;
578 	register 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 flush(ot)
622 {
623 	write(ot, stakbot, staktop - stakbot);
624 	if (flags & execpr)
625 		write(output, stakbot, staktop - stakbot);
626 	staktop = stakbot;
627 }
628