From 0b57abd4d3249dc38e6e08c238f5f5c620c9fe8e Mon Sep 17 00:00:00 2001 From: tg Date: Fri, 18 Feb 2011 22:26:13 +0000 Subject: [PATCH] =?UTF-8?q?=E2=80=A2=20add=20-u=20option=20(POSIX:=20unbuf?= =?UTF-8?q?fered=20=E2=87=92=20nop)=20to=20the=20built-in=20cat=20?= =?UTF-8?q?=E2=80=A2=20PIPESTATUS=20now=20supported=20(like=20bash=202)=20?= =?UTF-8?q?whose=20last=20member=20=20=20may=20actually=20differ=20from=20?= =?UTF-8?q?$=3F=20since=20the=20latter=20may=20not=20be=20the=20=20=20resu?= =?UTF-8?q?lt=20of=20a=20pipeline=20partial=20command=20=E2=80=A2=20add=20?= =?UTF-8?q?regression=20tests,=20documentation,=20etc.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- check.t | 29 +++++++++++++++++++++--- exec.c | 15 +++++++++++-- funcs.c | 22 ++++++++++++------ jobs.c | 70 ++++++++++++++++++++++++++++++++++++++++++++------------- main.c | 3 ++- mksh.1 | 21 +++++++++++------ sh.h | 8 ++++--- 7 files changed, 130 insertions(+), 38 deletions(-) diff --git a/check.t b/check.t index fee6d9d..50a629f 100644 --- a/check.t +++ b/check.t @@ -1,4 +1,4 @@ -# $MirOS: src/bin/mksh/check.t,v 1.408 2011/02/13 21:13:05 tg Exp $ +# $MirOS: src/bin/mksh/check.t,v 1.409 2011/02/18 22:26:06 tg Exp $ # $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $ # $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $ @@ -25,7 +25,7 @@ # http://www.research.att.com/~gsf/public/ifs.sh expected-stdout: - @(#)MIRBSD KSH R39 2011/02/13 + @(#)MIRBSD KSH R39 2011/02/18 description: Check version of shell. stdin: @@ -69,8 +69,9 @@ name: selftest-direct-builtin-call description: Check that direct builtin calls work stdin: + ln -s "$__progname" cat ln -s "$__progname" echo - ./echo -c 'echo foo' + ./echo -c 'echo foo' | ./cat -u expected-stdout: -c echo foo --- @@ -5244,6 +5245,28 @@ expected-stdout: 200 hi 300 hi --- +name: pipeline-3 +description: + Check that PIPESTATUS does what it's supposed to +stdin: + echo 1 $PIPESTATUS . + echo 2 ${PIPESTATUS[0]} . + echo 3 ${PIPESTATUS[1]} . + (echo x; exit 12) | (cat; exit 23) | (cat; exit 42) + echo 5 $? , $PIPESTATUS , ${PIPESTATUS[0]} , ${PIPESTATUS[1]} , ${PIPESTATUS[2]} , ${PIPESTATUS[3]} . + echo 6 ${PIPESTATUS[0]} . + set | fgrep PIPESTATUS + echo 8 $(set | fgrep PIPESTATUS) . +expected-stdout: + 1 0 . + 2 0 . + 3 . + x + 5 42 , 12 , 12 , 23 , 42 , . + 6 0 . + PIPESTATUS[0]=0 + 8 PIPESTATUS[0]=0 PIPESTATUS[1]=0 . +--- name: persist-history-1 description: Check if persistent history saving works diff --git a/exec.c b/exec.c index 3ade2e2..7b3a554 100644 --- a/exec.c +++ b/exec.c @@ -22,7 +22,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.86 2011/02/11 01:18:16 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.87 2011/02/18 22:26:08 tg Exp $"); #ifndef MKSH_DEFAULT_EXECSHELL #define MKSH_DEFAULT_EXECSHELL "/bin/sh" @@ -153,6 +153,9 @@ execute(struct op * volatile t, memset(e->savefd, 0, NUFILE * sizeof(short)); } + /* mark for replacement later (unless TPIPE) */ + vp_pipest->flag |= INT_L; + /* do redirection, to be restored in quitenv() */ if (t->ioact != NULL) for (iowp = t->ioact; *iowp != NULL; iowp++) { @@ -207,7 +210,7 @@ execute(struct op * volatile t, /* no need to re-restore this */ e->savefd[1] = 0; /* Let exchild() close 0 in parent, after fork, before wait */ - i = exchild(t, flags | XPCLOSE, xerrok, 0); + i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0); if (!(flags&XBGND) && !(flags&XXCOM)) rv = i; break; @@ -449,6 +452,12 @@ execute(struct op * volatile t, } Break: exstat = rv; + if (vp_pipest->flag & INT_L) { + unset(vp_pipest, 1); + vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY | + ARRAY | INT_U; + vp_pipest->val.i = rv; + } /* restores IO */ quitenv(NULL); @@ -570,6 +579,8 @@ comexec(struct op *t, struct tbl * volatile tp, const char **ap, * in theory, we could allow -u, but that would * mean to use ksh_getopt here and possibly ad- * ded complexity and more code and isn't worth + * additional hassle (and the builtin must call + * ksh_getopt already but can't come back here) */ if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' && /* argument, begins with -, is not - or -- */ diff --git a/funcs.c b/funcs.c index 12656e4..35dba6e 100644 --- a/funcs.c +++ b/funcs.c @@ -38,7 +38,7 @@ #endif #endif -__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.171 2011/02/13 21:13:06 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.172 2011/02/18 22:26:08 tg Exp $"); #if HAVE_KILLPG /* @@ -3599,7 +3599,7 @@ c_realpath(const char **wp) int c_cat(const char **wp) { - int fd = STDIN_FILENO, rv = 0; + int fd = STDIN_FILENO, rv; ssize_t n, w; const char *fn = ""; char *buf, *cp; @@ -3611,11 +3611,19 @@ c_cat(const char **wp) return (1); } - /* skip argv[0] */ - ++wp; - if (wp[0] && !strcmp(wp[0], "--")) - /* skip "--" (options separator) */ - ++wp; + /* parse options: POSIX demands we support "-u" as no-op */ + while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) { + switch (rv) { + case 'u': + /* we already operate unbuffered */ + break; + default: + bi_errorf(T_synerr); + return (1); + } + } + wp += builtin_opt.optind; + rv = 0; do { if (*wp) { diff --git a/jobs.c b/jobs.c index eb07f19..a75a47e 100644 --- a/jobs.c +++ b/jobs.c @@ -22,7 +22,7 @@ #include "sh.h" -__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.74 2011/01/30 01:35:34 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.75 2011/02/18 22:26:09 tg Exp $"); #if HAVE_KILLPG #define mksh_killpg killpg @@ -97,6 +97,7 @@ 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 @@ -207,6 +208,19 @@ j_init(void) tty_init(true, true); } +static int +proc_errorlevel(Proc *p) +{ + switch (p->state) { + case PEXITED: + return (WEXITSTATUS(p->status)); + case PSIGNALLED: + return (128 + WTERMSIG(p->status)); + default: + return (0); + } +} + /* job cleanup before shell exit */ void j_exit(void) @@ -349,7 +363,7 @@ exchild(struct op *t, int flags, /* for pipelines */ static Proc *last_proc; - int rv = 0, forksleep; + int rv = 0, forksleep, jwflags = JW_NONE; #ifndef MKSH_NOPROSPECTOFWORK sigset_t omask; #endif @@ -357,6 +371,11 @@ exchild(struct op *t, int flags, Job *j; pid_t cldpid; + if (flags & XPIPEST) { + flags &= ~XPIPEST; + jwflags |= JW_PIPEST; + } + if (flags & XEXEC) /* * Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND @@ -543,7 +562,7 @@ exchild(struct op *t, int flags, shf_flush(shl_out); } } else - rv = j_waitj(j, JW_NONE, "jw:last proc"); + rv = j_waitj(j, jwflags, "jw:last proc"); } #ifndef MKSH_NOPROSPECTOFWORK @@ -1158,6 +1177,38 @@ j_waitj(Job *j, j_systime = j->systime; rv = j->status; + if ((flags & JW_PIPEST) && (j->proc_list != NULL)) { + size_t num = 0; + Proc *p = j->proc_list; + 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); + p = p->next; + } + } + if (!(flags & JW_ASYNCNOTIFY) #ifndef MKSH_UNEMPLOYED && (!Flag(FMONITOR) || j->state != PSTOPPED) @@ -1295,18 +1346,7 @@ check_job(Job *j) 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 diff --git a/main.c b/main.c index dbb455a..28c0173 100644 --- a/main.c +++ b/main.c @@ -33,7 +33,7 @@ #include #endif -__RCSID("$MirOS: src/bin/mksh/main.c,v 1.176 2011/02/11 01:18:18 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/main.c,v 1.177 2011/02/18 22:26:10 tg Exp $"); extern char **environ; @@ -364,6 +364,7 @@ main(int argc, const char *argv[]) vp->flag |= INT_U; setint((vp = global("RANDOM")), rndsetup()); vp->flag |= INT_U; + setint((vp_pipest = global("PIPESTATUS")), 0); /* Set this before parsing arguments */ Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid; diff --git a/mksh.1 b/mksh.1 index c79eefe..9fe86ce 100644 --- a/mksh.1 +++ b/mksh.1 @@ -1,4 +1,4 @@ -.\" $MirOS: src/bin/mksh/mksh.1,v 1.250 2011/02/11 01:18:19 tg Exp $ +.\" $MirOS: src/bin/mksh/mksh.1,v 1.251 2011/02/18 22:26:11 tg Exp $ .\" $OpenBSD: ksh.1,v 1.138 2010/09/20 07:41:17 jmc Exp $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, @@ -72,7 +72,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" use our own definition. And .Dd must come *first*, always. .\" -.Dd $Mdocdate: February 11 2011 $ +.Dd $Mdocdate: February 18 2011 $ .\" .\" Check which macro package we use .\" @@ -1672,7 +1672,7 @@ arguments or splitting arguments with spaces. .El .Pp The following parameters are set and/or used by the shell: -.Bl -tag -width "EXECSHELL" +.Bl -tag -width "PIPESTATUS" .It Ev _ No (underscore) When an external command is executed by the shell, this parameter is set in the environment of the new process to the path of the executed command. @@ -1825,6 +1825,9 @@ colon, or two adjacent colons, is treated as a (the current directory). .It Ev PGRP The process ID of the shell's process group leader. +.It Ev PIPESTATUS +An array containing the errorlevel (exit status) codes, +one by one, of the last pipeline run in the foreground. .It Ev PPID The process ID of the shell's parent. .It Ev PS1 @@ -2995,19 +2998,23 @@ Execute the built-in command .Pp .It Xo .Ic cat -.Op Fl \- +.Op Fl u .Op Ar .Xc Read files sequentially, in command line order, and write them to standard output. -If +If a .Ar file is a single dash .Pq Sq - or absent, read from standard input. If any options are given, an external .Xr cat 1 -utility is invoked instead. +utility is invoked instead if called from the shell. +For direct builtin calls, the +.Tn POSIX +.Fl u +option is supported as a no-op. .Pp .It Xo .Ic cd @@ -4695,7 +4702,7 @@ Impose a limit of processes that can be run by the user at any one time. .It Fl q Ar n Limit the size of -.Px +.Tn POSIX message queues to .Ar n bytes. diff --git a/sh.h b/sh.h index d27a814..d09eab8 100644 --- a/sh.h +++ b/sh.h @@ -154,9 +154,9 @@ #endif #ifdef EXTERN -__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.431 2011/02/13 21:13:08 tg Exp $"); +__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.432 2011/02/18 22:26:13 tg Exp $"); #endif -#define MKSH_VERSION "R39 2011/02/13" +#define MKSH_VERSION "R39 2011/02/18" #ifndef MKSH_INCLUDES_ONLY @@ -577,6 +577,7 @@ extern struct env { EXTERN pid_t procpid; /* PID of executing process */ EXTERN int exstat; /* exit status */ EXTERN int subst_exstat; /* exit status of last $(..)/`..` */ +EXTERN struct tbl *vp_pipest; /* global PIPESTATUS array */ EXTERN short trap_exstat; /* exit status before running a trap */ EXTERN uint8_t trap_nested; /* running nested traps */ EXTERN uint8_t shell_flags[FNFLAGS]; @@ -948,7 +949,7 @@ struct tbl { /* table item */ #define LCASEV BIT(17) /* convert to lower case */ #define UCASEV_AL BIT(18) /* convert to upper case / autoload function */ #define INT_U BIT(19) /* unsigned integer */ -#define INT_L BIT(20) /* long integer (no-op) */ +#define INT_L BIT(20) /* long integer (no-op but used as magic) */ #define IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */ #define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */ #define EXPRINEVAL BIT(23) /* contents currently being evaluated */ @@ -1162,6 +1163,7 @@ struct ioword { #define XERROK BIT(8) /* non-zero exit ok (for set -e) */ #define XCOPROC BIT(9) /* starting a co-process */ #define XTIME BIT(10) /* timing TCOM command */ +#define XPIPEST BIT(11) /* want PIPESTATUS */ /* * flags to control expansion of words (assumed by t->evalflags to fit