• 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: 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
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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
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 $
.\"-
.\" 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
View File

@ -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