|  | /*	$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $	*/ | 
|  |  | 
|  | /*- | 
|  | * Copyright (c) 1991, 1993 | 
|  | *	The Regents of the University of California.  All rights reserved. | 
|  | * | 
|  | * This code is derived from software contributed to Berkeley by | 
|  | * Kenneth Almquist. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. Neither the name of the University nor the names of its contributors | 
|  | *    may be used to endorse or promote products derived from this software | 
|  | *    without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
|  | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
|  | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
|  | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
|  | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
|  | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
|  | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
|  | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
|  | * SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include <sys/cdefs.h> | 
|  | #ifndef lint | 
|  | #if 0 | 
|  | static char sccsid[] = "@(#)show.c	8.3 (Berkeley) 5/4/95"; | 
|  | #else | 
|  | __RCSID("$NetBSD: show.c,v 1.26 2003/11/14 10:46:13 dsl Exp $"); | 
|  | #endif | 
|  | #endif /* not lint */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "shell.h" | 
|  | #include "parser.h" | 
|  | #include "nodes.h" | 
|  | #include "mystring.h" | 
|  | #include "show.h" | 
|  | #include "options.h" | 
|  |  | 
|  |  | 
|  | #ifdef DEBUG | 
|  | static void shtree(union node *, int, char *, FILE*); | 
|  | static void shcmd(union node *, FILE *); | 
|  | static void sharg(union node *, FILE *); | 
|  | static void indent(int, char *, FILE *); | 
|  | static void trstring(char *); | 
|  |  | 
|  |  | 
|  | void | 
|  | showtree(union node *n) | 
|  | { | 
|  | trputs("showtree called\n"); | 
|  | shtree(n, 1, NULL, stdout); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | shtree(union node *n, int ind, char *pfx, FILE *fp) | 
|  | { | 
|  | struct nodelist *lp; | 
|  | const char *s; | 
|  |  | 
|  | if (n == NULL) | 
|  | return; | 
|  |  | 
|  | indent(ind, pfx, fp); | 
|  | switch(n->type) { | 
|  | case NSEMI: | 
|  | s = "; "; | 
|  | goto binop; | 
|  | case NAND: | 
|  | s = " && "; | 
|  | goto binop; | 
|  | case NOR: | 
|  | s = " || "; | 
|  | binop: | 
|  | shtree(n->nbinary.ch1, ind, NULL, fp); | 
|  | /*    if (ind < 0) */ | 
|  | fputs(s, fp); | 
|  | shtree(n->nbinary.ch2, ind, NULL, fp); | 
|  | break; | 
|  | case NCMD: | 
|  | shcmd(n, fp); | 
|  | if (ind >= 0) | 
|  | putc('\n', fp); | 
|  | break; | 
|  | case NPIPE: | 
|  | for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { | 
|  | shcmd(lp->n, fp); | 
|  | if (lp->next) | 
|  | fputs(" | ", fp); | 
|  | } | 
|  | if (n->npipe.backgnd) | 
|  | fputs(" &", fp); | 
|  | if (ind >= 0) | 
|  | putc('\n', fp); | 
|  | break; | 
|  | default: | 
|  | fprintf(fp, "<node type %d>", n->type); | 
|  | if (ind >= 0) | 
|  | putc('\n', fp); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | static void | 
|  | shcmd(union node *cmd, FILE *fp) | 
|  | { | 
|  | union node *np; | 
|  | int first; | 
|  | const char *s; | 
|  | int dftfd; | 
|  |  | 
|  | first = 1; | 
|  | for (np = cmd->ncmd.args ; np ; np = np->narg.next) { | 
|  | if (! first) | 
|  | putchar(' '); | 
|  | sharg(np, fp); | 
|  | first = 0; | 
|  | } | 
|  | for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { | 
|  | if (! first) | 
|  | putchar(' '); | 
|  | switch (np->nfile.type) { | 
|  | case NTO:	s = ">";  dftfd = 1; break; | 
|  | case NCLOBBER:	s = ">|"; dftfd = 1; break; | 
|  | case NAPPEND:	s = ">>"; dftfd = 1; break; | 
|  | case NTOFD:	s = ">&"; dftfd = 1; break; | 
|  | case NFROM:	s = "<";  dftfd = 0; break; | 
|  | case NFROMFD:	s = "<&"; dftfd = 0; break; | 
|  | case NFROMTO:	s = "<>"; dftfd = 0; break; | 
|  | default:  	s = "*error*"; dftfd = 0; break; | 
|  | } | 
|  | if (np->nfile.fd != dftfd) | 
|  | fprintf(fp, "%d", np->nfile.fd); | 
|  | fputs(s, fp); | 
|  | if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { | 
|  | fprintf(fp, "%d", np->ndup.dupfd); | 
|  | } else { | 
|  | sharg(np->nfile.fname, fp); | 
|  | } | 
|  | first = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | static void | 
|  | sharg(union node *arg, FILE *fp) | 
|  | { | 
|  | char *p; | 
|  | struct nodelist *bqlist; | 
|  | int subtype; | 
|  |  | 
|  | if (arg->type != NARG) { | 
|  | printf("<node type %d>\n", arg->type); | 
|  | abort(); | 
|  | } | 
|  | bqlist = arg->narg.backquote; | 
|  | for (p = arg->narg.text ; *p ; p++) { | 
|  | switch (*p) { | 
|  | case CTLESC: | 
|  | putc(*++p, fp); | 
|  | break; | 
|  | case CTLVAR: | 
|  | putc('$', fp); | 
|  | putc('{', fp); | 
|  | subtype = *++p; | 
|  | if (subtype == VSLENGTH) | 
|  | putc('#', fp); | 
|  |  | 
|  | while (*p != '=') | 
|  | putc(*p++, fp); | 
|  |  | 
|  | if (subtype & VSNUL) | 
|  | putc(':', fp); | 
|  |  | 
|  | switch (subtype & VSTYPE) { | 
|  | case VSNORMAL: | 
|  | putc('}', fp); | 
|  | break; | 
|  | case VSMINUS: | 
|  | putc('-', fp); | 
|  | break; | 
|  | case VSPLUS: | 
|  | putc('+', fp); | 
|  | break; | 
|  | case VSQUESTION: | 
|  | putc('?', fp); | 
|  | break; | 
|  | case VSASSIGN: | 
|  | putc('=', fp); | 
|  | break; | 
|  | case VSTRIMLEFT: | 
|  | putc('#', fp); | 
|  | break; | 
|  | case VSTRIMLEFTMAX: | 
|  | putc('#', fp); | 
|  | putc('#', fp); | 
|  | break; | 
|  | case VSTRIMRIGHT: | 
|  | putc('%', fp); | 
|  | break; | 
|  | case VSTRIMRIGHTMAX: | 
|  | putc('%', fp); | 
|  | putc('%', fp); | 
|  | break; | 
|  | case VSLENGTH: | 
|  | break; | 
|  | default: | 
|  | printf("<subtype %d>", subtype); | 
|  | } | 
|  | break; | 
|  | case CTLENDVAR: | 
|  | putc('}', fp); | 
|  | break; | 
|  | case CTLBACKQ: | 
|  | case CTLBACKQ|CTLQUOTE: | 
|  | putc('$', fp); | 
|  | putc('(', fp); | 
|  | shtree(bqlist->n, -1, NULL, fp); | 
|  | putc(')', fp); | 
|  | break; | 
|  | default: | 
|  | putc(*p, fp); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | indent(int amount, char *pfx, FILE *fp) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0 ; i < amount ; i++) { | 
|  | if (pfx && i == amount - 1) | 
|  | fputs(pfx, fp); | 
|  | putc('\t', fp); | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  |  | 
|  | /* | 
|  | * Debugging stuff. | 
|  | */ | 
|  |  | 
|  |  | 
|  | FILE *tracefile; | 
|  |  | 
|  |  | 
|  | #ifdef DEBUG | 
|  | void | 
|  | trputc(int c) | 
|  | { | 
|  | if (debug != 1) | 
|  | return; | 
|  | putc(c, tracefile); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void | 
|  | trace(const char *fmt, ...) | 
|  | { | 
|  | #ifdef DEBUG | 
|  | va_list va; | 
|  |  | 
|  | if (debug != 1) | 
|  | return; | 
|  | va_start(va, fmt); | 
|  | (void) vfprintf(tracefile, fmt, va); | 
|  | va_end(va); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void | 
|  | tracev(const char *fmt, va_list va) | 
|  | { | 
|  | #ifdef DEBUG | 
|  | if (debug != 1) | 
|  | return; | 
|  | (void) vfprintf(tracefile, fmt, va); | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifdef DEBUG | 
|  | void | 
|  | trputs(const char *s) | 
|  | { | 
|  | if (debug != 1) | 
|  | return; | 
|  | fputs(s, tracefile); | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | trstring(char *s) | 
|  | { | 
|  | char *p; | 
|  | char c; | 
|  |  | 
|  | if (debug != 1) | 
|  | return; | 
|  | putc('"', tracefile); | 
|  | for (p = s ; *p ; p++) { | 
|  | switch (*p) { | 
|  | case '\n':  c = 'n';  goto backslash; | 
|  | case '\t':  c = 't';  goto backslash; | 
|  | case '\r':  c = 'r';  goto backslash; | 
|  | case '"':  c = '"';  goto backslash; | 
|  | case '\\':  c = '\\';  goto backslash; | 
|  | case CTLESC:  c = 'e';  goto backslash; | 
|  | case CTLVAR:  c = 'v';  goto backslash; | 
|  | case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash; | 
|  | case CTLBACKQ:  c = 'q';  goto backslash; | 
|  | case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash; | 
|  | backslash:	  putc('\\', tracefile); | 
|  | putc(c, tracefile); | 
|  | break; | 
|  | default: | 
|  | if (*p >= ' ' && *p <= '~') | 
|  | putc(*p, tracefile); | 
|  | else { | 
|  | putc('\\', tracefile); | 
|  | putc(*p >> 6 & 03, tracefile); | 
|  | putc(*p >> 3 & 07, tracefile); | 
|  | putc(*p & 07, tracefile); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | putc('"', tracefile); | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | void | 
|  | trargs(char **ap) | 
|  | { | 
|  | #ifdef DEBUG | 
|  | if (debug != 1) | 
|  | return; | 
|  | while (*ap) { | 
|  | trstring(*ap++); | 
|  | if (*ap) | 
|  | putc(' ', tracefile); | 
|  | else | 
|  | putc('\n', tracefile); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  |  | 
|  | #ifdef DEBUG | 
|  | void | 
|  | opentrace(void) | 
|  | { | 
|  | char s[100]; | 
|  | #ifdef O_APPEND | 
|  | int flags; | 
|  | #endif | 
|  |  | 
|  | if (debug != 1) { | 
|  | if (tracefile) | 
|  | fflush(tracefile); | 
|  | /* leave open because libedit might be using it */ | 
|  | return; | 
|  | } | 
|  | #ifdef not_this_way | 
|  | { | 
|  | char *p; | 
|  | if ((p = getenv("HOME")) == NULL) { | 
|  | if (geteuid() == 0) | 
|  | p = "/"; | 
|  | else | 
|  | p = "/tmp"; | 
|  | } | 
|  | scopy(p, s); | 
|  | strcat(s, "/trace"); | 
|  | } | 
|  | #else | 
|  | scopy("./trace", s); | 
|  | #endif /* not_this_way */ | 
|  | if (tracefile) { | 
|  | if (!freopen(s, "a", tracefile)) { | 
|  | fprintf(stderr, "Can't re-open %s\n", s); | 
|  | debug = 0; | 
|  | return; | 
|  | } | 
|  | } else { | 
|  | if ((tracefile = fopen(s, "a")) == NULL) { | 
|  | fprintf(stderr, "Can't open %s\n", s); | 
|  | debug = 0; | 
|  | return; | 
|  | } | 
|  | } | 
|  | #ifdef O_APPEND | 
|  | if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) | 
|  | fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); | 
|  | #endif | 
|  | setlinebuf(tracefile); | 
|  | fputs("\nTracing started.\n", tracefile); | 
|  | } | 
|  | #endif /* DEBUG */ |