• add -u option (POSIX: unbuffered ⇒ nop) to the built-in cat

• PIPESTATUS now supported (like bash 2) whose last member
  may actually differ from $? since the latter may not be the
  result of a pipeline partial command
• add regression tests, documentation, etc.
This commit is contained in:
tg 2011-02-18 22:26:13 +00:00
parent 36d41ed9ee
commit 0b57abd4d3
7 changed files with 130 additions and 38 deletions

29
check.t
View File

@ -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: 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: 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 $ # $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 # http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout: expected-stdout:
@(#)MIRBSD KSH R39 2011/02/13 @(#)MIRBSD KSH R39 2011/02/18
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -69,8 +69,9 @@ name: selftest-direct-builtin-call
description: description:
Check that direct builtin calls work Check that direct builtin calls work
stdin: stdin:
ln -s "$__progname" cat
ln -s "$__progname" echo ln -s "$__progname" echo
./echo -c 'echo foo' ./echo -c 'echo foo' | ./cat -u
expected-stdout: expected-stdout:
-c echo foo -c echo foo
--- ---
@ -5244,6 +5245,28 @@ expected-stdout:
200 hi 200 hi
300 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 name: persist-history-1
description: description:
Check if persistent history saving works Check if persistent history saving works

15
exec.c
View File

@ -22,7 +22,7 @@
#include "sh.h" #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 #ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL "/bin/sh" #define MKSH_DEFAULT_EXECSHELL "/bin/sh"
@ -153,6 +153,9 @@ execute(struct op * volatile t,
memset(e->savefd, 0, NUFILE * sizeof(short)); 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() */ /* do redirection, to be restored in quitenv() */
if (t->ioact != NULL) if (t->ioact != NULL)
for (iowp = t->ioact; *iowp != NULL; iowp++) { for (iowp = t->ioact; *iowp != NULL; iowp++) {
@ -207,7 +210,7 @@ execute(struct op * volatile t,
/* no need to re-restore this */ /* no need to re-restore this */
e->savefd[1] = 0; e->savefd[1] = 0;
/* Let exchild() close 0 in parent, after fork, before wait */ /* 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)) if (!(flags&XBGND) && !(flags&XXCOM))
rv = i; rv = i;
break; break;
@ -449,6 +452,12 @@ execute(struct op * volatile t,
} }
Break: Break:
exstat = rv; 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 */ /* restores IO */
quitenv(NULL); 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 * in theory, we could allow -u, but that would
* mean to use ksh_getopt here and possibly ad- * mean to use ksh_getopt here and possibly ad-
* ded complexity and more code and isn't worth * 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' && if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
/* argument, begins with -, is not - or -- */ /* argument, begins with -, is not - or -- */

22
funcs.c
View File

@ -38,7 +38,7 @@
#endif #endif
#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 #if HAVE_KILLPG
/* /*
@ -3599,7 +3599,7 @@ c_realpath(const char **wp)
int int
c_cat(const char **wp) c_cat(const char **wp)
{ {
int fd = STDIN_FILENO, rv = 0; int fd = STDIN_FILENO, rv;
ssize_t n, w; ssize_t n, w;
const char *fn = "<stdin>"; const char *fn = "<stdin>";
char *buf, *cp; char *buf, *cp;
@ -3611,11 +3611,19 @@ c_cat(const char **wp)
return (1); return (1);
} }
/* skip argv[0] */ /* parse options: POSIX demands we support "-u" as no-op */
++wp; while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
if (wp[0] && !strcmp(wp[0], "--")) switch (rv) {
/* skip "--" (options separator) */ case 'u':
++wp; /* we already operate unbuffered */
break;
default:
bi_errorf(T_synerr);
return (1);
}
}
wp += builtin_opt.optind;
rv = 0;
do { do {
if (*wp) { if (*wp) {

70
jobs.c
View File

@ -22,7 +22,7 @@
#include "sh.h" #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 #if HAVE_KILLPG
#define mksh_killpg killpg #define mksh_killpg killpg
@ -97,6 +97,7 @@ struct job {
#define JW_INTERRUPT 0x01 /* ^C will stop the wait */ #define JW_INTERRUPT 0x01 /* ^C will stop the wait */
#define JW_ASYNCNOTIFY 0x02 /* asynchronous notification during wait ok */ #define JW_ASYNCNOTIFY 0x02 /* asynchronous notification during wait ok */
#define JW_STOPPEDWAIT 0x04 /* wait even if job stopped */ #define JW_STOPPEDWAIT 0x04 /* wait even if job stopped */
#define JW_PIPEST 0x08 /* want PIPESTATUS */
/* Error codes for j_lookup() */ /* Error codes for j_lookup() */
#define JL_OK 0 #define JL_OK 0
@ -207,6 +208,19 @@ j_init(void)
tty_init(true, true); 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 */ /* job cleanup before shell exit */
void void
j_exit(void) j_exit(void)
@ -349,7 +363,7 @@ exchild(struct op *t, int flags,
/* for pipelines */ /* for pipelines */
static Proc *last_proc; static Proc *last_proc;
int rv = 0, forksleep; int rv = 0, forksleep, jwflags = JW_NONE;
#ifndef MKSH_NOPROSPECTOFWORK #ifndef MKSH_NOPROSPECTOFWORK
sigset_t omask; sigset_t omask;
#endif #endif
@ -357,6 +371,11 @@ exchild(struct op *t, int flags,
Job *j; Job *j;
pid_t cldpid; pid_t cldpid;
if (flags & XPIPEST) {
flags &= ~XPIPEST;
jwflags |= JW_PIPEST;
}
if (flags & XEXEC) if (flags & XEXEC)
/* /*
* Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND * Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
@ -543,7 +562,7 @@ exchild(struct op *t, int flags,
shf_flush(shl_out); shf_flush(shl_out);
} }
} else } else
rv = j_waitj(j, JW_NONE, "jw:last proc"); rv = j_waitj(j, jwflags, "jw:last proc");
} }
#ifndef MKSH_NOPROSPECTOFWORK #ifndef MKSH_NOPROSPECTOFWORK
@ -1158,6 +1177,38 @@ j_waitj(Job *j,
j_systime = j->systime; j_systime = j->systime;
rv = j->status; 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) if (!(flags & JW_ASYNCNOTIFY)
#ifndef MKSH_UNEMPLOYED #ifndef MKSH_UNEMPLOYED
&& (!Flag(FMONITOR) || j->state != PSTOPPED) && (!Flag(FMONITOR) || j->state != PSTOPPED)
@ -1295,18 +1346,7 @@ check_job(Job *j)
jstate = p->state; jstate = p->state;
} }
j->state = jstate; j->state = jstate;
j->status = proc_errorlevel(j->last_proc);
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;
}
/* /*
* Note when co-process dies: can't be done in j_wait() nor * Note when co-process dies: can't be done in j_wait() nor

3
main.c
View File

@ -33,7 +33,7 @@
#include <locale.h> #include <locale.h>
#endif #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; extern char **environ;
@ -364,6 +364,7 @@ main(int argc, const char *argv[])
vp->flag |= INT_U; vp->flag |= INT_U;
setint((vp = global("RANDOM")), rndsetup()); setint((vp = global("RANDOM")), rndsetup());
vp->flag |= INT_U; vp->flag |= INT_U;
setint((vp_pipest = global("PIPESTATUS")), 0);
/* Set this before parsing arguments */ /* Set this before parsing arguments */
Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid; Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid;

21
mksh.1
View File

@ -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 $ .\" $OpenBSD: ksh.1,v 1.138 2010/09/20 07:41:17 jmc Exp $
.\"- .\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -72,7 +72,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to .\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always. .\" 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 .\" Check which macro package we use
.\" .\"
@ -1672,7 +1672,7 @@ arguments or splitting arguments with spaces.
.El .El
.Pp .Pp
The following parameters are set and/or used by the shell: The following parameters are set and/or used by the shell:
.Bl -tag -width "EXECSHELL" .Bl -tag -width "PIPESTATUS"
.It Ev _ No (underscore) .It Ev _ No (underscore)
When an external command is executed by the shell, this parameter is set in the 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. 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). (the current directory).
.It Ev PGRP .It Ev PGRP
The process ID of the shell's process group leader. 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 .It Ev PPID
The process ID of the shell's parent. The process ID of the shell's parent.
.It Ev PS1 .It Ev PS1
@ -2995,19 +2998,23 @@ Execute the built-in command
.Pp .Pp
.It Xo .It Xo
.Ic cat .Ic cat
.Op Fl \- .Op Fl u
.Op Ar .Op Ar
.Xc .Xc
Read files sequentially, in command line order, and write them to Read files sequentially, in command line order, and write them to
standard output. standard output.
If If a
.Ar file .Ar file
is a single dash is a single dash
.Pq Sq - .Pq Sq -
or absent, read from standard input. or absent, read from standard input.
If any options are given, an external If any options are given, an external
.Xr cat 1 .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 .Pp
.It Xo .It Xo
.Ic cd .Ic cd
@ -4695,7 +4702,7 @@ Impose a limit of
processes that can be run by the user at any one time. processes that can be run by the user at any one time.
.It Fl q Ar n .It Fl q Ar n
Limit the size of Limit the size of
.Px .Tn POSIX
message queues to message queues to
.Ar n .Ar n
bytes. bytes.

8
sh.h
View File

@ -154,9 +154,9 @@
#endif #endif
#ifdef EXTERN #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 #endif
#define MKSH_VERSION "R39 2011/02/13" #define MKSH_VERSION "R39 2011/02/18"
#ifndef MKSH_INCLUDES_ONLY #ifndef MKSH_INCLUDES_ONLY
@ -577,6 +577,7 @@ extern struct env {
EXTERN pid_t procpid; /* PID of executing process */ EXTERN pid_t procpid; /* PID of executing process */
EXTERN int exstat; /* exit status */ EXTERN int exstat; /* exit status */
EXTERN int subst_exstat; /* exit status of last $(..)/`..` */ 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 short trap_exstat; /* exit status before running a trap */
EXTERN uint8_t trap_nested; /* running nested traps */ EXTERN uint8_t trap_nested; /* running nested traps */
EXTERN uint8_t shell_flags[FNFLAGS]; EXTERN uint8_t shell_flags[FNFLAGS];
@ -948,7 +949,7 @@ struct tbl { /* table item */
#define LCASEV BIT(17) /* convert to lower case */ #define LCASEV BIT(17) /* convert to lower case */
#define UCASEV_AL BIT(18) /* convert to upper case / autoload function */ #define UCASEV_AL BIT(18) /* convert to upper case / autoload function */
#define INT_U BIT(19) /* unsigned integer */ #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 IMPORT BIT(21) /* flag to typeset(): no arrays, must have = */
#define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */ #define LOCAL_COPY BIT(22) /* with LOCAL - copy attrs from existing var */
#define EXPRINEVAL BIT(23) /* contents currently being evaluated */ #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 XERROK BIT(8) /* non-zero exit ok (for set -e) */
#define XCOPROC BIT(9) /* starting a co-process */ #define XCOPROC BIT(9) /* starting a co-process */
#define XTIME BIT(10) /* timing TCOM command */ #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 * flags to control expansion of words (assumed by t->evalflags to fit