/* 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); } }
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; } }
/* like errorf(), but no unwind is done */ void warningf(bool show_lineno, const char *fmt, ...) { va_list va; error_prefix(show_lineno); va_start(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); shf_flush(shl_out); }
/* 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); }
/* 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); }
/* A shell error occurred (eg, syntax error, etc.) */ void 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); va_start(va, fmt); shf_vfprintf(shl_out, fmt, va); va_end(va); shf_putchar('\n', shl_out); } shf_flush(shl_out); 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); }
/* variant of fputs for ptreef and wdstrip */ static const char * wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) { int c; const char *cs; /*- * problems: * `...` -> $(...) * 'foo' -> "foo" * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS * x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ * could change encoding to: * OQUOTE ["'] ... CQUOTE ["'] * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) */ while (/* CONSTCOND */ 1) switch (*wp++) { case EOS: return (--wp); case ADELIM: case CHAR: c = *wp++; if ((opmode & WDS_MAGIC) && (ISMAGIC(c) || c == '[' || c == '!' || c == '-' || c == ']' || c == '*' || c == '?')) shf_putc(MAGIC, shf); shf_putc(c, shf); break; case QCHAR: { bool doq; c = *wp++; doq = (c == '"' || c == '`' || c == '$' || c == '\\'); if (opmode & WDS_TPUTS) { if (quotelevel == 0) doq = true; } else { if (!(opmode & WDS_KEEPQ)) doq = false; } if (doq) shf_putc('\\', shf); shf_putc(c, shf); break; } case COMSUB: shf_puts("$(", shf); cs = ")"; pSUB: while ((c = *wp++) != 0) shf_putc(c, shf); shf_puts(cs, shf); break; case FUNSUB: c = ' '; if (0) /* FALLTHROUGH */ case VALSUB: c = '|'; shf_putc('$', shf); shf_putc('{', shf); shf_putc(c, shf); cs = ";}"; goto pSUB; case EXPRSUB: shf_puts("$((", shf); cs = "))"; goto pSUB; case OQUOTE: if (opmode & WDS_TPUTS) { quotelevel++; shf_putc('"', shf); } break; case CQUOTE: if (opmode & WDS_TPUTS) { if (quotelevel) quotelevel--; shf_putc('"', shf); } break; case OSUBST: shf_putc('$', shf); if (*wp++ == '{') shf_putc('{', shf); while ((c = *wp++) != 0) shf_putc(c, shf); wp = wdvarput(shf, wp, 0, opmode); break; case CSUBST: if (*wp++ == '}') shf_putc('}', shf); return (wp); case OPAT: if (opmode & WDS_MAGIC) { shf_putc(MAGIC, shf); shf_putchar(*wp++ | 0x80, shf); } else { shf_putchar(*wp++, shf); shf_putc('(', shf); } break; case SPAT: c = '|'; if (0) case CPAT: c = /*(*/ ')'; if (opmode & WDS_MAGIC) shf_putc(MAGIC, shf); shf_putc(c, shf); break; } }
/* 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; }
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, 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); }
/* * 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); }
/* variant of fputs for ptreef and wdstrip */ static const char * wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode) { int c; const char *cs; /*- * problems: * `...` -> $(...) * 'foo' -> "foo" * x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS * x${foo:-'hi'} -> x${foo:-hi} * could change encoding to: * OQUOTE ["'] ... CQUOTE ["'] * COMSUB [(`] ...\0 (handle $ ` \ and maybe " in `...` case) */ while (/* CONSTCOND */ 1) switch (*wp++) { case EOS: return (--wp); case ADELIM: if (ord(*wp) == ORD(/*{*/ '}')) { ++wp; goto wdvarput_csubst; } /* FALLTHROUGH */ case CHAR: c = ord(*wp++); shf_putc(c, shf); break; case QCHAR: c = ord(*wp++); if (opmode & WDS_TPUTS) switch (c) { case ORD('\n'): if (quotelevel == 0) { c = ORD('\''); shf_putc(c, shf); shf_putc(ORD('\n'), shf); } break; default: if (quotelevel == 0) /* FALLTHROUGH */ case ORD('"'): case ORD('`'): case ORD('$'): case ORD('\\'): shf_putc(ORD('\\'), shf); break; } shf_putc(c, shf); break; case COMASUB: case COMSUB: shf_puts("$(", shf); cs = ")"; if (ord(*wp) == ORD('(' /*)*/)) shf_putc(' ', shf); pSUB: while ((c = *wp++) != 0) shf_putc(c, shf); shf_puts(cs, shf); break; case FUNASUB: case FUNSUB: c = ORD(' '); if (0) /* FALLTHROUGH */ case VALSUB: c = ORD('|'); shf_putc('$', shf); shf_putc('{', shf); shf_putc(c, shf); cs = ";}"; goto pSUB; case EXPRSUB: shf_puts("$((", shf); cs = "))"; goto pSUB; case OQUOTE: if (opmode & WDS_TPUTS) { quotelevel++; shf_putc('"', shf); } break; case CQUOTE: if (opmode & WDS_TPUTS) { if (quotelevel) quotelevel--; shf_putc('"', shf); } break; case OSUBST: shf_putc('$', shf); if (ord(*wp++) == ORD('{')) shf_putc('{', shf); while ((c = *wp++) != 0) shf_putc(c, shf); wp = wdvarput(shf, wp, 0, opmode); break; case CSUBST: if (ord(*wp++) == ORD('}')) { wdvarput_csubst: shf_putc('}', shf); } return (wp); case OPAT: shf_putchar(*wp++, shf); shf_putc('(', shf); break; case SPAT: c = ORD('|'); if (0) /* FALLTHROUGH */ case CPAT: c = ORD(/*(*/ ')'); shf_putc(c, shf); break; } }