OpenSolaris_b135/cmd/sh/macro.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"
/*
 * UNIX shell
 */

#include	"defs.h"
#include	"sym.h"
#include	<wait.h>

static unsigned char	quote;	/* used locally */
static unsigned char	quoted;	/* used locally */
static int getch();
static void comsubst(int);
static void flush(int);

static void
copyto(unsigned char endch, int trimflag)
/* trimflag - flag to check if argument will be trimmed */
{
	unsigned int	c;
	unsigned int 	d;
	unsigned char *pc;

	while ((c = getch(endch, trimflag)) != endch && c)
		if (quote) {
			if(c == '\\') { /* don't interpret next character */
				if (staktop >= brkend)
					growstak(staktop);
				pushstak(c);
				d = readwc();
				if(!escchar(d)) { /* both \ and following
						     character are quoted if next
						     character is not $, `, ", or \*/
					if (staktop >= brkend)
						growstak(staktop);
					pushstak('\\'); 
					if (staktop >= brkend)
						growstak(staktop);
					pushstak('\\');
					pc = readw(d); 
					/* push entire multibyte char */
					while(*pc) {
						if (staktop >= brkend)
							growstak(staktop);
						pushstak(*pc++);
					}
				} else {
					pc = readw(d);
					/* d might be NULL */
					/* Evenif d is NULL, we have to save it */
					if (*pc) {
						while (*pc) {
							if (staktop >= brkend)
								growstak(staktop);
							pushstak(*pc++);
						}
					} else {
						if (staktop >= brkend)
							growstak(staktop);
						pushstak(*pc);
					}
				}
			} else { /* push escapes onto stack to quote characters */
				pc = readw(c);
				if (staktop >= brkend)
					growstak(staktop); 
				pushstak('\\');
				while(*pc) {
					if (staktop >= brkend)
						growstak(staktop);
					pushstak(*pc++);
				}
			}
		} else if(c == '\\') {
			c = readwc(); /* get character to be escaped */
			if (staktop >= brkend)
				growstak(staktop); 
			pushstak('\\');
			pc = readw(c);
			/* c might be NULL */
			/* Evenif c is NULL, we have to save it */
			if (*pc) {
				while (*pc) {
					if (staktop >= brkend)
						growstak(staktop); 
					pushstak(*pc++);
				}
			} else {
				if (staktop >= brkend)
					growstak(staktop);
				pushstak(*pc);
			}
		} else {
			pc = readw(c);
			while (*pc) {
				if (staktop >= brkend)
					growstak(staktop); 
				pushstak(*pc++);
			}
		}
	if (staktop >= brkend)
			growstak(staktop); 
	zerostak();
	if (c != endch)
		error(badsub);
}

static void
skipto(unsigned char endch)
{
	/*
	 * skip chars up to }
	 */
	unsigned int	c;

	while ((c = readwc()) && c != endch)
	{
		switch (c)
		{
		case SQUOTE:
			skipto(SQUOTE);
			break;

		case DQUOTE:
			skipto(DQUOTE);
			break;

		case DOLLAR:
			if (readwc() == BRACE)
				skipto('}');
		}
	}
	if (c != endch)
		error(badsub);
}

static
int getch(endch, trimflag)
unsigned char	endch;
int trimflag; /* flag to check if an argument is going to be trimmed, here document
		 output is never trimmed
	 */
{
	unsigned int	d;
	int atflag;  /* flag to check if $@ has already been seen within double 
		        quotes */
retry:
	d = readwc();
	if (!subchar(d))
		return(d);

	if (d == DOLLAR)
	{
		unsigned int c;

		if ((c = readwc(), dolchar(c)))
		{
			struct namnod *n = (struct namnod *)NIL;
			int		dolg = 0;
			BOOL		bra;
			BOOL		nulflg;
			unsigned char	*argp, *v;
			unsigned char		idb[2];
			unsigned char		*id = idb;

			if (bra = (c == BRACE))
				c = readwc();
			if (letter(c))
			{
				argp = (unsigned char *)relstak();
				while (alphanum(c))
				{
					if (staktop >= brkend)
						growstak(staktop);
					pushstak(c);
					c = readwc();
				}
				if (staktop >= brkend)
					growstak(staktop);
				zerostak();
				n = lookup(absstak(argp));
				setstak(argp);
				if (n->namflg & N_FUNCTN)
					error(badsub);
				v = n->namval;
				id = (unsigned char *)n->namid;
				peekc = c | MARK;
			}
			else if (digchar(c))
			{
				*id = c;
				idb[1] = 0;
				if (astchar(c))
				{
					if(c == '@' && !atflag && quote) {
						quoted--;
						atflag = 1;
					}
					dolg = 1;
					c = '1';
				}
				c -= '0';
				v = ((c == 0) ? cmdadr : ((int)c <= dolc) ? dolv[c] : (unsigned char *)(dolg = 0));
			}
			else if (c == '$')
				v = pidadr;
			else if (c == '!')
				v = pcsadr;
			else if (c == '#')
			{
				itos(dolc);
				v = numbuf;
			}
			else if (c == '?')
			{
				itos(retval);
				v = numbuf;
			}
			else if (c == '-')
				v = flagadr;
			else if (bra)
				error(badsub);
			else
				goto retry;
			c = readwc();
			if (c == ':' && bra)	/* null and unset fix */
			{
				nulflg = 1;
				c = readwc();
			}
			else
				nulflg = 0;
			if (!defchar(c) && bra)
				error(badsub);
			argp = 0;
			if (bra)
			{
				if (c != '}')
				{
					argp = (unsigned char *)relstak();
					if ((v == 0 || (nulflg && *v == 0)) ^ (setchar(c)))
						copyto('}', trimflag);
					else
						skipto('}');
					argp = absstak(argp);
				}
			}
			else
			{
				peekc = c | MARK;
				c = 0;
			}
			if (v && (!nulflg || *v))
			{

				if (c != '+')
				{
					for (;;)
					{
						if (*v == 0 && quote) {
							if (staktop >= brkend)
								growstak(staktop);
							pushstak('\\');
							if (staktop >= brkend)
								growstak(staktop);
							pushstak('\0');
						} else {
							while (c = *v) {
								wchar_t 	wc;
								int 	length;
								if ((length = mbtowc(&wc, (char *)v, MB_LEN_MAX)) <= 0)
									length = 1;

								if(quote || (c == '\\' && trimflag)) {
									if (staktop >= brkend)
										growstak(staktop);
									pushstak('\\');
								}
								while(length-- > 0) {
									if (staktop >= brkend)
										growstak(staktop);
									pushstak(*v++);
								}
							}
						}

						if (dolg == 0 || (++dolg > dolc))
							break;
						else /* $* and $@ expansion */
						{
							v = dolv[dolg];
							if(*id == '*' && quote) {
/* push quoted space so that " $* " will not be broken into separate arguments */
								if (staktop >= brkend)
									growstak(staktop);
								pushstak('\\');
							}
							if (staktop >= brkend)
								growstak(staktop);
							pushstak(' ');
						}
					}
				}
			}
			else if (argp)
			{
				if (c == '?') {
					if(trimflag)
						trim(argp);
					failed(id, *argp ? (const char *)argp :
					    badparam);
				}
				else if (c == '=')
				{
					if (n)
					{
						int strlngth = staktop - stakbot;
						unsigned char *savptr = fixstak();
						unsigned char *newargp;
					/*
					 * copy word onto stack, trim it, and then
					 * do assignment 
					 */
						usestak();
						while(c = *argp) {
							wchar_t 	wc;
							int 		len;

							if ((len = mbtowc(&wc, (char *)argp, MB_LEN_MAX)) <= 0)
								len = 1;
								
							if(c == '\\' && trimflag) {
								argp++;
								if (*argp == 0) {
									argp++;
									continue;
								}
								if ((len = mbtowc(&wc, (char *)argp, MB_LEN_MAX)) <= 0)
									len = 1;
							}
							while(len-- > 0) {
								if (staktop >= brkend)
									growstak(staktop);
								pushstak(*argp++);
							}
						}
						newargp = fixstak();
						assign(n, newargp);
						tdystak(savptr);
						(void) memcpystak(stakbot, savptr, strlngth);
						staktop = stakbot + strlngth;
					}
					else
						error(badsub);
				}
			}
			else if (flags & setflg)
				failed(id, unset);
			goto retry;
		}
		else
			peekc = c | MARK;
	}
	else if (d == endch)
		return(d);
	else if (d == SQUOTE)
	{
		comsubst(trimflag);
		goto retry;
	}
	else if (d == DQUOTE && trimflag)
	{
		if(!quote) {
			atflag = 0;
			quoted++;
		}
		quote ^= QUOTE;
		goto retry;
	}
	return(d);
}

unsigned char *
macro(as)
unsigned char	*as;
{
	/*
	 * Strip "" and do $ substitution
	 * Leaves result on top of stack
	 */
	BOOL	savqu = quoted;
	unsigned char	savq = quote;
	struct filehdr	fb;

	push(&fb);
	estabf(as);
	usestak();
	quote = 0;
	quoted = 0;
	copyto(0, 1);
	pop();
	if (quoted && (stakbot == staktop)) {
		if (staktop >= brkend)
			growstak(staktop);
		pushstak('\\');
		if (staktop >= brkend)
			growstak(staktop);
		pushstak('\0');
/*
 * above is the fix for *'.c' bug
 */
	}
	quote = savq;
	quoted = savqu;
	return(fixstak());
}
/* Save file descriptor for command substitution */
int savpipe = -1;

static void
comsubst(int trimflag)
/* trimflag - used to determine if argument will later be trimmed */
{
	/*
	 * command substn
	 */
	struct fileblk	cb;
	unsigned int	d;
	int strlngth = staktop - stakbot;
	unsigned char *oldstaktop;
	unsigned char *savptr = fixstak();
	unsigned char	*pc;

	usestak();
	while ((d = readwc()) != SQUOTE && d) {
		if(d == '\\') {
			d = readwc();
			if(!escchar(d) || (d == '"' && !quote)) {
		/* trim quotes for `, \, or " if command substitution is within
		   double quotes */
				if (staktop >= brkend)
					growstak(staktop);
				pushstak('\\');
			}
		}
		pc = readw(d);
		/* d might be NULL */
		if (*pc) {
			while (*pc) {
				if (staktop >= brkend)
					growstak(staktop);
				pushstak(*pc++);
			}
		} else {
			if (staktop >= brkend)
				growstak(staktop);
			pushstak(*pc);
		}
	}
	{
		unsigned char	*argc;

		argc = fixstak(); 
		push(&cb);
		estabf(argc);  /* read from string */
	}
	{
		struct trenod	*t;
		int		pv[2];

		/*
		 * this is done like this so that the pipe
		 * is open only when needed
		 */
	 	t = makefork(FPOU, cmd(EOFSYM, MTFLG | NLFLG ));
		chkpipe(pv);
		savpipe = pv[OTPIPE];
		initf(pv[INPIPE]); /* read from pipe */
		execute(t, XEC_NOSTOP, (int)(flags & errflg), 0, pv);
		close(pv[OTPIPE]);
		savpipe = -1;
	}
	tdystak(savptr);
	(void) memcpystak(stakbot, savptr, strlngth);
	oldstaktop = staktop = stakbot + strlngth;
	while (d = readwc()) {
		if(quote || (d == '\\' && trimflag)) {
			unsigned char *rest;
			/* quote output from command subst. if within double 
			   quotes or backslash part of output */
			rest = readw(d);
			if (staktop >= brkend)
				growstak(staktop);
			pushstak('\\');
			while(d = *rest++) {
			/* Pick up all of multibyte character */
				if (staktop >= brkend)
					growstak(staktop);
				pushstak(d);
			}
		}
		else {
			pc = readw(d);
			while (*pc) {
				if (staktop >= brkend)
					growstak(staktop);
				pushstak(*pc++);
			}
		}
	}
	{
		extern pid_t parent;
		int stat;
		int rc;
		int	ret = 0;

		while ((ret = waitpid(parent,&stat,0)) != parent) {
			/* break out if waitpid(2) has failed */
			if (ret == -1)
				break;
		}
		if (WIFEXITED(stat))
			rc = WEXITSTATUS(stat);
		else
			rc = (WTERMSIG(stat) | SIGFLG);
		if (rc && (flags & errflg))
			exitsh(rc);
		exitval = rc;
		flags |= eflag;
		exitset();
	}
	while (oldstaktop != staktop)
	{ /* strip off trailing newlines from command substitution only */
		if ((*--staktop) != NL)
		{
			++staktop;
			break;
		} else if(quote)
			staktop--; /* skip past backslashes if quoting */ 
	}
	pop();
}

#define CPYSIZ	512

void
subst(int in, int ot)
{
	unsigned int	c;
	struct fileblk	fb;
	int	count = CPYSIZ;
	unsigned char	*pc;

	push(&fb);
	initf(in);
	/*
	 * DQUOTE used to stop it from quoting
	 */
	while (c = (getch(DQUOTE, 0))) /* read characters from here document 
				       and interpret them */
	{
		if(c == '\\') {
			c = readwc(); /* check if character in here document is 
					escaped */
			if(!escchar(c) || c == '"') {
				if (staktop >= brkend)
					growstak(staktop);
				pushstak('\\');
			}
		}
		pc = readw(c);
		/* c might be NULL */
		if (*pc) {
			while (*pc) {
				if (staktop >= brkend)
					growstak(staktop);
				pushstak(*pc++);
			}
		} else {
			if (staktop >= brkend)
				growstak(staktop);
			pushstak(*pc);
		}
		if (--count == 0)
		{
			flush(ot);
			count = CPYSIZ;
		}
	}
	flush(ot);
	pop();
}

static void
flush(int ot)
{
	write(ot, stakbot, staktop - stakbot);
	if (flags & execpr)
		write(output, stakbot, staktop - stakbot);
	staktop = stakbot;
}