plug regression introduced with read -d by fixing the problem differently

This commit is contained in:
tg 2011-01-22 20:33:14 +00:00
parent 9b02e15d26
commit 414c09ab1a
3 changed files with 229 additions and 104 deletions

20
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.400 2011/01/21 22:25:31 tg Exp $ # $MirOS: src/bin/mksh/check.t,v 1.401 2011/01/22 20:33:11 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/01/21 @(#)MIRBSD KSH R39 2011/01/22
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -3411,6 +3411,22 @@ stdin:
expected-stdout: expected-stdout:
[abc] [abc]
--- ---
name: read-regress-1
description:
Check a regression of read
file-setup: file 644 "foo"
foo bar
baz
blah
stdin:
while read a b c; do
read d
break
done <foo
echo "<$a|$b|$c><$d>"
expected-stdout:
<foo|bar|><baz>
---
name: read-delim-1 name: read-delim-1
description: description:
Check read with delimiters Check read with delimiters

309
funcs.c
View File

@ -26,7 +26,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.166 2011/01/21 22:00:15 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.167 2011/01/22 20:33:13 tg Exp $");
#if HAVE_KILLPG #if HAVE_KILLPG
/* /*
@ -50,7 +50,8 @@ __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.166 2011/01/21 22:00:15 tg Exp $");
extern uint8_t set_refflag; extern uint8_t set_refflag;
/* A leading = means assignments before command are kept; /*
* A leading = means assignments before command are kept;
* a leading * means a POSIX special builtin; * a leading * means a POSIX special builtin;
* a leading + means a POSIX regular builtin * a leading + means a POSIX regular builtin
* (* and + should not be combined). * (* and + should not be combined).
@ -78,9 +79,11 @@ const struct builtin mkshbuiltins[] = {
{"ulimit", c_ulimit}, {"ulimit", c_ulimit},
{"+umask", c_umask}, {"+umask", c_umask},
{"*=unset", c_unset}, {"*=unset", c_unset},
{T_palias, c_alias}, /* no =: AT&T manual wrong */ /* no =: AT&T manual wrong */
{T_palias, c_alias},
{"+cd", c_cd}, {"+cd", c_cd},
{"chdir", c_cd}, /* dash compatibility hack */ /* dash compatibility hack */
{"chdir", c_cd},
{"+command", c_command}, {"+command", c_command},
{"echo", c_print}, {"echo", c_print},
{"*=export", c_typeset}, {"*=export", c_typeset},
@ -186,7 +189,6 @@ do_realpath(const char *upath)
XString xs; XString xs;
ptrdiff_t pos; ptrdiff_t pos;
size_t len; size_t len;
int symlinks = 32; /* max. recursion depth */
int llen; int llen;
struct stat sb; struct stat sb;
#ifdef NO_PATH_MAX #ifdef NO_PATH_MAX
@ -197,6 +199,8 @@ do_realpath(const char *upath)
#define pathlen PATH_MAX #define pathlen PATH_MAX
#define pathcnd (!ldest) #define pathcnd (!ldest)
#endif #endif
/* max. recursion depth */
int symlinks = 32;
if (upath[0] == '/') { if (upath[0] == '/') {
/* upath is an absolute pathname */ /* upath is an absolute pathname */
@ -341,7 +345,8 @@ do_realpath(const char *upath)
return (Xclose(xs, xp)); return (Xclose(xs, xp));
notfound: notfound:
llen = errno; /* save; free(3) might trash it */ /* save; free(3) might trash it */
llen = errno;
if (ldest != NULL) if (ldest != NULL)
afree(ldest, ATEMP); afree(ldest, ATEMP);
afree(ipath, ATEMP); afree(ipath, ATEMP);
@ -358,8 +363,10 @@ c_cd(const char **wp)
{ {
int optc, rv, phys_path; int optc, rv, phys_path;
bool physical = Flag(FPHYSICAL) ? true : false; bool physical = Flag(FPHYSICAL) ? true : false;
int cdnode; /* was a node from cdpath added in? */ /* was a node from cdpath added in? */
bool printpath = false; /* print where we cd'd? */ int cdnode;
/* print where we cd'd? */
bool printpath = false;
struct tbl *pwd_s, *oldpwd_s; struct tbl *pwd_s, *oldpwd_s;
XString xs; XString xs;
char *dir, *allocd = NULL, *tryp, *pwd, *cdpath; char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
@ -413,7 +420,8 @@ c_cd(const char **wp)
bi_errorf("can't determine current directory"); bi_errorf("can't determine current directory");
return (1); return (1);
} }
/* substitute arg1 for arg2 in current path. /*
* substitute arg1 for arg2 in current path.
* if the first substitution fails because the cd fails * if the first substitution fails because the cd fails
* we could try to find another substitution. For now * we could try to find another substitution. For now
* we don't * we don't
@ -476,7 +484,8 @@ c_cd(const char **wp)
/* Clear out tracked aliases with relative paths */ /* Clear out tracked aliases with relative paths */
flushcom(0); flushcom(0);
/* Set OLDPWD (note: unsetting OLDPWD does not disable this /*
* Set OLDPWD (note: unsetting OLDPWD does not disable this
* setting in AT&T ksh) * setting in AT&T ksh)
*/ */
if (current_wd[0]) if (current_wd[0])
@ -616,7 +625,8 @@ c_print(const char **wp)
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
switch (optc) { switch (optc) {
case 'R': /* fake BSD echo command */ case 'R':
/* fake BSD echo command */
flags |= PO_PMINUSMINUS; flags |= PO_PMINUSMINUS;
flags &= ~PO_EXPAND; flags &= ~PO_EXPAND;
opts = "ne"; opts = "ne";
@ -712,7 +722,8 @@ c_print(const char **wp)
int len = Xlength(xs, xp); int len = Xlength(xs, xp);
int opipe = 0; int opipe = 0;
/* Ensure we aren't killed by a SIGPIPE while writing to /*
* Ensure we aren't killed by a SIGPIPE while writing to
* a coprocess. AT&T ksh doesn't seem to do this (seems * a coprocess. AT&T ksh doesn't seem to do this (seems
* to just check that the co-process is alive which is * to just check that the co-process is alive which is
* not enough). * not enough).
@ -787,7 +798,8 @@ c_whence(const char **wp)
/* Note that -p on its own is deal with in comexec() */ /* Note that -p on its own is deal with in comexec() */
if (pflag) if (pflag)
fcflags |= FC_DEFPATH; fcflags |= FC_DEFPATH;
/* Convert command options to whence options - note that /*
* Convert command options to whence options - note that
* command -pV uses a different path search than whence -v * command -pV uses a different path search than whence -v
* or whence -pv. This should be considered a feature. * or whence -pv. This should be considered a feature.
*/ */
@ -889,7 +901,8 @@ c_whence(const char **wp)
int int
c_command(const char **wp) c_command(const char **wp)
{ {
/* Let c_whence do the work. Note that c_command() must be /*
* Let c_whence do the work. Note that c_command() must be
* a distinct function from c_whence() (tested in comexec()). * a distinct function from c_whence() (tested in comexec()).
*/ */
return (c_whence(wp)); return (c_whence(wp));
@ -908,18 +921,26 @@ c_typeset(const char **wp)
bool localv = false, func = false, pflag = false, istset = true; bool localv = false, func = false, pflag = false, istset = true;
switch (**wp) { switch (**wp) {
case 'e': /* export */
/* export */
case 'e':
fset |= EXPORT; fset |= EXPORT;
istset = false; istset = false;
break; break;
case 'r': /* readonly */
/* readonly */
case 'r':
fset |= RDONLY; fset |= RDONLY;
istset = false; istset = false;
break; break;
case 's': /* set */
/* set */
case 's':
/* called with 'typeset -' */ /* called with 'typeset -' */
break; break;
case 't': /* typeset */
/* typeset */
case 't':
localv = true; localv = true;
break; break;
} }
@ -929,7 +950,8 @@ c_typeset(const char **wp)
fieldstr = basestr = NULL; fieldstr = basestr = NULL;
builtin_opt.flags |= GF_PLUSOPT; builtin_opt.flags |= GF_PLUSOPT;
/* AT&T ksh seems to have 0-9 as options which are multiplied /*
* AT&T ksh seems to have 0-9 as options which are multiplied
* to get a number that is used with -L, -R, -Z or -i (eg, -1R2 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
* sets right justify in a field of 12). This allows options * sets right justify in a field of 12). This allows options
* to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
@ -949,7 +971,8 @@ c_typeset(const char **wp)
fieldstr = builtin_opt.optarg; fieldstr = builtin_opt.optarg;
break; break;
case 'U': case 'U':
/* AT&T ksh uses u, but this conflicts with /*
* AT&T ksh uses u, but this conflicts with
* upper/lower case. If this option is changed, * upper/lower case. If this option is changed,
* need to change the -U below as well * need to change the -U below as well
*/ */
@ -979,8 +1002,8 @@ c_typeset(const char **wp)
case 'n': case 'n':
set_refflag = (builtin_opt.info & GI_PLUS) ? 2 : 1; set_refflag = (builtin_opt.info & GI_PLUS) ? 2 : 1;
break; break;
/* export, readonly: POSIX -p flag */
case 'p': case 'p':
/* export, readonly: POSIX -p flag */
/* typeset: show values as well */ /* typeset: show values as well */
pflag = true; pflag = true;
if (istset) if (istset)
@ -993,7 +1016,8 @@ c_typeset(const char **wp)
flag = TRACE; flag = TRACE;
break; break;
case 'u': case 'u':
flag = UCASEV_AL; /* upper case / autoload */ /* upper case / autoload */
flag = UCASEV_AL;
break; break;
case 'x': case 'x':
flag = EXPORT; flag = EXPORT;
@ -1033,19 +1057,24 @@ c_typeset(const char **wp)
return (1); return (1);
} }
if (wp[builtin_opt.optind]) { if (wp[builtin_opt.optind]) {
/* Take care of exclusions. /*
* Take care of exclusions.
* At this point, flags in fset are cleared in fclr and vice * At this point, flags in fset are cleared in fclr and vice
* versa. This property should be preserved. * versa. This property should be preserved.
*/ */
if (fset & LCASEV) /* LCASEV has priority over UCASEV_AL */ if (fset & LCASEV)
/* LCASEV has priority over UCASEV_AL */
fset &= ~UCASEV_AL; fset &= ~UCASEV_AL;
if (fset & LJUST) /* LJUST has priority over RJUST */ if (fset & LJUST)
/* LJUST has priority over RJUST */
fset &= ~RJUST; fset &= ~RJUST;
if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */ if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
/* -Z implies -ZR */
fset |= RJUST; fset |= RJUST;
fclr &= ~RJUST; fclr &= ~RJUST;
} }
/* Setting these attributes clears the others, unless they /*
* Setting these attributes clears the others, unless they
* are also set in this command * are also set in this command
*/ */
if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV | if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
@ -1089,7 +1118,9 @@ c_typeset(const char **wp)
} }
/* list variables and attributes */ /* list variables and attributes */
flag = fset | fclr; /* no difference at this point.. */
/* no difference at this point.. */
flag = fset | fclr;
if (func) { if (func) {
for (l = e->loc; l; l = l->next) { for (l = e->loc; l; l = l->next) {
for (p = ktsort(&l->funs); (vp = *p++); ) { for (p = ktsort(&l->funs); (vp = *p++); ) {
@ -1133,15 +1164,18 @@ c_typeset(const char **wp)
if (flag && (vp->flag & flag) == 0) if (flag && (vp->flag & flag) == 0)
continue; continue;
for (; vp; vp = vp->u.array) { for (; vp; vp = vp->u.array) {
/* Ignore array elements that aren't /*
* Ignore array elements that aren't
* set unless there are no set elements, * set unless there are no set elements,
* in which case the first is reported on */ * in which case the first is reported on
*/
if ((vp->flag&ARRAY) && any_set && if ((vp->flag&ARRAY) && any_set &&
!(vp->flag & ISSET)) !(vp->flag & ISSET))
continue; continue;
/* no arguments */ /* no arguments */
if (thing == 0 && flag == 0) { if (thing == 0 && flag == 0) {
/* AT&T ksh prints things /*
* AT&T ksh prints things
* like export, integer, * like export, integer,
* leftadj, zerofill, etc., * leftadj, zerofill, etc.,
* but POSIX says must * but POSIX says must
@ -1207,8 +1241,10 @@ c_typeset(const char **wp)
char *s = str_val(vp); char *s = str_val(vp);
shf_putc('=', shl_stdout); shf_putc('=', shl_stdout);
/* AT&T ksh can't have /*
* justified integers.. */ * AT&T ksh can't have
* justified integers...
*/
if ((vp->flag & if ((vp->flag &
(INTEGER|LJUST|RJUST)) == (INTEGER|LJUST|RJUST)) ==
INTEGER) INTEGER)
@ -1218,7 +1254,8 @@ c_typeset(const char **wp)
} }
shf_putc('\n', shl_stdout); shf_putc('\n', shl_stdout);
} }
/* Only report first 'element' of an array with /*
* Only report first 'element' of an array with
* no set elements. * no set elements.
*/ */
if (!any_set) if (!any_set)
@ -1389,7 +1426,8 @@ c_unalias(const char **wp)
break; break;
case 'd': case 'd':
#ifdef MKSH_NOPWNAM #ifdef MKSH_NOPWNAM
t = NULL; /* fix "unalias -dt" */ /* fix "unalias -dt" */
t = NULL;
#else #else
t = &homedirs; t = &homedirs;
#endif #endif
@ -1409,7 +1447,8 @@ c_unalias(const char **wp)
for (; *wp != NULL; wp++) { for (; *wp != NULL; wp++) {
ap = ktsearch(t, *wp, hash(*wp)); ap = ktsearch(t, *wp, hash(*wp));
if (ap == NULL) { if (ap == NULL) {
rv = 1; /* POSIX */ /* POSIX */
rv = 1;
continue; continue;
} }
if (ap->flag&ALLOC) { if (ap->flag&ALLOC) {
@ -1440,12 +1479,14 @@ c_let(const char **wp)
int rv = 1; int rv = 1;
mksh_ari_t val; mksh_ari_t val;
if (wp[1] == NULL) /* AT&T ksh does this */ if (wp[1] == NULL)
/* AT&T ksh does this */
bi_errorf("no arguments"); bi_errorf("no arguments");
else else
for (wp++; *wp; wp++) for (wp++; *wp; wp++)
if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) { if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
rv = 2; /* distinguish error from zero result */ /* distinguish error from zero result */
rv = 2;
break; break;
} else } else
rv = val == 0; rv = val == 0;
@ -1468,7 +1509,8 @@ c_jobs(const char **wp)
case 'n': case 'n':
nflag = 1; nflag = 1;
break; break;
case 'z': /* debugging: print zombies */ case 'z':
/* debugging: print zombies */
nflag = -1; nflag = -1;
break; break;
case '?': case '?':
@ -1693,7 +1735,8 @@ c_getopts(const char **wp)
buf[1] = optc; buf[1] = optc;
buf[2] = '\0'; buf[2] = '\0';
} else { } else {
/* POSIX says var is set to ? at end-of-options, AT&T ksh /*
* POSIX says var is set to ? at end-of-options, AT&T ksh
* sets it to null - we go with POSIX... * sets it to null - we go with POSIX...
*/ */
buf[0] = optc < 0 ? '?' : optc; buf[0] = optc < 0 ? '?' : optc;
@ -1704,7 +1747,8 @@ c_getopts(const char **wp)
user_opt.uoptind = user_opt.optind; user_opt.uoptind = user_opt.optind;
voptarg = global("OPTARG"); voptarg = global("OPTARG");
voptarg->flag &= ~RDONLY; /* AT&T ksh clears ro and int */ /* AT&T ksh clears ro and int */
voptarg->flag &= ~RDONLY;
/* Paranoia: ensure no bizarre results. */ /* Paranoia: ensure no bizarre results. */
if (voptarg->flag & INTEGER) if (voptarg->flag & INTEGER)
typeset("OPTARG", 0, INTEGER, 0, 0); typeset("OPTARG", 0, INTEGER, 0, 0);
@ -1758,7 +1802,8 @@ c_bind(const char **wp)
} }
wp += builtin_opt.optind; wp += builtin_opt.optind;
if (*wp == NULL) /* list all */ if (*wp == NULL)
/* list all */
rv = x_bind(NULL, NULL, rv = x_bind(NULL, NULL,
#ifndef MKSH_SMALL #ifndef MKSH_SMALL
false, false,
@ -1876,7 +1921,8 @@ c_umask(const char **wp)
char op; char op;
old_umask = umask((mode_t)0); old_umask = umask((mode_t)0);
umask(old_umask); /* in case of error */ /* in case of error */
umask(old_umask);
old_umask = ~old_umask; old_umask = ~old_umask;
new_umask = old_umask; new_umask = old_umask;
positions = 0; positions = 0;
@ -1897,7 +1943,8 @@ c_umask(const char **wp)
break; break;
} }
if (!positions) if (!positions)
positions = 0111; /* default is a */ /* default is a */
positions = 0111;
if (!vstrchr("=+-", op = *cp)) if (!vstrchr("=+-", op = *cp))
break; break;
cp++; cp++;
@ -1975,7 +2022,8 @@ c_dot(const char **wp)
/* Set positional parameters? */ /* Set positional parameters? */
if (wp[builtin_opt.optind + 1]) { if (wp[builtin_opt.optind + 1]) {
argv = wp + builtin_opt.optind; argv = wp + builtin_opt.optind;
argv[0] = e->loc->argv[0]; /* preserve $0 */ /* preserve $0 */
argv[0] = e->loc->argv[0];
for (argc = 0; argv[argc + 1]; argc++) for (argc = 0; argv[argc + 1]; argc++)
; ;
} else { } else {
@ -2006,7 +2054,8 @@ c_wait(const char **wp)
for (; *wp; wp++) for (; *wp; wp++)
rv = waitfor(*wp, &sig); rv = waitfor(*wp, &sig);
if (rv < 0) if (rv < 0)
rv = sig ? sig : 127; /* magic exit code: bad job-id */ /* magic exit code: bad job-id */
rv = sig ? sig : 127;
} }
return (rv); return (rv);
} }
@ -2014,7 +2063,7 @@ c_wait(const char **wp)
int int
c_read(const char **wp) c_read(const char **wp)
{ {
int c = 0, ecode = 0, fd = 0, optc; int c, ecode = 0, fd = 0, optc;
bool expande = true, historyr = false, expanding; bool expande = true, historyr = false, expanding;
const char *cp, *emsg; const char *cp, *emsg;
struct shf *shf; struct shf *shf;
@ -2093,17 +2142,26 @@ c_read(const char **wp)
Xinit(xs, xp, 128, ATEMP); Xinit(xs, xp, 128, ATEMP);
expanding = false; expanding = false;
Xinit(cs, ccp, 128, ATEMP); Xinit(cs, ccp, 128, ATEMP);
/* initialise to something not EOF or delim or any character */
c = 0x100;
for (; *wp != NULL; wp++) { for (; *wp != NULL; wp++) {
for (ccp = Xstring(cs, ccp); ; ) { for (ccp = Xstring(cs, ccp); ; ) {
if (c == delim || c == EOF)
break;
/* loop to read one character */
while (1) { while (1) {
c = shf_getc(shf); c = shf_getc(shf);
/* we break unless NUL or EOF, so... */
if (c == delim) if (c == delim)
/* in case delim == NUL */
break; break;
if (c == '\0') if (c == '\0')
/* skip any read NUL byte */
continue; continue;
if (c == EOF && shf_error(shf) && if (c == EOF && shf_error(shf) &&
shf_errno(shf) == EINTR) { shf_errno(shf) == EINTR) {
/* Was the offending signal one that /*
* Was the offending signal one that
* would normally kill a process? * would normally kill a process?
* If so, pretend the read was killed. * If so, pretend the read was killed.
*/ */
@ -2124,7 +2182,7 @@ c_read(const char **wp)
Xcheck(cs, ccp); Xcheck(cs, ccp);
if (expanding) { if (expanding) {
expanding = false; expanding = false;
if (c == '\n') { if (c == delim) {
c = 0; c = 0;
if (Flag(FTALKING_I) && isatty(fd)) { if (Flag(FTALKING_I) && isatty(fd)) {
/* /*
@ -2272,7 +2330,8 @@ c_trap(const char **wp)
* command 'exit' isn't confused with the pseudo-signal * command 'exit' isn't confused with the pseudo-signal
* 'EXIT'. * 'EXIT'.
*/ */
s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; /* get command */ /* get command */
s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
if (s != NULL && s[0] == '-' && s[1] == '\0') if (s != NULL && s[0] == '-' && s[1] == '\0')
s = NULL; s = NULL;
@ -2306,10 +2365,12 @@ c_exitreturn(const char **wp)
exstat = n; exstat = n;
} else if (trap_exstat != -1) } else if (trap_exstat != -1)
exstat = trap_exstat; exstat = trap_exstat;
if (wp[0][0] == 'r') { /* return */ if (wp[0][0] == 'r') {
/* return */
struct env *ep; struct env *ep;
/* need to tell if this is exit or return so trap exit will /*
* need to tell if this is exit or return so trap exit will
* work right (POSIX) * work right (POSIX)
*/ */
for (ep = e; ep; ep = ep->oenv) for (ep = e; ep; ep = ep->oenv)
@ -2324,7 +2385,8 @@ c_exitreturn(const char **wp)
how = LSHELL; how = LSHELL;
} }
quitenv(NULL); /* get rid of any i/o redirections */ /* get rid of any i/o redirections */
quitenv(NULL);
unwind(how); unwind(how);
/* NOTREACHED */ /* NOTREACHED */
} }
@ -2361,7 +2423,8 @@ c_brkcont(const char **wp)
} }
if (quit) { if (quit) {
/* AT&T ksh doesn't print a message - just does what it /*
* AT&T ksh doesn't print a message - just does what it
* can. We print a message 'cause it helps in debugging * can. We print a message 'cause it helps in debugging
* scripts, but don't generate an error (ie, keep going). * scripts, but don't generate an error (ie, keep going).
*/ */
@ -2369,7 +2432,8 @@ c_brkcont(const char **wp)
warningf(true, "%s: %s %s", wp[0], "can't", wp[0]); warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
return (0); return (0);
} }
/* POSIX says if n is too big, the last enclosing loop /*
* POSIX says if n is too big, the last enclosing loop
* shall be used. Doesn't say to print an error but we * shall be used. Doesn't say to print an error but we
* do anyway 'cause the user messed up. * do anyway 'cause the user messed up.
*/ */
@ -2403,7 +2467,8 @@ c_set(const char **wp)
if (setargs) { if (setargs) {
wp += argi - 1; wp += argi - 1;
owp = wp; owp = wp;
wp[0] = l->argv[0]; /* save $0 */ /* save $0 */
wp[0] = l->argv[0];
while (*++wp != NULL) while (*++wp != NULL)
strdupx(*wp, *wp, &l->area); strdupx(*wp, *wp, &l->area);
l->argc = wp - owp - 1; l->argc = wp - owp - 1;
@ -2443,7 +2508,8 @@ c_unset(const char **wp)
} }
wp += builtin_opt.optind; wp += builtin_opt.optind;
for (; (id = *wp) != NULL; wp++) for (; (id = *wp) != NULL; wp++)
if (unset_var) { /* unset variable */ if (unset_var) {
/* unset variable */
struct tbl *vp; struct tbl *vp;
char *cp = NULL; char *cp = NULL;
size_t n; size_t n;
@ -2465,7 +2531,8 @@ c_unset(const char **wp)
return (1); return (1);
} }
unset(vp, optc); unset(vp, optc);
} else /* unset function */ } else
/* unset function */
define(id, NULL); define(id, NULL);
return (0); return (0);
} }
@ -2539,7 +2606,8 @@ timex(struct op *t, int f, volatile int *xerrok)
} else } else
tf = TF_NOARGS; tf = TF_NOARGS;
if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */ if (tf & TF_NOARGS) {
/* ksh93 - report shell times (shell+kids) */
tf |= TF_NOREAL; tf |= TF_NOREAL;
timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime); timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime); timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
@ -2584,7 +2652,8 @@ timex_hook(struct op *t, char **volatile *app)
Getopt opt; Getopt opt;
ksh_getopt_reset(&opt, 0); ksh_getopt_reset(&opt, 0);
opt.optind = 0; /* start at the start */ /* start at the start */
opt.optind = 0;
while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1) while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
switch (optc) { switch (optc) {
case 'p': case 'p':
@ -2719,7 +2788,8 @@ c_mknod(const char **wp)
} }
#endif #endif
/* test(1) accepts the following grammar: /*-
test(1) accepts the following grammar:
oexpr ::= aexpr | aexpr "-o" oexpr ; oexpr ::= aexpr | aexpr "-o" oexpr ;
aexpr ::= nexpr | nexpr "-a" aexpr ; aexpr ::= nexpr | nexpr "-a" aexpr ;
nexpr ::= primary | "!" nexpr ; nexpr ::= primary | "!" nexpr ;
@ -2740,7 +2810,8 @@ c_mknod(const char **wp)
operand ::= <any thing> operand ::= <any thing>
*/ */
#define T_ERR_EXIT 2 /* POSIX says > 1 for errors */ /* POSIX says > 1 for errors */
#define T_ERR_EXIT 2
int int
c_test(const char **wp) c_test(const char **wp)
@ -2853,88 +2924,121 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
/* /*
* Unary Operators * Unary Operators
*/ */
case TO_STNZE: /* -n */ case TO_STNZE:
/* -n */
return (*opnd1 != '\0'); return (*opnd1 != '\0');
case TO_STZER: /* -z */ case TO_STZER:
/* -z */
return (*opnd1 == '\0'); return (*opnd1 == '\0');
case TO_OPTION: /* -o */ case TO_OPTION:
/* -o */
if ((i = *opnd1) == '!' || i == '?') if ((i = *opnd1) == '!' || i == '?')
opnd1++; opnd1++;
if ((k = option(opnd1)) == (size_t)-1) if ((k = option(opnd1)) == (size_t)-1)
return (0); return (0);
return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k)); return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
case TO_FILRD: /* -r */ case TO_FILRD:
/* -r */
return (test_eaccess(opnd1, R_OK) == 0); return (test_eaccess(opnd1, R_OK) == 0);
case TO_FILWR: /* -w */ case TO_FILWR:
/* -w */
return (test_eaccess(opnd1, W_OK) == 0); return (test_eaccess(opnd1, W_OK) == 0);
case TO_FILEX: /* -x */ case TO_FILEX:
/* -x */
return (test_eaccess(opnd1, X_OK) == 0); return (test_eaccess(opnd1, X_OK) == 0);
case TO_FILAXST: /* -a */ case TO_FILAXST:
case TO_FILEXST: /* -e */ /* -a */
case TO_FILEXST:
/* -e */
return (stat(opnd1, &b1) == 0); return (stat(opnd1, &b1) == 0);
case TO_FILREG: /* -r */ case TO_FILREG:
/* -r */
return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode)); return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
case TO_FILID: /* -d */ case TO_FILID:
/* -d */
return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode)); return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
case TO_FILCDEV: /* -c */ case TO_FILCDEV:
/* -c */
return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode)); return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
case TO_FILBDEV: /* -b */ case TO_FILBDEV:
/* -b */
return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode)); return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
case TO_FILFIFO: /* -p */ case TO_FILFIFO:
/* -p */
return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode)); return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
case TO_FILSYM: /* -h -L */ case TO_FILSYM:
/* -h -L */
return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode)); return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
case TO_FILSOCK: /* -S */ case TO_FILSOCK:
/* -S */
return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode)); return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
case TO_FILCDF:/* -H HP context dependent files (directories) */ case TO_FILCDF:
/* -H HP context dependent files (directories) */
return (0); return (0);
case TO_FILSETU: /* -u */ case TO_FILSETU:
/* -u */
return (stat(opnd1, &b1) == 0 && return (stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISUID) == S_ISUID); (b1.st_mode & S_ISUID) == S_ISUID);
case TO_FILSETG: /* -g */ case TO_FILSETG:
/* -g */
return (stat(opnd1, &b1) == 0 && return (stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISGID) == S_ISGID); (b1.st_mode & S_ISGID) == S_ISGID);
case TO_FILSTCK: /* -k */ case TO_FILSTCK:
/* -k */
#ifdef S_ISVTX #ifdef S_ISVTX
return (stat(opnd1, &b1) == 0 && return (stat(opnd1, &b1) == 0 &&
(b1.st_mode & S_ISVTX) == S_ISVTX); (b1.st_mode & S_ISVTX) == S_ISVTX);
#else #else
return (0); return (0);
#endif #endif
case TO_FILGZ: /* -s */ case TO_FILGZ:
/* -s */
return (stat(opnd1, &b1) == 0 && b1.st_size > 0L); return (stat(opnd1, &b1) == 0 && b1.st_size > 0L);
case TO_FILTT: /* -t */ case TO_FILTT:
/* -t */
if (opnd1 && !bi_getn(opnd1, &i)) { if (opnd1 && !bi_getn(opnd1, &i)) {
te->flags |= TEF_ERROR; te->flags |= TEF_ERROR;
i = 0; i = 0;
} else } else
i = isatty(opnd1 ? i : 0); i = isatty(opnd1 ? i : 0);
return (i); return (i);
case TO_FILUID: /* -O */ case TO_FILUID:
/* -O */
return (stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid); return (stat(opnd1, &b1) == 0 && b1.st_uid == ksheuid);
case TO_FILGID: /* -G */ case TO_FILGID:
/* -G */
return (stat(opnd1, &b1) == 0 && b1.st_gid == getegid()); return (stat(opnd1, &b1) == 0 && b1.st_gid == getegid());
/* /*
* Binary Operators * Binary Operators
*/ */
case TO_STEQL: /* = */ case TO_STEQL:
/* = */
if (te->flags & TEF_DBRACKET) if (te->flags & TEF_DBRACKET)
return (gmatchx(opnd1, opnd2, false)); return (gmatchx(opnd1, opnd2, false));
return (strcmp(opnd1, opnd2) == 0); return (strcmp(opnd1, opnd2) == 0);
case TO_STNEQ: /* != */ case TO_STNEQ:
/* != */
if (te->flags & TEF_DBRACKET) if (te->flags & TEF_DBRACKET)
return (!gmatchx(opnd1, opnd2, false)); return (!gmatchx(opnd1, opnd2, false));
return (strcmp(opnd1, opnd2) != 0); return (strcmp(opnd1, opnd2) != 0);
case TO_STLT: /* < */ case TO_STLT:
/* < */
return (strcmp(opnd1, opnd2) < 0); return (strcmp(opnd1, opnd2) < 0);
case TO_STGT: /* > */ case TO_STGT:
/* > */
return (strcmp(opnd1, opnd2) > 0); return (strcmp(opnd1, opnd2) > 0);
case TO_INTEQ: /* -eq */ case TO_INTEQ:
case TO_INTNE: /* -ne */ /* -eq */
case TO_INTGE: /* -ge */ case TO_INTNE:
case TO_INTGT: /* -gt */ /* -ne */
case TO_INTLE: /* -le */ case TO_INTGE:
case TO_INTLT: /* -lt */ /* -ge */
case TO_INTGT:
/* -gt */
case TO_INTLE:
/* -le */
case TO_INTLT:
/* -lt */
if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) || if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
!evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) { !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
/* error already printed.. */ /* error already printed.. */
@ -2955,21 +3059,26 @@ test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
case TO_INTLT: case TO_INTLT:
return (v1 < v2); return (v1 < v2);
} }
case TO_FILNT: /* -nt */ case TO_FILNT:
/* ksh88/ksh93 succeed if file2 can't be stated /* -nt */
/*
* ksh88/ksh93 succeed if file2 can't be stated
* (subtly different from 'does not exist'). * (subtly different from 'does not exist').
*/ */
return (stat(opnd1, &b1) == 0 && return (stat(opnd1, &b1) == 0 &&
(((s = stat(opnd2, &b2)) == 0 && (((s = stat(opnd2, &b2)) == 0 &&
b1.st_mtime > b2.st_mtime) || s < 0)); b1.st_mtime > b2.st_mtime) || s < 0));
case TO_FILOT: /* -ot */ case TO_FILOT:
/* ksh88/ksh93 succeed if file1 can't be stated /* -ot */
/*
* ksh88/ksh93 succeed if file1 can't be stated
* (subtly different from 'does not exist'). * (subtly different from 'does not exist').
*/ */
return (stat(opnd2, &b2) == 0 && return (stat(opnd2, &b2) == 0 &&
(((s = stat(opnd1, &b1)) == 0 && (((s = stat(opnd1, &b1)) == 0 &&
b1.st_mtime < b2.st_mtime) || s < 0)); b1.st_mtime < b2.st_mtime) || s < 0));
case TO_FILEQ: /* -ef */ case TO_FILEQ:
/* -ef */
return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 && return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino); b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
} }

4
sh.h
View File

@ -154,9 +154,9 @@
#endif #endif
#ifdef EXTERN #ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.423 2011/01/21 22:25:34 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.424 2011/01/22 20:33:14 tg Exp $");
#endif #endif
#define MKSH_VERSION "R39 2011/01/21" #define MKSH_VERSION "R39 2011/01/22"
#ifndef MKSH_INCLUDES_ONLY #ifndef MKSH_INCLUDES_ONLY