From: Chet Ramey Date: Fri, 15 May 2015 15:05:19 +0000 (-0400) Subject: commit bash-20150501 snapshot X-Git-Tag: bash-4.4-alpha~11 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=34ec1876071b76d3654a418682e3f34ca9a72f1a;p=thirdparty%2Fbash.git commit bash-20150501 snapshot --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 52697fd9c..1edc56040 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -8413,3 +8413,108 @@ lib/readline/{rltty,terminal}.c - sys/ioctl.h: include unconditionally for ioctl declaration, avoid issues with `implicit declaration' warnings. Issue originally raised on gdb list by Chen Gang + + 4/27 + ---- +lib/malloc/table.c + - mem_table: now a circular buffer showing the state of the last + REG_TABLE_SIZE allocations rather than a hash table that quickly fills + up + +builtins/declare.def + - typeset_builtin: add -n to list of supported options. Omission reported + by Valentin Bajrami + +unwind_prot.c + - use object caches instead of malloc/free to allocate and deallocate + unwind-protect elements + - uwp_init: initialize unwind-protect element object cache + +unwind_prot.h + - uwp_init: extern declaration + +shell.c + - main: call uwp_init right after calling cmd_init -- initialize all the + object caches at the same place + +lib/malloc/table.[ch] + - mlocation_table: new table to keep track of allocation locations by + file and line, functions to initialize table, record an allocation, + and dump the table to stderr + - mregister_alloc: call mlocation_register_alloc to record the location + (source file/line) for each allocation to pinpoint malloc hot spots + by number of calls. More detail than gprof + +parse.y + - set_line_mbstate: replace free/xmalloc pair with xrealloc call + - read_token_word: call alloc_word_desc instead of xmalloc so we can take + advantage of the WORD_DESC cache + + 4/28 + ---- +execute_cmd.c + - execute_simple_command: if command execution fails because the command + is a directory, and the `autocd' option is set, add a `--' argument + to the constructed `cd' command to protect against command names with + the same name as options to `cd'. Report and fix from + isabella parakiss + + 4/30 + ---- +jobs.c + - printable_job_status, j_strsignal: change calls to strcpy and sprintf + that write to retcode_name_buffer to use strncpy and snprintf to + avoid buffer overflows caused by malicious translations. Bug and fix + from Trammell Hudson + + 5/1 + --- +strtrans.c + - ansicstr: make sure the buffer is at least 12 bytes to ensure enough + space for any eventual call to u32cesc for one multibyte char + + 5/4 + --- +jobs.c + - wait_for: if an interactive shell is running a loop and waiting for + a non-builtin command to exit, and the command exits due to SIGINT, + act as if the shell received the SIGINT as well and break the loop. + This matches the behavior when the shell is running a builtin command + in a loop, and when running a non-builtin command outside a loop, and + seems more broadly useful than running the trap handler over and over + again. Report originally from Kaz Kylheku + +builtins/set.def + - unset_builtin: use different variables for keeping the state of the + -f and -v options than the loop uses to decide whether or not to + treat a name as a function or a variable. Fixes problem with + unset_function setting `sticking' after you unset a function when + invoked with no options. Bug report from Dreamcat4 + + +shell.c + - open_shell_script: set running_shell_script to 1, set to 0 in every + other case (new variable) + - main: when checking whether or not to call start_debugger, test + running_shell_script instead of dollar_vars[1]. The goal is to not + invoke the debugger for interactive shells but allow it to run for + things like `bash --debugger -i /tmp/script'. Problem reported by + Rocky Bernstein + +lib/readline/histexpand.c + - history_event_delimiter_chars: new (as yet undocumented) variable + containing by default characters that can delimit a history event + specifier without requiring a `:': "^$*%-" as the documentation has + always said. Fixes bug reported by Anders Granlund + + + 5/10 + ---- +lib/glob/gmisc.c + - match_pattern_char, match_pattern_wchar: if passed an empty string, + return a match if the first character of the pattern is `*' + +subst.c + - pat_subst: change to allow empty strings to be replaced as long as + pattern matches empty string. Report and fix from isabella parakiss + diff --git a/MANIFEST b/MANIFEST index 0d3328b69..c8dc1324c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -857,6 +857,7 @@ tests/builtins2.sub f tests/builtins3.sub f tests/builtins4.sub f tests/builtins5.sub f +tests/builtins6.sub f tests/source1.sub f tests/source2.sub f tests/source3.sub f @@ -872,6 +873,7 @@ tests/casemod.right f tests/comsub.tests f tests/comsub.right f tests/comsub1.sub f +tests/comsub2.sub f tests/comsub-eof.tests f tests/comsub-eof0.sub f tests/comsub-eof1.sub f @@ -929,6 +931,8 @@ tests/errors.right f tests/errors1.sub f tests/errors2.sub f tests/errors3.sub f +tests/errors4.sub f +tests/errors5.sub f tests/execscript f tests/exec.right f tests/exec1.sub f 755 @@ -1043,6 +1047,7 @@ tests/nameref5.sub f tests/nameref6.sub f tests/nameref7.sub f tests/nameref8.sub f +tests/nameref9.sub f tests/nameref.right f tests/new-exp.tests f tests/new-exp1.sub f @@ -1261,6 +1266,7 @@ tests/vredir3.sub f tests/vredir4.sub f tests/vredir5.sub f tests/vredir6.sub f +tests/vredir7.sub f tests/misc/dev-tcp.tests f tests/misc/perf-script f tests/misc/perftest f diff --git a/builtins/complete.def b/builtins/complete.def index 57d45f1bc..450d7ecf7 100644 --- a/builtins/complete.def +++ b/builtins/complete.def @@ -171,7 +171,7 @@ find_compopt (name) /* Build the actions and compspec options from the options specified in LIST. ACTP is a pointer to an unsigned long in which to place the bitmap of actions. OPTP is a pointer to an unsigned long in which to place the - btmap of compspec options (arguments to `-o'). PP, if non-null, gets 1 + bitmap of compspec options (arguments to `-o'). PP, if non-null, gets 1 if -p is supplied; RP, if non-null, gets 1 if -r is supplied. If either is null, the corresponding option generates an error. This also sets variables corresponding to options that take arguments as diff --git a/builtins/declare.def b/builtins/declare.def index a41d8c9c6..4c0f514a8 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -62,7 +62,7 @@ $END $BUILTIN typeset $FUNCTION declare_builtin -$SHORT_DOC typeset [-aAfFgilrtux] [-p] name[=value] ... +$SHORT_DOC typeset [-aAfFgilnrtux] [-p] name[=value] ... Set variable values and attributes. Obsolete. See `help declare'. diff --git a/builtins/set.def b/builtins/set.def index 35b2025ca..e31b017cf 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -796,9 +796,11 @@ unset_builtin (list) WORD_LIST *list; { int unset_function, unset_variable, unset_array, opt, nameref, any_failed; + int global_unset_func, global_unset_var; char *name; unset_function = unset_variable = unset_array = nameref = any_failed = 0; + global_unset_func = global_unset_var = 0; reset_internal_getopt (); while ((opt = internal_getopt (list, "fnv")) != -1) @@ -806,10 +808,10 @@ unset_builtin (list) switch (opt) { case 'f': - unset_function = 1; + global_unset_func = 1; break; case 'v': - unset_variable = 1; + global_unset_var = 1; break; case 'n': nameref = 1; @@ -822,7 +824,7 @@ unset_builtin (list) list = loptend; - if (unset_function && unset_variable) + if (global_unset_func && global_unset_var) { builtin_error (_("cannot simultaneously unset a function and a variable")); return (EXECUTION_FAILURE); @@ -840,6 +842,9 @@ unset_builtin (list) name = list->word->word; + unset_function = global_unset_func; + unset_variable = global_unset_var; + #if defined (ARRAY_VARS) unset_array = 0; if (!unset_function && valid_array_reference (name, 0)) diff --git a/builtins/trap.def b/builtins/trap.def index 2119f5b3c..3179c28c9 100644 --- a/builtins/trap.def +++ b/builtins/trap.def @@ -86,7 +86,7 @@ static int display_traps __P((WORD_LIST *)); trap -p [sigspec ...] trap [--] - Set things up so that ARG is executed when SIGNAL(s) N is recieved. + Set things up so that ARG is executed when SIGNAL(s) N is received. If ARG is the empty string, then ignore the SIGNAL(s). If there is no ARG, then set the trap for SIGNAL(s) to its original value. Just plain "trap" means to print out the list of commands associated with diff --git a/execute_cmd.c b/execute_cmd.c index ad4d290cf..d0488fe0e 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -643,7 +643,7 @@ execute_command_internal (command, asynchronous, pipe_in, pipe_out, if (s) subshell_exit (last_command_exit_value); else - exit (last_command_exit_value); + sh_exit (last_command_exit_value); /* NOTREACHED */ } else @@ -3926,7 +3926,7 @@ is_dirname (pathname) int ret; temp = search_for_command (pathname, 0); - ret = (temp ? file_isdir (temp) : file_isdir (pathname)); + ret = temp ? file_isdir (temp) : file_isdir (pathname); free (temp); return ret; } @@ -4086,7 +4086,7 @@ execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close) pipe_in, pipe_out, already_forked ? 0 : async); if (already_forked) - exit (result); + sh_exit (result); else { bind_lastarg ((char *)NULL); @@ -4283,6 +4283,7 @@ run_builtin: if (autocd && interactive && words->word && is_dirname (words->word->word)) { + words = make_word_list (make_word ("--"), words); words = make_word_list (make_word ("cd"), words); xtrace_print_word_list (words, 0); goto run_builtin; @@ -4850,14 +4851,14 @@ execute_subshell_builtin_or_function (words, redirects, builtin, var, fflush (stdout); if (r == EX_USAGE) r = EX_BADUSAGE; - exit (r); + sh_exit (r); } } else { r = execute_function (var, words, flags, fds_to_close, async, 1); fflush (stdout); - exit (r); + sh_exit (r); } } diff --git a/general.c b/general.c index 61a5f0e2a..65a29128b 100644 --- a/general.c +++ b/general.c @@ -407,9 +407,7 @@ fd_ispipe (fd) int fd; { errno = 0; - if (lseek ((fd), 0L, SEEK_CUR) < 0) - return (errno == ESPIPE); - return 0; + return ((lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE)); } /* There is a bug in the NeXT 2.1 rlogind that causes opens diff --git a/general.h b/general.h index e252af569..d34bb3d4a 100644 --- a/general.h +++ b/general.h @@ -124,7 +124,7 @@ typedef struct { int token; } STRING_INT_ALIST; -/* A macro to avoid making an unneccessary function call. */ +/* A macro to avoid making an unnecessary function call. */ #define REVERSE_LIST(list, type) \ ((list && list->next) ? (type)list_reverse ((GENERIC_LIST *)list) \ : (type)(list)) diff --git a/jobs.c b/jobs.c index 706738dd9..bebae1b46 100644 --- a/jobs.c +++ b/jobs.c @@ -1489,7 +1489,7 @@ j_strsignal (s) if (x == 0) { x = retcode_name_buffer; - sprintf (x, _("Signal %d"), s); + snprintf (x, sizeof(retcode_name_buffer), _("Signal %d"), s); } return x; } @@ -1512,7 +1512,7 @@ printable_job_status (j, p, format) else { temp = retcode_name_buffer; - sprintf (temp, _("Stopped(%s)"), signal_name (WSTOPSIG (p->status))); + snprintf (temp, sizeof(retcode_name_buffer), _("Stopped(%s)"), signal_name (WSTOPSIG (p->status))); } } else if (RUNNING (j)) @@ -1528,11 +1528,14 @@ printable_job_status (j, p, format) temp = retcode_name_buffer; es = WEXITSTATUS (p->status); if (es == 0) - strcpy (temp, _("Done")); + { + strncpy (temp, _("Done"), sizeof (retcode_name_buffer) - 1); + temp[sizeof (retcode_name_buffer) - 1] = '\0'; + } else if (posixly_correct) - sprintf (temp, _("Done(%d)"), es); + snprintf (temp, sizeof(retcode_name_buffer), _("Done(%d)"), es); else - sprintf (temp, _("Exit %d"), es); + snprintf (temp, sizeof(retcode_name_buffer), _("Exit %d"), es); } else temp = _("Unknown status"); @@ -2709,6 +2712,14 @@ if (job == NO_JOB) SIGINT signal handler; maybe it should. */ if (signal_is_trapped (SIGINT) == 0 && (loop_level || (shell_compatibility_level > 32 && executing_list))) ADDINTERRUPT; + /* Call any SIGINT trap handler if the shell is running a loop, so + the loop can be broken. This seems more useful and matches the + behavior when the shell is running a builtin command in a loop + when it is interrupted. Change ADDINTERRUPT to + trap_handler (SIGINT) to run the trap without interrupting the + loop. */ + else if (signal_is_trapped (SIGINT) && loop_level) + ADDINTERRUPT; else { putchar ('\n'); diff --git a/lib/glob/gmisc.c b/lib/glob/gmisc.c index c28f7b12a..31906a4f2 100644 --- a/lib/glob/gmisc.c +++ b/lib/glob/gmisc.c @@ -64,7 +64,7 @@ match_pattern_wchar (wpat, wstring, flags) wchar_t wc; if (*wstring == 0) - return (0); + return (*wpat == L'*'); /* XXX - allow only * to match empty string */ switch (wc = *wpat++) { @@ -249,7 +249,7 @@ match_pattern_char (pat, string, flags) char c; if (*string == 0) - return (0); + return (*pat == '*'); /* XXX - allow only * to match empty string */ switch (c = *pat++) { diff --git a/lib/malloc/table.c b/lib/malloc/table.c index dfa29e222..1e642d334 100644 --- a/lib/malloc/table.c +++ b/lib/malloc/table.c @@ -37,14 +37,25 @@ extern int malloc_register; #ifdef MALLOC_REGISTER -#define FIND_ALLOC 0x01 /* allocate new entry or find existing */ -#define FIND_EXIST 0x02 /* find existing entry */ +extern FILE *_imalloc_fopen __P((char *, char *, char *, char *, size_t)); + +#define FIND_ALLOC 0x01 /* find slot for new allocation */ +#define FIND_EXIST 0x02 /* find slot for existing entry for free() or search */ static int table_count = 0; static int table_allocated = 0; +static int table_bucket_index = REG_TABLE_SIZE-1; static mr_table_t mem_table[REG_TABLE_SIZE]; static mr_table_t mem_overflow; +#ifndef STREQ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#endif + +static int location_table_index = 0; +static int location_table_count = 0; +static ma_table_t mlocation_table[REG_TABLE_SIZE]; + /* * NOTE: taken from dmalloc (http://dmalloc.com) and modified. */ @@ -72,8 +83,15 @@ which_bucket (mem) { return (mt_hash ((unsigned char *)mem) & (REG_TABLE_SIZE-1)); } + #else #define which_bucket(mem) (mt_hash ((unsigned char *)(mem)) & (REG_TABLE_SIZE-1)); + +#define next_bucket() ((table_bucket_index + 1) & (REG_TABLE_SIZE-1)) +#define next_entry(mem) ((mem == mem_table + REG_TABLE_SIZE - 1) ? mem_table : ++mem) + +#define prev_bucket() (table_bucket_index == 0 ? REG_TABLE_SIZE-1 : table_bucket_index-1) +#define prev_entry(mem) ((mem == mem_table) ? mem_table + REG_TABLE_SIZE - 1 : mem - 1) #endif static mr_table_t * @@ -83,60 +101,37 @@ find_entry (mem, flags) { unsigned int bucket; register mr_table_t *tp; - mr_table_t *endp, *lastp; + mr_table_t *endp; if (mem_overflow.mem == mem) return (&mem_overflow); - bucket = which_bucket (mem); /* get initial hash */ - tp = endp = mem_table + bucket; - lastp = mem_table + REG_TABLE_SIZE; + /* If we want to insert an allocation entry just use the next slot */ + if (flags & FIND_ALLOC) + { + table_bucket_index = next_bucket(); + table_count++; + tp = mem_table + table_bucket_index; + memset(tp, 0, sizeof (mr_table_t)); /* overwrite next existing entry */ + return tp; + } + + tp = endp = mem_table + table_bucket_index; + /* search for last allocation corresponding to MEM, return entry pointer */ while (1) { if (tp->mem == mem) return (tp); - if (tp->mem == 0 && (flags & FIND_ALLOC)) - { - table_count++; - return (tp); - } - - tp++; - if (tp == lastp) /* wrap around */ - tp = mem_table; + tp = prev_entry (tp); - if (tp == endp && (flags & FIND_EXIST)) + /* if we went all the way around and didn't find it, return NULL */ + if (tp == endp) return ((mr_table_t *)NULL); - - if (tp == endp && (flags & FIND_ALLOC)) - break; } - /* oops. table is full. replace an existing free entry. */ - do - { - /* If there are no free entries, punt right away without searching. */ - if (table_allocated == REG_TABLE_SIZE) - break; - - if (tp->flags & MT_FREE) - { - memset(tp, 0, sizeof (mr_table_t)); - return (tp); - } - tp++; - - if (tp == lastp) - tp = mem_table; - } - while (tp != endp); - - /* wow. entirely full. return mem_overflow dummy entry. */ - tp = &mem_overflow; - memset (tp, 0, sizeof (mr_table_t)); - return tp; + return (mr_table_t *)NULL; } mr_table_t * @@ -186,6 +181,8 @@ mregister_alloc (tag, mem, size, file, line) blocked_sigs = 1; } + mlocation_register_alloc (file, line); + tentry = find_entry (mem, FIND_ALLOC); if (tentry == 0) @@ -293,7 +290,9 @@ _register_dump_table(fp) { entry = mem_table[i]; if (entry.mem) - fprintf (fp, "[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", i, + fprintf (fp, "%s[%d] %p:%d:%s:%s:%s:%d:%d:%d\n", + (i == table_bucket_index) ? "*" : "", + i, entry.mem, entry.size, _entry_flags(entry.flags), entry.func ? entry.func : "unknown", @@ -317,6 +316,105 @@ mregister_table_init () table_count = 0; } +/* Simple for now */ + +static ma_table_t * +find_location_entry (file, line) + const char *file; + int line; +{ + register ma_table_t *tp, *endp; + + endp = mlocation_table + location_table_count; + for (tp = mlocation_table; tp <= endp; tp++) + { + if (tp->line == line && STREQ (file, tp->file)) + return tp; + } + return (ma_table_t *)NULL; +} + +void +mlocation_register_alloc (file, line) + const char *file; + int line; +{ + ma_table_t *lentry; + char *nfile; + + if (file == 0) + { + mlocation_table[0].nalloc++; + return; + } + + nfile = strrchr (file, '/'); + if (nfile) + nfile++; + else + nfile = file; + + lentry = find_location_entry (nfile, line); + if (lentry == 0) + { + location_table_index++; + if (location_table_index == REG_TABLE_SIZE) + location_table_index = 1; /* slot 0 reserved */ + lentry = mlocation_table + location_table_index; + lentry->file = nfile; + lentry->line = line; + lentry->nalloc = 1; + if (location_table_count < REG_TABLE_SIZE) + location_table_count++; /* clamp at REG_TABLE_SIZE for now */ + } + else + lentry->nalloc++; +} + +static void +_location_dump_table (fp) + FILE *fp; +{ + register ma_table_t *tp, *endp; + + endp = mlocation_table + location_table_count; + for (tp = mlocation_table; tp < endp; tp++) + fprintf (fp, "%s:%d\t%d\n", tp->file ? tp->file : "unknown", + tp->line ? tp->line : 0, + tp->nalloc); +} + +void +mlocation_dump_table () +{ + _location_dump_table (stderr); +} + +#define LOCROOT "/var/tmp/maltrace/locations." + +void +mlocation_write_table () +{ + FILE *fp; + char defname[sizeof (LOCROOT) + 64]; + + fp = _imalloc_fopen ((char *)NULL, (char *)NULL, LOCROOT, defname, sizeof (defname)); + if (fp == 0) + return; /* XXX - no error message yet */ + _location_dump_table (fp); + fclose (fp); +} + +void +mlocation_table_init () +{ + memset (mlocation_table, 0, sizeof (ma_table_t) * REG_TABLE_SIZE); + mlocation_table[0].file = ""; /* reserve slot 0 for unknown locations */ + mlocation_table[0].line = 0; + mlocation_table[0].nalloc = 0; + location_table_count = 1; +} + #endif /* MALLOC_REGISTER */ int diff --git a/lib/malloc/table.h b/lib/malloc/table.h index 41ce9f72d..f9c5f1c4a 100644 --- a/lib/malloc/table.h +++ b/lib/malloc/table.h @@ -64,6 +64,17 @@ extern void mregister_describe_mem (); extern void mregister_dump_table __P((void)); extern void mregister_table_init __P((void)); +typedef struct ma_table { + char *file; + int line; + int nalloc; +} ma_table_t; + +extern void mlocation_register_alloc __P((const char *, int)); +extern void mlocation_table_init __P((void)); +extern void mlocation_dump_table __P((void)); +extern void mlocation_write_table __P((void)); + /* NOTE: HASH_MIX taken from dmalloc (http://dmalloc.com) */ /* diff --git a/lib/readline/histexpand.c b/lib/readline/histexpand.c index b4d695744..fc1330364 100644 --- a/lib/readline/histexpand.c +++ b/lib/readline/histexpand.c @@ -50,6 +50,7 @@ #define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" #define HISTORY_QUOTE_CHARACTERS "\"'`" +#define HISTORY_EVENT_DELIMITERS "^$*%-" #define slashify_in_quotes "\\`\"$" @@ -62,6 +63,10 @@ static char *subst_rhs; static int subst_lhs_len; static int subst_rhs_len; +/* Characters that delimit history event specifications and separate event + specifications from word designators. Static for now */ +static char *history_event_delimiter_chars = HISTORY_EVENT_DELIMITERS; + static char *get_history_word_specifier PARAMS((char *, char *, int *)); static int history_tokenize_word PARAMS((const char *, int)); static char **history_tokenize_internal PARAMS((const char *, int, int *)); @@ -112,7 +117,6 @@ rl_linebuf_func_t *history_inhibit_expansion_function; /* The last string searched for by a !?string? search. */ static char *search_string; - /* The last string matched by a !?string? search. */ static char *search_match; @@ -225,6 +229,7 @@ get_history_event (string, caller_index, delimiting_quote) #endif /* HANDLE_MULTIBYTE */ if ((!substring_okay && (whitespace (c) || c == ':' || + (history_event_delimiter_chars && member (c, history_event_delimiter_chars)) || (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || string[i] == delimiting_quote)) || string[i] == '\n' || @@ -873,7 +878,7 @@ history_expand_internal (string, start, qc, end_index_ptr, ret_string, current_l 1) If expansions did take place 2) If the `p' modifier was given and the caller should print the result - If an error ocurred in expansion, then OUTPUT contains a descriptive + If an error occurred in expansion, then OUTPUT contains a descriptive error message. */ #define ADD_STRING(s) \ diff --git a/lib/sh/strtrans.c b/lib/sh/strtrans.c index ae88e694d..02f5c89af 100644 --- a/lib/sh/strtrans.c +++ b/lib/sh/strtrans.c @@ -60,7 +60,10 @@ ansicstr (string, len, flags, sawc, rlen) return ((char *)NULL); #if defined (HANDLE_MULTIBYTE) - ret = (char *)xmalloc (4*len + 1); + temp = 4*len + 1; + if (temp < 12) + temp = 12; /* ensure enough for eventual u32cesc */ + ret = (char *)xmalloc (temp); #else ret = (char *)xmalloc (2*len + 1); /* 2*len for possible CTLESC */ #endif diff --git a/lib/sh/unicode.c b/lib/sh/unicode.c index 798260ecd..a34336ef1 100644 --- a/lib/sh/unicode.c +++ b/lib/sh/unicode.c @@ -236,7 +236,7 @@ u32toutf16 (c, s) } /* convert a single unicode-32 character into a multibyte string and put the - result in S, which must be large enough (at least MB_LEN_MAX bytes) */ + result in S, which must be large enough (at least max(10,MB_LEN_MAX) bytes) */ int u32cconv (c, s) unsigned long c; diff --git a/parse.y b/parse.y index cefe7beb4..5b476b09a 100644 --- a/parse.y +++ b/parse.y @@ -4912,7 +4912,7 @@ got_token: #endif CHECK_FOR_RESERVED_WORD (token); - the_word = (WORD_DESC *)xmalloc (sizeof (WORD_DESC)); + the_word = alloc_word_desc (); the_word->word = (char *)xmalloc (1 + token_index); the_word->flags = 0; strcpy (the_word->word, token); @@ -6258,8 +6258,7 @@ set_line_mbstate () if (shell_input_line == NULL) return; len = strlen (shell_input_line); /* XXX - shell_input_line_len ? */ - FREE (shell_input_line_property); - shell_input_line_property = (char *)xmalloc (len + 1); + shell_input_line_property = (char *)xrealloc (shell_input_line_property, len + 1); memset (&prevs, '\0', sizeof (mbstate_t)); for (i = previ = 0; i < len; i++) diff --git a/redir.c b/redir.c index 4cc237cea..539af00d9 100644 --- a/redir.c +++ b/redir.c @@ -118,10 +118,13 @@ redirection_error (temp, error) allocname = 0; if ((temp->rflags & REDIR_VARASSIGN) && error < 0) filename = allocname = savestring (temp->redirector.filename->word); - else if (temp->redirector.dest < 0) + else if ((temp->rflags & REDIR_VARASSIGN) == 0 && temp->redirector.dest < 0) /* This can happen when read_token_word encounters overflow, like in exec 4294967297>x */ +{ +itrace("redirection_error: temp->redirector.dest = %d", temp->redirector.dest); filename = _("file descriptor out of range"); +} #ifdef EBADF /* This error can never involve NOCLOBBER */ else if (error != NOCLOBBER_REDIRECT && temp->redirector.dest >= 0 && error == EBADF) diff --git a/shell.c b/shell.c index b145af351..2b3f0ca41 100644 --- a/shell.c +++ b/shell.c @@ -160,6 +160,7 @@ int autocd = 0; This is a superset of the information provided by interactive_shell. */ int startup_state = 0; +int reading_shell_script = 0; /* Special debugging helper. */ int debugging_login_shell = 0; @@ -390,7 +391,7 @@ main (argc, argv, env) xtrace_init (); #if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS) - malloc_set_register (0); /* XXX - change to 1 for malloc debugging */ + malloc_set_register (1); /* XXX - change to 1 for malloc debugging */ #endif check_dev_tty (); @@ -680,7 +681,8 @@ main (argc, argv, env) } #endif - cmd_init(); /* initialize the command object caches */ + cmd_init (); /* initialize the command object caches */ + uwp_init (); if (command_execution_string) { @@ -721,7 +723,7 @@ main (argc, argv, env) /* Bind remaining args to $1 ... $n */ arg_index = bind_args (argv, arg_index, argc, 1); - if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && (dollar_vars[1] || interactive_shell == 0)) + if (debugging_mode && locally_skip_execution == 0 && running_setuid == 0 && (reading_shell_script || interactive_shell == 0)) start_debugger (); /* Do the things that should be done only for interactive shells. */ @@ -965,6 +967,7 @@ sh_exit (s) #if defined (MALLOC_DEBUG) && defined (USING_BASH_MALLOC) if (malloc_trace_at_exit) trace_malloc_stats (get_name_for_error (), (char *)NULL); + /* mlocation_write_table (); */ #endif exit (s); @@ -1465,7 +1468,7 @@ open_shell_script (script_name) { e = errno; file_error (filename); - exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT); + sh_exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT); } free (dollar_vars[0]); @@ -1484,7 +1487,7 @@ open_shell_script (script_name) errno = EINVAL; #endif file_error (filename); - exit (EX_NOINPUT); + sh_exit (EX_NOINPUT); } #if defined (ARRAY_VARS) @@ -1586,6 +1589,8 @@ open_shell_script (script_name) init_interactive_script (); free (filename); + + reading_shell_script = 1; return (fd); } diff --git a/subst.c b/subst.c index cb453299b..388e58828 100644 --- a/subst.c +++ b/subst.c @@ -595,6 +595,7 @@ quoted_strchr (s, c, flags) return ((char *)NULL); } +#if defined (INCLUDE_UNUSED) /* Return 1 if CHARACTER appears in an unquoted portion of STRING. Return 0 otherwise. CHARACTER must be a single-byte character. */ static int @@ -679,6 +680,7 @@ unquoted_substring (substr, string) } return (0); } +#endif /* Most of the substitutions must be done in parallel. In order to avoid using tons of unclear goto's, I have some functions @@ -4435,7 +4437,7 @@ match_pattern (string, pat, mtype, sp, ep) size_t slen, plen, mslen, mplen; #endif - if (string == 0 || *string == 0 || pat == 0 || *pat == 0) + if (string == 0 || pat == 0 || *pat == 0) return (0); #if defined (HANDLE_MULTIBYTE) @@ -4709,7 +4711,7 @@ string_var_assignment (v, s) val = sh_quote_reusable (s, 0); i = var_attribute_string (v, 0, flags); - ret = (char *)xmalloc (i + strlen (val) + strlen (v->name) + 16); + ret = (char *)xmalloc (i + strlen (val) + strlen (v->name) + 16 + MAX_ATTRIBUTES); if (i > 0) sprintf (ret, "declare -%s %s=%s", flags, v->name, val); else @@ -7001,6 +7003,7 @@ parameter_brace_substring (varname, value, ind, substr, quoted, flags) /* */ /****************************************************************/ +#if 0 /* Unused */ static int shouldexp_replacement (s) char *s; @@ -7016,6 +7019,7 @@ shouldexp_replacement (s) } return 0; } +#endif char * pat_subst (string, pat, rep, mflags) @@ -7041,6 +7045,8 @@ pat_subst (string, pat, rep, mflags) * with REP and return the result. * 2. A null pattern with mtype == MATCH_END means to append REP to * STRING and return the result. + * 3. A null STRING with a matching pattern means to append REP to + * STRING and return the result. * These don't understand or process `&' in the replacement string. */ if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END)) @@ -7062,11 +7068,17 @@ pat_subst (string, pat, rep, mflags) } return (ret); } + else if (*string == 0 && (match_pattern (string, pat, mtype, &s, &e) != 0)) + { + ret = (char *)xmalloc (STRLEN (rep) + 1); + strcpy (ret, rep); + return (ret); + } ret = (char *)xmalloc (rsize = 64); ret[0] = '\0'; - for (replen = STRLEN (rep), rptr = 0, str = string;;) + for (replen = STRLEN (rep), rptr = 0, str = string; *str;) { if (match_pattern (str, pat, mtype, &s, &e) == 0) break; diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 3efcf32d6..72ec06a2c 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/arith.right b/tests/arith.right index 680d9a954..69e7491d8 100644 --- a/tests/arith.right +++ b/tests/arith.right @@ -141,6 +141,8 @@ ok -7 7 7 +2 +2 ./arith1.sub: line 2: 4-- : syntax error: operand expected (error token is "- ") ./arith1.sub: line 3: 4++ : syntax error: operand expected (error token is "+ ") ./arith1.sub: line 4: 4 -- : syntax error: operand expected (error token is "- ") @@ -235,13 +237,13 @@ ok 0, 0 0, 1 8 12 -./arith.tests: line 286: ((: x=9 y=41 : syntax error in expression (error token is "y=41 ") -./arith.tests: line 290: a b: syntax error in expression (error token is "b") -./arith.tests: line 291: ((: a b: syntax error in expression (error token is "b") +./arith.tests: line 290: ((: x=9 y=41 : syntax error in expression (error token is "y=41 ") +./arith.tests: line 294: a b: syntax error in expression (error token is "b") +./arith.tests: line 295: ((: a b: syntax error in expression (error token is "b") 42 42 42 42 42 42 -./arith.tests: line 302: b[c]d: syntax error in expression (error token is "d") +./arith.tests: line 306: b[c]d: syntax error in expression (error token is "d") diff --git a/tests/arith.tests b/tests/arith.tests index 7dcc36d8d..394422d2f 100644 --- a/tests/arith.tests +++ b/tests/arith.tests @@ -260,6 +260,10 @@ echo $(( -7 )) echo $(( ++7 )) echo $(( --7 )) +# combinations of expansions +echo $(( "`echo 1+1`" )) +echo $(( `echo 1+1` )) + ${THIS_SH} ./arith1.sub ${THIS_SH} ./arith2.sub ${THIS_SH} ./arith3.sub diff --git a/tests/assoc.right b/tests/assoc.right index 315b0cec4..d51cc91ee 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -14,9 +14,10 @@ declare -Ai chaff=([one]="10" [zero]="5" ) declare -Ar waste=([version]="4.0-devel" [source]="./assoc.tests" [lineno]="28" [pid]="42134" ) declare -A wheat=([two]="b" [three]="c" [one]="a" [zero]="0" ) declare -A chaff=([one]="10" ["hello world"]="flip" [zero]="5" ) -./assoc.tests: line 38: unset: waste: cannot unset: readonly variable -./assoc.tests: line 39: chaff[*]: bad array subscript -./assoc.tests: line 40: [*]=12: invalid associative array key +./assoc.tests: line 38: waste: readonly variable +./assoc.tests: line 39: unset: waste: cannot unset: readonly variable +./assoc.tests: line 40: chaff[*]: bad array subscript +./assoc.tests: line 41: [*]=12: invalid associative array key declare -A chaff=([one]="a" ["hello world"]="flip" ) flip argv[1] = @@ -31,9 +32,9 @@ argv[2] = argv[3] = argv[4] = argv[1] = -./assoc.tests: line 57: declare: chaff: cannot destroy array variables in this way -./assoc.tests: line 59: chaff[*]: bad array subscript -./assoc.tests: line 60: [*]=12: invalid associative array key +./assoc.tests: line 58: declare: chaff: cannot destroy array variables in this way +./assoc.tests: line 60: chaff[*]: bad array subscript +./assoc.tests: line 61: [*]=12: invalid associative array key declare -A wheat=([six]="6" ["foo bar"]="qux qix" ) argv[1] = argv[2] = @@ -190,3 +191,7 @@ declare -A foo=(["bar\${foo}bie"]="doll" ) bar after printf after use: 0 +declare -A assoc=([0]="assoc" ) +assoc +declare -A assoc=([two]="twoless" [three]="three" [one]="onemore" ) +declare -Ar assoc=([two]="twoless" [three]="three" [one]="onemore" ) diff --git a/tests/assoc.tests b/tests/assoc.tests index c3fb18e60..1d4f4e5a5 100644 --- a/tests/assoc.tests +++ b/tests/assoc.tests @@ -35,6 +35,7 @@ chaff[hello world]=flip declare -p chaff # TEST - errors +waste[stuff]=other unset waste chaff[*]=12 chaff=( [one]=a [*]=12 ) @@ -186,3 +187,21 @@ ${THIS_SH} ./assoc5.sub ${THIS_SH} ./assoc6.sub ${THIS_SH} ./assoc7.sub + +# test converting between scalars and assoc arrays +unset assoc +assoc=assoc +declare -A assoc +declare -p assoc +echo ${assoc[@]} + +# weird syntax required to append to multiple existing array elements using +# compound assignment syntax +unset assoc +declare -A assoc +assoc=( [one]=one [two]=two [three]=three ) +assoc+=( [one]+=more [two]+=less ) +declare -p assoc + +readonly -A assoc +declare -p assoc diff --git a/tests/builtins.right b/tests/builtins.right index 8a5e3f925..32db2a535 100644 --- a/tests/builtins.right +++ b/tests/builtins.right @@ -194,4 +194,45 @@ scalar: 0 scalar: 1 scalar: 0 scalar: 0 -./builtins.tests: line 263: exit: status: numeric argument required +all set: +one +two +f1 () +{ + echo f1 +} +f2 () +{ + echo f2 +} +all unset: +unset1 +unset2 +./builtins6.sub: line 28: declare: f1: not found +./builtins6.sub: line 28: declare: f2: not found +all reset: +one-one +two-one +f1 () +{ + echo f1 +} +f2 () +{ + echo f2 +} +vars unset: +unset1 +unset2 +f1 () +{ + echo f1 +} +f2 () +{ + echo f2 +} +funcs unset: +one-two +two-two +./builtins.tests: line 266: exit: status: numeric argument required diff --git a/tests/builtins.tests b/tests/builtins.tests index ccf55a87e..ac74fe6fa 100644 --- a/tests/builtins.tests +++ b/tests/builtins.tests @@ -259,6 +259,9 @@ ${THIS_SH} ./builtins4.sub # test behavior of set and unset array variables ${THIS_SH} ./builtins5.sub +# test behavior of unset builtin with -f and -v options +${THIS_SH} ./builtins6.sub + # this must be last -- it is a fatal error exit status diff --git a/tests/builtins6.sub b/tests/builtins6.sub new file mode 100644 index 000000000..c18b47663 --- /dev/null +++ b/tests/builtins6.sub @@ -0,0 +1,68 @@ +f1() +{ + echo f1 +} + +f2() +{ + echo f2 +} + +v1=one +v2=two + +echo all set: + +echo ${v1-unset1} +echo ${v2-unset2} + +declare -f -p f1 f2 + +unset v1 f1 v2 f2 + +echo all unset: + +echo ${v1-unset1} +echo ${v2-unset2} + +declare -f -p f1 f2 + +f1() +{ + echo f1 +} + +f2() +{ + echo f2 +} + +v1=one-one +v2=two-one + +echo all reset: +echo ${v1-unset1} +echo ${v2-unset2} + +declare -f -p f1 f2 + +unset -v v1 f1 v2 f2 + +echo vars unset: + +echo ${v1-unset1} +echo ${v2-unset2} + +declare -f -p f1 f2 + +v1=one-two +v2=two-two + +unset -f v1 f1 v2 f2 + +echo funcs unset: + +echo ${v1-unset1} +echo ${v2-unset2} + +declare -f f1 f2 diff --git a/tests/casemod.right b/tests/casemod.right index 958b28dc6..ca1de49a9 100644 --- a/tests/casemod.right +++ b/tests/casemod.right @@ -43,3 +43,7 @@ Be Conservative in what you send and Liberal in what you accept BE CONSERVATIVE IN WHAT YOU SEND AND LIBERAL IN WHAT YOU ACCEPT Be conservative in what you send and liberal in what you accept BE CONSERVATIVE IN WHAT YOU SEND AND LIBERAL IN WHAT YOU ACCEPT +AcknOwlEdgEmEnt acknOwlEdgEmEnt +oeNoPHiLe OEnOphIlE +abcdexyz +ABCDEXYZ diff --git a/tests/casemod.tests b/tests/casemod.tests index 3c7ad23fe..dacdb0a87 100644 --- a/tests/casemod.tests +++ b/tests/casemod.tests @@ -97,3 +97,17 @@ echo ${TEXT^^} echo ${TEXT2^} echo ${TEXT2^^} + +M1=${S1^^[aeiou]} +M2=${U2,,[AEIOU]} + +echo ${M1} ${M1~} +echo ${M2} ${M2~~} + +declare -l lower=aBcDe +lower+=XyZ +echo $lower + +declare -u upper=aBcDe +upper+=xYZ +echo $upper diff --git a/tests/comsub-posix.right b/tests/comsub-posix.right index ab04c52ae..2290aa6dc 100644 --- a/tests/comsub-posix.right +++ b/tests/comsub-posix.right @@ -71,3 +71,4 @@ swap32_posix () done } yes +ab cde diff --git a/tests/comsub-posix.tests b/tests/comsub-posix.tests index bcfeede08..b2cb44182 100644 --- a/tests/comsub-posix.tests +++ b/tests/comsub-posix.tests @@ -217,3 +217,6 @@ unset x # fixed after bash-4.0 released : $(case a in a) echo ;; # comment esac) + +# recommended to be parsed as a nested comsub instead of arithsub +echo $(( echo ab cde ) ) diff --git a/tests/comsub.right b/tests/comsub.right index 9894529c0..2b5a23043 100644 --- a/tests/comsub.right +++ b/tests/comsub.right @@ -27,3 +27,8 @@ ok 3 ok 4 ok 5 ok 6 +xyz +\/tmp\/foo\/bar +/tmp/foo/bar +/tmp/foo/bar +/tmp/foo/bar diff --git a/tests/comsub.tests b/tests/comsub.tests index 493bbda07..862fc264b 100644 --- a/tests/comsub.tests +++ b/tests/comsub.tests @@ -42,3 +42,4 @@ echo $(recho 'foo\ bar') ${THIS_SH} ./comsub1.sub +${THIS_SH} ./comsub2.sub diff --git a/tests/comsub1.sub b/tests/comsub1.sub index 86565e2c4..216e03f1f 100644 --- a/tests/comsub1.sub +++ b/tests/comsub1.sub @@ -46,3 +46,9 @@ esac) echo $(case a in (a) echo ok 6 # comment ;; esac) + +echo $( # we just took and pasted in some +# code from another script inside a +# command substitution +echo xyz +) diff --git a/tests/comsub2.sub b/tests/comsub2.sub new file mode 100644 index 000000000..a2d58ad01 --- /dev/null +++ b/tests/comsub2.sub @@ -0,0 +1,8 @@ +qpath='\/tmp\/foo\/bar' + +echo "$qpath" + +# it's crazy that all three of these produce the same result +echo ${qpath//\\/} +echo ${qpath//"`echo \\`"/} +echo ${qpath//`echo "\\\\\\\\"`/} diff --git a/tests/errors.right b/tests/errors.right index 1ac9735fc..da9415335 100644 --- a/tests/errors.right +++ b/tests/errors.right @@ -110,4 +110,16 @@ after f ./errors3.sub: line 5: no_such_file: No such file or directory TEST ./errors3.sub: line 7: no_such_file: No such file or directory -./errors.tests: line 271: `!!': not a valid identifier +1 +2 +./errors4.sub: line 7: var: readonly variable +after readonly assignment +./errors4.sub: line 13: break: x: numeric argument required +1 +2 +./errors4.sub: line 7: var: readonly variable +./errors5.sub: line 6: array: unbound variable +./errors5.sub: line 7: array: unbound variable +./errors5.sub: line 10: 7: unbound variable +./errors5.sub: line 11: 7: unbound variable +./errors.tests: line 275: `!!': not a valid identifier diff --git a/tests/errors.tests b/tests/errors.tests index 14eeb08bb..fb47bf0a1 100644 --- a/tests/errors.tests +++ b/tests/errors.tests @@ -262,6 +262,10 @@ echo $? ${THIS_SH} ./errors1.sub ${THIS_SH} ./errors2.sub ${THIS_SH} ./errors3.sub +${THIS_SH} ./errors4.sub +${THIS_SH} -o posix ./errors4.sub + +${THIS_SH} ./errors5.sub # this must be last! # in posix mode, a function name must be a valid identifier diff --git a/tests/errors4.sub b/tests/errors4.sub new file mode 100644 index 000000000..8451b9611 --- /dev/null +++ b/tests/errors4.sub @@ -0,0 +1,18 @@ +# test effect of assigning to readonly vars on loops and non-interactive shells +# fatal error when in posix mode +var=foo +readonly var +for num in 1 2 3 4 5; do + if [ $num -eq 3 ]; then + var=bar + fi + echo $num +done +echo after readonly assignment + +# non-numeric arguments to break are fatal errors for all non-interactive shells +for f in 1 2 3 4 5 +do + break x +done +echo after loop diff --git a/tests/errors5.sub b/tests/errors5.sub new file mode 100644 index 000000000..7d9068b76 --- /dev/null +++ b/tests/errors5.sub @@ -0,0 +1,11 @@ +array[1]=one +array[2]=two + +set -u + +( echo ${#array} ) +( echo ${array} ) + +set -- 1 2 3 +( echo ${#7} ) +( echo ${7} ) diff --git a/tests/exp.right b/tests/exp.right index c027036ed..3c5fb8a5d 100644 --- a/tests/exp.right +++ b/tests/exp.right @@ -4,6 +4,11 @@ argv[1] = <^?> argv[1] = <^?> argv[1] = <^A> argv[1] = <^?> +argv[1] = +argv[1] = <^A> +argv[1] = <^?> +argv[1] = <^A> +argv[1] = <^?> argv[1] = argv[1] = argv[1] = diff --git a/tests/exp.tests b/tests/exp.tests index 9f77a7f93..53eeaa85d 100644 --- a/tests/exp.tests +++ b/tests/exp.tests @@ -31,6 +31,18 @@ recho `echo ` expect "<^?>" recho `echo ` +expect "bar" +recho ${foo:-"`echo bar`"} +expect "<^A>" +recho ${foo:-"`echo `"} +expect "<^?>" +recho ${foo:-"`echo `"} + +expect "<^A>" +recho "`echo `" +expect "<^?>" +recho "`echo `" + # Test null strings without variable expansion expect "" recho abcd""efgh diff --git a/tests/histexp.right b/tests/histexp.right index f1c9e9d9a..2b848bbec 100644 --- a/tests/histexp.right +++ b/tests/histexp.right @@ -127,3 +127,6 @@ echo '!!' \!\! ok 1 ok 2 ok 3 +echo shopt a +shopt a +echo a b c d 2 > /dev/null diff --git a/tests/histexp.tests b/tests/histexp.tests index add2a6747..d9be836dd 100644 --- a/tests/histexp.tests +++ b/tests/histexp.tests @@ -122,3 +122,8 @@ echo ${!var2} # Bash-2.01[.1] fails this test -- it attempts history expansion after the # history_comment_char echo ok 3 # !1200 + +shopt a b c d 2>/dev/null +echo !shopt-1 + +echo !shopt* diff --git a/tests/nameref.right b/tests/nameref.right index 532153488..c35fac1e3 100644 --- a/tests/nameref.right +++ b/tests/nameref.right @@ -34,6 +34,8 @@ one ./nameref.tests: line 106: foo: readonly variable ./nameref.tests: line 103: foo: readonly variable one +abxde +abxde one bar @@ -127,3 +129,7 @@ local ./nameref8.sub: line 54: warning: x: circular name reference ./nameref8.sub: line 55: warning: x: circular name reference x = +idx2 +idX2 +idx2 +idX2 diff --git a/tests/nameref.tests b/tests/nameref.tests index 438133c64..bce8a6806 100644 --- a/tests/nameref.tests +++ b/tests/nameref.tests @@ -108,6 +108,13 @@ readonly foo=one assignvar foo two three four echo $foo +var=abcde +x=var +declare -n v=var +# these two should display the same +echo ${!x//c/x} +echo ${v//c/x} + ${THIS_SH} ./nameref1.sub ${THIS_SH} ./nameref2.sub ${THIS_SH} ./nameref3.sub @@ -116,3 +123,4 @@ ${THIS_SH} ./nameref5.sub ${THIS_SH} ./nameref6.sub ${THIS_SH} ./nameref7.sub ${THIS_SH} ./nameref8.sub +${THIS_SH} ./nameref9.sub diff --git a/tests/nameref9.sub b/tests/nameref9.sub new file mode 100644 index 000000000..e76c21b86 --- /dev/null +++ b/tests/nameref9.sub @@ -0,0 +1,8 @@ +arr=( idx1 idx2 ) +i='arr[1]' +echo ${!i} +echo ${!i/x/X} + +typeset -n f='arr[1]' +echo ${f} +echo ${f/x/X} diff --git a/tests/new-exp.right b/tests/new-exp.right index e0f64d53f..7366e07ae 100644 --- a/tests/new-exp.right +++ b/tests/new-exp.right @@ -59,8 +59,15 @@ argv[1] = <4> argv[1] = argv[1] = argv[1] = -./new-exp.tests: line 172: ABX: unbound variable -./new-exp.tests: line 176: $6: cannot assign in this way +argv[1] = +argv[2] = +argv[3] = +argv[4] = +argv[1] = +argv[2] = +argv[3] = +./new-exp.tests: line 180: ABX: unbound variable +./new-exp.tests: line 184: $6: cannot assign in this way argv[1] = argv[1] = argv[1] = @@ -169,7 +176,7 @@ this is test 2 ./new-exp2.sub: line 42: 1111111111111111111111: command not found argv[1] = <6> -./new-exp.tests: line 277: ${#:}: bad substitution +./new-exp.tests: line 285: ${#:}: bad substitution argv[1] = <'> argv[1] = <"> argv[1] = <"hello"> @@ -398,13 +405,13 @@ argv[6] = argv[7] = argv[8] = argv[9] = -./new-exp.tests: line 482: $9: unbound variable -./new-exp.tests: line 483: 9: unbound variable -./new-exp.tests: line 484: UNSET: unbound variable -./new-exp.tests: line 485: UNSET: unbound variable -./new-exp.tests: line 486: UNSET: unbound variable -./new-exp.tests: line 487: UNSET: unbound variable -./new-exp.tests: line 488: UNSET: unbound variable +./new-exp.tests: line 490: $9: unbound variable +./new-exp.tests: line 491: 9: unbound variable +./new-exp.tests: line 492: UNSET: unbound variable +./new-exp.tests: line 493: UNSET: unbound variable +./new-exp.tests: line 494: UNSET: unbound variable +./new-exp.tests: line 495: UNSET: unbound variable +./new-exp.tests: line 496: UNSET: unbound variable argv[1] = <5> argv[1] = <#> argv[1] = <#> @@ -433,7 +440,7 @@ Case05---3---A:B:C--- Case06---1---A B C::--- Case07---3---A:B:C--- Case08---3---A:B:C--- -./new-exp.tests: line 508: ${$(($#-1))}: bad substitution +./new-exp.tests: line 516: ${$(($#-1))}: bad substitution argv[1] = argv[2] = argv[3] = @@ -450,7 +457,7 @@ argv[1] = argv[1] = argv[2] = argv[1] = <> -./new-exp.tests: line 527: $(($# - 2)): substring expression < 0 +./new-exp.tests: line 535: $(($# - 2)): substring expression < 0 argv[1] = argv[2] = argv[3] = @@ -614,4 +621,4 @@ a b c d e a5b argv[1] = argv[1] = -./new-exp.tests: line 587: ABXD: parameter unset +./new-exp.tests: line 595: ABXD: parameter unset diff --git a/tests/new-exp.tests b/tests/new-exp.tests index aa4eb626e..666d8a9d2 100644 --- a/tests/new-exp.tests +++ b/tests/new-exp.tests @@ -167,6 +167,14 @@ recho ${a-$z} expect nothing recho ${!1-$z} +set -- a 'b c' d +unset foo +foo=@ +expect ' ' +recho ${!foo} +expect ' ' +recho "${!foo}" + set -u expect $0: ABX: unbound variable ( recho ${ABX} ) diff --git a/tests/varenv.right b/tests/varenv.right index 7765e5c4d..62b3c1398 100644 --- a/tests/varenv.right +++ b/tests/varenv.right @@ -76,6 +76,9 @@ declare -ar myvar=([0]="0") declare -ir myvar="1" declare -rx tempvar1='foo' declare -rx tempvar2='qux' +./varenv7.sub: line 44: local: var: readonly variable +inside: outside +outside: outside a=z a=b a=z diff --git a/tests/varenv7.sub b/tests/varenv7.sub index cb7a47105..4741d2380 100644 --- a/tests/varenv7.sub +++ b/tests/varenv7.sub @@ -35,3 +35,16 @@ echo ${tempvar1@A} tempvar2=bar declare -r tempvar2=qux echo ${tempvar2@A} + +unset foo +readonly var=outside + +func() +{ + local var=inside + echo "inside: $var" +} + +func +echo outside: $var + diff --git a/tests/vredir.right b/tests/vredir.right index 5e081bee2..3985f22df 100644 --- a/tests/vredir.right +++ b/tests/vredir.right @@ -90,3 +90,14 @@ ok 1 ./vredir6.sub: redirection error: cannot duplicate fd: Invalid argument ./vredir6.sub: line 13: /dev/null: Invalid argument unset +12 10 +a +a +swizzle is a function +swizzle () +{ + exec {fd[0]}<&0; + exec {fd[1]}>&1; + exec {stdin}<&${fd[0]}-; + exec {stdout}>&${fd[1]}- +} diff --git a/tests/vredir.tests b/tests/vredir.tests index 5158fe397..b81c671c2 100644 --- a/tests/vredir.tests +++ b/tests/vredir.tests @@ -43,4 +43,6 @@ ${THIS_SH} ./vredir5.sub ${THIS_SH} ./vredir6.sub +${THIS_SH} ./vredir7.sub + exit 0 diff --git a/tests/vredir7.sub b/tests/vredir7.sub new file mode 100644 index 000000000..8cb80eddb --- /dev/null +++ b/tests/vredir7.sub @@ -0,0 +1,23 @@ +swizzle() +{ +exec {fd[0]}<&0 +exec {fd[1]}>&1 + +exec {stdin}<&${fd[0]}- +exec {stdout}>&${fd[1]}- +} + +swizzle + +echo $stdin $stdout + +read line <&$stdin <&$stdout + +type swizzle + +exit 0 diff --git a/unwind_prot.c b/unwind_prot.c index 0e480bf7a..c9196dc1c 100644 --- a/unwind_prot.c +++ b/unwind_prot.c @@ -49,6 +49,7 @@ #include "sig.h" #include "quit.h" #include "error.h" /* for internal_warning */ +#include "ocache.h" /* Structure describing a saved variable and the value to restore it to. */ typedef struct { @@ -75,7 +76,6 @@ typedef union uwp { } sv; } UNWIND_ELT; - static void without_interrupts __P((VFunction *, char *, char *)); static void unwind_frame_discard_internal __P((char *, char *)); static void unwind_frame_run_internal __P((char *, char *)); @@ -88,8 +88,24 @@ static void unwind_protect_mem_internal __P((char *, char *)); static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL; +/* Allocating from a cache of unwind-protect elements */ +#define UWCACHESIZE 128 + +sh_obj_cache_t uwcache = {0, 0, 0}; + +#if 0 #define uwpalloc(elt) (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT)) #define uwpfree(elt) free(elt) +#else +#define uwpalloc(elt) ocache_alloc (uwcache, UNWIND_ELT, elt) +#define uwpfree(elt) ocache_free (uwcache, UNWIND_ELT, elt) +#endif + +void +uwp_init () +{ + ocache_create (uwcache, UNWIND_ELT, UWCACHESIZE); +} /* Run a function without interrupts. This relies on the fact that the FUNCTION cannot change the value of interrupt_immediately. (I.e., does diff --git a/unwind_prot.h b/unwind_prot.h index 4a1b256b1..cbfc97ca2 100644 --- a/unwind_prot.h +++ b/unwind_prot.h @@ -21,6 +21,8 @@ #if !defined (_UNWIND_PROT_H) #define _UNWIND_PROT_H +extern void uwp_init __P((void)); + /* Run a function without interrupts. */ extern void begin_unwind_frame __P((char *)); extern void discard_unwind_frame __P((char *));