diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index c7f692f1..c99b8e7d 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -9029,3 +9029,22 @@ execute_cmd.c - builtin_status: make sure a status > EX_SHERRBASE gets translated to EXECUTION_FAILURE + 9/21 + ---- +subst.c + - process_substitute: set up input from /dev/null (standard for an + asynchronous process) only if the shell is interactive and reading + input from the terminal. This allows scripts to use process + substitution to filter stdin. From a report from + Andreas Schwab + + 9/23 + ---- +doc/{bash.1,bashref.texi} + - ENV: a couple of changes clarifying that it's only used when an + interactive shell is started in posix mode. Report from + Reuben Thomas + +examples/loadables/asort.c + - asort: loadable builtin to sort an array. Contributed by + Geir Hauge diff --git a/MANIFEST b/MANIFEST index c76131fb..bb1c0ac9 100644 --- a/MANIFEST +++ b/MANIFEST @@ -712,6 +712,7 @@ examples/loadables/Makefile.in f examples/loadables/Makefile.inc.in f examples/loadables/necho.c f examples/loadables/hello.c f +examples/loadables/asort.c f examples/loadables/accept.c f examples/loadables/print.c f examples/loadables/realpath.c f diff --git a/doc/bash.1 b/doc/bash.1 index 0f4e37f9..6a75e477 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Tue Aug 25 09:58:55 EDT 2020 +.\" Last Change: Wed Sep 23 09:28:31 EDT 2020 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2020 August 25" "GNU Bash 5.1" +.TH BASH 1 "2020 September 23" "GNU Bash 5.1" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -2136,10 +2136,11 @@ it assumes that the shell is running in an Emacs shell buffer and disables line editing. .TP .B ENV -Similar to +Expanded and executed similarly to .SM -.BR BASH_ENV ; -used when the shell is invoked in \fIposix mode\fP. +.B BASH_ENV +(see \fBINVOCATION\fP above) +when an interactive shell is invoked in \fIposix mode\fP. .TP .B EXECIGNORE A colon-separated list of shell patterns (see \fBPattern Matching\fP) diff --git a/doc/bashref.texi b/doc/bashref.texi index 3943b85b..03a18b89 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -5959,7 +5959,9 @@ starts with value @samp{t}, it assumes that the shell is running in an Emacs shell buffer and disables line editing. @item ENV -Similar to @code{BASH_ENV}; used when the shell is invoked in +Expanded and executed similarlty to @code{BASH_ENV} +(@pxref{Bash Startup Files}) +when an interactive shell is invoked in @sc{posix} Mode (@pxref{Bash POSIX Mode}). @item EPOCHREALTIME diff --git a/doc/version.texi b/doc/version.texi index 4a548f91..6312d387 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2020 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Tue Aug 25 09:58:38 EDT 2020 +@set LASTCHANGE Wed Sep 23 09:28:04 EDT 2020 @set EDITION 5.1 @set VERSION 5.1 -@set UPDATED 25 August 2020 -@set UPDATED-MONTH August 2020 +@set UPDATED 23 September 2020 +@set UPDATED-MONTH September 2020 diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in index e044d18d..be46121b 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -104,7 +104,7 @@ ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \ tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \ uname sync push ln unlink realpath strftime mypid setpgid seq rm \ accept csv cut -OTHERPROG = necho hello cat pushd stat +OTHERPROG = necho hello cat pushd stat asort all: $(SHOBJ_STATUS) @@ -233,6 +233,9 @@ setpgid: setpgid.o stat: stat.o $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ stat.o $(SHOBJ_LIBS) +asort: asort.o + $(SHOBJ_LD) $(SHOBJ_LDFLAGS) $(SHOBJ_XLDFLAGS) -o $@ asort.o $(SHOBJ_LIBS) + # pushd is a special case. We use the same source that the builtin version # uses, with special compilation options. # @@ -318,3 +321,4 @@ setpgid.o: setpgid.c stat.o: stat.c fdflags.o: fdflags.c seq.o: seq.c +asort.o: asort.c diff --git a/examples/loadables/asort.c b/examples/loadables/asort.c new file mode 100644 index 00000000..e847ef86 --- /dev/null +++ b/examples/loadables/asort.c @@ -0,0 +1,279 @@ +/* + Copyright (C) 2020 Free Software Foundation, Inc. + + Bash 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. + + Bash 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 Bash. If not, see . +*/ +#include +#include +#include + +#include "bashtypes.h" +#include "shell.h" +#include "builtins.h" +#include "common.h" +#include "xmalloc.h" +#include "bashgetopt.h" + +typedef struct sort_element { + ARRAY_ELEMENT *v; // used when sorting array in-place + char *key; // used when sorting assoc array + char *value; // points to value of array element or assoc entry + double num; // used for numeric sort +} sort_element; + +static int reverse_flag; +static int numeric_flag; + +static int +compare(const void *p1, const void *p2) { + const sort_element e1 = *(sort_element *) p1; + const sort_element e2 = *(sort_element *) p2; + + if (numeric_flag) { + if (reverse_flag) + return (e2.num > e1.num) ? 1 : (e2.num < e1.num) ? -1 : 0; + else + return (e1.num > e2.num) ? 1 : (e1.num < e2.num) ? -1 : 0; + } + else { + if (reverse_flag) + return strcoll(e2.value, e1.value); + else + return strcoll(e1.value, e2.value); + } +} + +static int +sort_index(SHELL_VAR *dest, SHELL_VAR *source) { + HASH_TABLE *hash; + BUCKET_CONTENTS *bucket; + sort_element *sa; + ARRAY *array, *dest_array; + ARRAY_ELEMENT *ae; + size_t i, j, n; + char ibuf[INT_STRLEN_BOUND (intmax_t) + 1]; // used by fmtulong + char *key; + + dest_array = array_cell(dest); + + if (assoc_p(source)) { + hash = assoc_cell(source); + n = hash->nentries; + sa = xmalloc(n * sizeof(sort_element)); + i = 0; + for ( j = 0; j < hash->nbuckets; ++j ) { + bucket = hash->bucket_array[j]; + while ( bucket ) { + sa[i].v = NULL; + sa[i].key = bucket->key; + if ( numeric_flag ) + sa[i].num = strtod(bucket->data, NULL); + else + sa[i].value = bucket->data; + i++; + bucket = bucket->next; + } + } + } + else { + array = array_cell(source); + n = array_num_elements(array); + sa = xmalloc(n * sizeof(sort_element)); + i = 0; + + for (ae = element_forw(array->head); ae != array->head; ae = element_forw(ae)) { + sa[i].v = ae; + if (numeric_flag) + sa[i].num = strtod(element_value(ae), NULL); + else + sa[i].value = element_value(ae); + i++; + } + } + + // sanity check + if ( i != n ) { + builtin_error("%s: corrupt array", source->name); + return EXECUTION_FAILURE; + } + + qsort(sa, n, sizeof(sort_element), compare); + + array_flush(dest_array); + + for ( i = 0; i < n; ++i ) { + if ( assoc_p(source) ) + key = sa[i].key; + else + key = fmtulong((long unsigned)sa[i].v->ind, 10, ibuf, sizeof(ibuf), 0); + + array_insert(dest_array, i, key); + } + + return EXECUTION_SUCCESS; +} + +static int +sort_inplace(SHELL_VAR *var) { + size_t i, n; + ARRAY *a; + ARRAY_ELEMENT *ae; + sort_element *sa = 0; + + a = array_cell(var); + n = array_num_elements(a); + + if ( n == 0 ) + return EXECUTION_SUCCESS; + + sa = xmalloc(n * sizeof(sort_element)); + + i = 0; + for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) { + sa[i].v = ae; + if (numeric_flag) + sa[i].num = strtod(element_value(ae), NULL); + else + sa[i].value = element_value(ae); + i++; + } + + // sanity check + if ( i != n ) { + builtin_error("%s: corrupt array", var->name); + return EXECUTION_FAILURE; + } + + qsort(sa, n, sizeof(sort_element), compare); + + // for in-place sort, simply "rewire" the array elements + sa[0].v->prev = sa[n-1].v->next = a->head; + a->head->next = sa[0].v; + a->head->prev = sa[n-1].v; + a->max_index = n - 1; + for (i = 0; i < n; i++) { + sa[i].v->ind = i; + if (i > 0) + sa[i].v->prev = sa[i-1].v; + if (i < n - 1) + sa[i].v->next = sa[i+1].v; + } + xfree(sa); + return EXECUTION_SUCCESS; +} + +int +asort_builtin(WORD_LIST *list) { + SHELL_VAR *var, *var2; + char *word; + int opt, ret; + int index_flag = 0; + + numeric_flag = 0; + reverse_flag = 0; + + reset_internal_getopt(); + while ((opt = internal_getopt(list, "inr")) != -1) { + switch (opt) { + case 'i': index_flag = 1; break; + case 'n': numeric_flag = 1; break; + case 'r': reverse_flag = 1; break; + CASE_HELPOPT; + default: + builtin_usage(); + return (EX_USAGE); + } + } + list = loptend; + + if (list == 0) { + builtin_usage(); + return EX_USAGE; + } + + if (legal_identifier (list->word->word) == 0) { + sh_invalidid (list->word->word); + return EXECUTION_FAILURE; + } + + if ( index_flag ) { + if ( list->next == 0 || list->next->next ) { + builtin_usage(); + return EX_USAGE; + } + if (legal_identifier (list->next->word->word) == 0) { + sh_invalidid (list->next->word->word); + return EXECUTION_FAILURE; + } + var = find_or_make_array_variable(list->word->word, 1); + if (var == 0) + return EXECUTION_FAILURE; + var2 = find_variable(list->next->word->word); + if ( !var2 || ( !array_p(var2) && !assoc_p(var2) ) ) { + builtin_error("%s: Not an array", list->next->word->word); + return EXECUTION_FAILURE; + } + return sort_index(var, var2); + } + + while (list) { + word = list->word->word; + var = find_variable(word); + list = list->next; + + if (var == 0 || array_p(var) == 0) { + builtin_error("%s: Not an array", word); + continue; + } + if (readonly_p(var) || noassign_p(var)) { + if (readonly_p(var)) + err_readonly(word); + continue; + } + + if ( (ret = sort_inplace(var)) != EXECUTION_SUCCESS ) + return ret; + } + return EXECUTION_SUCCESS; + +} + +char *asort_doc[] = { + "Sort arrays in-place.", + "", + "Options:", + " -n compare according to string numerical value", + " -r reverse the result of comparisons", + " -i sort using indices/keys", + "", + "If -i is supplied, SOURCE is not sorted in-place, but the indices (or keys", + "if associative) of SOURCE, after sorting it by its values, are placed as", + "values in the indexed array DEST", + "", + "Associative arrays may not be sorted in-place.", + "", + "Exit status:", + "Return value is zero unless an error happened (like invalid variable name", + "or readonly array).", + (char *)NULL +}; + +struct builtin asort_struct = { + "asort", + asort_builtin, + BUILTIN_ENABLED, + asort_doc, + "asort [-nr] array ... or asort [-nr] -i dest source", + 0 +}; diff --git a/subst.c b/subst.c index 5ffa43ab..34c581fa 100644 --- a/subst.c +++ b/subst.c @@ -5939,6 +5939,9 @@ process_substitute (string, open_for_read_in_child) pid = make_child ((char *)NULL, FORK_ASYNC); if (pid == 0) { + int old_interactive; + + old_interactive = interactive; /* The currently-executing shell is not interactive */ interactive = 0; @@ -5948,7 +5951,7 @@ process_substitute (string, open_for_read_in_child) restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */ QUIT; /* catch any interrupts we got post-fork */ setup_async_signals (); - if (open_for_read_in_child == 0) + if (open_for_read_in_child == 0 && old_interactive && (bash_input.type == st_stdin || bash_input.type == st_stream)) async_redirect_stdin (); subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB|SUBSHELL_ASYNC;