New functionality: assign here document to string variable directly,

without cat and temp files. Hacked in Lëtzebuerg ☺

This was the third try. Where to put this was not palpable… same for =(…)
This commit is contained in:
tg 2011-01-09 21:57:29 +00:00
parent b4948e430f
commit 230f59d064
7 changed files with 205 additions and 78 deletions

45
check.t
View File

@ -1,9 +1,9 @@
# $MirOS: src/bin/mksh/check.t,v 1.397 2010/12/19 20:00:53 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.398 2011/01/09 21:57:22 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 $
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
# Thorsten Glaser <tg@mirbsd.org>
#
# Provided that these terms and disclaimer and all copyright notices
@ -25,7 +25,7 @@
# http://www.research.att.com/~gsf/public/ifs.sh
expected-stdout:
@(#)MIRBSD KSH R39 2010/12/19
@(#)MIRBSD KSH R39 2011/01/08
description:
Check version of shell.
stdin:
@ -2007,6 +2007,45 @@ stdin:
expected-stdout:
one
---
name: heredoc-10
description:
Check direct here document assignment
stdin:
x=u
va=<<EOF
=a $x \x40=
EOF
vb=<<'EOF'
=b $x \x40=
EOF
function foo {
vc=<<-EOF
=c $x \x40=
EOF
}
typeset -f foo
foo
# rather nonsensical, but…
vd=<<<"=d $x \x40="
ve=<<<'=e $x \x40='
vf=<<<$'=f $x \x40='
# now check
print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |"
expected-stdout:
function foo {
vc= <<- EOF
=c $x \x40=
EOF
}
| va={=a u \x40=
} vb={=b $x \x40=
} vc={=c u \x40=
} vd={=d u \x40=
} ve={=e $x \x40=
} vf={=f $x @=
} |
---
name: heredoc-quoting-unsubst
description:
Check for correct handling of quoted characters in

167
exec.c
View File

@ -1,7 +1,7 @@
/* $OpenBSD: exec.c,v 1.49 2009/01/29 23:27:26 jaredy Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -22,18 +22,18 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.83 2010/09/15 21:08:17 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.84 2011/01/09 21:57:25 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL "/bin/sh"
#endif
static int comexec(struct op *, struct tbl *volatile, const char **,
static int comexec(struct op *, struct tbl * volatile, const char **,
int volatile, volatile int *);
static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
static int call_builtin(struct tbl *, const char **);
static int iosetup(struct ioword *, struct tbl *);
static int herein(const char *, int);
static int herein(const char *, int, char **);
static const char *do_selectargs(const char **, bool);
static Test_op dbteste_isa(Test_env *, Test_meta);
static const char *dbteste_getopnd(Test_env *, Test_op, bool);
@ -43,7 +43,7 @@ static void dbteste_error(Test_env *, int, const char *);
* execute command tree
*/
int
execute(struct op *volatile t,
execute(struct op * volatile t,
volatile int flags, /* if XEXEC don't fork */
volatile int * volatile xerrok)
{
@ -52,9 +52,10 @@ execute(struct op *volatile t,
int pv[2];
const char ** volatile ap;
char ** volatile up;
const char *s, *cp;
const char *s, *ccp;
struct ioword **iowp;
struct tbl *tp = NULL;
char *cp;
if (t == NULL)
return (0);
@ -71,7 +72,48 @@ execute(struct op *volatile t,
if (trap)
runtraps(0);
if (t->type == TCOM) {
if (t->type == TCOM /* we want to run an executable... */) {
/* check if this is 'var=<<EOF' */
if (
/* we have zero arguments, i.e. no programme to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
/* we have exactly one I/O redirection */
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
(t->ioact[0]->flag & IOTYPE) == IOHERE &&
/* the variable assignment begins with a valid varname */
(ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
/* and has no right-hand side (i.e. "varname=") */
ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS &&
/* plus we can have a here document content */
herein(t->ioact[0]->heredoc, t->ioact[0]->flag & IOEVAL,
&cp) == 0 && cp && *cp) {
char *sp = cp, *dp;
size_t n = ccp - t->vars[0] + 2, z;
/* drop redirection (will be garbage collected) */
t->ioact = NULL;
/* set variable to its expanded value */
z = strlen(cp) + 1;
if (notoktomul(z, 2) || notoktoadd(z * 2, n))
internal_errorf(T_oomem, (unsigned long)-1);
dp = alloc(z * 2 + n, ATEMP);
memcpy(dp, t->vars[0], n);
t->vars[0] = dp;
dp += n;
while (*sp) {
*dp++ = QCHAR;
*dp++ = *sp++;
}
*dp = EOS;
/* free the expanded value */
afree(cp, APERM);
}
/* Clear subst_exstat before argument expansion. Used by
* null commands (see comexec() and c_eval()) and by c_set().
*/
@ -293,12 +335,12 @@ execute(struct op *volatile t,
}
} else { /* TSELECT */
for (;;) {
if (!(cp = do_selectargs(ap, is_first))) {
if (!(ccp = do_selectargs(ap, is_first))) {
rv = 1;
break;
}
is_first = false;
setstr(global(t->str), cp, KSH_UNWIND_ERROR);
setstr(global(t->str), ccp, KSH_UNWIND_ERROR);
execute(t->left, flags & XERROK, xerrok);
}
}
@ -337,11 +379,11 @@ execute(struct op *volatile t,
break;
case TCASE:
cp = evalstr(t->str, DOTILDE);
ccp = evalstr(t->str, DOTILDE);
for (t = t->left; t != NULL && t->type == TPAT; t = t->right)
for (ap = (const char **)t->vars; *ap; ap++)
if ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
gmatchx(cp, s, false))
gmatchx(ccp, s, false))
goto Found;
break;
Found:
@ -400,7 +442,7 @@ execute(struct op *volatile t,
*/
static int
comexec(struct op *t, struct tbl *volatile tp, const char **ap,
comexec(struct op *t, struct tbl * volatile tp, const char **ap,
volatile int flags, volatile int *xerrok)
{
int i;
@ -566,7 +608,7 @@ comexec(struct op *t, struct tbl *volatile tp, const char **ap,
case CFUNC: { /* function call */
volatile unsigned char old_xflag;
volatile Tflag old_inuse;
const char *volatile old_kshname;
const char * volatile old_kshname;
if (!(tp->flag & ISSET)) {
struct tbl *ftp;
@ -1171,7 +1213,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
case IOHERE:
do_open = 0;
/* herein() returns -2 if error has been printed */
u = herein(iop->heredoc, iop->flag & IOEVAL);
u = herein(iop->heredoc, iop->flag & IOEVAL, NULL);
/* cp may have wrong name */
break;
@ -1261,66 +1303,91 @@ iosetup(struct ioword *iop, struct tbl *tp)
}
/*
* open here document temp file.
* if unquoted here, expand here temp file into second temp file.
* Process here documents by providing the content, either as
* result (globally allocated) string or in a temp file; if
* unquoted, the string is expanded first.
*/
static int
herein(const char *content, int sub)
hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
{
volatile int fd = -1;
struct source *s, *volatile osource;
struct shf *volatile shf;
struct temp *h;
int i;
/* ksh -c 'cat << EOF' can cause this... */
if (content == NULL) {
warningf(true, "%s missing", "here document");
return (-2); /* special to iosetup(): don't print error */
}
/* Create temp file to hold content (done before newenv so temp
* doesn't get removed too soon).
*/
h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) {
fd = errno;
warningf(true, "can't %s temporary file %s: %s",
!shf ? "create" : "open",
h->name, strerror(fd));
if (shf)
shf_close(shf);
return (-2 /* special to iosetup(): don't print error */);
}
const char *ccp;
struct source *s, *osource;
osource = source;
newenv(E_ERRH);
i = sigsetjmp(e->jbuf, 0);
if (i) {
if (sigsetjmp(e->jbuf, 0)) {
source = osource;
quitenv(shf);
close(fd);
return (-2); /* special to iosetup(): don't print error */
/* special to iosetup(): don't print error */
return (-2);
}
if (sub) {
/* Do substitutions on the content of heredoc */
/* do substitutions on the content of heredoc */
s = pushs(SSTRING, ATEMP);
s->start = s->str = content;
source = s;
if (yylex(ONEWORD|HEREDOC) != LWORD)
internal_errorf("%s: %s", "herein", "yylex");
source = osource;
shf_puts(evalstr(yylval.cp, 0), shf);
ccp = evalstr(yylval.cp, 0);
} else
shf_puts(content, shf);
ccp = content;
if (resbuf == NULL)
shf_puts(ccp, shf);
else
strdupx(*resbuf, ccp, APERM);
quitenv(NULL);
return (0);
}
static int
herein(const char *content, int sub, char **resbuf)
{
int fd = -1;
struct shf *shf;
struct temp *h;
int i;
/* ksh -c 'cat << EOF' can cause this... */
if (content == NULL) {
warningf(true, "%s missing", "here document");
/* special to iosetup(): don't print error */
return (-2);
}
/* skip all the fd setup if we just want the value */
if (resbuf != NULL)
return (hereinval(content, sub, resbuf, NULL));
/*
* Create temp file to hold content (done before newenv
* so temp doesn't get removed too soon).
*/
h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) {
i = errno;
warningf(true, "can't %s temporary file %s: %s",
!shf ? "create" : "open", h->name, strerror(i));
if (shf)
shf_close(shf);
/* special to iosetup(): don't print error */
return (-2);
}
if (hereinval(content, sub, NULL, shf) == -2) {
close(fd);
/* special to iosetup(): don't print error */
return (-2);
}
if (shf_close(shf) == EOF) {
i = errno;
close(fd);
warningf(true, "%s: %s: %s", "write", h->name, strerror(i));
return (-2); /* special to iosetup(): don't print error */
/* special to iosetup(): don't print error */
return (-2);
}
return (fd);

14
funcs.c
View File

@ -4,7 +4,8 @@
/* $OpenBSD: c_ulimit.c,v 1.17 2008/03/21 12:51:19 millert Exp $ */
/*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2010, 2011
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -25,7 +26,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.164 2010/11/01 17:29:02 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.165 2011/01/09 21:57:26 tg Exp $");
#if HAVE_KILLPG
/*
@ -1174,8 +1175,10 @@ c_typeset(const char **wp)
char *s = str_val(vp);
shf_putc('=', shl_stdout);
/* AT&T ksh can't have
* justified integers.. */
/*
* AT&T ksh can't have
* justified integers...
*/
if ((vp->flag &
(INTEGER|LJUST|RJUST)) ==
INTEGER)
@ -3464,8 +3467,7 @@ c_cat(const char **wp)
/* XXX uses malloc instead of lalloc (for alignment/speed) */
if ((buf = malloc(MKSH_CAT_BUFSIZ)) == NULL) {
bi_errorf("can't allocate %lu data bytes",
(unsigned long)MKSH_CAT_BUFSIZ);
bi_errorf(T_oomem, (unsigned long)MKSH_CAT_BUFSIZ);
return (1);
}

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2009, 2010
* Copyright (c) 2009, 2010, 2011
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -20,7 +20,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.13 2010/09/14 21:26:14 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/lalloc.c,v 1.14 2011/01/09 21:57:27 tg Exp $");
/* build with CPPFLAGS+= -DUSE_REALLOC_MALLOC=0 on ancient systems */
#if defined(USE_REALLOC_MALLOC) && (USE_REALLOC_MALLOC == 0)
@ -70,7 +70,7 @@ findptr(ALLOC_ITEM **lpp, char *ptr, Area *ap)
void *
aresize2(void *ptr, size_t fac1, size_t fac2, Area *ap)
{
if (fac1 && fac2 && (SIZE_MAX / fac1 < fac2))
if (notoktomul(fac1, fac2))
internal_errorf(T_intovfl, (unsigned long)fac1, '*',
(unsigned long)fac2);
return (aresize(ptr, fac1 * fac2, ap));
@ -95,8 +95,7 @@ aresize(void *ptr, size_t numb, Area *ap)
|| ALLOC_ISUNALIGNED(lp)
#endif
)
internal_errorf("can't allocate %lu data bytes",
(unsigned long)numb);
internal_errorf(T_oomem, (unsigned long)numb);
/* this only works because Area is an ALLOC_ITEM */
lp->next = ap->next;
ap->next = lp;

22
misc.c
View File

@ -2,7 +2,7 @@
/* $OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -29,7 +29,7 @@
#include <grp.h>
#endif
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.148 2010/09/19 19:28:22 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.149 2011/01/09 21:57:27 tg Exp $");
unsigned char chtypes[UCHAR_MAX + 1]; /* type bits for unsigned char */
@ -921,25 +921,33 @@ void
print_value_quoted(const char *s)
{
const char *p;
int inquote = 0;
bool inquote = false;
/* Test if any quotes are needed */
/* first, check whether any quotes are needed */
for (p = s; *p; p++)
if (ctype(*p, C_QUOTE))
break;
if (!*p) {
/* nope, use the shortcut */
shf_puts(s, shl_stdout);
return;
}
/* quote via state machine */
for (p = s; *p; p++) {
if (*p == '\'') {
if (inquote)
/*
* multiple '''s or any ' at beginning of string
* look nicer this way than when simply substituting
*/
if (inquote) {
shf_putc('\'', shl_stdout);
inquote = false;
}
shf_putc('\\', shl_stdout);
inquote = 0;
} else if (!inquote) {
shf_putc('\'', shl_stdout);
inquote = 1;
inquote = true;
}
shf_putc(*p, shl_stdout);
}

14
mksh.1
View File

@ -1,7 +1,8 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.243 2010/12/19 20:00:55 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.244 2011/01/09 21:57:27 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, 2010
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
.\" 2010, 2011
.\" Thorsten Glaser <tg@mirbsd.org>
.\"
.\" Provided that these terms and disclaimer and all copyright notices
@ -71,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: December 19 2010 $
.Dd $Mdocdate: January 9 2011 $
.\"
.\" Check which macro package we use
.\"
@ -6073,7 +6074,14 @@ code fails because of the parenthesis asymmetry
.Pq RedHat BZ#496791 .
A workaround exists; use
.Ql x=$(cat) \*(Lt\*(Lt"EOF"
or the newline-keeping
.Ql x=\*(Lt\*(Lt"EOF"
instead to merely slurp the string.
Of course, in this case, the form
.Ql x=$(case $foo in (bar) ...
actually recommended by
.St -p1003.1
would work as well, but we use it as an example of this parser bug.
.Bd -literal -offset indent
x=$(case $foo in bar) echo $bar ;; *) echo $baz ;; esac)
# above fails to parse; below is the workaround

12
sh.h
View File

@ -9,7 +9,7 @@
/* $OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $ */
/*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
* Thorsten Glaser <tg@mirbsd.org>
*
* Provided that these terms and disclaimer and all copyright notices
@ -154,9 +154,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.420 2010/12/19 20:00:56 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.421 2011/01/09 21:57:29 tg Exp $");
#endif
#define MKSH_VERSION "R39 2010/12/19"
#define MKSH_VERSION "R39 2011/01/08"
#ifndef MKSH_INCLUDES_ONLY
@ -630,6 +630,7 @@ extern const struct shoption options[];
EXTERN char null[] I__("");
/* helpers for string pooling */
EXTERN const char T_intovfl[] I__("integer overflow %lu %c %lu prevented");
EXTERN const char T_oomem[] I__("can't allocate %lu data bytes");
#if defined(__GNUC__)
/* trust this to have string pooling; -Wformat bitches otherwise */
#define T_synerr "syntax error"
@ -1199,7 +1200,7 @@ struct ioword {
#define DB_BE 4 /* an inserted -BE */
#define DB_PAT 5 /* a pattern argument */
#define X_EXTRA 8 /* this many extra bytes in X string */
#define X_EXTRA 20 /* this many extra bytes in X string */
typedef struct XString {
char *end, *beg; /* end, begin of string */
@ -1397,6 +1398,8 @@ EXTERN int histsize; /* history size */
/* user and system time of last j_waitjed job */
EXTERN struct timeval j_usrtime, j_systime;
#define notoktomul(fac1, fac2) ((fac1) && (fac2) && \
(SIZE_MAX / (fac1) < (fac2)))
#define notoktoadd(val, cnst) ((val) > (SIZE_MAX - (cnst)))
#define checkoktoadd(val, cnst) do { \
if (notoktoadd((val), (cnst))) \
@ -1637,6 +1640,7 @@ int xstrcmp(const void *, const void *);
void ksh_getopt_reset(Getopt *, int);
int ksh_getopt(const char **, Getopt *, const char *);
void print_value_quoted(const char *);
char *quote_value(const char *);
void print_columns(struct shf *, int,
char *(*)(char *, int, int, const void *),
const void *, int, int, bool);