OSDN Git Service

Upgrade to mksh R57.
[android-x86/external-mksh.git] / src / jobs.c
index 0b5df4e..66e31aa 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, 2011
- *     Thorsten Glaser <tg@mirbsd.org>
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
+ *              2012, 2013, 2014, 2015, 2016, 2018
+ *     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.81 2011/08/27 18:06:46 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.127 2018/07/15 16:23:10 tg Exp $");
 
 #if HAVE_KILLPG
 #define mksh_killpg            killpg
@@ -37,17 +38,31 @@ __RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.81 2011/08/27 18:06:46 tg Exp $");
 #define PSIGNALLED     2
 #define PSTOPPED       3
 
-typedef struct proc    Proc;
+typedef struct proc Proc;
+/* to take alignment into consideration */
+struct proc_dummy {
+       Proc *next;
+       pid_t pid;
+       int state;
+       int status;
+       char command[128];
+};
+/* real structure */
 struct proc {
-       Proc *next;             /* next process in pipeline (if any) */
-       pid_t pid;              /* process id */
+       /* next process in pipeline (if any) */
+       Proc *next;
+       /* process id of this Unix process in the job */
+       pid_t pid;
+       /* one of the four P… above */
        int state;
-       int status;             /* wait status */
-       char command[48];       /* process command string */
+       /* wait status */
+       int status;
+       /* process command string from vistree */
+       char command[256 - (ALLOC_OVERHEAD +
+           offsetof(struct proc_dummy, command[0]))];
 };
 
 /* 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 */
@@ -84,10 +99,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 ttystat; /* 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
 };
@@ -100,17 +115,14 @@ struct job {
 #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 */
@@ -119,7 +131,7 @@ 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
@@ -150,16 +162,13 @@ 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)
 {
-#ifndef MKSH_UNEMPLOYED
-       bool mflagset = Flag(FMONITOR) != 127;
-
-       Flag(FMONITOR) = 0;
-#endif
-
 #ifndef MKSH_NOPROSPECTOFWORK
        (void)sigemptyset(&sm_default);
        sigprocmask(SIG_SETMASK, &sm_default, NULL);
@@ -175,8 +184,8 @@ j_init(void)
 #endif
 
 #ifndef MKSH_UNEMPLOYED
-       if (!mflagset && Flag(FTALKING))
-               Flag(FMONITOR) = 1;
+       if (Flag(FMONITOR) == 127)
+               Flag(FMONITOR) = Flag(FTALKING);
 
        /*
         * shl_j is used to do asynchronous notification (used in
@@ -199,13 +208,15 @@ 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
@@ -213,14 +224,62 @@ proc_errorlevel(Proc *p)
 {
        switch (p->state) {
        case PEXITED:
-               return (WEXITSTATUS(p->status));
+               return ((WEXITSTATUS(p->status)) & 255);
        case PSIGNALLED:
-               return (128 + WTERMSIG(p->status));
+               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)
@@ -284,12 +343,12 @@ 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) */
@@ -297,9 +356,9 @@ j_change(void)
                                pid_t ttypgrp;
 
                                if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
-                                       warningf(false, "%s: %s %s: %s",
-                                           "j_init", "tcgetpgrp", "failed",
-                                           strerror(errno));
+                                       warningf(false, Tf_ssfaileds,
+                                           "j_init", "tcgetpgrp",
+                                           cstrerror(errno));
                                        ttypgrp_ok = false;
                                        break;
                                }
@@ -313,25 +372,25 @@ j_change(void)
                            SS_RESTORE_DFL|SS_FORCE);
                if (ttypgrp_ok && kshpgrp != kshpid) {
                        if (setpgid(0, kshpid) < 0) {
-                               warningf(false, "%s: %s %s: %s", "j_init",
-                                   "setpgid", "failed", strerror(errno));
+                               warningf(false, Tf_ssfaileds,
+                                   "j_init", "setpgid", cstrerror(errno));
                                ttypgrp_ok = false;
                        } else {
                                if (tcsetpgrp(tty_fd, kshpid) < 0) {
-                                       warningf(false, "%s: %s %s: %s",
-                                           "j_init", "tcsetpgrp", "failed",
-                                           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, "%s: %s", "warning",
+                       warningf(false, Tf_sD_s, "warning",
                            "won't have full job control");
-               if (tty_fd >= 0)
-                       tcgetattr(tty_fd, &tty_state);
+#endif
        } else {
                ttypgrp_ok = false;
                if (Flag(FTALKING))
@@ -347,9 +406,8 @@ j_change(void)
                                            SIG_IGN : SIG_DFL,
                                            SS_RESTORE_ORIG|SS_FORCE);
                        }
-               if (!Flag(FTALKING))
-                       tty_close();
        }
+       tty_init_state();
 }
 #endif
 
@@ -359,12 +417,12 @@ static void
 ksh_nice(int ness)
 {
 #if defined(__USE_FORTIFY_LEVEL) && (__USE_FORTIFY_LEVEL > 0)
-       int e;
+       int eno;
 
        errno = 0;
        /* this is gonna annoy users; complain to your distro, people! */
-       if (nice(ness) == -1 && (e = errno) != 0)
-               warningf(false, "%s: %s", "bgnice", strerror(e));
+       if (nice(ness) == -1 && (eno = errno) != 0)
+               warningf(false, Tf_sD_s, "bgnice", cstrerror(eno));
 #else
        (void)nice(ness);
 #endif
@@ -416,8 +474,7 @@ exchild(struct op *t, int flags,
        if (flags & XPIPEI) {
                /* continuing with a pipe */
                if (!last_job)
-                       internal_errorf("%s %d",
-                           "exchild: XPIPEI and no last_job - pid",
+                       internal_errorf("exchild: XPIPEI and no last_job - pid %d",
                            (int)procpid);
                j = last_job;
                if (last_proc)
@@ -457,7 +514,7 @@ exchild(struct op *t, int flags,
                forksleep <<= 1;
        }
        /* ensure $RANDOM changes between parent and child */
-       rndset((long)cldpid);
+       rndset((unsigned long)cldpid);
        /* fork failed? */
        if (cldpid < 0) {
                kill_job(j, SIGKILL);
@@ -473,6 +530,7 @@ exchild(struct op *t, int flags,
        /* job control set up */
        if (Flag(FMONITOR) && !(flags&XXCOM)) {
                bool dotty = false;
+
                if (j->pgrp == 0) {
                        /* First process */
                        j->pgrp = p->pid;
@@ -499,9 +557,6 @@ exchild(struct op *t, int flags,
                /* Do this before restoring signal */
                if (flags & XCOPROC)
                        coproc_cleanup(false);
-#ifndef MKSH_NOPROSPECTOFWORK
-               sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
                cleanup_parents_env();
 #ifndef MKSH_UNEMPLOYED
                /*
@@ -536,20 +591,23 @@ exchild(struct op *t, int flags,
                }
                /* 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("%s: %s", "exchild", "execute() returned");
+               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);
@@ -574,7 +632,7 @@ exchild(struct op *t, int flags,
                        if (Flag(FTALKING)) {
                                shf_fprintf(shl_out, "[%d]", j->job);
                                for (p = j->proc_list; p; p = p->next)
-                                       shf_fprintf(shl_out, " %d",
+                                       shf_fprintf(shl_out, Tf__d,
                                            (int)p->pid);
                                shf_putchar('\n', shl_out);
                                shf_flush(shl_out);
@@ -626,9 +684,9 @@ waitlast(void)
        j = last_job;
        if (!j || !(j->flags & JF_STARTED)) {
                if (!j)
-                       warningf(true, "%s: %s", "waitlast", "no last job");
+                       warningf(true, Tf_sD_s, "waitlast", "no last job");
                else
-                       internal_warningf("%s: %s", "waitlast", "not started");
+                       internal_warningf(Tf_sD_s, "waitlast", Tnot_started);
 #ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
@@ -688,7 +746,7 @@ waitfor(const char *cp, int *sigp)
                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);
        }
 
@@ -701,7 +759,7 @@ waitfor(const char *cp, int *sigp)
 
        if (rv < 0)
                /* we were interrupted */
-               *sigp = 128 + -rv;
+               *sigp = ksh_sigmask(-rv);
 
        return (rv);
 }
@@ -722,14 +780,14 @@ j_kill(const char *cp, int sig)
 #ifndef MKSH_NOPROSPECTOFWORK
                sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
-               bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+               bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                return (1);
        }
 
        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 {
@@ -738,7 +796,7 @@ 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;
                }
        }
@@ -765,7 +823,7 @@ j_resume(const char *cp, int bg)
 
        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);
        }
 
@@ -801,20 +859,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->ttystat);
+                               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);
+                                       mksh_tcset(tty_fd, &tty_state);
                                sigprocmask(SIG_SETMASK, &omask, NULL);
-                               bi_errorf("%s %s(%d, %ld) %s: %s",
-                                   "1st", "tcsetpgrp", tty_fd,
+                               bi_errorf(Tf_ldfailed,
+                                   "fg: 1st", "tcsetpgrp", tty_fd,
                                    (long)((j->flags & JF_SAVEDTTYPGRP) ?
-                                   j->saved_ttypgrp : j->pgrp), "failed",
-                                   strerror(rv));
+                                   j->saved_ttypgrp : j->pgrp),
+                                   cstrerror(rv));
                                return (1);
                        }
                }
@@ -825,20 +883,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, "%s %s(%d, %ld) %s: %s",
+                               warningf(true, Tf_ldfailed,
                                    "fg: 2nd", "tcsetpgrp", tty_fd,
-                                   (long)kshpgrp, "failed", strerror(errno));
+                                   (long)kshpgrp, cstrerror(errno));
                }
                sigprocmask(SIG_SETMASK, &omask, NULL);
-               bi_errorf("%s %s %s", "can't continue job",
-                   cp, strerror(err));
+               bi_errorf(Tf_s_sD_s, "can't continue job",
+                   cp, cstrerror(eno));
                return (1);
        }
        if (!bg) {
@@ -900,13 +958,13 @@ j_jobs(const char *cp, int slp,
                zflag = 1;
        }
        if (cp) {
-               int     ecode;
+               int ecode;
 
                if ((j = j_lookup(cp, &ecode)) == NULL) {
 #ifndef MKSH_NOPROSPECTOFWORK
                        sigprocmask(SIG_SETMASK, &omask, NULL);
 #endif
-                       bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+                       bi_errorf(Tf_sD_s, cp, lookup_msgs[ecode]);
                        return (1);
                }
        } else
@@ -926,7 +984,7 @@ 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);
@@ -958,8 +1016,14 @@ j_notify(void)
        }
        for (j = job_list; j; j = tmp) {
                tmp = j->next;
-               if (j->flags & JF_REMOVE)
-                       remove_job(j, "notify");
+               if (j->flags & JF_REMOVE) {
+                       if (j == async_job || (j->flags & JF_KNOWN)) {
+                               j->flags = (j->flags & ~JF_REMOVE) | JF_ZOMBIE;
+                               j->job = -1;
+                               nzombie++;
+                       } else
+                               remove_job(j, "notify");
+               }
        }
        shf_flush(shl_out);
 #ifndef MKSH_NOPROSPECTOFWORK
@@ -995,12 +1059,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("%s: %s", "j_async", "job not started");
+               internal_warningf(Tf_sD_s, "j_async", Tjob_not_started);
                return;
        }
        async_job = j;
@@ -1032,7 +1096,7 @@ 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)
@@ -1059,7 +1123,11 @@ j_waitj(Job *j,
     int flags,
     const char *where)
 {
+       Proc *p;
        int rv;
+#ifdef MKSH_NO_SIGSUSPEND
+       sigset_t omask;
+#endif
 
        /*
         * No auto-notify on the job we are waiting on.
@@ -1076,7 +1144,14 @@ 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
@@ -1113,16 +1188,16 @@ j_waitj(Job *j,
                            (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
                                j->flags |= JF_SAVEDTTYPGRP;
                        if (tcsetpgrp(tty_fd, kshpgrp) < 0)
-                               warningf(true, "%s %s(%d, %ld) %s: %s",
+                               warningf(true, Tf_ldfailed,
                                    "j_waitj:", "tcsetpgrp", tty_fd,
-                                   (long)kshpgrp, "failed", strerror(errno));
+                                   (long)kshpgrp, cstrerror(errno));
                        if (j->state == PSTOPPED) {
                                j->flags |= JF_SAVEDTTY;
-                               tcgetattr(tty_fd, &j->ttystat);
+                               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
@@ -1134,9 +1209,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
@@ -1160,14 +1235,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
        }
@@ -1176,9 +1251,10 @@ j_waitj(Job *j,
        j_systime = j->systime;
        rv = j->status;
 
-       if ((flags & JW_PIPEST) && (j->proc_list != NULL)) {
+       if (!(p = j->proc_list)) {
+               ;       /* nothing */
+       } else if (flags & JW_PIPEST) {
                uint32_t num = 0;
-               Proc *p = j->proc_list;
                struct tbl *vp;
 
                unset(vp_pipest, 1);
@@ -1204,8 +1280,17 @@ j_waitj(Job *j,
                            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)
@@ -1235,13 +1320,18 @@ 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
        /*
@@ -1253,14 +1343,18 @@ 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 {
 #ifndef MKSH_NOPROSPECTOFWORK
-               pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
+               pid = waitpid(-1, &status, (WNOHANG |
+#if defined(WCONTINUED) && defined(WIFCONTINUED)
+                   WCONTINUED |
+#endif
+                   WUNTRACED));
 #else
                pid = wait(&status);
 #endif
@@ -1270,7 +1364,7 @@ j_sigchld(int sig MKSH_A_UNUSED)
                 * or interrupted (-1)
                 */
                if (pid <= 0)
-                       return;
+                       goto j_sigchld_out;
 
                getrusage(RUSAGE_CHILDREN, &ru1);
 
@@ -1299,6 +1393,13 @@ 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;
@@ -1313,6 +1414,12 @@ j_sigchld(int sig MKSH_A_UNUSED)
 #else
            while (/* CONSTCOND */ 0);
 #endif
+
+ j_sigchld_out:
+#ifdef MKSH_NO_SIGSUSPEND
+       sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+       errno = saved_errno;
 }
 
 /*
@@ -1326,13 +1433,13 @@ 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;
        }
 
@@ -1429,14 +1536,16 @@ 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;
+#ifdef WCOREDUMP
+       bool coredumped;
+#endif
+       char jobchar = ' ';
+       char buf[64];
        const char *filler;
-       int     output = 0;
+       int output = 0;
 
        if (how == JP_PGRP) {
                /*
@@ -1444,7 +1553,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;
        }
@@ -1456,42 +1565,57 @@ j_print(Job *j, int how, struct shf *shf)
                jobchar = '-';
 
        for (p = j->proc_list; p != NULL;) {
-               coredumped = 0;
+#ifdef WCOREDUMP
+               coredumped = false;
+#endif
                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)) {
+                       if (how == JP_SHORT &&
+#ifdef WCOREDUMP
+                           !coredumped &&
+#endif
+                           (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)
@@ -1506,14 +1630,22 @@ j_print(Job *j, int how, struct shf *shf)
                if (how == JP_SHORT) {
                        if (buf[0]) {
                                output = 1;
+#ifdef WCOREDUMP
                                shf_fprintf(shf, "%s%s ",
                                    buf, coredumped ? " (core dumped)" : null);
+#else
+                               shf_puts(buf, shf);
+                               shf_putchar(' ', shf);
+#endif
                        }
                } else {
                        output = 1;
                        shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
                            p->next ? "|" : null,
-                           coredumped ? " (core dumped)" : null);
+#ifdef WCOREDUMP
+                           coredumped ? " (core dumped)" :
+#endif
+                            null);
                }
 
                state = p->state;
@@ -1522,10 +1654,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;
                }
@@ -1547,8 +1679,7 @@ j_lookup(const char *cp, int *ecodep)
        size_t len;
        int job = 0;
 
-       if (ksh_isdigit(*cp)) {
-               getn(cp, &job);
+       if (ctype(*cp, C_DIGIT) && 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)
@@ -1560,11 +1691,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);
@@ -1584,7 +1714,8 @@ 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);
@@ -1624,6 +1755,7 @@ j_lookup(const char *cp, int *ecodep)
                        return (last_match);
                break;
        }
+ j_lookup_nosuch:
        if (ecodep)
                *ecodep = JL_NOSUCH;
        return (NULL);
@@ -1640,8 +1772,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;
@@ -1669,7 +1801,7 @@ new_job(void)
 static Proc *
 new_proc(void)
 {
-       Proc    *p;
+       Proc *p;
 
        if (free_procs != NULL) {
                p = free_procs;
@@ -1689,15 +1821,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 %s (%s)", "not found", where);
+               internal_warningf("remove_job: job %s (%s)", Tnot_found, where);
                return;
        }
        *prev = curr->next;
@@ -1730,13 +1864,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;
 
@@ -1766,8 +1902,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)
@@ -1775,3 +1911,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_s_sD_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;
+       }
+}