diff -Naur dash-0.5.8/ChangeLog dash-0.5.8-git_d7582e6/ChangeLog --- dash-0.5.8/ChangeLog 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/ChangeLog 2015-08-05 13:55:25.055358032 -0400 @@ -1,3 +1,73 @@ +2014-11-17 Stéphane Aulery + + * Correct typo in manual page. + * Document redirection file descriptor limitation. + +2014-10-30 Herbert Xu + + * Catch variable length expansions on non-existant specials. + +2014-10-28 Herbert Xu + + * Removed unnecessary pungetc on EOF from parser. + * Simplify EOF/newline handling in list parser. + +2014-10-27 Herbert Xu + + * Add printf support for format string a, A, and F. + * Handle embedded NULs correctly in printf. + * Replace open-coded flushall in preadbuffer. + * Add likely tag in outmem. + * Add ifdefs around MEM_OUT handling in outmem. + * Remove unnecessary restoration of format string in printf. + * Remove getintmax in printf. + * Use error instead of warnx for fatal errors in printf. + * Optimise handling of backslash octals in printf. + * Simplify echo command. + * Handle -- in dotcmd. + +2014-10-13 Eric Blake + + * cd: support drive letters on Cygwin. + +2014-10-08 Herbert Xu + + * Split unquoted $@/$* correctly when IFS is set but empty. + * Do not split quoted VSLENGTH and VSTRIM. + * Optimise nulonly away and just use quoted as before. + +2014-10-07 Herbert Xu + + * Use setvareq to set OPTIND initially. + +2014-10-06 Herbert Xu + + * Exit without arguments in a trap should use status outside traps. + * Do not allow break to break across function calls. + * Move common skipcount logic into skiploop. + * Allow return in loop conditional to set exit status. + * Return without arguments in a trap should use status outside traps. + +2014-10-03 Herbert Xu + + * Do not clobber exitstatus in evalcommand. + +2014-10-02 Herbert Xu + + * Fix use-after-free in dotrap/evalstring. + * Make sure evalskip is zero before running traps. + * Set exitstatus in onint. + +2014-09-29 Herbert Xu + + * Kill pgetc_macro. + * Handle backslash newlines properly after dollar sign. + * Add nlprompt/nlnoprompt helpers. + +2014-09-28 Herbert Xu + + * Correctly handle test ! ! = !. + 2014-09-26 Herbert Xu * Small optimisation of command -pv change. diff -Naur dash-0.5.8/Makefile.in dash-0.5.8-git_d7582e6/Makefile.in --- dash-0.5.8/Makefile.in 2014-09-28 04:19:40.000000000 -0400 +++ dash-0.5.8-git_d7582e6/Makefile.in 2015-08-05 13:57:40.555361584 -0400 @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11.6 from Makefile.am. +# Makefile.in generated by automake 1.11.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -527,7 +527,7 @@ *.zip*) \ unzip $(distdir).zip ;;\ esac - chmod -R a-w $(distdir); chmod u+w $(distdir) + chmod -R a-w $(distdir); chmod a+w $(distdir) mkdir $(distdir)/_build mkdir $(distdir)/_inst chmod a-w $(distdir) diff -Naur dash-0.5.8/aclocal.m4 dash-0.5.8-git_d7582e6/aclocal.m4 --- dash-0.5.8/aclocal.m4 2014-09-28 04:19:39.000000000 -0400 +++ dash-0.5.8-git_d7582e6/aclocal.m4 2015-08-05 13:57:39.728694896 -0400 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.11.6 -*- Autoconf -*- +# generated automatically by aclocal 1.11.5 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, @@ -38,7 +38,7 @@ [am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.11.6], [], +m4_if([$1], [1.11.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -54,7 +54,7 @@ # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.11.6])dnl +[AM_AUTOMAKE_VERSION([1.11.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) diff -Naur dash-0.5.8/config.h.in dash-0.5.8-git_d7582e6/config.h.in --- dash-0.5.8/config.h.in 2014-09-28 04:19:39.000000000 -0400 +++ dash-0.5.8-git_d7582e6/config.h.in 2015-08-05 13:57:40.062028238 -0400 @@ -88,6 +88,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H +/* Define if your faccessat tells root all files are executable */ +#undef HAVE_TRADITIONAL_FACCESSAT + /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H diff -Naur dash-0.5.8/configure dash-0.5.8-git_d7582e6/configure --- dash-0.5.8/configure 2014-09-28 04:19:40.000000000 -0400 +++ dash-0.5.8-git_d7582e6/configure 2015-08-05 13:57:40.772028257 -0400 @@ -714,6 +714,7 @@ enable_static enable_fnmatch enable_glob +enable_test_workaround with_libedit enable_lineno ' @@ -1347,6 +1348,9 @@ --enable-static Build statical linked program --enable-fnmatch Use fnmatch(3) from libc --enable-glob Use glob(3) from libc + --enable-test-workaround + Guard against faccessat(2) that tells root all files + are executable --disable-lineno Disable LINENO support Optional Packages: @@ -4614,6 +4618,29 @@ done + +# Check whether --enable-test-workaround was given. +if test "${enable_test_workaround+set}" = set; then : + enableval=$enable_test_workaround; +else + enable_test_workaround=auto +fi + + +if test "enable_test_workaround" = "auto" && + test "$ac_cv_func_faccessat" = yes; then + case `uname -s 2>/dev/null` in + GNU/kFreeBSD | \ + FreeBSD) + enable_test_workaround=yes + esac +fi +if test "$enable_test_workaround" = "yes"; then + +$as_echo "#define HAVE_TRADITIONAL_FACCESSAT 1" >>confdefs.h + +fi + if test "$enable_fnmatch" = yes; then use_fnmatch= for ac_func in fnmatch diff -Naur dash-0.5.8/configure.ac dash-0.5.8-git_d7582e6/configure.ac --- dash-0.5.8/configure.ac 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/configure.ac 2015-08-05 13:55:25.055358032 -0400 @@ -90,6 +90,37 @@ sigsetmask stpcpy strchrnul strsignal strtod strtoimax \ strtoumax sysconf) +dnl Check whether it's worth working around FreeBSD PR kern/125009. +dnl The traditional behavior of access/faccessat is crazy, but +dnl POSIX.1-2008 explicitly allows those functions to misbehave. +dnl +dnl Unaffected kernels: +dnl +dnl - all versions of Linux +dnl - NetBSD sys/kern/vfs_subr.c 1.64, 1997-04-23 +dnl - FreeBSD 9 (r212002), 2010-09-10 +dnl - OpenBSD sys/kern/vfs_subr.c 1.166, 2008-06-09 +dnl +dnl Also worked around in Debian's libc0.1 2.13-19 when using +dnl kFreeBSD 8. + +AC_ARG_ENABLE(test-workaround, AS_HELP_STRING(--enable-test-workaround, \ + [Guard against faccessat(2) that tells root all files are executable]),, + [enable_test_workaround=auto]) + +if test "enable_test_workaround" = "auto" && + test "$ac_cv_func_faccessat" = yes; then + case `uname -s 2>/dev/null` in + GNU/kFreeBSD | \ + FreeBSD) + enable_test_workaround=yes + esac +fi +if test "$enable_test_workaround" = "yes"; then + AC_DEFINE([HAVE_TRADITIONAL_FACCESSAT], [1], + [Define if your faccessat tells root all files are executable]) +fi + if test "$enable_fnmatch" = yes; then use_fnmatch= AC_CHECK_FUNCS(fnmatch, use_fnmatch=yes) diff -Naur dash-0.5.8/src/Makefile.am dash-0.5.8-git_d7582e6/src/Makefile.am --- dash-0.5.8/src/Makefile.am 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/Makefile.am 2015-08-05 13:55:25.058691365 -0400 @@ -26,7 +26,7 @@ dash_SOURCES = \ $(dash_CFILES) \ alias.h arith_yacc.h bltin/bltin.h cd.h error.h eval.h exec.h \ - expand.h hetio.h \ + expand.h \ init.h input.h jobs.h machdep.h mail.h main.h memalloc.h miscbltin.h \ myhistedit.h mystring.h options.h output.h parser.h redir.h shell.h \ show.h system.h trap.h var.h diff -Naur dash-0.5.8/src/Makefile.in dash-0.5.8-git_d7582e6/src/Makefile.in --- dash-0.5.8/src/Makefile.in 2014-09-28 04:19:40.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/Makefile.in 2015-08-05 13:57:40.525361584 -0400 @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.11.6 from Makefile.am. +# Makefile.in generated by automake 1.11.5 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -236,7 +236,7 @@ dash_SOURCES = \ $(dash_CFILES) \ alias.h arith_yacc.h bltin/bltin.h cd.h error.h eval.h exec.h \ - expand.h hetio.h \ + expand.h \ init.h input.h jobs.h machdep.h mail.h main.h memalloc.h miscbltin.h \ myhistedit.h mystring.h options.h output.h parser.h redir.h shell.h \ show.h system.h trap.h var.h diff -Naur dash-0.5.8/src/bltin/printf.c dash-0.5.8-git_d7582e6/src/bltin/printf.c --- dash-0.5.8/src/bltin/printf.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/bltin/printf.c 2015-08-05 13:55:25.058691365 -0400 @@ -40,12 +40,11 @@ #include #include -static int conv_escape_str(char *); +static int conv_escape_str(char *, char **); static char *conv_escape(char *, int *); static int getchr(void); static double getdouble(void); -static intmax_t getintmax(void); -static uintmax_t getuintmax(void); +static uintmax_t getuintmax(int); static char *getstr(void); static char *mklong(const char *, const char *); static void check_conversion(const char *, const char *); @@ -73,6 +72,53 @@ } \ } +#define ASPF(sp, f, func) ({ \ + int ret; \ + switch ((char *)param - (char *)array) { \ + default: \ + ret = xasprintf(sp, f, array[0], array[1], func); \ + break; \ + case sizeof(*param): \ + ret = xasprintf(sp, f, array[0], func); \ + break; \ + case 0: \ + ret = xasprintf(sp, f, func); \ + break; \ + } \ + ret; \ +}) + + +static int print_escape_str(const char *f, int *param, int *array, char *s) +{ + struct stackmark smark; + char *p, *q; + int done; + int len; + int total; + + setstackmark(&smark); + done = conv_escape_str(s, &p); + q = stackblock(); + len = p - q; + + p = makestrspace(len, p); + memset(p, 'X', len - 1); + p[len - 1] = 0; + + q = stackblock(); + total = ASPF(&p, f, p); + + len = strchrnul(p, 'X') - p; + memcpy(p + len, q, strchrnul(p + len, ' ') - (p + len)); + + out1mem(p, total); + + popstackmark(&smark); + return done; +} + + int printfcmd(int argc, char *argv[]) { char *fmt; @@ -86,10 +132,8 @@ argv = argptr; format = *argv; - if (!format) { - warnx("usage: printf format [arg ...]"); - goto err; - } + if (!format) + error("usage: printf format [arg ...]"); gargv = ++argv; @@ -132,39 +176,33 @@ /* skip to field width */ fmt += strspn(fmt, SKIP1); if (*fmt == '*') - *param++ = getintmax(); + *param++ = getuintmax(1); /* skip to possible '.', get following precision */ fmt += strspn(fmt, SKIP2); if (*fmt == '.') ++fmt; if (*fmt == '*') - *param++ = getintmax(); + *param++ = getuintmax(1); fmt += strspn(fmt, SKIP2); ch = *fmt; - if (!ch) { - warnx("missing format character"); - goto err; - } + if (!ch) + error("missing format character"); /* null terminate format string to we can use it as an argument to printf. */ nextch = fmt[1]; fmt[1] = 0; switch (ch) { - case 'b': { - int done = conv_escape_str(getstr()); - char *p = stackblock(); + case 'b': *fmt = 's'; - PF(start, p); /* escape if a \c was encountered */ - if (done) + if (print_escape_str(start, param, array, + getstr())) goto out; - *fmt = 'b'; break; - } case 'c': { int p = getchr(); PF(start, p); @@ -177,23 +215,26 @@ } case 'd': case 'i': { - intmax_t p = getintmax(); - char *f = mklong(start, fmt); - PF(f, p); + uintmax_t p = getuintmax(1); + start = mklong(start, fmt); + PF(start, p); break; } case 'o': case 'u': case 'x': case 'X': { - uintmax_t p = getuintmax(); - char *f = mklong(start, fmt); - PF(f, p); + uintmax_t p = getuintmax(0); + start = mklong(start, fmt); + PF(start, p); break; } + case 'a': + case 'A': case 'e': case 'E': case 'f': + case 'F': case 'g': case 'G': { double p = getdouble(); @@ -201,8 +242,7 @@ break; } default: - warnx("%s: invalid directive", start); - goto err; + error("%s: invalid directive", start); } *++fmt = nextch; } @@ -210,8 +250,6 @@ out: return rval; -err: - return 1; } @@ -220,8 +258,9 @@ * Halts processing string if a \c escape is encountered. */ static int -conv_escape_str(char *str) +conv_escape_str(char *str, char **sp) { + int c; int ch; char *cp; @@ -229,16 +268,14 @@ STARTSTACKSTR(cp); do { - int c; - - ch = *str++; + c = ch = *str++; if (ch != '\\') continue; - ch = *str++; - if (ch == 'c') { + c = *str++; + if (c == 'c') { /* \c as in SYSV echo - abort all processing.... */ - ch = 0x100; + c = ch = 0x100; continue; } @@ -247,25 +284,14 @@ * They start with a \0, and are followed by 0, 1, 2, * or 3 octal digits. */ - if (ch == '0') { - unsigned char i; - i = 3; - ch = 0; - do { - unsigned k = octtobin(*str); - if (k > 7) - break; - str++; - ch <<= 3; - ch += k; - } while (--i); - continue; - } + if (c == '0' && isodigit(*str)) + str++; /* Finally test for sequences valid in the format string */ str = conv_escape(str - 1, &c); - ch = c; - } while (STPUTC(ch, cp), (char)ch); + } while (STPUTC(c, cp), (char)ch); + + *sp = cp; return ch; } @@ -283,12 +309,11 @@ switch (ch) { default: - case 0: - value = '\\'; - goto out; + if (!isodigit(*str)) { + value = '\\'; + goto out; + } - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': ch = 3; value = 0; do { @@ -357,30 +382,8 @@ return val; } -static intmax_t -getintmax(void) -{ - intmax_t val = 0; - char *cp, *ep; - - cp = *gargv; - if (cp == NULL) - goto out; - gargv++; - - val = (unsigned char) cp[1]; - if (*cp == '\"' || *cp == '\'') - goto out; - - errno = 0; - val = strtoimax(cp, &ep, 0); - check_conversion(cp, ep); -out: - return val; -} - static uintmax_t -getuintmax(void) +getuintmax(int sign) { uintmax_t val = 0; char *cp, *ep; @@ -395,7 +398,7 @@ goto out; errno = 0; - val = strtoumax(cp, &ep, 0); + val = sign ? strtoimax(cp, &ep, 0) : strtoumax(cp, &ep, 0); check_conversion(cp, ep); out: return val; @@ -439,34 +442,21 @@ int echocmd(int argc, char **argv) { - int nonl = 0; - struct output *outs = out1; + int nonl; - if (!*++argv) - goto end; - if (equal(*argv, "-n")) { - nonl = ~nonl; - if (!*++argv) - goto end; - } + nonl = *++argv ? equal(*argv, "-n") : 0; + argv += nonl; do { int c; - nonl += conv_escape_str(*argv); - outstr(stackblock(), outs); + if (likely(*argv)) + nonl += print_escape_str("%s", NULL, NULL, *argv++); if (nonl > 0) break; - c = ' '; - if (!*++argv) { -end: - if (nonl) { - break; - } - c = '\n'; - } - outc(c, outs); + c = *argv ? ' ' : '\n'; + out1c(c); } while (*argv); return 0; } diff -Naur dash-0.5.8/src/bltin/test.c dash-0.5.8-git_d7582e6/src/bltin/test.c --- dash-0.5.8/src/bltin/test.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/bltin/test.c 2015-08-05 13:55:25.058691365 -0400 @@ -155,6 +155,14 @@ static int bash_group_member(gid_t); #endif +#ifdef HAVE_FACCESSAT +# ifdef HAVE_TRADITIONAL_FACCESSAT +static inline int faccessat_confused_about_superuser(void) { return 1; } +# else +static inline int faccessat_confused_about_superuser(void) { return 0; } +# endif +#endif + static inline intmax_t getn(const char *s) { return atomax10(s); @@ -177,7 +185,7 @@ { const struct t_op *op; enum token n; - int res; + int res = 1; if (*argv[0] == '[') { if (*argv[--argc] != ']') @@ -185,11 +193,12 @@ argv[argc] = NULL; } +recheck: argv++; argc--; if (argc < 1) - return 1; + return res; /* * POSIX prescriptions: he who wrote this deserves the Nobel @@ -209,6 +218,9 @@ argv[--argc] = NULL; argv++; argc--; + } else if (!strcmp(argv[0], "!")) { + res = 0; + goto recheck; } } @@ -216,7 +228,7 @@ eval: t_wp = argv; - res = !oexpr(n); + res ^= oexpr(n); argv = t_wp; if (argv[0] != NULL && argv[1] != NULL) @@ -489,8 +501,20 @@ } #ifdef HAVE_FACCESSAT +static int has_exec_bit_set(const char *path) +{ + struct stat64 st; + + if (stat64(path, &st)) + return 0; + return st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH); +} + static int test_file_access(const char *path, int mode) { + if (faccessat_confused_about_superuser() && + mode == X_OK && geteuid() == 0 && !has_exec_bit_set(path)) + return 0; return !faccessat(AT_FDCWD, path, mode, AT_EACCESS); } #else /* HAVE_FACCESSAT */ diff -Naur dash-0.5.8/src/cd.c dash-0.5.8-git_d7582e6/src/cd.c --- dash-0.5.8/src/cd.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/cd.c 2015-08-05 13:55:25.058691365 -0400 @@ -38,6 +38,9 @@ #include #include #include +#ifdef __CYGWIN__ +#include +#endif /* * The cd and pwd commands. @@ -194,6 +197,17 @@ char *cdcomppath; const char *lim; +#ifdef __CYGWIN__ + /* On cygwin, thanks to drive letters, some absolute paths do + not begin with slash; but cygwin includes a function that + forces normalization to the posix form */ + char pathbuf[PATH_MAX]; + if (cygwin_conv_path(CCP_WIN_A_TO_POSIX | CCP_RELATIVE, dir, pathbuf, + sizeof(pathbuf)) < 0) + sh_error("can't normalize %s", dir); + dir = pathbuf; +#endif + cdcomppath = sstrdup(dir); STARTSTACKSTR(new); if (*dir != '/') { diff -Naur dash-0.5.8/src/dash.1 dash-0.5.8-git_d7582e6/src/dash.1 --- dash-0.5.8/src/dash.1 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/dash.1 2015-08-05 13:55:25.058691365 -0400 @@ -385,7 +385,7 @@ Following is a list of the possible redirections. The .Bq n -is an optional number, as in +is an optional number between 0 and 9, as in .Sq 3 (not .Sq Bq 3 ) , @@ -402,11 +402,13 @@ .It [n] Ns \*[Lt] file Redirect standard input (or n) from file. .It [n1] Ns \*[Lt]& Ns n2 -Duplicate standard input (or n1) from file descriptor n2. +Copy file descriptor n2 as stdout (or fd n1). +fd n2. .It [n] Ns \*[Lt]&- Close standard input (or n). .It [n1] Ns \*[Gt]& Ns n2 -Duplicate standard output (or n1) to n2. +Copy file descriptor n2 as stdin (or fd n1). +fd n2. .It [n] Ns \*[Gt]&- Close standard output (or n). .It [n] Ns \*[Lt]\*[Gt] file @@ -596,7 +598,7 @@ characters. The commands in a list are executed in the order they are written. If command is followed by an ampersand, the shell starts the -command and immediately proceed onto the next command; otherwise it waits +command and immediately proceeds onto the next command; otherwise it waits for the command to terminate before proceeding to the next one. .Ss Short-Circuit List Operators .Dq && @@ -1400,14 +1402,9 @@ .Va optstring all errors will be ignored. .Pp -A nonzero value is returned when the last option is reached. -If there are no remaining arguments, +After the last option .Ic getopts -will set -.Va var -to the special option, -.Dq -- , -otherwise, it will set +will return a non-zero value and set .Va var to .Dq \&? . diff -Naur dash-0.5.8/src/error.c dash-0.5.8-git_d7582e6/src/error.c --- dash-0.5.8/src/error.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/error.c 2015-08-05 13:55:25.058691365 -0400 @@ -105,6 +105,7 @@ signal(SIGINT, SIG_DFL); raise(SIGINT); } + exitstatus = SIGINT + 128; exraise(EXINT); /* NOTREACHED */ } diff -Naur dash-0.5.8/src/eval.c dash-0.5.8-git_d7582e6/src/eval.c --- dash-0.5.8/src/eval.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/eval.c 2015-08-05 13:55:25.058691365 -0400 @@ -74,6 +74,7 @@ char *commandname; int exitstatus; /* exit status of last command */ int back_exitstatus; /* exit status of backquoted command */ +int savestatus = -1; /* exit status of last command outside traps */ #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) @@ -114,6 +115,10 @@ RESET { evalskip = 0; loopnest = 0; + if (savestatus >= 0) { + exitstatus = savestatus; + savestatus = -1; + } } #endif @@ -160,6 +165,7 @@ struct stackmark smark; int status; + s = sstrdup(s); setinputstring(s); setstackmark(&smark); @@ -171,7 +177,9 @@ if (evalskip) break; } + popstackmark(&smark); popfile(); + stunalloc(s); return status; } @@ -194,6 +202,9 @@ TRACE(("evaltree(NULL) called\n")); goto out; } + + dotrap(); + #ifndef SMALL displayhist = 1; /* show history substitutions done with fc */ #endif @@ -305,8 +316,7 @@ if (checkexit & exitstatus) goto exexit; - if (pendingsigs) - dotrap(); + dotrap(); if (flags & EV_EXIT) { exexit: @@ -329,27 +339,45 @@ #endif +static int skiploop(void) +{ + int skip = evalskip; + + switch (skip) { + case 0: + break; + + case SKIPBREAK: + case SKIPCONT: + if (likely(--skipcount <= 0)) { + evalskip = 0; + break; + } + + skip = SKIPBREAK; + break; + } + + return skip; +} + + STATIC void evalloop(union node *n, int flags) { + int skip; int status; loopnest++; status = 0; flags &= EV_TESTED; - for (;;) { + do { int i; evaltree(n->nbinary.ch1, EV_TESTED); - if (evalskip) { -skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = 0; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = 0; - break; - } + skip = skiploop(); + if (skip) + continue; i = exitstatus; if (n->type != NWHILE) i = !i; @@ -357,11 +385,11 @@ break; evaltree(n->nbinary.ch2, flags); status = exitstatus; - if (evalskip) - goto skipping; - } + skip = skiploop(); + } while (!(skip & ~SKIPCONT)); + if (skip != SKIPFUNC) + exitstatus = status; loopnest--; - exitstatus = status; } @@ -382,9 +410,6 @@ arglist.lastp = &arglist.list; for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); - /* XXX */ - if (evalskip) - goto out; } *arglist.lastp = NULL; @@ -394,18 +419,10 @@ for (sp = arglist.list ; sp ; sp = sp->next) { setvar(n->nfor.var, sp->text, 0); evaltree(n->nfor.body, flags); - if (evalskip) { - if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = 0; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = 0; + if (skiploop() & ~SKIPCONT) break; - } } loopnest--; -out: popstackmark(&smark); } @@ -848,21 +865,12 @@ listsetvar(varlist.list, VEXPORT); } if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { - int status; - int i; - - i = exception; - if (i == EXEXIT) - goto raise; - - status = (i == EXINT) ? SIGINT + 128 : 2; - exitstatus = status; - - if (i == EXINT || spclbltin > 0) { -raise: - longjmp(handler->loc, 1); + if (exception == EXERROR && spclbltin <= 0) { + FORCEINTON; + break; } - FORCEINTON; +raise: + longjmp(handler->loc, 1); } break; @@ -927,9 +935,11 @@ struct jmploc jmploc; int e; int savefuncline; + int saveloopnest; saveparam = shellparam; savefuncline = funcline; + saveloopnest = loopnest; savehandler = handler; if ((e = setjmp(jmploc.loc))) { goto funcdone; @@ -939,6 +949,7 @@ shellparam.malloc = 0; func->count++; funcline = func->n.ndefun.linno; + loopnest = 0; INTON; shellparam.nparam = argc - 1; shellparam.p = argv + 1; @@ -949,13 +960,14 @@ poplocalvars(0); funcdone: INTOFF; + loopnest = saveloopnest; funcline = savefuncline; freefunc(func); freeparam(&shellparam); shellparam = saveparam; handler = savehandler; INTON; - evalskip &= ~SKIPFUNC; + evalskip &= ~(SKIPFUNC | SKIPFUNCDEF); return e; } @@ -1035,12 +1047,23 @@ int returncmd(int argc, char **argv) { + int skip; + int status; + /* * If called outside a function, do what ksh does; * skip the rest of the file. */ - evalskip = SKIPFUNC; - return argv[1] ? number(argv[1]) : exitstatus; + if (argv[1]) { + skip = SKIPFUNC; + status = number(argv[1]); + } else { + skip = SKIPFUNCDEF; + status = exitstatus; + } + evalskip = skip; + + return status; } diff -Naur dash-0.5.8/src/eval.h dash-0.5.8-git_d7582e6/src/eval.h --- dash-0.5.8/src/eval.h 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/eval.h 2015-08-05 13:55:25.058691365 -0400 @@ -37,6 +37,7 @@ extern char *commandname; /* currently executing command */ extern int exitstatus; /* exit status of last command */ extern int back_exitstatus; /* exit status of backquoted command */ +extern int savestatus; /* exit status of last command outside traps */ struct backcmd { /* result of evalbackcmd */ @@ -61,3 +62,4 @@ #define SKIPBREAK (1 << 0) #define SKIPCONT (1 << 1) #define SKIPFUNC (1 << 2) +#define SKIPFUNCDEF (1 << 3) diff -Naur dash-0.5.8/src/expand.c dash-0.5.8-git_d7582e6/src/expand.c --- dash-0.5.8/src/expand.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/expand.c 2015-08-05 13:55:25.058691365 -0400 @@ -116,7 +116,7 @@ STATIC char *evalvar(char *, int); STATIC size_t strtodest(const char *, const char *, int); STATIC void memtodest(const char *, size_t, const char *, int); -STATIC ssize_t varvalue(char *, int, int); +STATIC ssize_t varvalue(char *, int, int, int *); STATIC void expandmeta(struct strlist *, int); #ifdef HAVE_GLOB STATIC void addglob(const glob_t *); @@ -736,7 +736,7 @@ p = strchr(p, '=') + 1; again: - varlen = varvalue(var, varflags, flag); + varlen = varvalue(var, varflags, flag, "ed); if (varflags & VSNUL) varlen--; @@ -751,28 +751,22 @@ argstr(p, flag | EXP_TILDE | EXP_WORD); goto end; } - if (easy) - goto record; - goto end; + goto record; } if (subtype == VSASSIGN || subtype == VSQUESTION) { - if (varlen < 0) { - if (subevalvar(p, var, 0, subtype, startloc, - varflags, flag & ~QUOTES_ESC)) { - varflags &= ~VSNUL; - /* - * Remove any recorded regions beyond - * start of variable - */ - removerecordregions(startloc); - goto again; - } - goto end; - } - if (easy) + if (varlen >= 0) goto record; - goto end; + + subevalvar(p, var, 0, subtype, startloc, varflags, + flag & ~QUOTES_ESC); + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; } if (varlen < 0 && uflag) @@ -784,9 +778,9 @@ } if (subtype == VSNORMAL) { +record: if (!easy) goto end; -record: recordregion(startloc, expdest - (char *)stackblock(), quoted); goto end; } @@ -892,7 +886,7 @@ */ STATIC ssize_t -varvalue(char *name, int varflags, int flags) +varvalue(char *name, int varflags, int flags, int *quotedp) { int num; char *p; @@ -901,13 +895,13 @@ char sepc; char **ap; char const *syntax; - int quoted = flags & EXP_QUOTED; + int quoted = *quotedp; int subtype = varflags & VSTYPE; int discard = subtype == VSPLUS || subtype == VSLENGTH; int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; ssize_t len = 0; - sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; + sep = (flags & EXP_FULL) << CHAR_BIT; syntax = quoted ? DQSYNTAX : BASESYNTAX; switch (*name) { @@ -938,15 +932,18 @@ expdest = p; break; case '@': - if (sep) + if (quoted && sep) goto param; /* fall through */ case '*': - sep = ifsset() ? ifsval()[0] : ' '; + if (quoted) + sep = 0; + sep |= ifsset() ? ifsval()[0] : ' '; param: + sepc = sep; + *quotedp = !sepc; if (!(ap = shellparam.p)) return -1; - sepc = sep; while ((p = *ap++)) { len += strtodest(p, syntax, quotes); diff -Naur dash-0.5.8/src/hetio.h dash-0.5.8-git_d7582e6/src/hetio.h --- dash-0.5.8/src/hetio.h 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/hetio.h 1969-12-31 19:00:00.000000000 -0500 @@ -1,22 +0,0 @@ -/* - * Termios command line History and Editting for NetBSD sh (ash) - * Copyright (c) 1999 - * Main code: Adam Rogoyski - * Etc: Dave Cinege - * - * You may use this code as you wish, so long as the original author(s) - * are attributed in any redistributions of the source code. - * This code is 'as is' with no warranty. - * This code may safely be consumed by a BSD or GPL license. - * - * v 0.5 19990328 Initial release - * - * Future plans: Simple file and path name completion. (like BASH) - * - */ - -void hetio_init(void); -int hetio_read_input(int fd); -void hetio_reset_term(void); - -extern int hetio_inter; diff -Naur dash-0.5.8/src/histedit.c dash-0.5.8-git_d7582e6/src/histedit.c --- dash-0.5.8/src/histedit.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/histedit.c 2015-08-05 13:55:25.062024698 -0400 @@ -372,8 +372,7 @@ out2str(s); } - evalstring(strcpy(stalloc(strlen(s) + 1), s), - 0); + evalstring(s, 0); if (displayhist && hist) { /* * XXX what about recursive and diff -Naur dash-0.5.8/src/input.c dash-0.5.8-git_d7582e6/src/input.c --- dash-0.5.8/src/input.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/input.c 2015-08-05 13:55:25.062024698 -0400 @@ -58,45 +58,10 @@ #include "myhistedit.h" #endif -#ifdef HETIO -#include "hetio.h" -#endif - #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ #define IBUFSIZ (BUFSIZ + 1) -MKINIT -struct strpush { - struct strpush *prev; /* preceding string on stack */ - char *prevstring; - int prevnleft; - struct alias *ap; /* if push was associated with an alias */ - char *string; /* remember the string since it may change */ -}; - -/* - * The parsefile structure pointed to by the global variable parsefile - * contains information about the current file being read. - */ -MKINIT -struct parsefile { - struct parsefile *prev; /* preceding file on stack */ - int linno; /* current line */ - int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - char *nextc; /* next char in buffer */ - char *buf; /* input buffer */ - struct strpush *strpush; /* for pushing strings at this level */ - struct strpush basestrpush; /* so pushing one is fast */ -}; - - -int plinno = 1; /* input line number */ -int parsenleft; /* copy of parsefile->nleft */ -MKINIT int parselleft; /* copy of parsefile->lleft */ -char *parsenextc; /* copy of parsefile->nextc */ MKINIT struct parsefile basepf; /* top level input file */ MKINIT char basebuf[IBUFSIZ]; /* buffer for top level input file */ struct parsefile *parsefile = &basepf; /* current input file */ @@ -109,6 +74,7 @@ STATIC void pushfile(void); static int preadfd(void); static void setinputfd(int fd, int push); +static int preadbuffer(void); #ifdef mkinit INCLUDE @@ -117,10 +83,12 @@ INIT { basepf.nextc = basepf.buf = basebuf; + basepf.linno = 1; } RESET { - parselleft = parsenleft = 0; /* clear input buffer */ + /* clear input buffer */ + basepf.lleft = basepf.nleft = 0; popallfiles(); } #endif @@ -134,7 +102,20 @@ int pgetc(void) { - return pgetc_macro(); + int c; + + if (parsefile->unget) + return parsefile->lastc[--parsefile->unget]; + + if (--parsefile->nleft >= 0) + c = (signed char)*parsefile->nextc++; + else + c = preadbuffer(); + + parsefile->lastc[1] = parsefile->lastc[0]; + parsefile->lastc[0] = c; + + return c; } @@ -147,7 +128,7 @@ { int c; do { - c = pgetc_macro(); + c = pgetc(); } while (c == PEOA); return c; } @@ -158,7 +139,7 @@ { int nr; char *buf = parsefile->buf; - parsenextc = buf; + parsefile->nextc = buf; retry: #ifndef SMALL @@ -184,11 +165,6 @@ } else #endif - -#ifdef HETIO - nr = hetio_read_input(parsefile->fd); - if (nr == -255) -#endif nr = read(parsefile->fd, buf, IBUFSIZ - 1); @@ -219,8 +195,7 @@ * 4) Process input up to the next newline, deleting nul characters. */ -int -preadbuffer(void) +static int preadbuffer(void) { char *q; int more; @@ -229,34 +204,33 @@ #endif char savec; - while (unlikely(parsefile->strpush)) { + if (unlikely(parsefile->strpush)) { if ( - parsenleft == -1 && parsefile->strpush->ap && - parsenextc[-1] != ' ' && parsenextc[-1] != '\t' + parsefile->nleft == -1 && + parsefile->strpush->ap && + parsefile->nextc[-1] != ' ' && + parsefile->nextc[-1] != '\t' ) { return PEOA; } popstring(); - if (--parsenleft >= 0) - return (signed char)*parsenextc++; + return pgetc(); } - if (unlikely(parsenleft == EOF_NLEFT || parsefile->buf == NULL)) + if (unlikely(parsefile->nleft == EOF_NLEFT || + parsefile->buf == NULL)) return PEOF; - flushout(&output); -#ifdef FLUSHERR - flushout(&errout); -#endif + flushall(); - more = parselleft; + more = parsefile->lleft; if (more <= 0) { again: if ((more = preadfd()) <= 0) { - parselleft = parsenleft = EOF_NLEFT; + parsefile->lleft = parsefile->nleft = EOF_NLEFT; return PEOF; } } - q = parsenextc; + q = parsefile->nextc; /* delete nul characters */ #ifndef SMALL @@ -274,7 +248,7 @@ q++; if (c == '\n') { - parsenleft = q - parsenextc - 1; + parsefile->nleft = q - parsefile->nextc - 1; break; } @@ -291,13 +265,13 @@ } if (more <= 0) { - parsenleft = q - parsenextc - 1; - if (parsenleft < 0) + parsefile->nleft = q - parsefile->nextc - 1; + if (parsefile->nleft < 0) goto again; break; } } - parselleft = more; + parsefile->lleft = more; savec = *q; *q = '\0'; @@ -307,13 +281,13 @@ HistEvent he; INTOFF; history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, - parsenextc); + parsefile->nextc); INTON; } #endif if (vflag) { - out2str(parsenextc); + out2str(parsefile->nextc); #ifdef FLUSHERR flushout(out2); #endif @@ -321,19 +295,18 @@ *q = savec; - return (signed char)*parsenextc++; + return (signed char)*parsefile->nextc++; } /* - * Undo the last call to pgetc. Only one character may be pushed back. + * Undo a call to pgetc. Only two characters may be pushed back. * PEOF may be pushed back. */ void pungetc(void) { - parsenleft++; - parsenextc--; + parsefile->unget++; } /* @@ -355,15 +328,18 @@ parsefile->strpush = sp; } else sp = parsefile->strpush = &(parsefile->basestrpush); - sp->prevstring = parsenextc; - sp->prevnleft = parsenleft; + sp->prevstring = parsefile->nextc; + sp->prevnleft = parsefile->nleft; + sp->unget = parsefile->unget; + memcpy(sp->lastc, parsefile->lastc, sizeof(sp->lastc)); sp->ap = (struct alias *)ap; if (ap) { ((struct alias *)ap)->flag |= ALIASINUSE; sp->string = s; } - parsenextc = s; - parsenleft = len; + parsefile->nextc = s; + parsefile->nleft = len; + parsefile->unget = 0; INTON; } @@ -374,7 +350,8 @@ INTOFF; if (sp->ap) { - if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + if (parsefile->nextc[-1] == ' ' || + parsefile->nextc[-1] == '\t') { checkkwd |= CHKALIAS; } if (sp->string != sp->ap->val) { @@ -385,8 +362,10 @@ unalias(sp->ap->name); } } - parsenextc = sp->prevstring; - parsenleft = sp->prevnleft; + parsefile->nextc = sp->prevstring; + parsefile->nleft = sp->prevnleft; + parsefile->unget = sp->unget; + memcpy(parsefile->lastc, sp->lastc, sizeof(sp->lastc)); /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ parsefile->strpush = sp->prev; if (sp != &(parsefile->basestrpush)) @@ -435,7 +414,7 @@ parsefile->fd = fd; if (parsefile->buf == NULL) parsefile->buf = ckmalloc(IBUFSIZ); - parselleft = parsenleft = 0; + parsefile->lleft = parsefile->nleft = 0; plinno = 1; } @@ -449,8 +428,8 @@ { INTOFF; pushfile(); - parsenextc = string; - parsenleft = strlen(string); + parsefile->nextc = string; + parsefile->nleft = strlen(string); parsefile->buf = NULL; plinno = 1; INTON; @@ -468,15 +447,12 @@ { struct parsefile *pf; - parsefile->nleft = parsenleft; - parsefile->lleft = parselleft; - parsefile->nextc = parsenextc; - parsefile->linno = plinno; pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); pf->prev = parsefile; pf->fd = -1; pf->strpush = NULL; pf->basestrpush.prev = NULL; + pf->unget = 0; parsefile = pf; } @@ -495,10 +471,6 @@ popstring(); parsefile = pf->prev; ckfree(pf); - parsenleft = parsefile->nleft; - parselleft = parsefile->lleft; - parsenextc = parsefile->nextc; - plinno = parsefile->linno; INTON; } diff -Naur dash-0.5.8/src/input.h dash-0.5.8-git_d7582e6/src/input.h --- dash-0.5.8/src/input.h 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/input.h 2015-08-05 13:55:25.062024698 -0400 @@ -41,18 +41,56 @@ INPUT_NOFILE_OK = 2, }; +struct alias; + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; + struct alias *ap; /* if push was associated with an alias */ + char *string; /* remember the string since it may change */ + + /* Remember last two characters for pungetc. */ + int lastc[2]; + + /* Number of outstanding calls to pungetc. */ + int unget; +}; + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of chars left in this buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ + + /* Remember last two characters for pungetc. */ + int lastc[2]; + + /* Number of outstanding calls to pungetc. */ + int unget; +}; + +extern struct parsefile *parsefile; + /* * The input line number. Input.c just defines this variable, and saves * and restores it when files are pushed and popped. The user of this * package must set its value. */ -extern int plinno; -extern int parsenleft; /* number of characters left in input buffer */ -extern char *parsenextc; /* next character in input buffer */ +#define plinno (parsefile->linno) int pgetc(void); int pgetc2(void); -int preadbuffer(void); void pungetc(void); void pushstring(char *, void *); void popstring(void); @@ -61,6 +99,3 @@ void popfile(void); void popallfiles(void); void closescript(void); - -#define pgetc_macro() \ - (--parsenleft >= 0 ? (signed char)*parsenextc++ : preadbuffer()) diff -Naur dash-0.5.8/src/main.c dash-0.5.8-git_d7582e6/src/main.c --- dash-0.5.8/src/main.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/main.c 2015-08-05 13:55:25.062024698 -0400 @@ -60,10 +60,6 @@ #include "exec.h" #include "cd.h" -#ifdef HETIO -#include "hetio.h" -#endif - #define PROFILE 0 int rootpid; @@ -206,10 +202,6 @@ int numeof = 0; TRACE(("cmdloop(%d) called\n", top)); -#ifdef HETIO - if(iflag && top) - hetio_init(); -#endif for (;;) { int skip; @@ -242,7 +234,7 @@ skip = evalskip; if (skip) { - evalskip &= ~SKIPFUNC; + evalskip &= ~(SKIPFUNC | SKIPFUNCDEF); break; } } @@ -321,15 +313,19 @@ { int status = 0; - if (argc >= 2) { /* That's what SVR2 does */ + nextopt(nullstr); + argv = argptr; + + if (*argv) { char *fullname; - fullname = find_dot_file(argv[1]); + fullname = find_dot_file(*argv); setinputfile(fullname, INPUT_PUSH_FILE); commandname = fullname; status = cmdloop(0); popfile(); } + return status; } @@ -339,8 +335,15 @@ { if (stoppedjobs()) return 0; - if (argc > 1) - exitstatus = number(argv[1]); + + if (argc > 1) { + int status = number(argv[1]); + + exitstatus = status; + if (savestatus >= 0) + savestatus = status; + } + exraise(EXEXIT); /* NOTREACHED */ } diff -Naur dash-0.5.8/src/mkbuiltins dash-0.5.8-git_d7582e6/src/mkbuiltins --- dash-0.5.8/src/mkbuiltins 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/mkbuiltins 2015-08-05 13:55:25.062024698 -0400 @@ -69,7 +69,7 @@ #include "builtins.h" ! -< $builtins sed '/^#/d; /^$/d' > $temp +< $builtins sed '/^#/d; /^ *$/d' > $temp awk '{ printf "int %s(int, char **);\n", $1}' $temp echo ' const struct builtincmd builtincmd[] = {' @@ -78,7 +78,7 @@ if ($i ~ /^-/) line = $(++i) "\t" line print line - }}' $temp | LC_COLLATE=C sort -k 1,1 | tee $temp2 | awk '{ + }}' $temp | LC_ALL= LC_COLLATE=C sort -k 1,1 | tee $temp2 | awk '{ opt = "" if (NF > 2) { opt = substr($2, 2) @@ -97,8 +97,9 @@ */ ! -sed 's/ -[a-z]*//' $temp2 | nl -b a -v 0 | LC_COLLATE=C sort -u -k 3,3 | -tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | +sed 's/ -[a-z]*//' $temp2 | nl -b a -v 0 | + LC_ALL= LC_COLLATE=C sort -u -k 3,3 | + tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ | awk '{ printf "#define %s (builtincmd + %d)\n", $3, $1}' printf '\n#define NUMBUILTINS %d\n' $(wc -l < $temp2) echo ' diff -Naur dash-0.5.8/src/output.c dash-0.5.8-git_d7582e6/src/output.c --- dash-0.5.8/src/output.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/output.c 2015-08-05 13:55:25.062024698 -0400 @@ -99,9 +99,6 @@ struct output *out2 = &errout; -#ifndef USE_GLIBC_STDIO -static void __outstr(const char *, size_t, struct output *); -#endif static int xvsnprintf(char *, size_t, const char *, va_list); @@ -134,16 +131,20 @@ #endif -#ifndef USE_GLIBC_STDIO -static void -__outstr(const char *p, size_t len, struct output *dest) +void +outmem(const char *p, size_t len, struct output *dest) { +#ifdef USE_GLIBC_STDIO + INTOFF; + fwrite(p, 1, len, dest->stream); + INTON; +#else size_t bufsize; size_t offset; size_t nleft; nleft = dest->end - dest->nextc; - if (nleft >= len) { + if (likely(nleft >= len)) { buffered: dest->nextc = mempcpy(dest->nextc, p, len); return; @@ -153,10 +154,13 @@ if (!bufsize) { ; } else if (dest->buf == NULL) { +#ifdef notyet if (dest->fd == MEM_OUT && len > bufsize) { bufsize = len; } +#endif offset = 0; +#ifdef notyet goto alloc; } else if (dest->fd == MEM_OUT) { offset = bufsize; @@ -168,6 +172,7 @@ if (bufsize < offset) goto err; alloc: +#endif INTOFF; dest->buf = ckrealloc(dest->buf, bufsize); dest->bufsize = bufsize; @@ -183,11 +188,13 @@ goto buffered; if ((xwrite(dest->fd, p, len))) { +#ifdef notyet err: +#endif dest->flags |= OUTPUT_ERR; } -} #endif +} void @@ -201,7 +208,7 @@ size_t len; len = strlen(p); - __outstr(p, len, file); + outmem(p, len, file); #endif } @@ -213,7 +220,7 @@ outcslow(int c, struct output *dest) { char buf = c; - __outstr(&buf, 1, dest); + outmem(&buf, 1, dest); } #endif @@ -283,35 +290,58 @@ } +static int xvasprintf(char **sp, size_t size, const char *f, va_list ap) +{ + char *s; + int len; + va_list ap2; + + va_copy(ap2, ap); + len = xvsnprintf(*sp, size, f, ap2); + va_end(ap2); + if (len < 0) + sh_error("xvsnprintf failed"); + if (len < size) + return len; + + s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1); + *sp = s; + len = xvsnprintf(s, len + 1, f, ap); + return len; +} + + +int xasprintf(char **sp, const char *f, ...) +{ + va_list ap; + int ret; + + va_start(ap, f); + ret = xvasprintf(sp, 0, f, ap); + va_end(ap); + return ret; +} + + #ifndef USE_GLIBC_STDIO void doformat(struct output *dest, const char *f, va_list ap) { struct stackmark smark; char *s; - int len, ret; - size_t size; - va_list ap2; + int len; + int olen; - va_copy(ap2, ap); - size = dest->end - dest->nextc; - len = xvsnprintf(dest->nextc, size, f, ap2); - va_end(ap2); - if (len < 0) { - dest->flags |= OUTPUT_ERR; - return; - } - if (len < size) { + setstackmark(&smark); + s = dest->nextc; + olen = dest->end - dest->nextc; + len = xvasprintf(&s, olen, f, ap); + if (likely(olen > len)) { dest->nextc += len; - return; + goto out; } - setstackmark(&smark); - s = stalloc((len >= stackblocksize() ? len : stackblocksize()) + 1); - ret = xvsnprintf(s, len + 1, f, ap); - if (ret == len) - __outstr(s, len, dest); - else - dest->flags |= OUTPUT_ERR; + outmem(s, len, dest); +out: popstackmark(&smark); } #endif diff -Naur dash-0.5.8/src/output.h dash-0.5.8-git_d7582e6/src/output.h --- dash-0.5.8/src/output.h 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/output.h 2015-08-05 13:55:25.062024698 -0400 @@ -63,6 +63,7 @@ extern struct output *out1; extern struct output *out2; +void outmem(const char *, size_t, struct output *); void outstr(const char *, struct output *); #ifndef USE_GLIBC_STDIO void outcslow(int, struct output *); @@ -75,6 +76,7 @@ __attribute__((__format__(__printf__,1,2))); int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4))); +int xasprintf(char **, const char *, ...); #ifndef USE_GLIBC_STDIO void doformat(struct output *, const char *, va_list); #endif @@ -115,6 +117,7 @@ #endif #define out1c(c) outc((c), out1) #define out2c(c) outcslow((c), out2) +#define out1mem(s, l) outmem((s), (l), out1) #define out1str(s) outstr((s), out1) #define out2str(s) outstr((s), out2) #define outerr(f) (f)->flags diff -Naur dash-0.5.8/src/parser.c dash-0.5.8-git_d7582e6/src/parser.c --- dash-0.5.8/src/parser.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/parser.c 2015-08-05 13:55:25.062024698 -0400 @@ -135,19 +135,13 @@ union node * parsecmd(int interact) { - int t; - tokpushback = 0; + checkkwd = 0; + heredoclist = 0; doprompt = interact; if (doprompt) setprompt(doprompt); needprompt = 0; - t = readtoken(); - if (t == TEOF) - return NEOF; - if (t == TNL) - return NULL; - tokpushback++; return list(1); } @@ -158,11 +152,27 @@ union node *n1, *n2, *n3; int tok; - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (nlflag == 2 && tokendlist[peektoken()]) - return NULL; n1 = NULL; for (;;) { + switch (peektoken()) { + case TNL: + if (!(nlflag & 1)) + break; + parseheredoc(); + return n1; + + case TEOF: + if (!n1 && (nlflag & 1)) + n1 = NEOF; + parseheredoc(); + return n1; + } + + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (nlflag == 2 && tokendlist[peektoken()]) + return n1; + nlflag |= 2; + n2 = andor(); tok = readtoken(); if (tok == TBACKGND) { @@ -189,31 +199,15 @@ n1 = n3; } switch (tok) { - case TBACKGND: - case TSEMI: - tok = readtoken(); - /* fall through */ case TNL: - if (tok == TNL) { - parseheredoc(); - if (nlflag == 1) - return n1; - } else { - tokpushback++; - } - checkkwd = CHKNL | CHKKWD | CHKALIAS; - if (tokendlist[peektoken()]) - return n1; - break; case TEOF: - if (heredoclist) - parseheredoc(); - else - pungetc(); /* push back EOF on input */ tokpushback++; - return n1; + /* fall through */ + case TBACKGND: + case TSEMI: + break; default: - if (nlflag == 1) + if ((nlflag & 1)) synexpect(-1); tokpushback++; return n1; @@ -743,6 +737,19 @@ return (t); } +static void nlprompt(void) +{ + plinno++; + if (doprompt) + setprompt(2); +} + +static void nlnoprompt(void) +{ + plinno++; + needprompt = doprompt; +} + /* * Read the next input token. @@ -775,7 +782,7 @@ setprompt(2); } for (;;) { /* until token or start of word found */ - c = pgetc_macro(); + c = pgetc(); switch (c) { case ' ': case '\t': case PEOA: @@ -786,16 +793,13 @@ continue; case '\\': if (pgetc() == '\n') { - plinno++; - if (doprompt) - setprompt(2); + nlprompt(); continue; } pungetc(); goto breakloop; case '\n': - plinno++; - needprompt = doprompt; + nlnoprompt(); RETURN(TNL); case PEOF: RETURN(TEOF); @@ -827,6 +831,22 @@ #undef RETURN } +static int pgetc_eatbnl(void) +{ + int c; + + while ((c = pgetc()) == '\\') { + if (pgetc() != '\n') { + pungetc(); + break; + } + + nlprompt(); + } + + return c; +} + /* @@ -895,9 +915,7 @@ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); - plinno++; - if (doprompt) - setprompt(2); + nlprompt(); c = pgetc(); goto loop; /* continue outer loop */ case CWORD: @@ -916,9 +934,7 @@ USTPUTC('\\', out); pungetc(); } else if (c == '\n') { - plinno++; - if (doprompt) - setprompt(2); + nlprompt(); } else { if ( dblquote && @@ -1009,7 +1025,7 @@ USTPUTC(c, out); } } - c = pgetc_macro(); + c = pgetc(); } } endword: @@ -1074,8 +1090,7 @@ if (c == '\n' || c == PEOF) { c = PEOF; - plinno++; - needprompt = doprompt; + nlnoprompt(); } else { int len; @@ -1179,7 +1194,7 @@ char *p; static const char types[] = "}-+?="; - c = pgetc(); + c = pgetc_eatbnl(); if ( (checkkwd & CHKEOFMARK) || c <= PEOA || @@ -1188,7 +1203,7 @@ USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ - if (pgetc() == '(') { + if (pgetc_eatbnl() == '(') { PARSEARITH(); } else { pungetc(); @@ -1200,25 +1215,24 @@ STADJUST(1, out); subtype = VSNORMAL; if (likely(c == '{')) { - c = pgetc(); + c = pgetc_eatbnl(); subtype = 0; } varname: if (is_name(c)) { do { STPUTC(c, out); - c = pgetc(); + c = pgetc_eatbnl(); } while (is_in_name(c)); } else if (is_digit(c)) { do { STPUTC(c, out); - c = pgetc(); + c = pgetc_eatbnl(); } while (is_digit(c)); - } - else if (is_special(c)) { + } else { int cc = c; - c = pgetc(); + c = pgetc_eatbnl(); if (!subtype && cc == '#') { subtype = VSLENGTH; @@ -1227,7 +1241,7 @@ goto varname; cc = c; - c = pgetc(); + c = pgetc_eatbnl(); if (cc == '}' || c != '}') { pungetc(); subtype = 0; @@ -1236,16 +1250,20 @@ } } + if (!is_special(cc)) { + if (subtype == VSLENGTH) + subtype = 0; + goto badsub; + } + USTPUTC(cc, out); } - else - goto badsub; if (subtype == 0) { switch (c) { case ':': subtype = VSNUL; - c = pgetc(); + c = pgetc_eatbnl(); /*FALLTHROUGH*/ default: p = strchr(types, c); @@ -1259,7 +1277,7 @@ int cc = c; subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT; - c = pgetc(); + c = pgetc_eatbnl(); if (c == cc) subtype++; else @@ -1324,9 +1342,7 @@ case '\\': if ((pc = pgetc()) == '\n') { - plinno++; - if (doprompt) - setprompt(2); + nlprompt(); /* * If eating a newline, avoid putting * the newline into the new character @@ -1348,8 +1364,7 @@ synerror("EOF in backquote substitution"); case '\n': - plinno++; - needprompt = doprompt; + nlnoprompt(); break; default: @@ -1427,10 +1442,6 @@ #ifdef mkinit INCLUDE "parser.h" -RESET { - tokpushback = 0; - checkkwd = 0; -} #endif diff -Naur dash-0.5.8/src/trap.c dash-0.5.8-git_d7582e6/src/trap.c --- dash-0.5.8/src/trap.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/trap.c 2015-08-05 13:55:25.062024698 -0400 @@ -51,10 +51,6 @@ #include "trap.h" #include "mystring.h" -#ifdef HETIO -#include "hetio.h" -#endif - /* * Sigmode records the current value of the signal handlers for the various * modes. A value of zero means that the current handler is not known. @@ -314,25 +310,40 @@ char *p; char *q; int i; - int savestatus; + int status, last_status; - savestatus = exitstatus; + if (!pendingsigs) + return; + + status = savestatus; + last_status = status; + if (likely(status < 0)) { + status = exitstatus; + savestatus = status; + } pendingsigs = 0; barrier(); for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) { if (!*q) continue; + + if (evalskip) { + pendingsigs = i + 1; + break; + } + *q = 0; p = trap[i + 1]; if (!p) continue; evalstring(p, 0); - exitstatus = savestatus; - if (evalskip) - break; + if (evalskip != SKIPFUNC) + exitstatus = status; } + + savestatus = last_status; } @@ -366,18 +377,11 @@ { struct jmploc loc; char *p; - volatile int status; -#ifdef HETIO - hetio_reset_term(); -#endif - status = exitstatus; - TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); - if (setjmp(loc.loc)) { - if (exception == EXEXIT) - status = exitstatus; + savestatus = exitstatus; + TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus)); + if (setjmp(loc.loc)) goto out; - } handler = &loc; if ((p = trap[0])) { trap[0] = NULL; @@ -392,7 +396,7 @@ if (likely(!setjmp(loc.loc))) setjobctl(0); flushall(); - _exit(status); + _exit(savestatus); /* NOTREACHED */ } diff -Naur dash-0.5.8/src/var.c dash-0.5.8-git_d7582e6/src/var.c --- dash-0.5.8/src/var.c 2014-09-28 04:19:32.000000000 -0400 +++ dash-0.5.8-git_d7582e6/src/var.c 2015-08-05 13:55:25.065358031 -0400 @@ -80,6 +80,7 @@ #else const char defifs[] = " \t\n"; #endif +MKINIT char defoptindvar[] = "OPTIND=1"; int lineno; char linenovar[sizeof("LINENO=")+sizeof(int)*CHAR_BIT/3+1] = "LINENO="; @@ -100,7 +101,7 @@ { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 }, { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 }, - { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, + { 0, VSTRFIXED|VTEXTFIXED, defoptindvar, getoptsreset }, #ifdef WITH_LINENO { 0, VSTRFIXED|VTEXTFIXED, linenovar, 0 }, #endif @@ -142,7 +143,7 @@ } } - setvarint("OPTIND", 1, 0); + setvareq(defoptindvar, VTEXTFIXED); fmtstr(ppid + 5, sizeof(ppid) - 5, "%ld", (long) getppid()); setvareq(ppid, VTEXTFIXED);