From: Chet Ramey Date: Mon, 15 Oct 2018 13:08:29 +0000 (-0400) Subject: commit bash-20181012 snapshot X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=93e3d9f6aed3d276c35ec0f907f6fc6dd1b12dad;p=thirdparty%2Fbash.git commit bash-20181012 snapshot --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index db59b332d..05b025db5 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -4550,3 +4550,23 @@ trap.c or without, case-insensitively if requested, and return SIGRTMIN+n. These values could be different than what was available at compile time. Report and patch from Rasmus Villemoes + + 10/8 + ---- +execute_cmd.c + - execute_command_internal: only set line_number from command->value.Subshell + if the type == cm_subshell; otherwise defer and set later + + 10/10 + ----- +examples/loadables/seq.c + - seq: new loadable builtin, derived originally from coreutils:seq.c + but with very little of that code remaining + + 10/12 + ----- +trap.c + - run_pending_traps,_run_trap_internal: honor evalnest_max and + increment/decrement evalnest accordingly, since trap actions + are processed as if run by `eval'. Feature suggsted by Mike + Gerwitz diff --git a/MANIFEST b/MANIFEST index a5875966b..aa9819e4a 100644 --- a/MANIFEST +++ b/MANIFEST @@ -675,6 +675,7 @@ examples/loadables/necho.c f examples/loadables/hello.c f examples/loadables/print.c f examples/loadables/realpath.c f +examples/loadables/seq.c f examples/loadables/setpgid.c f examples/loadables/sleep.c f examples/loadables/strftime.c f diff --git a/config-top.h b/config-top.h index 9b345507d..56dbd51e6 100644 --- a/config-top.h +++ b/config-top.h @@ -161,7 +161,8 @@ no longer exists. This behavior is the default in Posix mode. */ #define CHECKHASH_DEFAULT 0 -/* Define to the maximum level of recursion you want for the eval builtin. +/* Define to the maximum level of recursion you want for the eval builtin + and trap handlers (since traps are run as if run by eval). 0 means the limit is not active. */ #define EVALNEST_MAX 0 diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in index 0facc0ba9..54424cea4 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -103,7 +103,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \ ALLPROG = print truefalse sleep finfo logname basename dirname \ tty pathchk tee head mkdir rmdir printenv id whoami \ uname sync push ln unlink realpath strftime mypid setpgid -OTHERPROG = necho hello cat pushd stat rm fdflags +OTHERPROG = necho hello cat pushd stat rm fdflags seq all: $(SHOBJ_STATUS) @@ -148,6 +148,9 @@ rm: rm.o fdflags: fdflags.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ fdflags.o $(SHOBJ_LIBS) +seq: seq.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ seq.o $(SHOBJ_LIBS) + logname: logname.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ logname.o $(SHOBJ_LIBS) @@ -293,3 +296,5 @@ realpath.o: realpath.c strftime.o: strftime.c setpgid.o: setpgid.c stat.o: stat.c +fdflags.o: fdflags.c +seq.o: seq.c diff --git a/examples/loadables/README b/examples/loadables/README index 91febd48e..f9bcfac64 100644 --- a/examples/loadables/README +++ b/examples/loadables/README @@ -50,6 +50,7 @@ ln.c Make links. loadables.h File loadable builtins can include for shell definitions. logname.c Print login name of current user. Makefile.in Simple makefile for the sample loadable builtins. +Makefile.inc.in Sample makefile to use for loadable builtin development. mkdir.c Make directories. mypid.c Add $MYPID variable, demonstrate use of unload hook functio.n necho.c echo without options or argument interpretation. @@ -60,6 +61,7 @@ push.c Anyone remember TOPS-20? realpath.c Canonicalize pathnames, resolving symlinks. rm.c Remove files and directories. rmdir.c Remove directory. +seq.c Print a sequence of decimal or floating point numbers. setpgid.c Set a process's pgrp; example of how to wrap a system call. sleep.c sleep for fractions of a second. stat.c populate an associative array with information about a file diff --git a/examples/loadables/seq.c b/examples/loadables/seq.c new file mode 100644 index 000000000..ebf12994c --- /dev/null +++ b/examples/loadables/seq.c @@ -0,0 +1,490 @@ +/* seq - print sequence of numbers to standard output. + Copyright (C) 2018 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written as bash builtin by Chet Ramey. Portions from seq.c by Ulrich Drepper. */ + +#include + +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include + +#include "bashansi.h" +#include "loadables.h" +#include "bashintl.h" + +#ifndef errno +extern int errno; +#endif + +#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD && !defined(STRTOLD_BROKEN) +typedef long double floatmax_t; +# define FLOATMAX_CONV "L" +# define strtofltmax strtold +# define FLOATMAX_FMT "%Lg" +# define FLOATMAX_WFMT "%0.Lf" +# define USE_LONG_DOUBLE +#else +typedef double floatmax_t; +# define FLOATMAX_CONV "" +# define strtofltmax strtod +# define FLOATMAX_FMT "%g" +# define FLOATMAX_WFMT "%0.f" +#endif +static floatmax_t getfloatmax __P((const char *)); +static char *genformat __P((floatmax_t, floatmax_t, floatmax_t)); + +#define MAX(a, b) (((a) < (b))? (b) : (a)) + +static int conversion_error = 0; + +/* If true print all number with equal width. */ +static int equal_width; + +/* The string used to separate two numbers. */ +static char const *separator; + +/* The string output after all numbers have been output. */ +static char const terminator[] = "\n"; + +static char decimal_point; + +/* Pretty much the same as the version in builtins/printf.def */ +static floatmax_t +getfloatmax (arg) + const char *arg; +{ + floatmax_t ret; + char *ep; + + errno = 0; + ret = strtofltmax (arg, &ep); + + if (*ep) + { + sh_invalidnum ((char *)arg); + conversion_error = 1; + } + else if (errno == ERANGE) + { + builtin_error ("warning: %s: %s", arg, strerror(ERANGE)); + conversion_error = 1; + } + + if (ret == -0.0) + ret = 0.0; + + return (ret); +} + +/* If FORMAT is a valid printf format for a double argument, return + its long double equivalent, allocated from dynamic storage. This + was written by Ulrich Drepper, taken from coreutils:seq.c */ +static char * +long_double_format (char const *fmt) +{ + size_t i; + size_t length_modifier_offset; + int has_L; + + for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1) + { + if (!fmt[i]) + { + builtin_error ("format %s has no %% directive", fmt); + return 0; + } + } + + i++; + i += strspn (fmt + i, "-+#0 '"); /* zero or more flags */ + i += strspn (fmt + i, "0123456789"); /* optional minimum field width */ + if (fmt[i] == '.') /* optional precision */ + { + i++; + i += strspn (fmt + i, "0123456789"); + } + + length_modifier_offset = i; /* optional length modifier */ + /* we could ignore an 'l' length modifier here */ + has_L = (fmt[i] == 'L'); + i += has_L; + switch (fmt[i]) + { + case '\0': + builtin_error ("format %s ends in %%", fmt); + return 0; + case 'A': + case 'a': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + break; + default: + builtin_error ("format %s has unknown `%%%c' directive", fmt, fmt[i]); + return 0; + } + for (i++; ; i += (fmt[i] == '%') + 1) + if (fmt[i] == '%' && fmt[i + 1] != '%') + { + builtin_error ("format %s has too many %% directives", fmt); + return 0; + } + else if (fmt[i] == 0) + { + size_t format_size = i + 1; + char *ldfmt = xmalloc (format_size + 1); + memcpy (ldfmt, fmt, length_modifier_offset); +#ifdef USE_LONG_DOUBLE + ldfmt[length_modifier_offset] = 'L'; + strcpy (ldfmt + length_modifier_offset + 1, + fmt + length_modifier_offset + has_L); +#else + strcpy (ldfmt + length_modifier_offset, fmt + length_modifier_offset) +#endif + return ldfmt; + } +} + +/* Return the number of digits following the decimal point in NUMBUF */ +static int +getprec (numbuf) + const char *numbuf; +{ + int p; + char *dp; + + if (dp = strchr (numbuf, decimal_point)) + dp++; /* skip over decimal point */ + for (p = 0; dp && *dp && ISDIGIT (*dp); dp++) + p++; + return p; +} + +/* Return the default format given FIRST, INCR, and LAST. */ +static char * +genformat (first, incr, last) + floatmax_t first, incr, last; +{ + static char buf[6 + 2 * INT_STRLEN_BOUND (int)]; + int wfirst, wlast, width; + int iprec, fprec, lprec, prec; + + if (equal_width == 0) + return (FLOATMAX_FMT); + + /* OK, we have to figure out the largest number of decimal places. This is + a little more expensive than using the original strings. */ + snprintf (buf, sizeof (buf), FLOATMAX_FMT, incr); + iprec = getprec (buf); + + wfirst = snprintf (buf, sizeof (buf), FLOATMAX_FMT, first); + fprec = getprec (buf); + + prec = MAX (fprec, iprec); + + wlast = snprintf (buf, sizeof (buf), FLOATMAX_FMT, last); + lprec = getprec (buf); + + /* increase first width by any increased precision in increment */ + wfirst += (prec - fprec); + + /* adjust last width to use precision from first/incr */ + wlast += (prec - lprec); + + if (lprec && prec == 0) + wlast--; /* no decimal point */ + if (lprec == 0 && prec) + wlast++; /* include decimal point */ + if (fprec == 0 && prec) + wfirst++; /* include decimal point */ + + width = MAX (wfirst, wlast); + if (width) + sprintf (buf, "%%0%d.%d%sf", width, prec, FLOATMAX_CONV); + else + sprintf (buf, "%%.%d%sf", prec, FLOATMAX_CONV); + + return buf; +} + +int +print_fltseq (fmt, first, last, incr) + const char *fmt; + floatmax_t first, last, incr; +{ + int n; + floatmax_t next; + const char *s; + + n = 0; /* interation counter */ + s = ""; + for (next = first; incr >= 0 ? (next <= last) : (next >= last); next = first + n * incr) + { + QUIT; + if (*s && fputs (s, stdout) == EOF) + return (sh_chkwrite (EXECUTION_FAILURE)); + if (printf (fmt, next) < 0) + return (sh_chkwrite (EXECUTION_FAILURE)); + s = separator; + n++; + } + + if (n > 0 && fputs (terminator, stdout) == EOF) + return (sh_chkwrite (EXECUTION_FAILURE)); + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +/* must be <= INT_STRLEN_BOUND(intmax_t) */ +int +width_needed (num) + intmax_t num; +{ + int ret; + + ret = num < 0; /* sign */ + if (ret) + num = -num; + do + ret++; + while (num /= 10); + return ret; +} + +int +print_intseq (ifirst, ilast, iincr) + intmax_t ifirst, ilast, iincr; +{ + char intwfmt[6 + INT_STRLEN_BOUND(int) + sizeof (PRIdMAX)]; + const char *s; + intmax_t i, next; + + /* compute integer format string */ + if (equal_width) /* -w supplied */ + { + int wfirst, wlast, width; + + wfirst = width_needed (ifirst); + wlast = width_needed (ilast); + width = MAX(wfirst, wlast); + + /* The leading %s is for the separator */ + snprintf (intwfmt, sizeof (intwfmt), "%%s%%0%u" PRIdMAX, width); + } + + /* We could use braces.c:mkseq here but that allocates lots of memory */ + s = ""; + for (i = ifirst; (ifirst <= ilast) ? (i <= ilast) : (i >= ilast); i = next) + { + QUIT; + /* The leading %s is for the separator */ + if (printf (equal_width ? intwfmt : "%s%" PRIdMAX, s, i) < 0) + return (sh_chkwrite (EXECUTION_FAILURE)); + s = separator; + next = i + iincr; + } + + if (fputs (terminator, stdout) == EOF) + return (sh_chkwrite (EXECUTION_FAILURE)); + return (sh_chkwrite (EXECUTION_SUCCESS)); +} + +int +seq_builtin (list) + WORD_LIST *list; +{ + floatmax_t first, last, incr; + intmax_t ifirst, ilast, iincr; + WORD_LIST *l; + int opt, nargs, intseq, freefmt; + char *first_str, *incr_str, *last_str; + char const *fmtstr; /* The printf(3) format used for output. */ + + equal_width = 0; + separator = "\n"; + fmtstr = NULL; + + first = 1.0; + last = 0.0; + incr = 0.0; /* set later */ + ifirst = ilast = iincr = 0; + first_str = incr_str = last_str = 0; + + intseq = freefmt = 0; + opt = 0; + + reset_internal_getopt (); + while (opt != -1) + { + l = lcurrent ? lcurrent : list; + if (l && l->word && l->word->word && l->word->word[0] == '-' && + (l->word->word[1] == '.' || DIGIT (l->word->word[1]))) + { + loptend = l; + break; /* negative number */ + } + if ((opt = internal_getopt (list, "f:s:w")) == -1) + break; + + switch (opt) + { + case 'f': + fmtstr = list_optarg; + break; + case 's': + separator = list_optarg; + break; + case 'w': + equal_width = 1; + break; + CASE_HELPOPT; + default: + builtin_usage (); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + for (nargs = 1, l = list; l->next; l = l->next) + nargs++; + if (nargs > 3) + { + builtin_usage (); + return (EXECUTION_FAILURE); + } + + /* LAST */ + conversion_error = 0; + last = getfloatmax (last_str = l->word->word); + if (conversion_error) + return (EXECUTION_FAILURE); + + /* FIRST LAST */ + if (nargs > 1) + { + conversion_error = 0; + first = getfloatmax (first_str = list->word->word); + if (conversion_error) + return (EXECUTION_FAILURE); + } + + /* FIRST INCR LAST */ + if (nargs > 2) + { + conversion_error = 0; + incr = getfloatmax (incr_str = list->next->word->word); + if (conversion_error) + return (EXECUTION_FAILURE); + if (incr == 0.0) + { + builtin_error ("zero %screment", (first < last) ? "in" : "de"); + return (EXECUTION_FAILURE); + } + } + + /* Sanitize arguments */ + if (incr == 0.0) + incr = (first <= last) ? 1.0 : -1.0; + if ((incr < 0.0 && first < last) || (incr > 0 && first > last)) + { + builtin_error ("incorrect %screment", (first < last) ? "in" : "de"); + return (EXECUTION_FAILURE); + } + + /* validate format here */ + if (fmtstr) + { + fmtstr = long_double_format (fmtstr); + freefmt = 1; + if (fmtstr == 0) + return (EXECUTION_FAILURE); + } + + if (fmtstr != NULL && equal_width) + { + builtin_warning ("-w ignored when the format string is specified"); + equal_width = 0; + } + + /* Placeholder for later additional conditions */ + if (last_str && all_digits (last_str) && + (first_str == 0 || all_digits (first_str)) && + (incr_str == 0 || all_digits (incr_str)) && + fmtstr == NULL) + intseq = 1; + + if (intseq) + { + ifirst = (intmax_t)first; /* truncation */ + ilast = (intmax_t)last; + iincr = (intmax_t)incr; + + return (print_intseq (ifirst, ilast, iincr)); + } + + decimal_point = locale_decpoint (); + if (fmtstr == NULL) + fmtstr = genformat (first, incr, last); + + print_fltseq (fmtstr, first, last, incr); + + if (freefmt) + free ((void *)fmtstr); + return sh_chkwrite (EXECUTION_SUCCESS); +} + +/* Taken largely from GNU seq. */ +char *seq_doc[] = { + "Print numbers from FIRST to LAST, in steps of INCREMENT.", + "", + "-f FORMAT use printf style floating-point FORMAT", + "-s STRING use STRING to separate numbers (default: \\n)", + "-w equalize width by padding with leading zeroes", + "", + "If FIRST or INCREMENT is omitted, it defaults to 1. However, an", + "omitted INCREMENT defaults to -1 when LAST is smaller than FIRST.", + "The sequence of numbers ends when the sum of the current number and", + "INCREMENT would become greater than LAST.", + "FIRST, INCREMENT, and LAST are interpreted as floating point values.", + "", + "FORMAT must be suitable for printing one argument of type 'double';", + "it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point", + "decimal numbers with maximum precision PREC, and to %g otherwise.", + (char *)NULL +}; + +struct builtin seq_struct = { + "seq", + seq_builtin, + BUILTIN_ENABLED, + seq_doc, + "seq [-f format] [-s separator] [-w] [FIRST [INCR]] LAST", + 0 +}; diff --git a/execute_cmd.c b/execute_cmd.c index a418bbb58..045863302 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -624,7 +624,9 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, /* Fork a subshell, turn off the subshell bit, turn off job control and call execute_command () on the command again. */ - line_number_for_err_trap = line_number = command->value.Subshell->line; /* XXX - save value? */ + if (command->type == cm_subshell) + line_number_for_err_trap = line_number = command->value.Subshell->line; /* XXX - save value? */ + /* Otherwise we defer setting line_number */ tcmd = make_command_string (command); paren_pid = make_child (p = savestring (tcmd), asynchronous); diff --git a/po/de.po b/po/de.po index 752238ec8..a314f9ad7 100644 --- a/po/de.po +++ b/po/de.po @@ -7,7 +7,7 @@ msgstr "" "Project-Id-Version: bash 4.4\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-09-10 12:42-0400\n" -"PO-Revision-Date: 2018-07-22 00:02+0200\n" +"PO-Revision-Date: 2018-10-14 21:29+0200\n" "Last-Translator: Nils Naumann \n" "Language-Team: German \n" "Language: de\n" @@ -1549,7 +1549,7 @@ msgstr "Dateiende beim Suchen nach passender `)' erreicht." #: pcomplete.c:1126 #, c-format msgid "completion: function `%s' not found" -msgstr "" +msgstr "completion: Funktion `%s' nicht gefunden." #: pcomplete.c:1646 #, c-format @@ -1583,7 +1583,7 @@ msgstr "" #: print_cmd.c:1534 #, c-format msgid "cprintf: `%c': invalid format character" -msgstr "" +msgstr "cprintf: `%c': Ungültiges Formatsymbol." #: redir.c:124 redir.c:171 msgid "file descriptor out of range" @@ -4636,8 +4636,33 @@ msgid "" " Returns success unless an invalid option is given or a write or assignment\n" " error occurs." msgstr "" -"Returns success unless an invalid option is given or a write or\n" -" assignment error occurs.<" +"Formatierte Ausgabe der ARGUMENTE.\n" +" \n" +" Optionen:\n" +" -v var\tDie formatierte Ausgabe ver Variable var zuweisen statt\n" +" \t\tsie an die Standardausgebe zu senden.\n" +" \n" +" Die FORMAT Zeichenkette kann einfache Zeichen enthalten, die unverändert an\n" +" die Standardausgabe geschickt werden. Escape-Sequenzen werden umgewandelt\n" +" und an die Standardausgabe geschickt sowie Formatanweisungen, welche das \n" +" nachfolgende ARGUMENT auswerten und ausgeben.\n" +" \n" +" Gegenüber der in printf(1) beschriebenen Standardverion werden zusätzliche\n" +" Formatanweisungen ausgewertet:\n" +" \n" +" %b\tWertet Escape-Sequenzen des zugehörigen Arguments aus.\n" +" %q\tBettet das Argument so ein, dass es als Shelleingabe\n" +" verwendet werden kann.\n" +" %(fmt)T\tAusgabe der aus FMT entstehende Datum-Zeit Zeichenkette, dass\n" +" \t sie als Zeichenkette für strftime(3) verwendet werden kann.\n" +" \n" +" Die Formatangebe wird wiederverwendet bis alle Argmente ausgewertet sind.\n" +" Wenn weniger Argumente als Formatangaben vorhanden sind, werden für die\n" +" Argumente Nullwerte bzw. leere Zeichenketten eingesetzt.\n" +" \n" +" Rücgabewert:\n" +" Gibt Erfolg zurück, außer es wird eine ungültige Option angegeben oder ein\n" +" Aus- bzw. Zuweisungsfehler auftritt." #: builtins.c:1953 msgid "" diff --git a/tests/redir.right b/tests/redir.right index 75ed45551..d4ab60d52 100644 --- a/tests/redir.right +++ b/tests/redir.right @@ -64,14 +64,14 @@ f typeset -f f # make sure it was closed -read -u 5 foo +read -u 5 foo 2>&1 | grep -q 'invalid file descriptor' echo after read exec 5<&0 exec <&- -read abcde +read abcde 2>&1 | grep -q 'read error' exec 0<&9- read line @@ -84,9 +84,7 @@ f () echo "$line"; done } -./redir5.sub: line 20: read: 5: invalid file descriptor: Bad file descriptor after read -./redir5.sub: line 27: read: read error: 0: Bad file descriptor # tests of ksh93-like dup-and-close redirection operators / / @@ -141,19 +139,10 @@ bix () foo :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 1 -./redir11.sub: line 8: $a: Bad file descriptor -./redir11.sub: line 9: $(echo $a): Bad file descriptor 7 after: 42 -./redir11.sub: line 24: echo: write error: Bad file descriptor -./redir11.sub: line 25: echo: write error: Bad file descriptor -./redir11.sub: line 26: $(a=4 foo): Bad file descriptor -./redir11.sub: line 27: $(a=4 foo): Bad file descriptor -./redir11.sub: line 30: $a: Bad file descriptor -./redir11.sub: line 31: $(echo $a): Bad file descriptor -./redir11.sub: line 39: $(ss= declare -i ss): ambiguous redirect +./redir11.sub: line 40: $(ss= declare -i ss): ambiguous redirect after: 42 a+=3 foo foo -./redir11.sub: line 53: $(echo $a): Bad file descriptor diff --git a/tests/redir11.sub b/tests/redir11.sub index 59ed493a3..a919602c7 100644 --- a/tests/redir11.sub +++ b/tests/redir11.sub @@ -4,9 +4,10 @@ a=1 a=4 b=7 ss=4 echo $a +# use grep to avoid differences due to different system error messages a=42 -a=2 echo foo >&$a -a=2 echo foo >&$(echo $a) +a=2 echo foo 2>&1 >&$a | { grep -q '\$a: Bad file' || echo 'redir11 bad 1'; } +a=2 echo foo 2>&1 >&$(echo $a) | { grep -q 'Bad file' || echo 'redir11 bad 2'; } foo() { @@ -21,14 +22,14 @@ a=4 b=7 foo echo after: $a unset a -a=4 echo foo >&$(foo) -a=1 echo foo >&$(foo) -a=1 echo foo >&$(a=4 foo) -echo foo >&$(a=4 foo) +a=4 echo foo 2>&1 >&$(foo) | { grep -q 'Bad file' || echo 'redir11 bad 3'; } +a=1 echo foo 2>&1 >&$(foo) | { grep -q 'Bad file' || echo 'redir11 bad 4'; } +a=1 echo foo 2>&1 >&$(a=4 foo) | { grep -q 'Bad file' || echo 'redir11 bad 5'; } +echo foo 2>&1 >&$(a=4 foo) | { grep -q 'Bad file' || echo 'redir11 bad 6'; } a=42 -a=2 echo foo >&$a -a=2 echo foo >&$(echo $a) +a=2 echo foo 2>&1 >&$a | { grep -q 'Bad file' || echo 'redir11 bad 7'; } +a=2 echo foo 2>&1 >&$(echo $a) | { grep -q 'Bad file' || echo 'redir11 bad 8'; } unset -f foo foo() @@ -50,4 +51,4 @@ a=9 echo foo >&$(echo $a) a=2 a=9 eval echo foo >&$(echo $a) a=2 -a=9 eval echo foo '>&$(echo $a)' +a=9 eval echo foo '2>&1 >&$(echo $a)' | { grep -q 'Bad file' || echo 'redir11 bad 9'; } diff --git a/tests/redir5.sub b/tests/redir5.sub index 5d59d39c5..ee7b045af 100644 --- a/tests/redir5.sub +++ b/tests/redir5.sub @@ -17,14 +17,14 @@ f typeset -f f # make sure it was closed -read -u 5 foo +read -u 5 foo 2>&1 | grep -q 'invalid file descriptor' echo after read exec 5<&0 exec <&- -read abcde +read abcde 2>&1 | grep -q 'read error' exec 0<&9- read line diff --git a/tests/vredir.right b/tests/vredir.right index f449ae6fa..f4fe0db83 100644 --- a/tests/vredir.right +++ b/tests/vredir.right @@ -11,9 +11,6 @@ bar () ./vredir.tests: line 6: v: readonly variable ./vredir.tests: line 6: v: cannot assign fd to variable 42 -./vredir.tests: line 25: $v: Bad file descriptor -./vredir.tests: line 26: $v: Bad file descriptor -./vredir.tests: line 27: $v: Bad file descriptor bar is a function bar () { diff --git a/tests/vredir.tests b/tests/vredir.tests index b81c671c2..ce77deb75 100644 --- a/tests/vredir.tests +++ b/tests/vredir.tests @@ -22,9 +22,9 @@ exec {v}>&- readonly v=42 bar -echo foo 1 >&$v -echo foo 2 >&$v -echo foo 3 >&$v +echo foo 1 2>&1 >&$v | { grep -q '\$v: Bad' || echo 'bad foo 1'; } +echo foo 2 2>&1 >&$v | { grep -q '\$v: Bad' || echo 'bad foo 2'; } +echo foo 3 2>&1 >&$v | { grep -q '\$v: Bad' || echo 'bad foo 3'; } cat $TMPFILE rm -f $TMPFILE diff --git a/trap.c b/trap.c index 6c7447876..ceb83951a 100644 --- a/trap.c +++ b/trap.c @@ -1,7 +1,7 @@ /* trap.c -- Not the trap command, but useful functions for manipulating those objects. The trap command is in builtins/trap.def. */ -/* Copyright (C) 1987-2015 Free Software Foundation, Inc. +/* Copyright (C) 1987-2018 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -312,9 +312,15 @@ run_pending_traps () #if defined (SIGWINCH) if (running_trap == SIGWINCH+1 && pending_traps[SIGWINCH]) return; /* no recursive SIGWINCH trap invocations */ -#else - ; #endif + /* could check for running the trap handler for the same signal here + (running_trap == sig+1) */ + if (evalnest_max && evalnest > evalnest_max) + { + internal_error (_("trap handler: maximum trap handler level exceeded (%d)"), evalnest_max); + evalnest = 0; + jump_to_top_level (DISCARD); + } } catch_flag = trapped_signal_received = 0; @@ -339,6 +345,8 @@ run_pending_traps () if (sig == SIGINT) { pending_traps[sig] = 0; /* XXX */ + /* We don't modify evalnest here, since run_interrupt_trap() calls + _run_trap_internal, which does. */ run_interrupt_trap (0); CLRINTERRUPT; } @@ -348,10 +356,14 @@ run_pending_traps () (sigmodes[SIGCHLD] & SIG_INPROGRESS) == 0) { sigmodes[SIGCHLD] |= SIG_INPROGRESS; + /* We modify evalnest here even though run_sigchld_trap can run + the trap action more than once */ + evalnest++; x = pending_traps[sig]; pending_traps[sig] = 0; run_sigchld_trap (x); /* use as counter */ running_trap = 0; + evalnest--; sigmodes[SIGCHLD] &= ~SIG_INPROGRESS; /* continue here rather than reset pending_traps[SIGCHLD] below in case there are recursive calls to run_pending_traps and children @@ -415,7 +427,9 @@ run_pending_traps () #endif /* XXX - set pending_traps[sig] = 0 here? */ pending_traps[sig] = 0; + evalnest++; evalstring (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST|SEVAL_RESETLINE); + evalnest--; #if defined (JOB_CONTROL) restore_pipeline (1); #endif @@ -488,7 +502,7 @@ trap_handler (sig) errno = oerrno; } - + SIGRETURN (0); } @@ -1012,6 +1026,7 @@ _run_trap_internal (sig, tag) flags = SEVAL_NONINT|SEVAL_NOHIST; if (sig != DEBUG_TRAP && sig != RETURN_TRAP && sig != ERROR_TRAP) flags |= SEVAL_RESETLINE; + evalnest++; if (function_code == 0) { parse_and_execute (trap_command, tag, flags); @@ -1019,6 +1034,7 @@ _run_trap_internal (sig, tag) } else trap_exit_value = return_catch_value; + evalnest--; #if defined (JOB_CONTROL) if (sig != DEBUG_TRAP) /* run_debug_trap does this */