diff --git a/check.t b/check.t index a8d0098..7beebe6 100644 --- a/check.t +++ b/check.t @@ -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 # # 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=< * * 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=<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); diff --git a/funcs.c b/funcs.c index 10efc45..f04c795 100644 --- a/funcs.c +++ b/funcs.c @@ -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 * * 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); } diff --git a/lalloc.c b/lalloc.c index b6341e2..c3dc23a 100644 --- a/lalloc.c +++ b/lalloc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2009, 2010 + * Copyright (c) 2009, 2010, 2011 * Thorsten Glaser * * 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; diff --git a/misc.c b/misc.c index 6efda16..e1699fe 100644 --- a/misc.c +++ b/misc.c @@ -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 * * Provided that these terms and disclaimer and all copyright notices @@ -29,7 +29,7 @@ #include #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); } diff --git a/mksh.1 b/mksh.1 index 544f2a0..03f4c4b 100644 --- a/mksh.1 +++ b/mksh.1 @@ -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 .\" .\" 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 diff --git a/sh.h b/sh.h index 1c1d07b..ddb9b22 100644 --- a/sh.h +++ b/sh.h @@ -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 * * 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);