• 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:
parent
36d41ed9ee
commit
0b57abd4d3
29
check.t
29
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
|
||||
|
15
exec.c
15
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 -- */
|
||||
|
22
funcs.c
22
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 = "<stdin>";
|
||||
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) {
|
||||
|
70
jobs.c
70
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
|
||||
|
3
main.c
3
main.c
@ -33,7 +33,7 @@
|
||||
#include <locale.h>
|
||||
#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;
|
||||
|
21
mksh.1
21
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.
|
||||
|
8
sh.h
8
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user