From ea32b611f76a549cb961885e8b3e64467436d215 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 23 Aug 2021 15:54:49 -0400 Subject: [PATCH] new "@k" parameter transformation; minor completion fix for uncommon case --- CWRU/CWRU.chlog | 29 +++++++++++++++++++++++++++++ MANIFEST | 1 + array.c | 21 +++++++++++++++++++++ array.h | 1 + assoc.c | 26 +++++++++++++++++++++++++- assoc.h | 1 + bashline.c | 9 +++++++++ builtins/common.c | 7 +++++++ builtins/common.h | 3 ++- doc/bash.1 | 8 ++++++-- doc/bashref.texi | 3 +++ doc/version.texi | 4 ++-- subst.c | 21 ++++++++++++++++++++- tests/RUN-ONE-TEST | 2 +- tests/assoc.right | 41 +++++++++++++++++++++++++++++++++++++++++ tests/assoc.tests | 3 +++ tests/assoc14.sub | 21 +++++++++++++++++++++ tests/new-exp.right | 10 ++++++++++ tests/new-exp14.sub | 19 ++++++++++++++++++- 19 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 tests/assoc14.sub diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index f46ea062..aee60786 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -1746,4 +1746,33 @@ bashline.c - orig_rl_completer_word_break_characters: now const char * like rl_completer_word_break_characters + 8/20 + ---- +bashline.c + - bash_directory_completion_hook: if direxpand and dirspell are both + set while trying to complete an absolute pathname as a command, don't + take a spell-corrected directory name that is shorter than the + original hint. https://bugzilla.redhat.com/show_bug.cgi?id=1782809 +builtins/common.[ch] + - sh_noassign: convenience function to print an error message when a + user attempts an assignment to a "noassign" variable. Not used yet + +assoc.[ch] + - assoc_to_kvpair_list: new function, returns a WORD_LIST containing + key-value pairs as separate words + +array.[ch] + - array_to_kvpair_list: new function, returns a WORD_LIST containing + index-value pairs as separate words + +subst.c + - string_transform: handle '@k' transform like '@K' + - valid_parameter_transform: 'k' is a valid transform operator + - array_transform: handle '@k' transformation by calling one of + array_to_kvpair_list or assoc_to_kvpair_list and treating the + result as if expanding the array depending on whether the index is + `@' or `*' with the usual meanings + +doc/{bash.1,bashref.texi} + - document new '@k' parameter transformation operator diff --git a/MANIFEST b/MANIFEST index 301c9b69..8288ddc7 100644 --- a/MANIFEST +++ b/MANIFEST @@ -949,6 +949,7 @@ tests/assoc10.sub f tests/assoc11.sub f tests/assoc12.sub f tests/assoc13.sub f +tests/assoc14.sub f tests/attr.tests f tests/attr.right f tests/attr1.sub f diff --git a/array.c b/array.c index 2521ce1a..fe4641b4 100644 --- a/array.c +++ b/array.c @@ -793,6 +793,27 @@ ARRAY *a; return (REVERSE_LIST(list, WORD_LIST *)); } +WORD_LIST * +array_to_kvpair_list(a) +ARRAY *a; +{ + WORD_LIST *list; + ARRAY_ELEMENT *ae; + char *k, *v; + + if (a == 0 || array_empty(a)) + return((WORD_LIST *)NULL); + list = (WORD_LIST *)NULL; + for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) { + k = itos(element_index(ae)); + v = element_value(ae); + list = make_word_list (make_bare_word(k), list); + list = make_word_list (make_bare_word(v), list); + free(k); + } + return (REVERSE_LIST(list, WORD_LIST *)); +} + ARRAY * array_assign_list (array, list) ARRAY *array; diff --git a/array.h b/array.h index 3bc76953..3a4349ae 100644 --- a/array.h +++ b/array.h @@ -80,6 +80,7 @@ extern char *array_reference PARAMS((ARRAY *, arrayind_t)); extern WORD_LIST *array_to_word_list PARAMS((ARRAY *)); extern ARRAY *array_from_word_list PARAMS((WORD_LIST *)); extern WORD_LIST *array_keys_to_word_list PARAMS((ARRAY *)); +extern WORD_LIST *array_to_kvpair_list PARAMS((ARRAY *)); extern ARRAY *array_assign_list PARAMS((ARRAY *, WORD_LIST *)); diff --git a/assoc.c b/assoc.c index 5782e856..cbb5c7e4 100644 --- a/assoc.c +++ b/assoc.c @@ -7,7 +7,7 @@ * chet@ins.cwru.edu */ -/* Copyright (C) 2008,2009,2011-2020 Free Software Foundation, Inc. +/* Copyright (C) 2008,2009,2011-2021 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -544,6 +544,30 @@ assoc_keys_to_word_list (h) return (assoc_to_word_list_internal (h, 1)); } +WORD_LIST * +assoc_to_kvpair_list (h) + HASH_TABLE *h; +{ + WORD_LIST *list; + int i; + BUCKET_CONTENTS *tlist; + char *k, *v; + + if (h == 0 || assoc_empty (h)) + return((WORD_LIST *)NULL); + list = (WORD_LIST *)NULL; + + for (i = 0; i < h->nbuckets; i++) + for (tlist = hash_items (i, h); tlist; tlist = tlist->next) + { + k = (char *)tlist->key; + v = (char *)tlist->data; + list = make_word_list (make_bare_word (k), list); + list = make_word_list (make_bare_word (v), list); + } + return (REVERSE_LIST(list, WORD_LIST *)); +} + char * assoc_to_string (h, sep, quoted) HASH_TABLE *h; diff --git a/assoc.h b/assoc.h index 78ec0738..1bfef16c 100644 --- a/assoc.h +++ b/assoc.h @@ -60,6 +60,7 @@ extern char *assoc_to_assign PARAMS((HASH_TABLE *, int)); extern WORD_LIST *assoc_to_word_list PARAMS((HASH_TABLE *)); extern WORD_LIST *assoc_keys_to_word_list PARAMS((HASH_TABLE *)); +extern WORD_LIST *assoc_to_kvpair_list PARAMS((HASH_TABLE *)); extern char *assoc_to_string PARAMS((HASH_TABLE *, char *, int)); #endif /* _ASSOC_H_ */ diff --git a/bashline.c b/bashline.c index 249033d4..63891d6a 100644 --- a/bashline.c +++ b/bashline.c @@ -3580,7 +3580,16 @@ bash_directory_completion_hook (dirname) subsequent directory checks don't fail. */ if (temp2 == 0 && dircomplete_spelling && dircomplete_expand) { + size_t l1, l2; + temp2 = dirspell (temp1); + l2 = STRLEN (temp2); + /* Don't take matches if they are shorter than the original path */ + if (temp2 && l2 < strlen (temp1) && STREQN (temp1, temp2, l2)) + { + free (temp2); + temp2 = 0; + } if (temp2) { free (temp1); diff --git a/builtins/common.c b/builtins/common.c index 200f9ba6..a115ed9e 100644 --- a/builtins/common.c +++ b/builtins/common.c @@ -266,6 +266,13 @@ sh_readonly (s) builtin_error (_("%s: readonly variable"), s); } +void +sh_noassign (s) + const char *s; +{ + internal_error (_("%s: cannot assign"), s); /* XXX */ +} + void sh_erange (s, desc) char *s, *desc; diff --git a/builtins/common.h b/builtins/common.h index e5bf2e1b..2e69885c 100644 --- a/builtins/common.h +++ b/builtins/common.h @@ -95,10 +95,11 @@ extern void sh_invalidoptname PARAMS((char *)); extern void sh_invalidid PARAMS((char *)); extern void sh_invalidnum PARAMS((char *)); extern void sh_invalidsig PARAMS((char *)); +extern void sh_readonly PARAMS((const char *)); +extern void sh_noassign PARAMS((const char *)); extern void sh_erange PARAMS((char *, char *)); extern void sh_badpid PARAMS((char *)); extern void sh_badjob PARAMS((char *)); -extern void sh_readonly PARAMS((const char *)); extern void sh_nojobs PARAMS((char *)); extern void sh_restricted PARAMS((char *)); extern void sh_notbuiltin PARAMS((char *)); diff --git a/doc/bash.1 b/doc/bash.1 index cc2ce58c..dbc2d4bc 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 10 11:06:20 EDT 2021 +.\" Last Change: Mon Aug 23 10:08:28 EDT 2021 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2021 August 10" "GNU Bash 5.1" +.TH BASH 1 "2021 August 23" "GNU Bash 5.1" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -3461,6 +3461,10 @@ indexed and associative arrays as a sequence of quoted key-value pairs .B a The expansion is a string consisting of flag values representing \fIparameter\fP's attributes. +.TP +.B k +Like the K transformation, but expands the keys and values of +indexed and associative arrays to separate words after word splitting. .PD .PP If diff --git a/doc/bashref.texi b/doc/bashref.texi index c2537ba2..61db0207 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -2478,6 +2478,9 @@ indexed and associative arrays as a sequence of quoted key-value pairs @item a The expansion is a string consisting of flag values representing @var{parameter}'s attributes. +@item k +Like the @samp{K} transformation, but expands the keys and values of +indexed and associative arrays to separate words after word splitting. @end table If @var{parameter} is @samp{@@} or @samp{*}, diff --git a/doc/version.texi b/doc/version.texi index ffe45ab9..ae9d1f7e 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2021 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Tue Aug 10 11:06:03 EDT 2021 +@set LASTCHANGE Mon Aug 23 10:07:59 EDT 2021 @set EDITION 5.1 @set VERSION 5.1 -@set UPDATED 10 August 2021 +@set UPDATED 23 August 2021 @set UPDATED-MONTH August 2021 diff --git a/subst.c b/subst.c index a20855dd..43862cb9 100644 --- a/subst.c +++ b/subst.c @@ -7915,6 +7915,7 @@ string_transform (xc, v, s) ret = string_var_assignment (v, s); break; case 'K': + case 'k': ret = sh_quote_reusable (s, 0); break; /* Transformations that modify the variable's value */ @@ -8008,7 +8009,7 @@ array_transform (xc, var, starsub, quoted) { ARRAY *a; HASH_TABLE *h; - int itype; + int itype, qflags; char *ret; WORD_LIST *list; SHELL_VAR *v; @@ -8035,6 +8036,23 @@ array_transform (xc, var, starsub, quoted) a = (v && array_p (v)) ? array_cell (v) : 0; h = (v && assoc_p (v)) ? assoc_cell (v) : 0; + /* XXX - for now */ + if (xc == 'k') + { + if (v == 0) + return ((char *)NULL); + list = array_p (v) ? array_to_kvpair_list (a) : assoc_to_kvpair_list (h); + qflags = quoted; + /* If we are expanding in a context where word splitting will not be + performed, treat as quoted. This changes how $* will be expanded. */ + if (itype == '*' && expand_no_split_dollar_star && ifs_is_null) + qflags |= Q_DOUBLE_QUOTES; /* Posix interp 888 */ + + ret = string_list_pos_params (itype, list, qflags, 0); + dispose_words (list); + return ret; + } + list = a ? array_to_word_list (a) : (h ? assoc_to_word_list (h) : 0); if (list == 0) return ((char *)NULL); @@ -8058,6 +8076,7 @@ valid_parameter_transform (xform) case 'a': /* expand to a string with just attributes */ case 'A': /* expand as an assignment statement with attributes */ case 'K': /* expand assoc array to list of key/value pairs */ + case 'k': /* XXX - for now */ case 'E': /* expand like $'...' */ case 'P': /* expand like prompt string */ case 'Q': /* quote reusably */ diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 0b063810..c8bef8dd 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/assoc.right b/tests/assoc.right index b8325685..cd730413 100644 --- a/tests/assoc.right +++ b/tests/assoc.right @@ -288,3 +288,44 @@ declare -a ia declare -A a=(["@"]="at2" ) declare -A a=(["@"]=" string" ) declare -A a=(["*"]="star2" ["@"]="at" ) +declare -A assoc=([hello]="world" ["key with spaces"]="value with spaces" [foo]="bar" [one]="1" ) +argv[1] = +argv[2] = +argv[3] = +argv[4] = <1> +argv[1] = +argv[2] = +argv[3] = +argv[4] = +argv[5] = +argv[6] = +argv[7] = +argv[8] = <1> +argv[1] = +argv[1] = +argv[1] = +argv[2] = +argv[3] = +argv[4] = +argv[5] = +argv[6] = <1> +argv[7] = +argv[8] = +argv[1] = <'hello'> +argv[2] = <'world'> +argv[3] = <'key with spaces'> +argv[4] = <'value with spaces'> +argv[5] = <'one'> +argv[6] = <'1'> +argv[7] = <'foo'> +argv[8] = <'bar'> +argv[1] = <'hello'> +argv[2] = <'world'> +argv[3] = <'key with spaces'> +argv[4] = <'value with spaces'> +argv[5] = <'one'> +argv[6] = <'1'> +argv[7] = <'foo'> +argv[8] = <'bar'> +declare -A clone=([hello]="world" ["key with spaces"]="value with spaces" [foo]="bar" [one]="1" ) +declare -A posparams=([hello]="world" ["key with spaces"]="value with spaces" [foo]="bar" [one]="1" ) diff --git a/tests/assoc.tests b/tests/assoc.tests index a99f5209..6158f74d 100644 --- a/tests/assoc.tests +++ b/tests/assoc.tests @@ -250,3 +250,6 @@ ${THIS_SH} ./assoc12.sub # assignment to @ and * ${THIS_SH} ./assoc13.sub + +# tests of the @k transformation on associative arrays +${THIS_SH} ./assoc14.sub diff --git a/tests/assoc14.sub b/tests/assoc14.sub new file mode 100644 index 00000000..854878c0 --- /dev/null +++ b/tests/assoc14.sub @@ -0,0 +1,21 @@ +declare -A assoc=(hello world "key with spaces" "value with spaces" one 1 foo bar) +declare -p assoc + +recho "${assoc[@]}" +recho "${assoc[@]@k}" + +recho "${assoc[*]}" +recho "${assoc[*]@k}" + +set -- hello world "key with spaces" "value with spaces" one 1 foo bar +recho "${@}" +recho "${@@K}" +recho "${@@k}" + +declare -A clone +eval clone=\( "${assoc[@]@K}" \) +declare -p clone + +declare -A posparams +eval posparams=\( "${@@K}" \) +declare -p posparams diff --git a/tests/new-exp.right b/tests/new-exp.right index 752bba5c..8a99fe98 100644 --- a/tests/new-exp.right +++ b/tests/new-exp.right @@ -703,11 +703,21 @@ aaa bbb a bbb aaa bb 'string' +'string' +'value with spaces' 'value with spaces' 'a b' 'c d' 'e f' 'a b' 'c d' 'e f' 0 "zero" 1 "one" 2 "two" 3 "three" 0 "zero z" 1 "one o" 2 "two t" 3 "three t" +argv[1] = <0> +argv[2] = +argv[3] = <1> +argv[4] = +argv[5] = <2> +argv[6] = +argv[7] = <3> +argv[8] = declare -a foo=() ai declare -ai foo diff --git a/tests/new-exp14.sub b/tests/new-exp14.sub index b8bda8de..ce963763 100644 --- a/tests/new-exp14.sub +++ b/tests/new-exp14.sub @@ -1,4 +1,18 @@ -# test the other uses of the 'K' tranform operator +# 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 . +# + +# test the other uses of the 'K' tranform operator and its sibling 'k' # the associative array tests are performed separately, since that was the # original motivation for this feature foo=string @@ -7,7 +21,9 @@ bar='value with spaces' set -- 'a b' 'c d' 'e f' echo ${foo@K} +echo ${foo@k} echo ${bar@K} +echo ${bar@k} echo ${@@K} echo "${@@K}" @@ -17,3 +33,4 @@ echo ${foo[@]@K} foo=( 'zero z' 'one o' 'two t' 'three t' ) echo ${foo[@]@K} +recho "${foo[@]@k}"