/* used by error reporting functions to print "ksh: .kshrc[25]: " */ void error_prefix(int fileline) { /* Avoid foo: foo[2]: ... */ if (!fileline || !source || !source->file || strcmp(source->file, kshname) != 0) shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-')); if (fileline && source && source->file != NULL) { shf_fprintf(shl_out, "%s[%d]: ", source->file, source->errline > 0 ? source->errline : source->line); source->errline = 0; } }
static void p_time(struct shf *shf, int posix, struct timeval *tv, int width, char *prefix, char *suffix) { if (posix) shf_fprintf(shf, "%s%*lld.%02ld%s", prefix ? prefix : "", width, (long long)tv->tv_sec, tv->tv_usec / 10000, suffix); else shf_fprintf(shf, "%s%*lldm%lld.%02lds%s", prefix ? prefix : "", width, (long long)tv->tv_sec / 60, (long long)tv->tv_sec % 60, tv->tv_usec / 10000, suffix); }
/* * time pipeline (really a statement, not a built-in command) */ int timex(struct op *t, int f, volatile int *xerrok) { #define TF_NOARGS BIT(0) #define TF_NOREAL BIT(1) /* don't report real time */ #define TF_POSIX BIT(2) /* report in posix format */ int rv = 0; struct tms t0, t1, tms; clock_t t0t, t1t = 0; int tf = 0; extern clock_t j_usrtime, j_systime; /* computed by j_wait */ t0t = ksh_times(&t0); if (t->left) { /* * Two ways of getting cpu usage of a command: just use t0 * and t1 (which will get cpu usage from other jobs that * finish while we are executing t->left), or get the * cpu usage of t->left. at&t ksh does the former, while * pdksh tries to do the later (the j_usrtime hack doesn't * really work as it only counts the last job). */ j_usrtime = j_systime = 0; rv = execute(t->left, f | XTIME, xerrok); if (t->left->type == TCOM) tf |= t->left->str[0]; t1t = ksh_times(&t1); } else tf = TF_NOARGS; if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */ tf |= TF_NOREAL; tms.tms_utime = t0.tms_utime + t0.tms_cutime; tms.tms_stime = t0.tms_stime + t0.tms_cstime; } else { tms.tms_utime = t1.tms_utime - t0.tms_utime + j_usrtime; tms.tms_stime = t1.tms_stime - t0.tms_stime + j_systime; } if (!(tf & TF_NOREAL)) shf_fprintf(shl_out, tf & TF_POSIX ? "real %8s\n" : "%8ss real ", clocktos(t1t - t0t)); shf_fprintf(shl_out, tf & TF_POSIX ? "user %8s\n" : "%8ss user ", clocktos(tms.tms_utime)); shf_fprintf(shl_out, tf & TF_POSIX ? "sys %8s\n" : "%8ss system\n", clocktos(tms.tms_stime)); shf_flush(shl_out); return rv; }
/* used by error reporting functions to print "ksh: .kshrc[25]: " */ void error_prefix(bool fileline) { /* Avoid foo: foo[2]: ... */ if (!fileline || !source || !source->file || strcmp(source->file, kshname) != 0) shf_fprintf(shl_out, Tf_sD_, kshname + (*kshname == '-')); if (fileline && source && source->file != NULL) { shf_fprintf(shl_out, "%s[%lu]: ", source->file, (unsigned long)(source->errline ? source->errline : source->line)); source->errline = 0; } }
/* Print things in columns and rows - func() is called to format the ith * element */ void print_columns(struct shf *shf, int n, char *(*func) (void *, int, char *, int), void *arg, int max_width, int prefcol) { char *str = (char *) alloc(max_width + 1, ATEMP); int i; int r, c; int rows, cols; int nspace; int col_width; /* max_width + 1 for the space. Note that no space * is printed after the last column to avoid problems * with terminals that have auto-wrap. */ cols = x_cols / (max_width + 1); if (!cols) cols = 1; rows = (n + cols - 1) / cols; if (prefcol && n && cols > rows) { int tmp = rows; rows = cols; cols = tmp; if (rows > n) rows = n; } col_width = max_width; if (cols == 1) col_width = 0; /* Don't pad entries in single column output. */ nspace = (x_cols - max_width * cols) / cols; if (nspace <= 0) nspace = 1; for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { i = c * rows + r; if (i < n) { shf_fprintf(shf, "%-*s", col_width, (*func)(arg, i, str, max_width + 1)); if (c + 1 < cols) shf_fprintf(shf, "%*s", nspace, null); } } shf_putchar('\n', shf); } afree(str, ATEMP); }
/* Used by built-in utilities to prefix shell and utility name to message * (also unwinds environments for special builtins). */ void bi_errorf(const char *fmt, ...) { va_list va; shl_stdout_ok = 0; /* debugging: note that stdout not valid */ exstat = 1; if (fmt != NULL && *fmt != '\0') { error_prefix(true); /* not set when main() calls parse_args() */ if (builtin_argv0) shf_fprintf(shl_out, "%s: ", builtin_argv0); va_start(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); } shf_flush(shl_out); /* POSIX special builtins and ksh special builtins cause * non-interactive shells to exit. * XXX odd use of KEEPASN; also may not want LERROR here */ if ((builtin_flag & SPEC_BI) || (Flag(FPOSIX) && (builtin_flag & KEEPASN))) { builtin_argv0 = NULL; unwind(LERROR); } }
static void vwarningf(unsigned int flags, const char *fmt, va_list ap) { if (fmt) { if (flags & VWARNINGF_INTERNAL) shf_fprintf(shl_out, Tf_sD_, "internal error"); if (flags & VWARNINGF_ERRORPREFIX) error_prefix(tobool(flags & VWARNINGF_FILELINE)); if ((flags & VWARNINGF_BUILTIN) && /* not set when main() calls parse_args() */ builtin_argv0 && builtin_argv0 != kshname) shf_fprintf(shl_out, Tf_sD_, builtin_argv0); shf_vfprintf(shl_out, fmt, ap); shf_putchar('\n', shl_out); } shf_flush(shl_out); }
void set_prompt(int to, Source *s) { cur_prompt = (uint8_t)to; switch (to) { /* command */ case PS1: /* * Substitute ! and !! here, before substitutions are done * so ! in expanded variables are not expanded. * NOTE: this is not what AT&T ksh does (it does it after * substitutions, POSIX doesn't say which is to be done. */ { struct shf *shf; char * volatile ps1; Area *saved_atemp; int saved_lineno; ps1 = str_val(global("PS1")); shf = shf_sopen(NULL, strlen(ps1) * 2, SHF_WR | SHF_DYNAMIC, NULL); while (*ps1) if (*ps1 != '!' || *++ps1 == '!') shf_putchar(*ps1++, shf); else shf_fprintf(shf, "%lu", s ? (unsigned long)s->line + 1 : 0UL); ps1 = shf_sclose(shf); saved_lineno = current_lineno; if (s) current_lineno = s->line + 1; saved_atemp = ATEMP; newenv(E_ERRH); if (kshsetjmp(e->jbuf)) { prompt = safe_prompt; /* * Don't print an error - assume it has already * been printed. Reason is we may have forked * to run a command and the child may be * unwinding its stack through this code as it * exits. */ } else { char *cp = substitute(ps1, 0); strdupx(prompt, cp, saved_atemp); } current_lineno = saved_lineno; quitenv(NULL); } break; /* command continuation */ case PS2: prompt = str_val(global("PS2")); break; } }
void kshdebug_init_(void) { if (kshdebug_shf) shf_close(kshdebug_shf); kshdebug_shf = shf_open("/tmp/ksh-debug.log", O_WRONLY|O_APPEND|O_CREAT, 0600, SHF_WR|SHF_MAPHI); if (kshdebug_shf) { shf_fprintf(kshdebug_shf, "\nNew shell[pid %d]\n", getpid()); shf_flush(kshdebug_shf); } }
void kshdebug_dump_(const char *str, const void *mem, int nbytes) { int i, j; int nprow = 16; if (!kshdebug_shf) return; shf_fprintf(kshdebug_shf, "[%d] %s:\n", getpid(), str); for (i = 0; i < nbytes; i += nprow) { char c = '\t'; for (j = 0; j < nprow && i + j < nbytes; j++) { shf_fprintf(kshdebug_shf, "%c%02x", c, ((const unsigned char *) mem)[i + j]); c = ' '; } shf_fprintf(kshdebug_shf, "\n"); } shf_flush(kshdebug_shf); }
static void pioact(struct shf *shf, struct ioword *iop) { int flag = iop->flag; int type = flag & IOTYPE; int expected; expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 : (type == IOCAT || type == IOWRITE) ? 1 : (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit : iop->unit + 1; if (iop->unit != expected) shf_fprintf(shf, "%d", iop->unit); switch (type) { case IOREAD: shf_putc('<', shf); break; case IOHERE: shf_puts("<<", shf); if (flag & IOSKIP) shf_putc('-', shf); break; case IOCAT: shf_puts(">>", shf); break; case IOWRITE: shf_putc('>', shf); if (flag & IOCLOB) shf_putc('|', shf); break; case IORDWR: shf_puts("<>", shf); break; case IODUP: shf_puts(flag & IORDUP ? "<&" : ">&", shf); break; } /* name/delim are NULL when printing syntax errors */ if (type == IOHERE) { if (iop->delim) wdvarput(shf, iop->delim, 0, WDS_TPUTS); if (iop->flag & IOHERESTR) shf_putc(' ', shf); } else if (iop->name) { if (iop->flag & IONAMEXP) print_value_quoted(shf, iop->name); else wdvarput(shf, iop->name, 0, WDS_TPUTS); shf_putc(' ', shf); } prevent_semicolon = false; }
/* print to debugging log */ void kshdebug_printf_(const char *fmt, ...) { va_list va; if (!kshdebug_shf) return; va_start(va, fmt); shf_fprintf(kshdebug_shf, "[%d] ", getpid()); shf_vfprintf(kshdebug_shf, fmt, va); va_end(va); shf_flush(kshdebug_shf); }
/* Called when something that shouldn't happen does */ void internal_errorf(int jump, const char *fmt, ...) { va_list va; error_prefix(true); shf_fprintf(shl_out, "internal error: "); va_start(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); shf_flush(shl_out); if (jump) unwind(LERROR); }
int exchild(struct op *t, int flags, int close_fd) /* used if XPCLOSE or XCCLOSE */ { static Proc *last_proc; /* for pipelines */ int i; sigset_t omask; Proc *p; Job *j; int rv = 0; int forksleep; int ischild; if (flags & XEXEC) /* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND * (also done in another execute() below) */ return execute(t, flags & (XEXEC | XERROK)); p = new_proc(); p->next = (Proc *) 0; p->state = PRUNNING; p->status = 0; p->pid = 0; /* link process into jobs list */ if (flags&XPIPEI) { /* continuing with a pipe */ if (!last_job) internal_errorf(1, "exchild: XPIPEI and no last_job - pid %d", (int) procpid); j = last_job; last_proc->next = p; last_proc = p; } else { j = new_job(); /* fills in j->job */ /* we don't consider XXCOM's foreground since they don't get * tty process group and we don't save or restore tty modes. */ j->flags = (flags & XXCOM) ? JF_XXCOM : ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE)); j->usrtime = j->systime = 0; j->state = PRUNNING; j->pgrp = 0; j->ppid = procpid; j->age = ++njobs; j->proc_list = p; j->coproc_id = 0; last_job = j; last_proc = p; put_job(j, PJ_PAST_STOPPED); } snptreef(p->command, sizeof(p->command), "%T", t); /* create child process */ forksleep = 1; while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) { if (intrsig) /* allow user to ^C out... */ break; sleep(forksleep); forksleep <<= 1; } if (i < 0) { kill_job(j, SIGKILL); remove_job(j, "fork failed"); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); errorf("cannot fork - try again"); } ischild = i == 0; if (ischild) p->pid = procpid = getpid(); else p->pid = i; /* used to close pipe input fd */ if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild) || ((flags & XCCLOSE) && ischild))) close(close_fd); if (ischild) { /* child */ /* Do this before restoring signal */ if (flags & XCOPROC) coproc_cleanup(false); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); cleanup_parents_env(); if ((flags & XBGND)) { setsig(&sigtraps[SIGINT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); setsig(&sigtraps[SIGQUIT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); if (!(flags & (XPIPEI | XCOPROC))) { int fd = open("/dev/null", 0); if (fd != 0) { (void) ksh_dup2(fd, 0, true); close(fd); } } } remove_job(j, "child"); /* in case of `jobs` command */ nzombie = 0; Flag(FTALKING) = 0; tty_close(); cleartraps(); execute(t, (flags & XERROK) | XEXEC); /* no return */ internal_errorf(0, "exchild: execute() returned"); unwind(LLEAVE); /* NOTREACHED */ } /* shell (parent) stuff */ /* Ensure next child gets a (slightly) different $RANDOM sequence */ change_random(); if (!(flags & XPIPEO)) { /* last process in a job */ j_startjob(j); if (flags & XCOPROC) { j->coproc_id = coproc.id; coproc.njobs++; /* n jobs using co-process output */ coproc.job = (void *) j; /* j using co-process input */ } if (flags & XBGND) { j_set_async(j); if (Flag(FTALKING)) { shf_fprintf(shl_out, "[%d]", j->job); for (p = j->proc_list; p; p = p->next) shf_fprintf(shl_out, " %d", p->pid); shf_putchar('\n', shl_out); shf_flush(shl_out); } } else rv = j_waitj(j, JW_NONE, "jw:last proc"); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv; }
/* * Print things in columns and rows - func() is called to format * the i-th element */ void print_columns(struct shf *shf, unsigned int n, void (*func)(char *, size_t, unsigned int, const void *), const void *arg, size_t max_oct, size_t max_colz, bool prefcol) { unsigned int i, r, c, rows, cols, nspace, max_col; char *str; if (!n) return; if (max_colz > 2147483646) { #ifndef MKSH_SMALL internal_warningf("print_columns called with %s=%zu >= INT_MAX", "max_col", max_colz); #endif return; } max_col = (unsigned int)max_colz; if (max_oct > 2147483646) { #ifndef MKSH_SMALL internal_warningf("print_columns called with %s=%zu >= INT_MAX", "max_oct", max_oct); #endif return; } ++max_oct; str = alloc(max_oct, ATEMP); /* * We use (max_col + 2) to consider the separator space. * Note that no spaces are printed after the last column * to avoid problems with terminals that have auto-wrap, * but we need to also take this into account in x_cols. */ cols = (x_cols + 1) / (max_col + 2); /* if we can only print one column anyway, skip the goo */ if (cols < 2) { for (i = 0; i < n; ++i) { (*func)(str, max_oct, i, arg); shf_puts(str, shf); shf_putc('\n', shf); } goto out; } rows = (n + cols - 1) / cols; if (prefcol && cols > rows) { cols = rows; rows = (n + cols - 1) / cols; } nspace = (x_cols - max_col * cols) / cols; if (nspace < 2) nspace = 2; max_col = -max_col; for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { if ((i = c * rows + r) >= n) break; (*func)(str, max_oct, i, arg); if (i + rows >= n) shf_puts(str, shf); else shf_fprintf(shf, "%*s%*s", max_col, str, nspace, null); } shf_putchar('\n', shf); } out: afree(str, ATEMP); }
/* execute tree in child subprocess */ int exchild(struct op *t, int flags, volatile int *xerrok, int close_fd) /* used if XPCLOSE or XCCLOSE */ { static Proc *last_proc; /* for pipelines */ int i; sigset_t omask; Proc *p; Job *j; int rv = 0; int forksleep; int ischild; if (flags & XEXEC) /* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND * (also done in another execute() below) */ return execute(t, flags & (XEXEC | XERROK), xerrok); /* no SIGCHLD's while messing with job and process lists */ sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); p = new_proc(); p->next = (Proc *) 0; p->state = PRUNNING; p->status = 0; p->pid = 0; /* link process into jobs list */ if (flags&XPIPEI) { /* continuing with a pipe */ if (!last_job) internal_errorf(1, "exchild: XPIPEI and no last_job - pid %d", (int) procpid); j = last_job; last_proc->next = p; last_proc = p; } else { j = new_job(); /* fills in j->job */ /* we don't consider XXCOM's foreground since they don't get * tty process group and we don't save or restore tty modes. */ j->flags = (flags & XXCOM) ? JF_XXCOM : ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE)); timerclear(&j->usrtime); timerclear(&j->systime); j->state = PRUNNING; j->pgrp = 0; j->ppid = procpid; j->age = ++njobs; j->proc_list = p; j->coproc_id = 0; last_job = j; last_proc = p; put_job(j, PJ_PAST_STOPPED); } snptreef(p->command, sizeof(p->command), "%T", t); /* create child process */ forksleep = 1; while ((i = fork()) < 0 && errno == EAGAIN && forksleep < 32) { if (intrsig) /* allow user to ^C out... */ break; sleep(forksleep); forksleep <<= 1; } if (i < 0) { kill_job(j, SIGKILL); remove_job(j, "fork failed"); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); errorf("cannot fork - try again"); } ischild = i == 0; if (ischild) p->pid = procpid = getpid(); else p->pid = i; #ifdef JOBS /* job control set up */ if (Flag(FMONITOR) && !(flags&XXCOM)) { int dotty = 0; if (j->pgrp == 0) { /* First process */ j->pgrp = p->pid; dotty = 1; } /* set pgrp in both parent and child to deal with race * condition */ setpgid(p->pid, j->pgrp); /* YYY: should this be if (ttypgrp_ok && ischild && !(flags&XBGND)) tcsetpgrp(tty_fd, j->pgrp); instead? (see also YYY below) */ if (ttypgrp_ok && dotty && !(flags & XBGND)) tcsetpgrp(tty_fd, j->pgrp); } #endif /* JOBS */ /* used to close pipe input fd */ if (close_fd >= 0 && (((flags & XPCLOSE) && !ischild) || ((flags & XCCLOSE) && ischild))) close(close_fd); if (ischild) { /* child */ /* Do this before restoring signal */ if (flags & XCOPROC) coproc_cleanup(false); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); cleanup_parents_env(); #ifdef JOBS /* If FMONITOR or FTALKING is set, these signals are ignored, * if neither FMONITOR nor FTALKING are set, the signals have * their inherited values. */ if (Flag(FMONITOR) && !(flags & XXCOM)) { for (i = NELEM(tt_sigs); --i >= 0; ) setsig(&sigtraps[tt_sigs[i]], SIG_DFL, SS_RESTORE_DFL|SS_FORCE); } #endif /* JOBS */ if (Flag(FBGNICE) && (flags & XBGND)) nice(4); if ((flags & XBGND) && !Flag(FMONITOR)) { setsig(&sigtraps[SIGINT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); setsig(&sigtraps[SIGQUIT], SIG_IGN, SS_RESTORE_IGN|SS_FORCE); if (!(flags & (XPIPEI | XCOPROC))) { int fd = open("/dev/null", 0); if (fd != 0) { (void) ksh_dup2(fd, 0, true); close(fd); } } } remove_job(j, "child"); /* in case of `jobs` command */ nzombie = 0; #ifdef JOBS ttypgrp_ok = 0; Flag(FMONITOR) = 0; #endif /* JOBS */ Flag(FTALKING) = 0; tty_close(); cleartraps(); execute(t, (flags & XERROK) | XEXEC, NULL); /* no return */ internal_errorf(0, "exchild: execute() returned"); unwind(LLEAVE); /* NOTREACHED */ } /* shell (parent) stuff */ /* Ensure next child gets a (slightly) different $RANDOM sequence */ change_random(); if (!(flags & XPIPEO)) { /* last process in a job */ #ifdef JOBS /* YYY: Is this needed? (see also YYY above) if (Flag(FMONITOR) && !(flags&(XXCOM|XBGND))) tcsetpgrp(tty_fd, j->pgrp); */ #endif /* JOBS */ j_startjob(j); if (flags & XCOPROC) { j->coproc_id = coproc.id; coproc.njobs++; /* n jobs using co-process output */ coproc.job = (void *) j; /* j using co-process input */ } if (flags & XBGND) { j_set_async(j); if (Flag(FTALKING)) { shf_fprintf(shl_out, "[%d]", j->job); for (p = j->proc_list; p; p = p->next) shf_fprintf(shl_out, " %d", p->pid); shf_putchar('\n', shl_out); shf_flush(shl_out); } } else rv = j_waitj(j, JW_NONE, "jw:last proc"); } sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); return rv; }
/* * Print job status in either short, medium or long format. * * If jobs are compiled in then this routine expects sigchld to be blocked. */ static void j_print(Job *j, int how, struct shf *shf) { Proc *p; int state; int status; int coredumped; char jobchar = ' '; char buf[64]; const char *filler; int output = 0; if (how == JP_PGRP) { /* POSIX doesn't say what to do it there is no process * group leader (ie, !FMONITOR). We arbitrarily return * last pid (which is what $! returns). */ shf_fprintf(shf, "%d\n", j->pgrp ? j->pgrp : (j->last_proc ? j->last_proc->pid : 0)); return; } j->flags &= ~JF_CHANGED; filler = j->job > 10 ? "\n " : "\n "; if (j == job_list) jobchar = '+'; else if (j == job_list->next) jobchar = '-'; for (p = j->proc_list; p != (Proc *) 0;) { coredumped = 0; switch (p->state) { case PRUNNING: strlcpy(buf, "Running", sizeof buf); break; case PSTOPPED: strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess, sizeof buf); break; case PEXITED: if (how == JP_SHORT) buf[0] = '\0'; else if (WEXITSTATUS(p->status) == 0) strlcpy(buf, "Done", sizeof buf); else shf_snprintf(buf, sizeof(buf), "Done (%d)", WEXITSTATUS(p->status)); break; case PSIGNALLED: if (WCOREDUMP(p->status)) coredumped = 1; /* kludge for not reporting `normal termination signals' * (ie, SIGINT, SIGPIPE) */ if (how == JP_SHORT && !coredumped && (WTERMSIG(p->status) == SIGINT || WTERMSIG(p->status) == SIGPIPE)) { buf[0] = '\0'; } else strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess, sizeof buf); break; } if (how != JP_SHORT) { if (p == j->proc_list) shf_fprintf(shf, "[%d] %c ", j->job, jobchar); else shf_fprintf(shf, "%s", filler); } if (how == JP_LONG) shf_fprintf(shf, "%5d ", p->pid); if (how == JP_SHORT) { if (buf[0]) { output = 1; shf_fprintf(shf, "%s%s ", buf, coredumped ? " (core dumped)" : null); } } else { output = 1; shf_fprintf(shf, "%-20s %s%s%s", buf, p->command, p->next ? "|" : null, coredumped ? " (core dumped)" : null); } state = p->state; status = p->status; p = p->next; while (p && p->state == state && p->status == status) { if (how == JP_LONG) shf_fprintf(shf, "%s%5d %-20s %s%s", filler, p->pid, space, p->command, p->next ? "|" : null); else if (how == JP_MEDIUM) shf_fprintf(shf, " %s%s", p->command, p->next ? "|" : null); p = p->next; } } if (output) shf_fprintf(shf, newline); }
/* * execute command tree */ int execute(struct op *volatile t, volatile int flags, volatile int *xerrok) /* if XEXEC don't fork */ { int i, dummy = 0; volatile int rv = 0; int pv[2]; char ** volatile ap; char *s, *cp; struct ioword **iowp; struct tbl *tp = NULL; if (t == NULL) return 0; /* Caller doesn't care if XERROK should propagate. */ if (xerrok == NULL) xerrok = &dummy; /* Is this the end of a pipeline? If so, we want to evaluate the * command arguments bool eval_done = false; if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { eval_done = true; tp = eval_execute_args(t, &ap); } */ if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) return exchild(t, flags & ~XTIME, xerrok, -1); /* run in sub-process */ newenv(E_EXEC); if (trap) runtraps(0); if (t->type == TCOM) { /* Clear subst_exstat before argument expansion. Used by * null commands (see comexec() and c_eval()) and by c_set(). */ subst_exstat = 0; current_lineno = t->lineno; /* for $LINENO */ /* POSIX says expand command words first, then redirections, * and assignments last.. */ ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (flags & XTIME) /* Allow option parsing (bizarre, but POSIX) */ timex_hook(t, &ap); if (Flag(FXTRACE) && ap[0]) { shf_fprintf(shl_out, "%s", PS4_SUBSTITUTE(str_val(global("PS4")))); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%s", ap[i], ap[i + 1] ? space : newline); shf_flush(shl_out); } if (ap[0]) tp = findcom(ap[0], FC_BI|FC_FUNC); } flags &= ~XTIME; if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { e->savefd = (short *) alloc(sizeofN(short, NUFILE), ATEMP); /* initialize to not redirected */ memset(e->savefd, 0, sizeofN(short, NUFILE)); }
/* * print variable/alias value using necessary quotes * (POSIX says they should be suitable for re-entry...) * No trailing newline is printed. */ void print_value_quoted(struct shf *shf, const char *s) { unsigned char c; const unsigned char *p = (const unsigned char *)s; bool inquote = true; /* first, check whether any quotes are needed */ while ((c = *p++) >= 32) if (ctype(c, C_QUOTE)) inquote = false; p = (const unsigned char *)s; if (c == 0) { if (inquote) { /* nope, use the shortcut */ shf_puts(s, shf); return; } /* otherwise, quote nicely via state machine */ while ((c = *p++) != 0) { if (c == '\'') { /* * multiple single quotes or any of them * at the beginning of a string look nicer * this way than when simply substituting */ if (inquote) { shf_putc('\'', shf); inquote = false; } shf_putc('\\', shf); } else if (!inquote) { shf_putc('\'', shf); inquote = true; } shf_putc(c, shf); } } else { unsigned int wc; size_t n; /* use $'...' quote format */ shf_putc('$', shf); shf_putc('\'', shf); while ((c = *p) != 0) { if (c >= 0xC2) { n = utf_mbtowc(&wc, (const char *)p); if (n != (size_t)-1) { p += n; shf_fprintf(shf, "\\u%04X", wc); continue; } } ++p; switch (c) { /* see unbksl() in this file for comments */ case 7: c = 'a'; if (0) /* FALLTHROUGH */ case '\b': c = 'b'; if (0) /* FALLTHROUGH */ case '\f': c = 'f'; if (0) /* FALLTHROUGH */ case '\n': c = 'n'; if (0) /* FALLTHROUGH */ case '\r': c = 'r'; if (0) /* FALLTHROUGH */ case '\t': c = 't'; if (0) /* FALLTHROUGH */ case 11: c = 'v'; if (0) /* FALLTHROUGH */ case '\033': /* take E not e because \e is \ in *roff */ c = 'E'; /* FALLTHROUGH */ case '\\': shf_putc('\\', shf); if (0) /* FALLTHROUGH */ default: if (c < 32 || c > 0x7E) { /* FALLTHROUGH */ case '\'': shf_fprintf(shf, "\\%03o", c); break; } shf_putc(c, shf); break; } } inquote = true; } if (inquote) shf_putc('\'', shf); }
static int comexec(struct op *t, struct tbl * volatile tp, const char **ap, volatile int flags, volatile int *xerrok) { int i; volatile int rv = 0; const char *cp; const char **lastp; /* Must be static (XXX but why?) */ static struct op texec; int type_flags; bool keepasn_ok; int fcflags = FC_BI|FC_FUNC|FC_PATH; bool bourne_function_call = false; struct block *l_expand, *l_assign; /* * snag the last argument for $_ XXX not the same as AT&T ksh, * which only seems to set $_ after a newline (but not in * functions/dot scripts, but in interactive and script) - * perhaps save last arg here and set it in shell()?. */ if (Flag(FTALKING) && *(lastp = ap)) { while (*++lastp) ; /* setstr() can't fail here */ setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, KSH_RETURN_ERROR); } /** * Deal with the shell builtins builtin, exec and command since * they can be followed by other commands. This must be done before * we know if we should create a local block which must be done * before we can do a path search (in case the assignments change * PATH). * Odd cases: * FOO=bar exec >/dev/null FOO is kept but not exported * FOO=bar exec foobar FOO is exported * FOO=bar command exec >/dev/null FOO is neither kept nor exported * FOO=bar command FOO is neither kept nor exported * PATH=... foobar use new PATH in foobar search */ keepasn_ok = true; while (tp && tp->type == CSHELL) { /* undo effects of command */ fcflags = FC_BI|FC_FUNC|FC_PATH; if (tp->val.f == c_builtin) { if ((cp = *++ap) == NULL || (!strcmp(cp, "--") && (cp = *++ap) == NULL)) { tp = NULL; break; } if ((tp = findcom(cp, FC_BI)) == NULL) errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin"); continue; } else if (tp->val.f == c_exec) { if (ap[1] == NULL) break; ap++; flags |= XEXEC; } else if (tp->val.f == c_command) { int optc, saw_p = 0; /* * Ugly dealing with options in two places (here * and in c_command(), but such is life) */ ksh_getopt_reset(&builtin_opt, 0); while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p') saw_p = 1; if (optc != EOF) /* command -vV or something */ break; /* don't look for functions */ fcflags = FC_BI|FC_PATH; if (saw_p) { if (Flag(FRESTRICTED)) { warningf(true, "%s: %s", "command -p", "restricted"); rv = 1; goto Leave; } fcflags |= FC_DEFPATH; } ap += builtin_opt.optind; /* * POSIX says special builtins lose their status * if accessed using command. */ keepasn_ok = false; if (!ap[0]) { /* ensure command with no args exits with 0 */ subst_exstat = 0; break; } #ifndef MKSH_NO_EXTERNAL_CAT } else if (tp->val.f == c_cat) { /* * if we have any flags, do not use the builtin * in theory, we could allow -u, but that would * mean to use ksh_getopt here and possibly ad- * ded complexity and more code and isn't worth * additional hassle (and the builtin must call * ksh_getopt already but can't come back here) */ if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' && /* argument, begins with -, is not - or -- */ (ap[1][1] != '-' || ap[1][2] != '\0')) /* don't look for builtins or functions */ fcflags = FC_PATH; else /* go on, use the builtin */ break; #endif #if !defined(MKSH_SMALL) } else if (tp->val.f == c_trap) { t->u.evalflags &= ~DOTCOMEXEC; break; #endif } else break; tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC)); } #if !defined(MKSH_SMALL) if (t->u.evalflags & DOTCOMEXEC) flags |= XEXEC; #endif l_expand = e->loc; if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN)))) type_flags = 0; else { /* create new variable/function block */ newblock(); /* ksh functions don't keep assignments, POSIX functions do. */ if (keepasn_ok && tp && tp->type == CFUNC && !(tp->flag & FKSH)) { bourne_function_call = true; type_flags = EXPORT; } else type_flags = LOCAL|LOCAL_COPY|EXPORT; } l_assign = e->loc; if (Flag(FEXPORT)) type_flags |= EXPORT; for (i = 0; t->vars[i]; i++) { /* do NOT lookup in the new var/fn block just created */ e->loc = l_expand; cp = evalstr(t->vars[i], DOASNTILDE); e->loc = l_assign; /* but assign in there as usual */ if (Flag(FXTRACE)) { if (i == 0) shf_puts(substitute(str_val(global("PS4")), 0), shl_out); shf_fprintf(shl_out, "%s%c", cp, t->vars[i + 1] ? ' ' : '\n'); if (!t->vars[i + 1]) shf_flush(shl_out); } typeset(cp, type_flags, 0, 0, 0); if (bourne_function_call && !(type_flags & EXPORT)) typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0); } if ((cp = *ap) == NULL) { rv = subst_exstat; goto Leave; } else if (!tp) { if (Flag(FRESTRICTED) && vstrchr(cp, '/')) { warningf(true, "%s: %s", cp, "restricted"); rv = 1; goto Leave; } tp = findcom(cp, fcflags); } switch (tp->type) { /* shell built-in */ case CSHELL: rv = call_builtin(tp, (const char **)ap, null); if (!keepasn_ok && tp->val.f == c_shift) { l_expand->argc = l_assign->argc; l_expand->argv = l_assign->argv; } break; /* function call */ case CFUNC: { volatile unsigned char old_xflag; volatile uint32_t old_inuse; const char * volatile old_kshname; if (!(tp->flag & ISSET)) { struct tbl *ftp; if (!tp->u.fpath) { rv = (tp->u2.errnov == ENOENT) ? 127 : 126; warningf(true, "%s: %s %s: %s", cp, "can't find", "function definition file", cstrerror(tp->u2.errnov)); break; } if (include(tp->u.fpath, 0, NULL, false) < 0) { rv = errno; warningf(true, "%s: %s %s %s: %s", cp, "can't open", "function definition file", tp->u.fpath, cstrerror(rv)); rv = 127; break; } if (!(ftp = findfunc(cp, hash(cp), false)) || !(ftp->flag & ISSET)) { warningf(true, "%s: %s %s", cp, "function not defined by", tp->u.fpath); rv = 127; break; } tp = ftp; } /* * ksh functions set $0 to function name, POSIX * functions leave $0 unchanged. */ old_kshname = kshname; if (tp->flag & FKSH) kshname = ap[0]; else ap[0] = kshname; e->loc->argv = ap; for (i = 0; *ap++ != NULL; i++) ; e->loc->argc = i - 1; /* * ksh-style functions handle getopts sanely, * Bourne/POSIX functions are insane... */ if (tp->flag & FKSH) { e->loc->flags |= BF_DOGETOPTS; e->loc->getopts_state = user_opt; getopts_reset(1); } old_xflag = Flag(FXTRACE); Flag(FXTRACE) |= tp->flag & TRACE ? 1 : 0; old_inuse = tp->flag & FINUSE; tp->flag |= FINUSE; e->type = E_FUNC; if (!(i = kshsetjmp(e->jbuf))) { execute(tp->val.t, flags & XERROK, NULL); i = LRETURN; } kshname = old_kshname; Flag(FXTRACE) = old_xflag; tp->flag = (tp->flag & ~FINUSE) | old_inuse; /* * Were we deleted while executing? If so, free the * execution tree. TODO: Unfortunately, the table entry * is never re-used until the lookup table is expanded. */ if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { if (tp->flag & ALLOC) { tp->flag &= ~ALLOC; tfree(tp->val.t, tp->areap); } tp->flag = 0; } switch (i) { case LRETURN: case LERROR: rv = exstat & 0xFF; break; case LINTR: case LEXIT: case LLEAVE: case LSHELL: quitenv(NULL); unwind(i); /* NOTREACHED */ default: quitenv(NULL); internal_errorf("%s %d", "CFUNC", i); } break; } /* executable command */ case CEXEC: /* tracked alias */ case CTALIAS: if (!(tp->flag&ISSET)) { if (tp->u2.errnov == ENOENT) { rv = 127; warningf(true, "%s: %s", cp, "not found"); } else { rv = 126; warningf(true, "%s: %s: %s", cp, "can't execute", cstrerror(tp->u2.errnov)); } break; } /* set $_ to programme's full path */ /* setstr() can't fail here */ setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), tp->val.s, KSH_RETURN_ERROR); if (flags&XEXEC) { j_exit(); if (!(flags&XBGND) #ifndef MKSH_UNEMPLOYED || Flag(FMONITOR) #endif ) { setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG); setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG); } } /* to fork we set up a TEXEC node and call execute */ texec.type = TEXEC; /* for tprint */ texec.left = t; texec.str = tp->val.s; texec.args = ap; rv = exchild(&texec, flags, xerrok, -1); break; } Leave: if (flags & XEXEC) { exstat = rv & 0xFF; unwind(LLEAVE); } return (rv); }
/* * execute command tree */ int execute(struct op * volatile t, /* if XEXEC don't fork */ volatile int flags, volatile int * volatile xerrok) { int i; volatile int rv = 0, dummy = 0; int pv[2]; const char ** volatile ap = NULL; char ** volatile up; const char *s, *ccp; struct ioword **iowp; struct tbl *tp = NULL; char *cp; if (t == NULL) return (0); /* Caller doesn't care if XERROK should propagate. */ if (xerrok == NULL) xerrok = &dummy; if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE) /* run in sub-process */ return (exchild(t, flags & ~XTIME, xerrok, -1)); newenv(E_EXEC); if (trap) runtraps(0); /* we want to run an executable, do some variance checks */ if (t->type == TCOM) { /* check if this is 'var=<<EOF' */ if ( /* we have zero arguments, i.e. no programme to run */ t->args[0] == NULL && /* we have exactly one variable assignment */ t->vars[0] != NULL && t->vars[1] == NULL && /* we have exactly one I/O redirection */ t->ioact != NULL && t->ioact[0] != NULL && t->ioact[1] == NULL && /* of type "here document" (or "here string") */ (t->ioact[0]->flag & IOTYPE) == IOHERE && /* the variable assignment begins with a valid varname */ (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] && /* and has no right-hand side (i.e. "varname=") */ ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS && /* plus we can have a here document content */ herein(t->ioact[0], &cp) == 0 && cp && *cp) { char *sp = cp, *dp; size_t n = ccp - t->vars[0] + 2, z; /* drop redirection (will be garbage collected) */ t->ioact = NULL; /* set variable to its expanded value */ z = strlen(cp) + 1; if (notoktomul(z, 2) || notoktoadd(z * 2, n)) internal_errorf(Toomem, (unsigned long)-1); dp = alloc(z * 2 + n, ATEMP); memcpy(dp, t->vars[0], n); t->vars[0] = dp; dp += n; while (*sp) { *dp++ = QCHAR; *dp++ = *sp++; } *dp = EOS; /* free the expanded value */ afree(cp, APERM); } /* * Clear subst_exstat before argument expansion. Used by * null commands (see comexec() and c_eval()) and by c_set(). */ subst_exstat = 0; /* for $LINENO */ current_lineno = t->lineno; /* * POSIX says expand command words first, then redirections, * and assignments last.. */ up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE); if (flags & XTIME) /* Allow option parsing (bizarre, but POSIX) */ timex_hook(t, &up); ap = (const char **)up; if (Flag(FXTRACE) && ap[0]) { shf_puts(substitute(str_val(global("PS4")), 0), shl_out); for (i = 0; ap[i]; i++) shf_fprintf(shl_out, "%s%c", ap[i], ap[i + 1] ? ' ' : '\n'); shf_flush(shl_out); } if (ap[0]) tp = findcom(ap[0], FC_BI|FC_FUNC); } flags &= ~XTIME; if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { e->savefd = alloc2(NUFILE, sizeof(short), ATEMP); /* initialise to not redirected */ memset(e->savefd, 0, NUFILE * sizeof(short)); } /* mark for replacement later (unless TPIPE) */ vp_pipest->flag |= INT_L; /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { if (iosetup(*iowp, tp) < 0) { exstat = rv = 1; /* * Redirection failures for special commands * cause (non-interactive) shell to exit. */ if (tp && tp->type == CSHELL && (tp->flag & SPEC_BI)) errorfz(); /* Deal with FERREXIT, quitenv(), etc. */ goto Break; } } switch (t->type) { case TCOM: rv = comexec(t, tp, (const char **)ap, flags, xerrok); break; case TPAREN: rv = execute(t->left, flags | XFORK, xerrok); break; case TPIPE: flags |= XFORK; flags &= ~XEXEC; e->savefd[0] = savefd(0); e->savefd[1] = savefd(1); while (t->type == TPIPE) { openpipe(pv); /* stdout of curr */ ksh_dup2(pv[1], 1, false); /** * Let exchild() close pv[0] in child * (if this isn't done, commands like * (: ; cat /etc/termcap) | sleep 1 * will hang forever). */ exchild(t->left, flags | XPIPEO | XCCLOSE, NULL, pv[0]); /* stdin of next */ ksh_dup2(pv[0], 0, false); closepipe(pv); flags |= XPIPEI; t = t->right; } /* stdout of last */ restfd(1, e->savefd[1]); /* no need to re-restore this */ e->savefd[1] = 0; /* Let exchild() close 0 in parent, after fork, before wait */ i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0); if (!(flags&XBGND) && !(flags&XXCOM)) rv = i; break; case TLIST: while (t->type == TLIST) { execute(t->left, flags & XERROK, NULL); t = t->right; } rv = execute(t, flags & XERROK, xerrok); break; case TCOPROC: { #ifndef MKSH_NOPROSPECTOFWORK sigset_t omask; /* * Block sigchild as we are using things changed in the * signal handler */ sigprocmask(SIG_BLOCK, &sm_sigchld, &omask); e->type = E_ERRH; if ((i = kshsetjmp(e->jbuf))) { sigprocmask(SIG_SETMASK, &omask, NULL); quitenv(NULL); unwind(i); /* NOTREACHED */ } #endif /* Already have a (live) co-process? */ if (coproc.job && coproc.write >= 0) errorf("coprocess already exists"); /* Can we re-use the existing co-process pipe? */ coproc_cleanup(true); /* do this before opening pipes, in case these fail */ e->savefd[0] = savefd(0); e->savefd[1] = savefd(1); openpipe(pv); if (pv[0] != 0) { ksh_dup2(pv[0], 0, false); close(pv[0]); } coproc.write = pv[1]; coproc.job = NULL; if (coproc.readw >= 0) ksh_dup2(coproc.readw, 1, false); else { openpipe(pv); coproc.read = pv[0]; ksh_dup2(pv[1], 1, false); /* closed before first read */ coproc.readw = pv[1]; coproc.njobs = 0; /* create new coprocess id */ ++coproc.id; } #ifndef MKSH_NOPROSPECTOFWORK sigprocmask(SIG_SETMASK, &omask, NULL); /* no more need for error handler */ e->type = E_EXEC; #endif /* * exchild() closes coproc.* in child after fork, * will also increment coproc.njobs when the * job is actually created. */ flags &= ~XEXEC; exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE, NULL, coproc.readw); break; } case TASYNC: /* * XXX non-optimal, I think - "(foo &)", forks for (), * forks again for async... parent should optimise * this to "foo &"... */ rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok); break; case TOR: case TAND: rv = execute(t->left, XERROK, xerrok); if ((rv == 0) == (t->type == TAND)) rv = execute(t->right, XERROK, xerrok); flags |= XERROK; if (xerrok) *xerrok = 1; break; case TBANG: rv = !execute(t->right, XERROK, xerrok); flags |= XERROK; if (xerrok) *xerrok = 1; break; case TDBRACKET: { Test_env te; te.flags = TEF_DBRACKET; te.pos.wp = t->args; te.isa = dbteste_isa; te.getopnd = dbteste_getopnd; te.eval = test_eval; te.error = dbteste_error; rv = test_parse(&te); break; } case TFOR: case TSELECT: { volatile bool is_first = true; ap = (t->vars == NULL) ? e->loc->argv + 1 : (const char **)eval((const char **)t->vars, DOBLANK | DOGLOB | DOTILDE); e->type = E_LOOP; while ((i = kshsetjmp(e->jbuf))) { if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(NULL); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } /* in case of a continue */ rv = 0; if (t->type == TFOR) { while (*ap != NULL) { setstr(global(t->str), *ap++, KSH_UNWIND_ERROR); rv = execute(t->left, flags & XERROK, xerrok); } } else { /* TSELECT */ for (;;) { if (!(ccp = do_selectargs(ap, is_first))) { rv = 1; break; } is_first = false; setstr(global(t->str), ccp, KSH_UNWIND_ERROR); execute(t->left, flags & XERROK, xerrok); } } break; } case TWHILE: case TUNTIL: e->type = E_LOOP; while ((i = kshsetjmp(e->jbuf))) { if ((e->flags&EF_BRKCONT_PASS) || (i != LBREAK && i != LCONTIN)) { quitenv(NULL); unwind(i); } else if (i == LBREAK) { rv = 0; goto Break; } } /* in case of a continue */ rv = 0; while ((execute(t->left, XERROK, NULL) == 0) == (t->type == TWHILE)) rv = execute(t->right, flags & XERROK, xerrok); break; case TIF: case TELIF: if (t->right == NULL) /* should be error */ break; rv = execute(t->left, XERROK, NULL) == 0 ? execute(t->right->left, flags & XERROK, xerrok) : execute(t->right->right, flags & XERROK, xerrok); break; case TCASE: i = 0; ccp = evalstr(t->str, DOTILDE); for (t = t->left; t != NULL && t->type == TPAT; t = t->right) { for (ap = (const char **)t->vars; *ap; ap++) { if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) && gmatchx(ccp, s, false))) { rv = execute(t->left, flags & XERROK, xerrok); i = 0; switch (t->u.charflag) { case '&': i = 1; /* FALLTHROUGH */ case '|': goto TCASE_next; } goto TCASE_out; } } i = 0; TCASE_next: /* empty */; } TCASE_out: break; case TBRACE: rv = execute(t->left, flags & XERROK, xerrok); break; case TFUNCT: rv = define(t->str, t); break; case TTIME: /* * Clear XEXEC so nested execute() call doesn't exit * (allows "ls -l | time grep foo"). */ rv = timex(t, flags & ~XEXEC, xerrok); break; case TEXEC: /* an eval'd TCOM */ s = t->args[0]; up = makenv(); restoresigs(); cleanup_proc_env(); { union mksh_ccphack cargs; cargs.ro = t->args; execve(t->str, cargs.rw, up); rv = errno; } if (rv == ENOEXEC) scriptexec(t, (const char **)up); else errorf("%s: %s", s, cstrerror(rv)); } Break: exstat = rv & 0xFF; if (vp_pipest->flag & INT_L) { unset(vp_pipest, 1); vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY | ARRAY | INT_U; vp_pipest->val.i = rv; } /* restores IO */ quitenv(NULL); if ((flags&XEXEC)) /* exit child */ unwind(LEXIT); if (rv != 0 && !(flags & XERROK) && (xerrok == NULL || !*xerrok)) { if (Flag(FERREXIT) & 0x80) { /* inside eval */ Flag(FERREXIT) = 0; } else { trapsig(ksh_SIGERR); if (Flag(FERREXIT)) unwind(LERROR); } } return (rv); }
/* * Print things in columns and rows - func() is called to format * the i-th element */ void print_columns(struct shf *shf, int n, char *(*func)(char *, int, int, const void *), const void *arg, int max_oct, int max_col, bool prefcol) { int i, r, c, rows, cols, nspace; char *str; if (n <= 0) { #ifndef MKSH_SMALL internal_warningf("print_columns called with n=%d <= 0", n); #endif return; } ++max_oct; str = alloc(max_oct, ATEMP); /* ensure x_cols is valid first */ if (x_cols < MIN_COLS) change_winsz(); /* * We use (max_col + 1) to consider the space separator. * Note that no space is printed after the last column * to avoid problems with terminals that have auto-wrap. */ cols = x_cols / (max_col + 1); /* if we can only print one column anyway, skip the goo */ if (cols < 2) { for (i = 0; i < n; ++i) shf_fprintf(shf, "%s \n", (*func)(str, max_oct, i, arg)); goto out; } rows = (n + cols - 1) / cols; if (prefcol && cols > rows) { i = rows; rows = cols > n ? n : cols; cols = i; } max_col = -max_col; nspace = (x_cols + max_col * cols) / cols; if (nspace <= 0) nspace = 1; for (r = 0; r < rows; r++) { for (c = 0; c < cols; c++) { i = c * rows + r; if (i < n) { shf_fprintf(shf, "%*s", max_col, (*func)(str, max_oct, i, arg)); if (c + 1 < cols) shf_fprintf(shf, "%*s", nspace, null); } } shf_putchar('\n', shf); } out: afree(str, ATEMP); }