
/*
 *
 * text.c - textual representations of syntax trees
 *
 * This file is part of zsh, the Z shell.
 *
 * This software is Copyright 1992 by Paul Falstad
 *
 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
 * use this software as long as: there is no monetary profit gained
 * specifically from the use or reproduction of this software, it is not
 * sold, rented, traded or otherwise marketed, and this copyright notice is
 * included prominently in any copy made.
 *
 * The author make no claims as to the fitness or correctness of this software
 * for any use whatsoever, and it is provided as is. Any use of this software
 * is at the user's own risk.
 *
 */

#include "zsh.h"

static char *tptr, *tbuf, *tlim;
static int tsiz, tindent, tnewlins;

/* add a character to the text buffer */

void taddchr(c)			/**/
int c;
{
    *tptr++ = c;
    if (tptr == tlim) {
	if (!tbuf) {
	    tptr--;
	    return;
	}
	tbuf = realloc(tbuf, tsiz *= 2);
	tlim = tbuf + tsiz;
	tptr = tbuf + tsiz / 2;
    }
}

/* add a string to the text buffer */

void taddstr(s)			/**/
char *s;
{
    int sl = strlen(s);

    while (tptr + sl >= tlim) {
	int x = tptr - tbuf;

	if (!tbuf)
	    return;
	tbuf = realloc(tbuf, tsiz *= 2);
	tlim = tbuf + tsiz;
	tptr = tbuf + x;
    }
    strcpy(tptr, s);
    tptr += sl;
}

/* add an integer to the text buffer */

void taddint(x)			/**/
int x;
{
    char buf[10];

    sprintf(buf, "%d", x);
    taddstr(buf);
}

/* add a newline, or something equivalent, to the text buffer */

void taddnl()
{				/**/
    int t0;

    if (tnewlins) {
	taddchr('\n');
	for (t0 = 0; t0 != tindent; t0++)
	    taddchr('\t');
    } else
	taddstr("; ");
}

/* get a permanent textual representation of n */

char *getpermtext(n)		/**/
struct node *n;
{
    tnewlins = 1;
    tbuf = (char *)zalloc(tsiz = 32);
    tptr = tbuf;
    tlim = tbuf + tsiz;
    tindent = 1;
    gettext2(n);
    *tptr = '\0';
    untokenize(tbuf);
    return tbuf;
}

/* get a representation of n in a job text buffer */

char *getjobtext(n)		/**/
struct node *n;
{
    static char jbuf[JOBTEXTSIZE];

    tnewlins = 0;
    tbuf = NULL;
    tptr = jbuf;
    tlim = tptr + JOBTEXTSIZE - 1;
    tindent = 1;
    gettext2(n);
    *tptr = '\0';
    untokenize(jbuf);
    return jbuf;
}

#define gt2(X) gettext2((struct node *) (X))

/*
	"gettext2" or "type checking and how to avoid it"
	an epic function by Paul Falstad
*/

#define _Cond(X) ((Cond) (X))
#define _Cmd(X) ((Cmd) (X))
#define _Pline(X) ((Pline) (X))
#define _Sublist(X) ((Sublist) (X))
#define _List(X) ((List) (X))
#define _casecmd(X) ((struct casecmd *) (X))
#define _ifcmd(X) ((struct ifcmd *) (X))
#define _whilecmd(X) ((struct whilecmd *) (X))

void gettext2(n)		/**/
struct node *n;
{
    Cmd nn;
    Cond nm;

    if (!n || ((List) n) == &dummy_list)
	return;
    switch (NT_TYPE(n->type)) {
    case N_LIST:
	gt2(_List(n)->left);
	if (_List(n)->type == ASYNC)
	    taddstr(" &");
	simplifyright(_List(n));
	if (_List(n)->right) {
	    if (tnewlins)
		taddnl();
	    else
		taddstr((_List(n)->type == ASYNC) ? " " : "; ");
	    gt2(_List(n)->right);
	}
	break;
    case N_SUBLIST:
	if (_Sublist(n)->flags & PFLAG_NOT)
	    taddstr("! ");
	if (_Sublist(n)->flags & PFLAG_COPROC)
	    taddstr("coproc ");
	gt2(_Sublist(n)->left);
	if (_Sublist(n)->right) {
	    taddstr((_Sublist(n)->type == ORNEXT) ? " || " : " && ");
	    gt2(_Sublist(n)->right);
	}
	break;
    case N_PLINE:
	gt2(_Pline(n)->left);
	if (_Pline(n)->type == PIPE) {
	    taddstr(" | ");
	    gt2(_Pline(n)->right);
	}
	break;
    case N_CMD:
	nn = _Cmd(n);
	if (nn->flags & CFLAG_EXEC)
	    taddstr("exec ");
	if (nn->flags & CFLAG_COMMAND)
	    taddstr("command ");
	switch (nn->type) {
	case SIMPLE:
	    getsimptext(nn);
	    break;
	case SUBSH:
	    taddstr("( ");
	    tindent++;
	    gt2(nn->u.list);
	    tindent--;
	    taddstr(" )");
	    break;
	case ZCTIME:
	    taddstr("time ");
	    tindent++;
	    gt2(nn->u.pline);
	    tindent--;
	    break;
	case FUNCDEF:
	    taddlist(nn->args);
	    taddstr(" () {");
	    tindent++;
	    taddnl();
	    gt2(nn->u.list);
	    tindent--;
	    taddnl();
	    taddstr("}");
	    break;
	case CURSH:
	    taddstr("{ ");
	    tindent++;
	    gt2(nn->u.list);
	    tindent--;
	    taddstr(" }");
	    break;
	case CFOR:
	case CSELECT:
	    taddstr((nn->type == CFOR) ? "for " : "select ");
	    taddstr(nn->u.forcmd->name);
	    if (nn->u.forcmd->inflag) {
		taddstr(" in ");
		taddlist(nn->args);
	    }
	    taddnl();
	    taddstr("do");
	    tindent++;
	    taddnl();
	    gt2(nn->u.forcmd->list);
	    tindent--;
	    taddnl();
	    taddstr("done");
	    break;
	case CIF:
	    gt2(nn->u.ifcmd);
	    taddstr("fi");
	    break;
	case CCASE:
	    taddstr("case ");
	    taddlist(nn->args);
	    taddstr(" in");
	    tindent++;
	    taddnl();
	    gt2(nn->u.casecmd);
	    tindent--;
	    if (tnewlins)
		taddnl();
	    else
		taddchr(' ');
	    taddstr("esac");
	    break;
	case COND:
	    taddstr("[[ ");
	    gt2(nn->u.cond);
	    taddstr(" ]]");
	    break;
	case CREPEAT:
	    taddstr("repeat ");
	    taddlist(nn->args);
	    taddnl();
	    taddstr("do");
	    tindent++;
	    taddnl();
	    gt2(nn->u.list);
	    tindent--;
	    taddnl();
	    taddstr("done");
	    break;
	case CWHILE:
	    gt2(nn->u.whilecmd);
	    break;
	}
	getredirs(nn);
	break;
    case N_COND:
	nm = _Cond(n);
	switch (nm->type) {
	case COND_NOT:
	    taddstr("! ");
	    gt2(nm->left);
	    break;
	case COND_AND:
	    taddstr("( ");
	    gt2(nm->left);
	    taddstr(" && ");
	    gt2(nm->right);
	    taddstr(" )");
	    break;
	case COND_OR:
	    taddstr("( ");
	    gt2(nm->left);
	    taddstr(" || ");
	    gt2(nm->right);
	    taddstr(" )");
	    break;
	default:
	    {
		static char *c1[] =
		{
		    " = ", " != ", " < ", " > ", " -nt ", " -ot ", " -ef ", " -eq ",
		    " -ne ", " -lt ", " -gt ", " -le ", " -ge "
		};

		if (nm->right)
		    taddstr(nm->left);
		if (nm->type <= COND_GE)
		    taddstr(c1[nm->type - COND_STREQ]);
		else {
		    char c2[5];

		    c2[0] = ' ';
		    c2[1] = '-';
		    c2[2] = nm->type;
		    c2[3] = ' ';
		    c2[4] = '\0';
		    taddstr(c2);
		}
		taddstr((nm->right) ? nm->right : nm->left);
	    }
	    break;
	}
	break;
    case N_CASE:
	{
	    List *l;
	    char **p;

	    l = _casecmd(n)->lists;
	    p = _casecmd(n)->pats;

	    for (; *l; p++, l++) {
		taddstr(*p);
		taddstr(") ");
		tindent++;
		gt2(*l);
		tindent--;
		taddstr(";;");
		if (tnewlins)
		    taddnl();
		else
		    taddchr(' ');
	    }
	    break;
	}
    case N_IF:
	{
	    List *i, *t;

	    taddstr("if ");
	    for (i = _ifcmd(n)->ifls, t = _ifcmd(n)->thenls; *i; i++, t++) {
		tindent++;
		gt2(*i);
		tindent--;
		taddnl();
		taddstr("then");
		tindent++;
		taddnl();
		gt2(*t);
		tindent--;
		taddnl();
		if (i[1]) {
		    taddstr("elif ");
		}
	    }
	    if (*t) {
		taddstr("else");
		tindent++;
		taddnl();
		gt2(*t);
		tindent--;
		taddnl();
	    }
	    break;
	}
    case N_WHILE:
	taddstr((_whilecmd(n)->cond) ? "until " : "while ");
	tindent++;
	gt2(_whilecmd(n)->cont);
	tindent--;
	taddnl();
	taddstr("do");
	tindent++;
	taddnl();
	gt2(_whilecmd(n)->loop);
	tindent--;
	taddnl();
	taddstr("done");
	break;
    }
}

void getsimptext(cmd)		/**/
Cmd cmd;
{
    Lknode n;

    for (n = firstnode(cmd->vars); n; incnode(n)) {
	struct varasg *v = (struct varasg *)getdata(n);

	taddstr(v->name);
	taddchr('=');
	if ((v->type & PMTYPE) == PMFLAG_A) {
	    taddchr('(');
	    taddlist(v->arr);
	    taddstr(") ");
	} else {
	    taddstr(v->str);
	    taddchr(' ');
	}
    }
    taddlist(cmd->args);
}

void getredirs(cmd)		/**/
Cmd cmd;
{
    Lknode n;
    static char *fstr[] =
    {
	">", ">!", ">>", ">>!", ">&", ">&!", ">>&", ">>&!", "<", "<<",
	"<<-", "<<<", "<&", ">&-", "..", ".."
    };

    taddchr(' ');
    for (n = firstnode(cmd->redir); n; incnode(n)) {
	struct redir *f = (struct redir *)getdata(n);

	switch (f->type) {
	case WRITE:
	case WRITENOW:
	case APP:
	case APPNOW:
	case READ:
	case HERESTR:
	    if (f->fd1 != ((f->type == READ) ? 0 : 1))
		taddchr('0' + f->fd1);
	    taddstr(fstr[f->type]);
	    taddchr(' ');
	    taddstr(f->name);
	    taddchr(' ');
	    break;
	case MERGE:
	case MERGEOUT:
	    if (f->fd1 != ((f->type == MERGEOUT) ? 1 : 0))
		taddchr('0' + f->fd1);
	    taddstr(fstr[f->type]);
	    if (f->fd2 == FD_COPROC)
		taddchr('p');
	    else
		taddint(f->fd2);
	    taddchr(' ');
	    break;
	case CLOSE:
	    taddchr(f->fd1 + '0');
	    taddstr(">&- ");
	    break;
	case INPIPE:
	case OUTPIPE:
	    if (f->fd1 != ((f->type == INPIPE) ? 0 : 1))
		taddchr('0' + f->fd1);
	    taddstr((f->type == INPIPE) ? "< " : "> ");
	    taddstr(f->name);
	    taddchr(' ');
	    break;
	}
    }
    tptr--;
}

void taddlist(l)		/**/
Lklist l;
{
    Lknode n;

    if (!(n = firstnode(l)))
	return;
    for (; n; incnode(n)) {
	taddstr(getdata(n));
	taddchr(' ');
    }
    tptr--;
}
