OSDN Git Service

Upgrade to mksh R53a.
[android-x86/external-mksh.git] / src / jobs.c
index 47326a1..0366004 100644 (file)
@@ -1,8 +1,9 @@
-/*     $OpenBSD: jobs.c,v 1.38 2009/12/12 04:28:44 deraadt Exp $       */
+/*     $OpenBSD: jobs.c,v 1.43 2015/09/10 22:48:58 nicm Exp $  */
 
 /*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009
- *     Thorsten Glaser <tg@mirbsd.org>
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
+ *              2012, 2013, 2014, 2015, 2016
+ *     mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
  * are retained or reproduced in an accompanying document, permission
@@ -22,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.69 2010/07/04 17:33:54 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.121 2016/07/25 00:04:44 tg Exp $");
 
 #if HAVE_KILLPG
 #define mksh_killpg            killpg
@@ -37,17 +38,18 @@ __RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.69 2010/07/04 17:33:54 tg Exp $");
 #define PSIGNALLED     2
 #define PSTOPPED       3
 
-typedef struct proc    Proc;
+typedef struct proc Proc;
 struct proc {
        Proc *next;             /* next process in pipeline (if any) */
        pid_t pid;              /* process id */
        int state;
        int status;             /* wait status */
-       char command[48];       /* process command string */
+       /* process command string from vistree */
+       char command[256 - (ALLOC_OVERHEAD + sizeof(Proc *) +
+           sizeof(pid_t) + 2 * sizeof(int))];
 };
 
 /* Notify/print flag - j_print() argument */
-#define JP_NONE                0       /* don't print anything */
 #define JP_SHORT       1       /* print signals processes were killed by */
 #define JP_MEDIUM      2       /* print [job-num] -/+ command */
 #define JP_LONG                3       /* print [job-num] -/+ pid command */
@@ -63,7 +65,7 @@ struct proc {
 #define JF_W_ASYNCNOTIFY 0x004 /* set if waiting and async notification ok */
 #define JF_XXCOM       0x008   /* set for $(command) jobs */
 #define JF_FG          0x010   /* running in foreground (also has tty pgrp) */
-#define JF_SAVEDTTY    0x020   /* j->ttystate is valid */
+#define JF_SAVEDTTY    0x020   /* j->ttystat is valid */
 #define JF_CHANGED     0x040   /* process has changed state */
 #define JF_KNOWN       0x080   /* $! referenced */
 #define JF_ZOMBIE      0x100   /* known, unwaited process */
@@ -84,10 +86,10 @@ struct job {
        int flags;              /* see JF_* */
        volatile int state;     /* job state */
        int status;             /* exit status of last process */
-       int32_t age;            /* number of jobs started */
+       int age;                /* number of jobs started */
        Coproc_id coproc_id;    /* 0 or id of coprocess output pipe */
 #ifndef MKSH_UNEMPLOYED
-       struct termios ttystate;/* saved tty state for stopped jobs */
+       mksh_ttyst ttystat;     /* saved tty state for stopped jobs */
        pid_t saved_ttypgrp;    /* saved tty process group for stopped jobs */
 #endif
 };
@@ -97,19 +99,17 @@ struct job {
 #define JW_INTERRUPT   0x01    /* ^C will stop the wait */
 #define JW_ASYNCNOTIFY 0x02    /* asynchronous notification during wait ok */
 #define JW_STOPPEDWAIT 0x04    /* wait even if job stopped */
+#define JW_PIPEST      0x08    /* want PIPESTATUS */
 
 /* Error codes for j_lookup() */
-#define JL_OK          0
-#define JL_NOSUCH      1       /* no such job */
-#define JL_AMBIG       2       /* %foo or %?foo is ambiguous */
-#define JL_INVALID     3       /* non-pid, non-% job id */
+#define JL_NOSUCH      0       /* no such job */
+#define JL_AMBIG       1       /* %foo or %?foo is ambiguous */
+#define JL_INVALID     2       /* non-pid, non-% job id */
 
-static const char *const lookup_msgs[] = {
-       null,
+static const char * const lookup_msgs[] = {
        "no such job",
        "ambiguous",
-       "argument must be %job or process id",
-       NULL
+       "argument must be %job or process id"
 };
 
 static Job *job_list;          /* job list */
@@ -118,14 +118,16 @@ static Job *async_job;
 static pid_t async_pid;
 
 static int nzombie;            /* # of zombies owned by this process */
-static int32_t njobs;          /* # of jobs started */
+static int njobs;              /* # of jobs started */
 
 #ifndef CHILD_MAX
 #define CHILD_MAX      25
 #endif
 
+#ifndef MKSH_NOPROSPECTOFWORK
 /* held_sigchld is set if sigchld occurs before a job is completely started */
 static volatile sig_atomic_t held_sigchld;
+#endif
 
 #ifndef MKSH_UNEMPLOYED
 static struct shf      *shl_j;
@@ -147,6 +149,9 @@ static void         put_job(Job *, int);
 static void            remove_job(Job *, const char *);
 static int             kill_job(Job *, int);
 
+static void tty_init_talking(void);
+static void tty_init_state(void);
+
 /* initialise job control */
 void
 j_init(void)
@@ -157,6 +162,7 @@ j_init(void)
        Flag(FMONITOR) = 0;
 #endif
 
+#ifndef MKSH_NOPROSPECTOFWORK
        (void)sigemptyset(&sm_default);
        sigprocmask(SIG_SETMASK, &sm_default, NULL);
 
@@ -165,6 +171,10 @@ j_init(void)
 
        setsig(&sigtraps[SIGCHLD], j_sigchld,
            SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
+#else
+       /* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */
+       setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE);
+#endif
 
 #ifndef MKSH_UNEMPLOYED
        if (!mflagset && Flag(FTALKING))
@@ -191,22 +201,85 @@ j_init(void)
                }
        }
 
-       /* j_change() calls tty_init() */
+       /* j_change() calls tty_init_talking() and tty_init_state() */
        if (Flag(FMONITOR))
                j_change();
        else
 #endif
-         if (Flag(FTALKING))
-               tty_init(true, true);
+         if (Flag(FTALKING)) {
+               tty_init_talking();
+               tty_init_state();
+       }
+}
+
+static int
+proc_errorlevel(Proc *p)
+{
+       switch (p->state) {
+       case PEXITED:
+               return ((WEXITSTATUS(p->status)) & 255);
+       case PSIGNALLED:
+               return (ksh_sigmask(WTERMSIG(p->status)));
+       default:
+               return (0);
+       }
+}
+
+#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
+/* suspend the shell */
+void
+j_suspend(void)
+{
+       struct sigaction sa, osa;
+
+       /* Restore tty and pgrp. */
+       if (ttypgrp_ok) {
+               if (tty_hasstate)
+                       mksh_tcset(tty_fd, &tty_state);
+               if (restore_ttypgrp >= 0) {
+                       if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) {
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "tcsetpgrp", cstrerror(errno));
+                       } else if (setpgid(0, restore_ttypgrp) < 0) {
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "setpgid", cstrerror(errno));
+                       }
+               }
+       }
+
+       /* Suspend the shell. */
+       memset(&sa, 0, sizeof(sa));
+       sigemptyset(&sa.sa_mask);
+       sa.sa_handler = SIG_DFL;
+       sigaction(SIGTSTP, &sa, &osa);
+       kill(0, SIGTSTP);
+
+       /* Back from suspend, reset signals, pgrp and tty. */
+       sigaction(SIGTSTP, &osa, NULL);
+       if (ttypgrp_ok) {
+               if (restore_ttypgrp >= 0) {
+                       if (setpgid(0, kshpid) < 0) {
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "setpgid", cstrerror(errno));
+                               ttypgrp_ok = false;
+                       } else if (tcsetpgrp(tty_fd, kshpid) < 0) {
+                               warningf(false, Tf_ssfaileds,
+                                   Tj_suspend, "tcsetpgrp", cstrerror(errno));
+                               ttypgrp_ok = false;
+                       }
+               }
+               tty_init_state();
+       }
 }
+#endif
 
 /* job cleanup before shell exit */
 void
 j_exit(void)
 {
        /* kill stopped, and possibly running, jobs */
-       Job     *j;
-       int     killed = 0;
+       Job *j;
+       bool killed = false;
 
        for (j = job_list; j != NULL; j = j->next) {
                if (j->ppid == procpid &&
@@ -214,7 +287,7 @@ j_exit(void)
                    (j->state == PRUNNING &&
                    ((j->flags & JF_FG) ||
                    (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) {
-                       killed = 1;
+                       killed = true;
                        if (j->pgrp == 0)
                                kill_job(j, SIGHUP);
                        else
@@ -263,22 +336,22 @@ j_change(void)
        if (Flag(FMONITOR)) {
                bool use_tty = Flag(FTALKING);
 
-               /* Don't call tcgetattr() 'til we own the tty process group */
+               /* don't call mksh_tcget until we own the tty process group */
                if (use_tty)
-                       tty_init(false, true);
+                       tty_init_talking();
 
                /* no controlling tty, no SIGT* */
-               if ((ttypgrp_ok = use_tty && tty_fd >= 0 && tty_devtty)) {
+               if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
                        setsig(&sigtraps[SIGTTIN], SIG_DFL,
                            SS_RESTORE_ORIG|SS_FORCE);
                        /* wait to be given tty (POSIX.1, B.2, job control) */
-                       while (1) {
+                       while (/* CONSTCOND */ 1) {
                                pid_t ttypgrp;
 
                                if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
-                                       warningf(false,
-                                           "j_init: tcgetpgrp() failed: %s",
-                                           strerror(errno));
+                                       warningf(false, Tf_ssfaileds,
+                                           "j_init", "tcgetpgrp",
+                                           cstrerror(errno));
                                        ttypgrp_ok = false;
                                        break;
                                }
@@ -292,25 +365,25 @@ j_change(void)
                            SS_RESTORE_DFL|SS_FORCE);
                if (ttypgrp_ok && kshpgrp != kshpid) {
                        if (setpgid(0, kshpid) < 0) {
-                               warningf(false,
-                                   "j_init: setpgid() failed: %s",
-                                   strerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   "j_init", "setpgid", cstrerror(errno));
                                ttypgrp_ok = false;
                        } else {
                                if (tcsetpgrp(tty_fd, kshpid) < 0) {
-                                       warningf(false,
-                                           "j_init: tcsetpgrp() failed: %s",
-                                           strerror(errno));
+                                       warningf(false, Tf_ssfaileds,
+                                           "j_init", "tcsetpgrp",
+                                           cstrerror(errno));
                                        ttypgrp_ok = false;
                                } else
                                        restore_ttypgrp = kshpgrp;
                                kshpgrp = kshpid;
                        }
                }
+#ifndef MKSH_DISABLE_TTY_WARNING
                if (use_tty && !ttypgrp_ok)
-                       warningf(false, "warning: won't have full job control");
-               if (tty_fd >= 0)
-                       tcgetattr(tty_fd, &tty_state);
+                       warningf(false, Tf_sD_s, "warning",
+                           "won't have full job control");
+#endif
        } else {
                ttypgrp_ok = false;
                if (Flag(FTALKING))
@@ -326,9 +399,26 @@ j_change(void)
                                            SIG_IGN : SIG_DFL,
                                            SS_RESTORE_ORIG|SS_FORCE);
                        }
-               if (!Flag(FTALKING))
-                       tty_close();
        }
+       tty_init_state();
+}
+#endif
+
+#if HAVE_NICE
+/* run nice(3) and ignore the result */
+static void
+ksh_nice(int ness)
+{
+#if defined(__USE_FORTIFY_LEVEL) && (__USE_FORTIFY_LEVEL > 0)
+       int eno;
+
+       errno = 0;
+       /* this is gonna annoy users; complain to your distro, people! */
+       if (nice(ness) == -1 && (eno = errno) != 0)
+               warningf(false, Tf_sD_s, "bgnice", cstrerror(eno));
+#else
+       (void)nice(ness);
+#endif
 }
 #endif
 
@@ -336,17 +426,24 @@ j_change(void)
 int
 exchild(struct op *t, int flags,
     volatile int *xerrok,
-    /* used if XPCLOSE or XCCLOSE */ int close_fd)
+    /* used if XPCLOSE or XCCLOSE */
+    int close_fd)
 {
-       static Proc *last_proc;         /* for pipelines */
+       /* for pipelines */
+       static Proc *last_proc;
 
-       int rv = 0, forksleep;
+       int rv = 0, forksleep, jwflags = JW_NONE;
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
-       struct {
-               Proc *p;
-               Job *j;
-               pid_t cldpid;
-       } pi;
+#endif
+       Proc *p;
+       Job *j;
+       pid_t cldpid;
+
+       if (flags & XPIPEST) {
+               flags &= ~XPIPEST;
+               jwflags |= JW_PIPEST;
+       }
 
        if (flags & XEXEC)
                /*
@@ -355,103 +452,108 @@ exchild(struct op *t, int flags,
                 */
                return (execute(t, flags & (XEXEC | XERROK), xerrok));
 
+#ifndef MKSH_NOPROSPECTOFWORK
        /* no SIGCHLDs while messing with job and process lists */
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
-       pi.p = new_proc();
-       pi.p->next = NULL;
-       pi.p->state = PRUNNING;
-       pi.p->status = 0;
-       pi.p->pid = 0;
+       p = new_proc();
+       p->next = NULL;
+       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(
-                           "exchild: XPIPEI and no last_job - pid %d",
+                       internal_errorf("exchild: XPIPEI and no last_job - pid %d",
                            (int)procpid);
-               pi.j = last_job;
+               j = last_job;
                if (last_proc)
-                       last_proc->next = pi.p;
-               last_proc = pi.p;
+                       last_proc->next = p;
+               last_proc = p;
        } else {
-               pi.j = new_job(); /* fills in pi.j->job */
+               /* fills in j->job */
+               j = new_job();
                /*
                 * we don't consider XXCOMs foreground since they don't get
                 * tty process group and we don't save or restore tty modes.
                 */
-               pi.j->flags = (flags & XXCOM) ? JF_XXCOM :
+               j->flags = (flags & XXCOM) ? JF_XXCOM :
                    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
-               timerclear(&pi.j->usrtime);
-               timerclear(&pi.j->systime);
-               pi.j->state = PRUNNING;
-               pi.j->pgrp = 0;
-               pi.j->ppid = procpid;
-               pi.j->age = ++njobs;
-               pi.j->proc_list = pi.p;
-               pi.j->coproc_id = 0;
-               last_job = pi.j;
-               last_proc = pi.p;
-               put_job(pi.j, PJ_PAST_STOPPED);
+               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(pi.p->command, sizeof(pi.p->command), "%T", t);
+       vistree(p->command, sizeof(p->command), t);
 
        /* create child process */
        forksleep = 1;
-       while ((pi.cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
-               if (intrsig)     /* allow user to ^C out... */
+       while ((cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
+               if (intrsig)
+                       /* allow user to ^C out... */
                        break;
                sleep(forksleep);
                forksleep <<= 1;
        }
-       if (pi.cldpid < 0) {
-               kill_job(pi.j, SIGKILL);
-               remove_job(pi.j, "fork failed");
+       /* ensure $RANDOM changes between parent and child */
+       rndset((unsigned long)cldpid);
+       /* fork failed? */
+       if (cldpid < 0) {
+               kill_job(j, SIGKILL);
+               remove_job(j, "fork failed");
+#ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               errorf("cannot fork - try again");
+#endif
+               errorf("can't fork - try again");
        }
-       pi.p->pid = pi.cldpid ? pi.cldpid : (procpid = getpid());
-
-       /*
-        * ensure next child gets a (slightly) different $RANDOM sequence
-        * from its parent process and other child processes
-        */
-       change_random(&pi, sizeof(pi));
+       p->pid = cldpid ? cldpid : (procpid = getpid());
 
 #ifndef MKSH_UNEMPLOYED
        /* job control set up */
        if (Flag(FMONITOR) && !(flags&XXCOM)) {
-               int     dotty = 0;
-               if (pi.j->pgrp == 0) {  /* First process */
-                       pi.j->pgrp = pi.p->pid;
-                       dotty = 1;
+               bool dotty = false;
+
+               if (j->pgrp == 0) {
+                       /* First process */
+                       j->pgrp = p->pid;
+                       dotty = true;
                }
 
-               /* set pgrp in both parent and child to deal with race
+               /*
+                * set pgrp in both parent and child to deal with race
                 * condition
                 */
-               setpgid(pi.p->pid, pi.j->pgrp);
+               setpgid(p->pid, j->pgrp);
                if (ttypgrp_ok && dotty && !(flags & XBGND))
-                       tcsetpgrp(tty_fd, pi.j->pgrp);
+                       tcsetpgrp(tty_fd, j->pgrp);
        }
 #endif
 
        /* used to close pipe input fd */
-       if (close_fd >= 0 && (((flags & XPCLOSE) && pi.cldpid) ||
-           ((flags & XCCLOSE) && !pi.cldpid)))
+       if (close_fd >= 0 && (((flags & XPCLOSE) && cldpid) ||
+           ((flags & XCCLOSE) && !cldpid)))
                close(close_fd);
-       if (!pi.cldpid) {
+       if (!cldpid) {
                /* child */
 
                /* Do this before restoring signal */
                if (flags & XCOPROC)
                        coproc_cleanup(false);
-               sigprocmask(SIG_SETMASK, &omask, NULL);
                cleanup_parents_env();
 #ifndef MKSH_UNEMPLOYED
-               /* If FMONITOR or FTALKING is set, these signals are ignored,
+               /*
+                * If FMONITOR or FTALKING is set, these signals are ignored,
                 * if neither FMONITOR nor FTALKING are set, the signals have
                 * their inherited values.
                 */
@@ -463,7 +565,7 @@ exchild(struct op *t, int flags,
 #endif
 #if HAVE_NICE
                if (Flag(FBGNICE) && (flags & XBGND))
-                       (void)nice(4);
+                       ksh_nice(4);
 #endif
                if ((flags & XBGND)
 #ifndef MKSH_UNEMPLOYED
@@ -480,22 +582,27 @@ exchild(struct op *t, int flags,
                                close(forksleep);
                        }
                }
-               remove_job(pi.j, "child");      /* in case of $(jobs) command */
+               /* in case of $(jobs) command */
+               remove_job(j, "child");
+#ifndef MKSH_NOPROSPECTOFWORK
+               /* remove_job needs SIGCHLD blocked still */
+               sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
                nzombie = 0;
 #ifndef MKSH_UNEMPLOYED
                ttypgrp_ok = false;
                Flag(FMONITOR) = 0;
 #endif
                Flag(FTALKING) = 0;
-               tty_close();
                cleartraps();
                /* no return */
                execute(t, (flags & XERROK) | XEXEC, NULL);
 #ifndef MKSH_SMALL
                if (t->type == TPIPE)
                        unwind(LLEAVE);
-               internal_warningf("exchild: execute() returned");
-               fptreef(shl_out, 2, "exchild: tried to execute {\n%T\n}\n", t);
+               internal_warningf("%s: execute() returned", "exchild");
+               fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
+                   "exchild", t);
                shf_flush(shl_out);
 #endif
                unwind(LLEAVE);
@@ -503,31 +610,33 @@ exchild(struct op *t, int flags,
        }
 
        /* shell (parent) stuff */
-       if (!(flags & XPIPEO)) {        /* last process in a job */
-               j_startjob(pi.j);
+       if (!(flags & XPIPEO)) {
+               /* last process in a job */
+               j_startjob(j);
                if (flags & XCOPROC) {
-                       pi.j->coproc_id = coproc.id;
+                       j->coproc_id = coproc.id;
                        /* n jobs using co-process output */
                        coproc.njobs++;
                        /* j using co-process input */
-                       coproc.job = (void *)pi.j;
+                       coproc.job = (void *)j;
                }
                if (flags & XBGND) {
-                       j_set_async(pi.j);
+                       j_set_async(j);
                        if (Flag(FTALKING)) {
-                               shf_fprintf(shl_out, "[%d]", pi.j->job);
-                               for (pi.p = pi.j->proc_list; pi.p;
-                                   pi.p = pi.p->next)
-                                       shf_fprintf(shl_out, " %d",
-                                           (int)pi.p->pid);
+                               shf_fprintf(shl_out, "[%d]", j->job);
+                               for (p = j->proc_list; p; p = p->next)
+                                       shf_fprintf(shl_out, Tf__d,
+                                           (int)p->pid);
                                shf_putchar('\n', shl_out);
                                shf_flush(shl_out);
                        }
                } else
-                       rv = j_waitj(pi.j, JW_NONE, "jw:last proc");
+                       rv = j_waitj(j, jwflags, "jw:last proc");
        }
 
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 
        return (rv);
 }
@@ -536,41 +645,53 @@ exchild(struct op *t, int flags,
 void
 startlast(void)
 {
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
-       if (last_job) { /* no need to report error - waitlast() will do it */
+       /* no need to report error - waitlast() will do it */
+       if (last_job) {
                /* ensure it isn't removed by check_job() */
                last_job->flags |= JF_WAITING;
                j_startjob(last_job);
        }
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 }
 
 /* wait for last job: only used for $(command) jobs */
 int
 waitlast(void)
 {
-       int     rv;
-       Job     *j;
+       int rv;
+       Job *j;
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
        j = last_job;
        if (!j || !(j->flags & JF_STARTED)) {
                if (!j)
-                       warningf(true, "waitlast: no last job");
+                       warningf(true, Tf_sD_s, "waitlast", "no last job");
                else
-                       internal_warningf("waitlast: not started");
+                       internal_warningf(Tf_sD_s, "waitlast", Tnot_started);
+#ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               return (125); /* not so arbitrary, non-zero value */
+#endif
+               /* not so arbitrary, non-zero value */
+               return (125);
        }
 
-       rv = j_waitj(j, JW_NONE, "jw:waitlast");
+       rv = j_waitj(j, JW_NONE, "waitlast");
 
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 
        return (rv);
 }
@@ -579,13 +700,13 @@ waitlast(void)
 int
 waitfor(const char *cp, int *sigp)
 {
-       int     rv;
-       Job     *j;
-       int     ecode;
-       int     flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
+       int rv, ecode, flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
+       Job *j;
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
        *sigp = 0;
 
@@ -599,30 +720,39 @@ waitfor(const char *cp, int *sigp)
                        if (j->ppid == procpid && j->state == PRUNNING)
                                break;
                if (!j) {
+#ifndef MKSH_NOPROSPECTOFWORK
                        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
                        return (-1);
                }
        } else if ((j = j_lookup(cp, &ecode))) {
                /* don't report normal job completion */
                flags &= ~JW_ASYNCNOTIFY;
                if (j->ppid != procpid) {
+#ifndef MKSH_NOPROSPECTOFWORK
                        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
                        return (-1);
                }
        } else {
+#ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
                if (ecode != JL_NOSUCH)
-                       bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+                       bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (-1);
        }
 
        /* AT&T ksh will wait for stopped jobs - we don't */
        rv = j_waitj(j, flags, "jw:waitfor");
 
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 
-       if (rv < 0) /* we were interrupted */
-               *sigp = 128 + -rv;
+       if (rv < 0)
+               /* we were interrupted */
+               *sigp = ksh_sigmask(-rv);
 
        return (rv);
 }
@@ -631,22 +761,26 @@ waitfor(const char *cp, int *sigp)
 int
 j_kill(const char *cp, int sig)
 {
-       Job     *j;
-       int     rv = 0;
-       int     ecode;
+       Job *j;
+       int rv = 0, ecode;
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
        if ((j = j_lookup(cp, &ecode)) == NULL) {
+#ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+#endif
+               bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (1);
        }
 
-       if (j->pgrp == 0) {     /* started when !Flag(FMONITOR) */
+       if (j->pgrp == 0) {
+               /* started when !Flag(FMONITOR) */
                if (kill_job(j, sig) < 0) {
-                       bi_errorf("%s: %s", cp, strerror(errno));
+                       bi_errorf(Tf_sD_s, cp, cstrerror(errno));
                        rv = 1;
                }
        } else {
@@ -655,12 +789,14 @@ j_kill(const char *cp, int sig)
                        mksh_killpg(j->pgrp, SIGCONT);
 #endif
                if (mksh_killpg(j->pgrp, sig) < 0) {
-                       bi_errorf("%s: %s", cp, strerror(errno));
+                       bi_errorf(Tf_sD_s, cp, cstrerror(errno));
                        rv = 1;
                }
        }
 
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 
        return (rv);
 }
@@ -670,18 +806,17 @@ j_kill(const char *cp, int sig)
 int
 j_resume(const char *cp, int bg)
 {
-       Job     *j;
-       Proc    *p;
-       int     ecode;
-       int     running;
-       int     rv = 0;
+       Job *j;
+       Proc *p;
+       int ecode, rv = 0;
+       bool running;
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
 
        if ((j = j_lookup(cp, &ecode)) == NULL) {
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+               bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (1);
        }
 
@@ -694,12 +829,12 @@ j_resume(const char *cp, int bg)
        if (bg)
                shprintf("[%d] ", j->job);
 
-       running = 0;
+       running = false;
        for (p = j->proc_list; p != NULL; p = p->next) {
                if (p->state == PSTOPPED) {
                        p->state = PRUNNING;
                        p->status = 0;
-                       running = 1;
+                       running = true;
                }
                shf_puts(p->command, shl_stdout);
                if (p->next)
@@ -717,21 +852,20 @@ j_resume(const char *cp, int bg)
                /* attach tty to job */
                if (j->state == PRUNNING) {
                        if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
-                               tcsetattr(tty_fd, TCSADRAIN, &j->ttystate);
+                               mksh_tcset(tty_fd, &j->ttystat);
                        /* See comment in j_waitj regarding saved_ttypgrp. */
                        if (ttypgrp_ok &&
                            tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ?
                            j->saved_ttypgrp : j->pgrp) < 0) {
                                rv = errno;
                                if (j->flags & JF_SAVEDTTY)
-                                       tcsetattr(tty_fd, TCSADRAIN, &tty_state);
-                               sigprocmask(SIG_SETMASK, &omask,
-                                   NULL);
-                               bi_errorf("1st tcsetpgrp(%d, %d) failed: %s",
-                                   tty_fd,
-                                   (int)((j->flags & JF_SAVEDTTYPGRP) ?
+                                       mksh_tcset(tty_fd, &tty_state);
+                               sigprocmask(SIG_SETMASK, &omask, NULL);
+                               bi_errorf(Tf_ldfailed,
+                                   "fg: 1st", "tcsetpgrp", tty_fd,
+                                   (long)((j->flags & JF_SAVEDTTYPGRP) ?
                                    j->saved_ttypgrp : j->pgrp),
-                                   strerror(rv));
+                                   cstrerror(rv));
                                return (1);
                        }
                }
@@ -742,20 +876,20 @@ j_resume(const char *cp, int bg)
        }
 
        if (j->state == PRUNNING && mksh_killpg(j->pgrp, SIGCONT) < 0) {
-               int err = errno;
+               int eno = errno;
 
                if (!bg) {
                        j->flags &= ~JF_FG;
                        if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
-                               tcsetattr(tty_fd, TCSADRAIN, &tty_state);
+                               mksh_tcset(tty_fd, &tty_state);
                        if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
-                               warningf(true,
-                                   "fg: 2nd tcsetpgrp(%d, %ld) failed: %s",
-                                   tty_fd, (long)kshpgrp, strerror(errno));
+                               warningf(true, Tf_ldfailed,
+                                   "fg: 2nd", "tcsetpgrp", tty_fd,
+                                   (long)kshpgrp, cstrerror(errno));
                }
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               bi_errorf("cannot continue job %s: %s",
-                   cp, strerror(err));
+               bi_errorf(Tf_s_sD_s, "can't continue job",
+                   cp, cstrerror(eno));
                return (1);
        }
        if (!bg) {
@@ -773,8 +907,8 @@ j_resume(const char *cp, int bg)
 int
 j_stopped_running(void)
 {
-       Job     *j;
-       int     which = 0;
+       Job *j;
+       int which = 0;
 
        for (j = job_list; j != NULL; j = j->next) {
 #ifndef MKSH_UNEMPLOYED
@@ -796,44 +930,34 @@ j_stopped_running(void)
        return (0);
 }
 
-int
-j_njobs(void)
-{
-       Job *j;
-       int nj = 0;
-       sigset_t omask;
-
-       sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-       for (j = job_list; j; j = j->next)
-               nj++;
-
-       sigprocmask(SIG_SETMASK, &omask, NULL);
-       return (nj);
-}
-
 
 /* list jobs for jobs built-in */
 int
 j_jobs(const char *cp, int slp,
-    int nflag)         /* 0: short, 1: long, 2: pgrp */
+    /* 0: short, 1: long, 2: pgrp */
+    int nflag)
 {
-       Job     *j, *tmp;
-       int     how;
-       int     zflag = 0;
+       Job *j, *tmp;
+       int how, zflag = 0;
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
-       if (nflag < 0) { /* kludge: print zombies */
+       if (nflag < 0) {
+               /* kludge: print zombies */
                nflag = 0;
                zflag = 1;
        }
        if (cp) {
-               int     ecode;
+               int ecode;
 
                if ((j = j_lookup(cp, &ecode)) == NULL) {
+#ifndef MKSH_NOPROSPECTOFWORK
                        sigprocmask(SIG_SETMASK, &omask, NULL);
-                       bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+#endif
+                       bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                        return (1);
                }
        } else
@@ -853,9 +977,11 @@ j_jobs(const char *cp, int slp,
        for (j = job_list; j; j = tmp) {
                tmp = j->next;
                if (j->flags & JF_REMOVE)
-                       remove_job(j, "jobs");
+                       remove_job(j, Tjobs);
        }
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
        return (0);
 }
 
@@ -863,16 +989,19 @@ j_jobs(const char *cp, int slp,
 void
 j_notify(void)
 {
-       Job     *j, *tmp;
+       Job *j, *tmp;
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
        for (j = job_list; j; j = j->next) {
 #ifndef MKSH_UNEMPLOYED
                if (Flag(FMONITOR) && (j->flags & JF_CHANGED))
                        j_print(j, JP_MEDIUM, shl_out);
 #endif
-               /* Remove job after doing reports so there aren't
+               /*
+                * Remove job after doing reports so there aren't
                 * multiple +/- jobs.
                 */
                if (j->state == PEXITED || j->state == PSIGNALLED)
@@ -884,21 +1013,27 @@ j_notify(void)
                        remove_job(j, "notify");
        }
        shf_flush(shl_out);
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 }
 
 /* Return pid of last process in last asynchronous job */
 pid_t
 j_async(void)
 {
+#ifndef MKSH_NOPROSPECTOFWORK
        sigset_t omask;
 
        sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
 
        if (async_job)
                async_job->flags |= JF_KNOWN;
 
+#ifndef MKSH_NOPROSPECTOFWORK
        sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
 
        return (async_pid);
 }
@@ -911,12 +1046,12 @@ j_async(void)
 static void
 j_set_async(Job *j)
 {
-       Job     *jl, *oldest;
+       Job *jl, *oldest;
 
        if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
                remove_job(async_job, "async");
        if (!(j->flags & JF_STARTED)) {
-               internal_warningf("j_async: job not started");
+               internal_warningf(Tf_sD_s, "j_async", Tjob_not_started);
                return;
        }
        async_job = j;
@@ -930,8 +1065,8 @@ j_set_async(Job *j)
                if (!oldest) {
                        /* XXX debugging */
                        if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) {
-                               internal_warningf("j_async: bad nzombie (%d)",
-                                   nzombie);
+                               internal_warningf("%s: bad nzombie (%d)",
+                                   "j_async", nzombie);
                                nzombie = 0;
                        }
                        break;
@@ -948,18 +1083,20 @@ j_set_async(Job *j)
 static void
 j_startjob(Job *j)
 {
-       Proc    *p;
+       Proc *p;
 
        j->flags |= JF_STARTED;
        for (p = j->proc_list; p->next; p = p->next)
                ;
        j->last_proc = p;
 
+#ifndef MKSH_NOPROSPECTOFWORK
        if (held_sigchld) {
                held_sigchld = 0;
                /* Don't call j_sigchld() as it may remove job... */
                kill(procpid, SIGCHLD);
        }
+#endif
 }
 
 /*
@@ -969,10 +1106,15 @@ j_startjob(Job *j)
  */
 static int
 j_waitj(Job *j,
-    int flags,                 /* see JW_* */
+    /* see JW_* */
+    int flags,
     const char *where)
 {
-       int     rv;
+       Proc *p;
+       int rv;
+#ifdef MKSH_NO_SIGSUSPEND
+       sigset_t omask;
+#endif
 
        /*
         * No auto-notify on the job we are waiting on.
@@ -988,12 +1130,24 @@ j_waitj(Job *j,
 
        while (j->state == PRUNNING ||
            ((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) {
+#ifndef MKSH_NOPROSPECTOFWORK
+#ifdef MKSH_NO_SIGSUSPEND
+               sigprocmask(SIG_SETMASK, &sm_default, &omask);
+               pause();
+               /* note that handlers may run here so they need to know */
+               sigprocmask(SIG_SETMASK, &omask, NULL);
+#else
                sigsuspend(&sm_default);
+#endif
+#else
+               j_sigchld(SIGCHLD);
+#endif
                if (fatal_trap) {
                        int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);
                        j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
                        runtraps(TF_FATAL);
-                       j->flags |= oldf; /* not reached... */
+                       /* not reached... */
+                       j->flags |= oldf;
                }
                if ((flags & JW_INTERRUPT) && (rv = trap_pending())) {
                        j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
@@ -1021,16 +1175,16 @@ j_waitj(Job *j,
                            (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
                                j->flags |= JF_SAVEDTTYPGRP;
                        if (tcsetpgrp(tty_fd, kshpgrp) < 0)
-                               warningf(true,
-                                   "j_waitj: tcsetpgrp(%d, %ld) failed: %s",
-                                   tty_fd, (long)kshpgrp, strerror(errno));
+                               warningf(true, Tf_ldfailed,
+                                   "j_waitj:", "tcsetpgrp", tty_fd,
+                                   (long)kshpgrp, cstrerror(errno));
                        if (j->state == PSTOPPED) {
                                j->flags |= JF_SAVEDTTY;
-                               tcgetattr(tty_fd, &j->ttystate);
+                               mksh_tcget(tty_fd, &j->ttystat);
                        }
                }
 #endif
-               if (tty_fd >= 0) {
+               if (tty_hasstate) {
                        /*
                         * Only restore tty settings if job was originally
                         * started in the foreground. Problems can be
@@ -1042,9 +1196,9 @@ j_waitj(Job *j,
                         */
                        if (j->state == PEXITED && j->status == 0 &&
                            (j->flags & JF_USETTYMODE)) {
-                               tcgetattr(tty_fd, &tty_state);
+                               mksh_tcget(tty_fd, &tty_state);
                        } else {
-                               tcsetattr(tty_fd, TCSADRAIN, &tty_state);
+                               mksh_tcset(tty_fd, &tty_state);
                                /*-
                                 * Don't use tty mode if job is stopped and
                                 * later restarted and exits. Consider
@@ -1068,14 +1222,14 @@ j_waitj(Job *j,
                 * even when not monitoring, but this doesn't make sense since
                 * a tty generated ^C goes to the whole process group)
                 */
-               {
-                       int status;
-
-                       status = j->last_proc->status;
-                       if (Flag(FMONITOR) && j->state == PSIGNALLED &&
-                           WIFSIGNALED(status) &&
-                           (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))
-                               trapsig(WTERMSIG(status));
+               if (Flag(FMONITOR) && j->state == PSIGNALLED &&
+                   WIFSIGNALED(j->last_proc->status)) {
+                       int termsig;
+
+                       if ((termsig = WTERMSIG(j->last_proc->status)) > 0 &&
+                           termsig < ksh_NSIG &&
+                           (sigtraps[termsig].flags & TF_TTY_INTR))
+                               trapsig(termsig);
                }
 #endif
        }
@@ -1084,6 +1238,48 @@ j_waitj(Job *j,
        j_systime = j->systime;
        rv = j->status;
 
+       if (!(p = j->proc_list)) {
+               ;       /* nothing */
+       } else if (flags & JW_PIPEST) {
+               uint32_t num = 0;
+               struct tbl *vp;
+
+               unset(vp_pipest, 1);
+               vp = vp_pipest;
+               vp->flag = DEFINED | ISSET | INTEGER | RDONLY | ARRAY | INT_U;
+               goto got_array;
+
+               while (p != NULL) {
+                       {
+                               struct tbl *vq;
+
+                               /* strlen(vp_pipest->name) == 10 */
+                               vq = alloc(offsetof(struct tbl, name[0]) + 11,
+                                   vp_pipest->areap);
+                               memset(vq, 0, offsetof(struct tbl, name[0]));
+                               memcpy(vq->name, vp_pipest->name, 11);
+                               vp->u.array = vq;
+                               vp = vq;
+                       }
+                       vp->areap = vp_pipest->areap;
+                       vp->ua.index = ++num;
+                       vp->flag = DEFINED | ISSET | INTEGER | RDONLY |
+                           ARRAY | INT_U | AINDEX;
+ got_array:
+                       vp->val.i = proc_errorlevel(p);
+                       if (Flag(FPIPEFAIL) && vp->val.i)
+                               rv = vp->val.i;
+                       p = p->next;
+               }
+       } else if (Flag(FPIPEFAIL)) {
+               do {
+                       const int i = proc_errorlevel(p);
+
+                       if (i)
+                               rv = i;
+               } while ((p = p->next));
+       }
+
        if (!(flags & JW_ASYNCNOTIFY)
 #ifndef MKSH_UNEMPLOYED
            && (!Flag(FMONITOR) || j->state != PSTOPPED)
@@ -1111,14 +1307,20 @@ j_waitj(Job *j,
 static void
 j_sigchld(int sig MKSH_A_UNUSED)
 {
-       /* this runs inside interrupt context, with errno saved */
-
+       int saved_errno = errno;
        Job *j;
        Proc *p = NULL;
        pid_t pid;
        int status;
        struct rusage ru0, ru1;
+#ifdef MKSH_NO_SIGSUSPEND
+       sigset_t omask;
 
+       /* this handler can run while SIGCHLD is not blocked, so block it now */
+       sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+#ifndef MKSH_NOPROSPECTOFWORK
        /*
         * Don't wait for any processes if a job is partially started.
         * This is so we don't do away with the process group leader
@@ -1128,19 +1330,28 @@ j_sigchld(int sig MKSH_A_UNUSED)
        for (j = job_list; j; j = j->next)
                if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
                        held_sigchld = 1;
-                       return;
+                       goto j_sigchld_out;
                }
+#endif
 
        getrusage(RUSAGE_CHILDREN, &ru0);
        do {
-               pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
+#ifndef MKSH_NOPROSPECTOFWORK
+               pid = waitpid(-1, &status, (WNOHANG |
+#if defined(WCONTINUED) && defined(WIFCONTINUED)
+                   WCONTINUED |
+#endif
+                   WUNTRACED));
+#else
+               pid = wait(&status);
+#endif
 
                /*
                 * return if this would block (0) or no children
                 * or interrupted (-1)
                 */
                if (pid <= 0)
-                       return;
+                       goto j_sigchld_out;
 
                getrusage(RUSAGE_CHILDREN, &ru1);
 
@@ -1169,14 +1380,33 @@ j_sigchld(int sig MKSH_A_UNUSED)
                if (WIFSTOPPED(status))
                        p->state = PSTOPPED;
                else
+#if defined(WCONTINUED) && defined(WIFCONTINUED)
+                 if (WIFCONTINUED(status)) {
+                       p->state = j->state = PRUNNING;
+                       /* skip check_job(), no-op in this case */
+                       continue;
+               } else
+#endif
 #endif
                  if (WIFSIGNALED(status))
                        p->state = PSIGNALLED;
                else
                        p->state = PEXITED;
 
-               check_job(j);   /* check to see if entire job is done */
-       } while (1);
+               /* check to see if entire job is done */
+               check_job(j);
+       }
+#ifndef MKSH_NOPROSPECTOFWORK
+           while (/* CONSTCOND */ 1);
+#else
+           while (/* CONSTCOND */ 0);
+#endif
+
+ j_sigchld_out:
+#ifdef MKSH_NO_SIGSUSPEND
+       sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+       errno = saved_errno;
 }
 
 /*
@@ -1190,36 +1420,26 @@ j_sigchld(int sig MKSH_A_UNUSED)
 static void
 check_job(Job *j)
 {
-       int     jstate;
-       Proc    *p;
+       int jstate;
+       Proc *p;
 
        /* XXX debugging (nasty - interrupt routine using shl_out) */
        if (!(j->flags & JF_STARTED)) {
-               internal_warningf("check_job: job started (flags 0x%x)",
-                   j->flags);
+               internal_warningf("check_job: job started (flags 0x%X)",
+                   (unsigned int)j->flags);
                return;
        }
 
        jstate = PRUNNING;
        for (p=j->proc_list; p != NULL; p = p->next) {
                if (p->state == PRUNNING)
-                       return; /* some processes still running */
+                       /* some processes still running */
+                       return;
                if (p->state > jstate)
                        jstate = p->state;
        }
        j->state = jstate;
-
-       switch (j->last_proc->state) {
-       case PEXITED:
-               j->status = WEXITSTATUS(j->last_proc->status);
-               break;
-       case PSIGNALLED:
-               j->status = 128 + WTERMSIG(j->last_proc->status);
-               break;
-       default:
-               j->status = 0;
-               break;
-       }
+       j->status = proc_errorlevel(j->last_proc);
 
        /*
         * Note when co-process dies: can't be done in j_wait() nor
@@ -1303,14 +1523,14 @@ check_job(Job *j)
 static void
 j_print(Job *j, int how, struct shf *shf)
 {
-       Proc    *p;
-       int     state;
-       int     status;
-       int     coredumped;
-       char    jobchar = ' ';
-       char    buf[64];
+       Proc *p;
+       int state;
+       int status;
+       bool coredumped;
+       char jobchar = ' ';
+       char buf[64];
        const char *filler;
-       int     output = 0;
+       int output = 0;
 
        if (how == JP_PGRP) {
                /*
@@ -1318,7 +1538,7 @@ j_print(Job *j, int how, struct shf *shf)
                 * group leader (ie, !FMONITOR). We arbitrarily return
                 * last pid (which is what $! returns).
                 */
-               shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
+               shf_fprintf(shf, Tf_dN, (int)(j->pgrp ? j->pgrp :
                    (j->last_proc ? j->last_proc->pid : 0)));
                return;
        }
@@ -1330,48 +1550,58 @@ j_print(Job *j, int how, struct shf *shf)
                jobchar = '-';
 
        for (p = j->proc_list; p != NULL;) {
-               coredumped = 0;
+               coredumped = false;
                switch (p->state) {
                case PRUNNING:
                        memcpy(buf, "Running", 8);
                        break;
-               case PSTOPPED:
-                       strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess,
-                           sizeof(buf));
+               case PSTOPPED: {
+                       int stopsig = WSTOPSIG(p->status);
+
+                       strlcpy(buf, stopsig > 0 && stopsig < ksh_NSIG ?
+                           sigtraps[stopsig].mess : "Stopped", sizeof(buf));
                        break;
-               case PEXITED:
+               }
+               case PEXITED: {
+                       int exitstatus = (WEXITSTATUS(p->status)) & 255;
+
                        if (how == JP_SHORT)
                                buf[0] = '\0';
-                       else if (WEXITSTATUS(p->status) == 0)
+                       else if (exitstatus == 0)
                                memcpy(buf, "Done", 5);
                        else
                                shf_snprintf(buf, sizeof(buf), "Done (%d)",
-                                   WEXITSTATUS(p->status));
+                                   exitstatus);
                        break;
-               case PSIGNALLED:
+               }
+               case PSIGNALLED: {
+                       int termsig = WTERMSIG(p->status);
 #ifdef WCOREDUMP
                        if (WCOREDUMP(p->status))
-                               coredumped = 1;
+                               coredumped = true;
 #endif
                        /*
                         * kludge for not reporting 'normal termination
                         * signals' (i.e. SIGINT, SIGPIPE)
                         */
                        if (how == JP_SHORT && !coredumped &&
-                           (WTERMSIG(p->status) == SIGINT ||
-                           WTERMSIG(p->status) == SIGPIPE)) {
+                           (termsig == SIGINT || termsig == SIGPIPE)) {
                                buf[0] = '\0';
                        } else
-                               strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,
+                               strlcpy(buf, termsig > 0 && termsig < ksh_NSIG ?
+                                   sigtraps[termsig].mess : "Signalled",
                                    sizeof(buf));
                        break;
                }
+               default:
+                       buf[0] = '\0';
+               }
 
                if (how != JP_SHORT) {
                        if (p == j->proc_list)
                                shf_fprintf(shf, "[%d] %c ", j->job, jobchar);
                        else
-                               shf_fprintf(shf, "%s", filler);
+                               shf_puts(filler, shf);
                }
 
                if (how == JP_LONG)
@@ -1396,10 +1626,10 @@ j_print(Job *j, int how, struct shf *shf)
                while (p && p->state == state && p->status == status) {
                        if (how == JP_LONG)
                                shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
-                                   (int)p->pid, " ", p->command,
+                                   (int)p->pid, T1space, p->command,
                                    p->next ? "|" : null);
                        else if (how == JP_MEDIUM)
-                               shf_fprintf(shf, " %s%s", p->command,
+                               shf_fprintf(shf, Tf__ss, p->command,
                                    p->next ? "|" : null);
                        p = p->next;
                }
@@ -1416,12 +1646,12 @@ j_print(Job *j, int how, struct shf *shf)
 static Job *
 j_lookup(const char *cp, int *ecodep)
 {
-       Job             *j, *last_match;
-       Proc            *p;
-       int             len, job = 0;
+       Job *j, *last_match;
+       Proc *p;
+       size_t len;
+       int job = 0;
 
-       if (ksh_isdigit(*cp)) {
-               getn(cp, &job);
+       if (ksh_isdigit(*cp) && getn(cp, &job)) {
                /* Look for last_proc->pid (what $! returns) first... */
                for (j = job_list; j != NULL; j = j->next)
                        if (j->last_proc && j->last_proc->pid == job)
@@ -1433,11 +1663,10 @@ j_lookup(const char *cp, int *ecodep)
                for (j = job_list; j != NULL; j = j->next)
                        if (j->pgrp && j->pgrp == job)
                                return (j);
-               if (ecodep)
-                       *ecodep = JL_NOSUCH;
-               return (NULL);
+               goto j_lookup_nosuch;
        }
        if (*cp != '%') {
+ j_lookup_invalid:
                if (ecodep)
                        *ecodep = JL_INVALID;
                return (NULL);
@@ -1457,13 +1686,15 @@ j_lookup(const char *cp, int *ecodep)
 
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
-               getn(cp, &job);
+               if (!getn(cp, &job))
+                       goto j_lookup_invalid;
                for (j = job_list; j != NULL; j = j->next)
                        if (j->job == job)
                                return (j);
                break;
 
-       case '?':               /* %?string */
+       /* %?string */
+       case '?':
                last_match = NULL;
                for (j = job_list; j != NULL; j = j->next)
                        for (p = j->proc_list; p != NULL; p = p->next)
@@ -1479,7 +1710,8 @@ j_lookup(const char *cp, int *ecodep)
                        return (last_match);
                break;
 
-       default:                /* %string */
+       /* %string */
+       default:
                len = strlen(cp);
                last_match = NULL;
                for (j = job_list; j != NULL; j = j->next)
@@ -1495,6 +1727,7 @@ j_lookup(const char *cp, int *ecodep)
                        return (last_match);
                break;
        }
+ j_lookup_nosuch:
        if (ecodep)
                *ecodep = JL_NOSUCH;
        return (NULL);
@@ -1511,8 +1744,8 @@ static Proc       *free_procs;
 static Job *
 new_job(void)
 {
-       int     i;
-       Job     *newj, *j;
+       int i;
+       Job *newj, *j;
 
        if (free_jobs != NULL) {
                newj = free_jobs;
@@ -1540,7 +1773,7 @@ new_job(void)
 static Proc *
 new_proc(void)
 {
-       Proc    *p;
+       Proc *p;
 
        if (free_procs != NULL) {
                p = free_procs;
@@ -1560,15 +1793,17 @@ new_proc(void)
 static void
 remove_job(Job *j, const char *where)
 {
-       Proc    *p, *tmp;
-       Job     **prev, *curr;
+       Proc *p, *tmp;
+       Job **prev, *curr;
 
        prev = &job_list;
-       curr = *prev;
-       for (; curr != NULL && curr != j; prev = &curr->next, curr = *prev)
-               ;
+       curr = job_list;
+       while (curr && curr != j) {
+               prev = &curr->next;
+               curr = *prev;
+       }
        if (curr != j) {
-               internal_warningf("remove_job: job not found (%s)", where);
+               internal_warningf("remove_job: job %s (%s)", Tnot_found, where);
                return;
        }
        *prev = curr->next;
@@ -1601,13 +1836,15 @@ remove_job(Job *j, const char *where)
 static void
 put_job(Job *j, int where)
 {
-       Job     **prev, *curr;
+       Job **prev, *curr;
 
        /* Remove job from list (if there) */
        prev = &job_list;
        curr = job_list;
-       for (; curr && curr != j; prev = &curr->next, curr = *prev)
-               ;
+       while (curr && curr != j) {
+               prev = &curr->next;
+               curr = *prev;
+       }
        if (curr == j)
                *prev = curr->next;
 
@@ -1637,8 +1874,8 @@ put_job(Job *j, int where)
 static int
 kill_job(Job *j, int sig)
 {
-       Proc    *p;
-       int     rval = 0;
+       Proc *p;
+       int rval = 0;
 
        for (p = j->proc_list; p != NULL; p = p->next)
                if (p->pid != 0)
@@ -1646,3 +1883,41 @@ kill_job(Job *j, int sig)
                                rval = -1;
        return (rval);
 }
+
+static void
+tty_init_talking(void)
+{
+       switch (tty_init_fd()) {
+       case 0:
+               break;
+       case 1:
+#ifndef MKSH_DISABLE_TTY_WARNING
+               warningf(false, Tf_sD_s_sD_s,
+                   "No controlling tty", Topen, T_devtty, cstrerror(errno));
+#endif
+               break;
+       case 2:
+#ifndef MKSH_DISABLE_TTY_WARNING
+               warningf(false, Tf_sD_s_s, Tcant_find, Ttty_fd,
+                   cstrerror(errno));
+#endif
+               break;
+       case 3:
+               warningf(false, Tf_ssfaileds, "j_ttyinit",
+                   Ttty_fd_dupof, cstrerror(errno));
+               break;
+       case 4:
+               warningf(false, Tf_sD_sD_s, "j_ttyinit",
+                   "can't set close-on-exec flag", cstrerror(errno));
+               break;
+       }
+}
+
+static void
+tty_init_state(void)
+{
+       if (tty_fd >= 0) {
+               mksh_tcget(tty_fd, &tty_state);
+               tty_hasstate = true;
+       }
+}