/*
 * command tree climbing
 */

#ifndef lint
static char *RCSid = "$Id: tree.c,v 1.3 93/05/05 21:17:14 sjg Exp $";
#endif

#include "stdh.h"
#include <errno.h>
#include <setjmp.h>
#include <memory.h>

#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#include "sh.h"

#define	FSTRING	(FILE*)NULL

static int      tputc       ARGS((int c, FILE *f));
static void     tputC       ARGS((int c, FILE *f));
static void     tputS       ARGS((char *wp, FILE *f));
static struct ioword **iocopy ARGS((struct ioword **iow, Area *ap));
static void     iofree      ARGS((struct ioword **iow, Area *ap));

/*
 * print a command tree
 */

void
ptree(t, f)
	register struct op *t;
	register FILE *f;
{
	register char **w;
	struct ioword **ioact;
	struct op *t1;

 Chain:
	if (t == NULL)
		return;
	switch (t->type) {
	  case TCOM:
		for (w = t->vars; *w != NULL; )
			fptreef(f, "%S ", *w++);
		for (w = t->args; *w != NULL; )
			fptreef(f, "%S ", *w++);
		break;
	  case TEXEC:
		t = t->left;
		goto Chain;
	  case TPAREN:
		fptreef(f, "(%T)", t->left);
		break;
	  case TPIPE:
		fptreef(f, "%T| ", t->left);
		t = t->right;
		goto Chain;
	  case TLIST:
		fptreef(f, "%T", t->left);
		fptreef(f, "%;");
		t = t->right;
		goto Chain;
	  case TOR:
	  case TAND:
		fptreef(f, "%T", t->left);
		fptreef(f, "%s %T", (t->type==TOR) ? "||" : "&&", t->right);
		break;
	  case TFOR:
		fptreef(f, "for %s ", t->str);
		if (t->vars != NULL) {
			fptreef(f, "in ");
			for (w = t->vars; *w; )
				fptreef(f, "%S ", *w++);
			fptreef(f, "%;");
		}
		fptreef(f, "do %T", t->left);
		fptreef(f, "%;done ");
		break;
	  case TCASE:
		fptreef(f, "case %S in%;", t->str);
		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
			fptreef(f, "(");
			for (w = t1->vars; *w != NULL; w++) {
				fptreef(f, "%S", *w);
				fptreef(f, "%c", (w[1] != NULL) ? '|' : ')');
			}
			fptreef(f, " %T;;%;", t1->left);
		}
		fptreef(f, "esac ");
		break;
	  case TIF:
		fptreef(f, "if %T", t->left);
		fptreef(f, "%;");
		t = t->right;
		if (t->left != NULL) {
			fptreef(f, "then %T", t->left);
			fptreef(f, "%;");
		}
		if (t->right != NULL) {
			fptreef(f, "else %T", t->right);
			fptreef(f, "%;");
		}
		fptreef(f, "fi ");
		break;
	  case TWHILE:
	  case TUNTIL:
		fptreef(f, "%s %T",
			(t->type==TWHILE) ? "while" : "until",
			t->left);
		fptreef(f, "%;do %T", t->right);
		fptreef(f, "%;done ");
		break;
	  case TBRACE:
		fptreef(f, "{%;%T", t->left);
		fptreef(f, "%;} ");
		break;
	  case TASYNC:
		fptreef(f, "%T&", t->left);
		break;
	  case TFUNCT:
		fptreef(f, "function %s %T", t->str, t->left);
		break;
	  case TTIME:
		fptreef(f, "time %T", t->left);
		break;
	  default:
		fptreef(f, "<botch>");
		break;
	}
	if ((ioact = t->ioact) != NULL)
		while (*ioact != NULL)
			pioact(f, *ioact++);
}

pioact(f, iop)
	register FILE *f;
	register struct ioword *iop;
{
	register int flag = iop->flag;
	if (iop->unit > 1)
		fptreef(f, "%c", '0' + iop->unit );

	switch(flag&IOTYPE) {
	case IOREAD:
		fptreef(f, "< ");
		break;
	case IOHERE:
		if (flag&IOSKIP)
			fptreef(f, "<<- ");
		else
			fptreef(f, "<< ");
		if (!(flag&IOEVAL))
			fptreef(f, "'");
		break;
	case IOCAT:
		fptreef(f, ">> ");
		break;
	case IOWRITE:
		if (flag&IOCLOB)
			fptreef(f, ">! ");
		else
			fptreef(f, "> ");
		break;
	case IODUP:			/* Needs help */
		if (iop->unit == 0)
			fptreef(f, "<&");
		else
			fptreef(f, ">&");
		break;
	}

	if ((flag&IOTYPE) == IOHERE) {
		if (flag&IOEVAL)
			fptreef(f, "%s ", iop->name);
		else
			fptreef(f, "%s' ", iop->name);
	} else {
		fptreef(f, "%S ", iop->name);
	}
}


/*
 * variants of fputc, fputs for ptreef and snptreef
 */

static	char   *snpf_s;		/* snptreef string */
static	int	snpf_n;		/* snptreef length */

static int
tputc(c, f)
	int c;
	register FILE *f;
{
	if (f != NULL)
		putc(c, f);
	else
		if (--snpf_n >= 0)
			*snpf_s++ = c;
	return c;
}

static void
tputC(c, f)
	register int c;
	register FILE *f;
{
	if ((c&0x60) == 0) {		/* C0|C1 */
		tputc((c&0x80) ? '$' : '^', f);
		tputc(((c&0x7F)|0x40), f);
	} else if ((c&0x7F) == 0x7F) {	/* DEL */
		tputc((c&0x80) ? '$' : '^', f);
		tputc('?', f);
	} else
		tputc(c, f);
}

static void
tputS(wp, f)
	register char *wp;
	register FILE *f;
{
	register int c, quoted=0;

	while (1)
		switch ((c = *wp++)) {
		  case EOS:
			return;
		  case CHAR:
			tputC(*wp++, f);
			break;
		  case QCHAR:
			if (!quoted)
				tputc('\\', f);
			tputC(*wp++, f);
			break;
		  case OQUOTE:
		  	quoted = 1;
			tputc('"', f);
			break;
		  case CQUOTE:
			quoted = 0;
			tputc('"', f);
			break;
		  case OSUBST:
			tputc('$', f);
			tputc('{', f);
			while ((c = *wp++) != 0)
				tputc(c, f);
			if (*wp != CSUBST)
				tputC(*wp++, f);
			break;
		  case CSUBST:
			tputc('}', f);
			break;
		  case COMSUB:
			tputc('$', f);
			tputc('(', f);
			while (*wp != 0)
				tputC(*wp++, f);
			tputc(')', f);
			break;
		}
}

/*
 * this is the _only_ way to reliably handle
 * variable args with an ANSI compiler
 */
#ifdef __STDC__
/* VARARGS */ int
fptreef(FILE *f, char *fmt, ...)
{
#else
fptreef(va_alist) 
  va_dcl
{
  FILE *f;
  char *fmt;
#endif
  va_list	va;

#ifdef __STDC__
  va_start(va, fmt);
#else
  va_start(va);
  f = va_arg(va, FILE *);
  fmt = va_arg(va, char *);
#endif
  
  vfptreef(f, fmt, va);
  va_end(va);
  return 0;
}

/* VARARGS */ int
#ifdef __STDC__
snptreef(char *s, int n, char *fmt, ...)
{
#else
snptreef(va_alist)
  va_dcl
{
  char *s;
  int n;
  char *fmt;
#endif
  va_list va;

#ifdef __STDC__
  va_start(va, fmt);
#else
  va_start(va);
  s = va_arg(va, char *);
  n = va_arg(va, int);
  fmt = va_arg(va, char *);
#endif

  snpf_s = s;
  snpf_n = n;
  vfptreef(FSTRING, fmt, va);
  tputc('\0', FSTRING);
  va_end(va);
  return 0;
}


vfptreef(f, fmt, va)
	register FILE *f;
	register char *fmt;
	register va_list va;
{
	register int c;

	while ((c = *fmt++))
	    if (c == '%') {
		register long n;
		register char *p;
		int neg;

		switch ((c = *fmt++)) {
		  case 'c':
			tputc(va_arg(va, int), f);
			break;
		  case 's':
			p = va_arg(va, char *);
			while (*p)
				tputc(*p++, f);
			break;
		  case 'S':	/* word */
			p = va_arg(va, char *);
			tputS(p, f);
			break;
		  case 'd': case 'u': /* decimal */
			n = (c == 'd') ? va_arg(va, int) : va_arg(va, unsigned int);
			neg = c=='d' && n<0;
			p = ulton((neg) ? -n : n, 10);
			if (neg)
				*--p = '-';
			while (*p)
				tputc(*p++, f);
			break;
		  case 'T':	/* format tree */
			ptree(va_arg(va, struct op *), f);
			break;
		  case ';':	/* newline or ; */
			p = (f == FSTRING) ? "; " : "\n";
			while (*p)
				tputc(*p++, f);
			break;
		  default:
			tputc(c, f);
			break;
		}
	    } else
		tputc(c, f);
}

/*
 * copy tree (for function definition)
 */

static	struct ioword **iocopy();

struct op *
tcopy(t, ap)
	register struct op *t;
	Area *ap;
{
	register struct op *r;
	register char **tw, **rw;

	if (t == NULL)
		return NULL;

	r = (struct op *) alloc(sizeof(struct op), ap);

	r->type = t->type;

	/* this will copy function and for identifiers quite accidently */
	r->str = (t->str == NULL) ? NULL : wdcopy(t->str, ap);

	if (t->vars == NULL)
		r->vars = NULL;
	else {
		for (tw = t->vars; *tw++ != NULL; )
			;
		rw = r->vars = (char **)
			alloc((int)(tw - t->vars) * sizeof(*tw), ap);
		for (tw = t->vars; *tw != NULL; )
			*rw++ = wdcopy(*tw++, ap);
		*rw = NULL;
	}

	if (t->args == NULL)
		r->args = NULL;
	else {
		for (tw = t->args; *tw++ != NULL; )
			;
		rw = r->args = (char **)
			alloc((int)(tw - t->args) * sizeof(*tw), ap);
		for (tw = t->args; *tw != NULL; )
			*rw++ = wdcopy(*tw++, ap);
		*rw = NULL;
	}

	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);

	r->left = tcopy(t->left, ap);
	r->right = tcopy(t->right, ap);

	return r;
}

char *
wdcopy(wp, ap)
	char *wp;
	Area *ap;
{
	size_t len = wdscan(wp, EOS) - wp;
	return memcpy(alloc(len, ap), wp, len);
}

/* return the position of prefix c in wp plus 1 */
char *
wdscan(wp, c)
	register char *wp;
	register int c;
{
	register int nest = 0;

	while (1)
		switch (*wp++) {
		  case EOS:
			return wp;
		  case CHAR:
		  case QCHAR:
			wp++;
			break;
		  case OQUOTE:
		  case CQUOTE:
			break;
		  case OSUBST:
			nest++;
			while (*wp++ != 0)
				;
			if (*wp != CSUBST)
				wp++;
			break;
		  case CSUBST:
			if (c == CSUBST && nest == 0)
				return wp;
			nest--;
			break;
		  case COMSUB:
			while (*wp++ != 0)
				;
			break;
		}
}

static	struct ioword **
iocopy(iow, ap)
	register struct ioword **iow;
	Area *ap;
{
	register struct ioword **ior;
	register int i;

	for (ior = iow; *ior++ != NULL; )
		;
	ior = (struct ioword **) alloc((int)(ior - iow) * sizeof(*ior), ap);

	for (i = 0; iow[i] != NULL; i++) {
		register struct ioword *p, *q;

		p = iow[i];
		q = (struct ioword *) alloc(sizeof(*p), ap);
		ior[i] = q;
		*q = *p;
		if (p->name != NULL)
			q->name = wdcopy(p->name, ap);
	}
	ior[i] = NULL;

	return ior;
}

/*
 * free tree (for function definition)
 */

static	void iofree();

void
tfree(t, ap)
	register struct op *t;
	Area *ap;
{
	register char **w;

	if (t == NULL)
		return;

	if (t->str != NULL)
		afree((void*)t->str, ap);

	if (t->vars != NULL) {
		for (w = t->vars; *w != NULL; w++)
			afree((void*)*w, ap);
		afree((void*)t->vars, ap);
	}

	if (t->args != NULL) {
		for (w = t->args; *w != NULL; w++)
			afree((void*)*w, ap);
		afree((void*)t->args, ap);
	}

	if (t->ioact != NULL)
		iofree(t->ioact, ap);

	tfree(t->left, ap);
	tfree(t->right, ap);

	afree((void*)t, ap);
}

static	void
iofree(iow, ap)
	struct ioword **iow;
	Area *ap;
{
	register struct ioword **iop;
	register struct ioword *p;

	for (iop = iow; (p = *iop++) != NULL; ) {
		if (p->name != NULL)
			afree((void*)p->name, ap);
		afree((void*)p, ap);
	}
}

