| /*- |
| * Copyright (c) 1991, 1993 |
| * The Regents of the University of California. All rights reserved. |
| * Copyright (c) 1997-2005 |
| * Herbert Xu <herbert@gondor.apana.org.au>. 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 <stdio.h> |
| #include <stdarg.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 ((signed char)*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: |
| 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); |
| } |
| } |
| |
| |
| |
| /* |
| * Debugging stuff. |
| */ |
| |
| |
| FILE *tracefile; |
| |
| |
| void |
| trputc(int c) |
| { |
| if (debug != 1) |
| return; |
| putc(c, tracefile); |
| } |
| |
| void |
| trace(const char *fmt, ...) |
| { |
| va_list va; |
| |
| if (debug != 1) |
| return; |
| va_start(va, fmt); |
| (void) vfprintf(tracefile, fmt, va); |
| va_end(va); |
| } |
| |
| void |
| tracev(const char *fmt, va_list va) |
| { |
| if (debug != 1) |
| return; |
| (void) vfprintf(tracefile, fmt, va); |
| } |
| |
| |
| 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 ((signed char)*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 CTLBACKQ: 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); |
| } |
| |
| |
| void |
| trargs(char **ap) |
| { |
| if (debug != 1) |
| return; |
| while (*ap) { |
| trstring(*ap++); |
| if (*ap) |
| putc(' ', tracefile); |
| else |
| putc('\n', tracefile); |
| } |
| } |
| |
| |
| 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(homestr)) == NULL) { |
| if (geteuid() == 0) |
| p = "/"; |
| else |
| p = "/tmp"; |
| } |
| scopy(p, s); |
| strcat(s, "/trace"); |
| } |
| #else |
| scopy("./trace", s); |
| #endif /* not_this_way */ |
| if (tracefile) { |
| #ifndef __KLIBC__ |
| if (!freopen(s, "a", tracefile)) { |
| #else |
| if (!(!fclose(tracefile) && (tracefile = fopen(s, "a")))) { |
| #endif /* __KLIBC__ */ |
| 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 |
| #ifndef __KLIBC__ |
| setlinebuf(tracefile); |
| #endif /* __KLIBC__ */ |
| fputs("\nTracing started.\n", tracefile); |
| } |
| #endif /* DEBUG */ |