From 80df5e50417561d3da31f3de95b8678e6f102217 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Tue, 31 May 2016 11:18:22 -0400 Subject: [PATCH] commit bash-20160527 snapshot --- CWRU/CWRU.chlog | 56 ++++++++++++++++++++++ MANIFEST | 3 ++ arrayfunc.c | 2 +- builtins/declare.def | 87 ++++++++++++++++++++++++++++------ doc/bash.1 | 9 ++-- doc/bashref.texi | 5 +- doc/version.texi | 6 +-- lib/readline/display.c | 62 +++++++++++++------------ tests/nameref.right | 93 ++++++++++++++++++++++++++++++++++++- tests/nameref.tests | 3 ++ tests/nameref15.sub | 52 +++++++++++++++++++++ tests/nameref16.sub | 44 ++++++++++++++++++ tests/nameref17.sub | 103 +++++++++++++++++++++++++++++++++++++++++ variables.c | 23 +++++++-- variables.h | 2 +- 15 files changed, 490 insertions(+), 60 deletions(-) create mode 100644 tests/nameref15.sub create mode 100644 tests/nameref16.sub create mode 100644 tests/nameref17.sub diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 375e087b..7869e0bb 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -11097,3 +11097,59 @@ variables.c things based on whether or not we get it. Right now we don't do anything different, but we could + 5/24 + ---- +lib/readline/display.c + - update_line,_rl_move_cursor_relative: save value of MB_CUR_MAX in a + variable and use it throughout the functions instead of recomputing it + every time (which possibly costs a function call) + + 5/25 + ---- +builtins/declare.def + - declare_internal: only check whether the value being assigned to a + nameref variable is a valid identifier if we're not appending to an + existing value. Suggested by Grisha Levit + - declare_internal: add a couple of more checks on nameref names: + o make sure `declare -n x[3]=y' is an error + o nameref variable self-references now produce a warning when in + function scope + o nameref variable self-references using subscripted arrays + (a=a[0]) now produce warnings at function scope and errors at + global scope + + 5/29 + ---- +builtins/declare.def + - declare_internal: use name of readonly variable when calling sh_readonly + instead of name passed as argument to declare in case we've followed + a nameref chain + - declare_internal: when turning off attributes on a nameref variable + after following the nameref chain and turning them off on the target + variable, don't allow the readonly attribute to be removed. This is + primarily intended to turn off the nameref attribute. Issue pointed + out by Grisha Levit + - declare_internal: if we are attempting to modify an existing global + nameref variable, and the -n option is supplied, operate on the + nameref variable itself, not the variable it references + +doc/{bash.1,bashref.texi} + - change the descriptions of namerefs and declare -n slightly to note + that using the -n option to declare will operate on the nameref + variable itself rather than the variable it references + +variables.c + - bind_variable_internal: if trying to assign to a read-only variable, + print the error message using the variable's name instead of the + name passed to bind_variable in case we followed a nameref + +builtins/declare.def + - declare_internal: when we're creating a (possibly invisible) variable + as part of another operation, assigning NULL, make sure to use + ASS_FORCE to avoid warning messages from any existing nameref + variables we follow + - declare_internal: allow the nameref attribute to be removed from a + readonly nameref variable without a value but do not allow it to be + removed from a readonly nameref variable that has a value, even if it + doesn't reference an existing variable. This distinction is for ksh93 + compatibility. Pointed out by Grisha Levit diff --git a/MANIFEST b/MANIFEST index a636d0c6..8e16b116 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1085,6 +1085,9 @@ tests/nameref11.sub f tests/nameref12.sub f tests/nameref13.sub f tests/nameref14.sub f +tests/nameref15.sub f +tests/nameref16.sub f +tests/nameref17.sub f tests/nameref.right f tests/new-exp.tests f tests/new-exp1.sub f diff --git a/arrayfunc.c b/arrayfunc.c index af43191d..70d50ce2 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -280,7 +280,7 @@ assign_array_element (name, value, flags) { char *sub, *vname; int sublen; - SHELL_VAR *entry; + SHELL_VAR *entry, *nv; vname = array_variable_name (name, &sub, &sublen); diff --git a/builtins/declare.def b/builtins/declare.def index 1f227e52..527f0146 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -325,24 +325,46 @@ declare_internal (list, local_var) that is specific to nameref variables. */ if (flags_on & att_nameref) { -#if defined (ARRAY_VARIABLES) +#if defined (ARRAY_VARS) if (valid_array_reference (name, 0)) { builtin_error (_("%s: reference variable cannot be an array"), name); assign_error++; NEXT_VARIABLE (); } + else if (valid_array_reference (value, 0)) + { + t = array_variable_name (value, (int *)NULL, (int *)NULL); + if (t && STREQ (name, t)) + { + if (variable_context == 0) + { + free (t); + builtin_error (_("%s: nameref variable self references not allowed"), name); + assign_error++; + NEXT_VARIABLE (); + } + else + builtin_warning (_("%s: circular name reference"), name); + } + free (t); + } else #endif - /* disallow self references at global scope */ - if (STREQ (name, value) && variable_context == 0) + /* disallow self references at global scope, warn at function scope */ + if (STREQ (name, value)) { - builtin_error (_("%s: nameref variable self references not allowed"), name); - assign_error++; - NEXT_VARIABLE (); + if (variable_context == 0) + { + builtin_error (_("%s: nameref variable self references not allowed"), name); + assign_error++; + NEXT_VARIABLE (); + } + else + builtin_warning (_("%s: circular name reference"), name); } #if 1 - if (value && *value && valid_nameref_value (value, 0) == 0) + if (value && *value && (aflags & ASS_APPEND) == 0 && valid_nameref_value (value, 0) == 0) { builtin_error (_("`%s': invalid variable name for name reference"), value); assign_error++; @@ -430,6 +452,12 @@ declare_internal (list, local_var) any_failed++; NEXT_VARIABLE (); } + if (var && nameref_p (var) && readonly_p (var) && nameref_cell (var) && (flags_off & att_nameref)) + { + sh_readonly (name); + any_failed++; + NEXT_VARIABLE (); + } } else var = (SHELL_VAR *)NULL; @@ -493,18 +521,33 @@ declare_internal (list, local_var) NEXT_VARIABLE (); } } - else /* declare -[aAirx] name [name...] */ + else /* declare -[aAinrx] name [name...] */ { /* Non-null if we just created or fetched a local variable. */ +#if 0 + /* This is bash-4.3 code. */ /* Here's what ksh93 seems to do. If we are modifying an existing nameref variable, we don't follow the nameref chain past the last nameref, and we set the nameref variable's value so future references to that variable will return the value of the variable we're assigning right now. */ +#else + /* Here's what ksh93 seems to do as of the 2012 version: if we are + using declare -n to modify the value of an existing nameref + variable, don't follow the nameref chain at all and just search + for a nameref at the current context. If we have a nameref, + modify its value (changing which variable it references). */ +#endif if (var == 0 && (flags_on & att_nameref)) { +#if 0 /* See if we are trying to modify an existing nameref variable */ var = mkglobal ? find_global_variable_last_nameref (name, 1) : find_variable_last_nameref (name, 1); +#else + /* See if we are trying to modify an existing nameref variable, + but don't follow the nameref chain. */ + var = mkglobal ? find_global_variable_noref (name) : find_variable_noref (name); +#endif if (var && nameref_p (var) == 0) var = 0; } @@ -519,6 +562,16 @@ declare_internal (list, local_var) refvar = mkglobal ? find_global_variable_last_nameref (name, 0) : find_variable_last_nameref (name, 0); if (refvar && nameref_p (refvar) == 0) refvar = 0; + /* If the nameref is readonly but doesn't have a value, ksh93 + allows the nameref attribute to be removed. If it's readonly + and has a value, even if the value doesn't reference an + existing variable, we disallow the modification */ + if (refvar && nameref_cell (refvar) && readonly_p (refvar)) + { + sh_readonly (name); + any_failed++; + NEXT_VARIABLE (); + } if (refvar) var = mkglobal ? find_global_variable (nameref_cell (refvar)) : find_variable (nameref_cell (refvar)); } @@ -574,7 +627,7 @@ declare_internal (list, local_var) var = mkglobal ? bind_global_variable (name, (char *)NULL, ASS_FORCE) : bind_variable (name, (char *)NULL, ASS_FORCE); else { - var = mkglobal ? bind_global_variable (name, (char *)NULL, 0) : bind_variable (name, (char *)NULL, 0); + var = mkglobal ? bind_global_variable (name, (char *)NULL, ASS_FORCE) : bind_variable (name, (char *)NULL, ASS_FORCE); if (var && no_invisible_vars == 0) VSETATTR (var, att_invisible); } @@ -622,7 +675,7 @@ declare_internal (list, local_var) /* Cannot use declare +r to turn off readonly attribute. */ if (readonly_p (var) && (flags_off & att_readonly)) { - sh_readonly (name); + sh_readonly (name_cell (var)); any_failed++; NEXT_VARIABLE (); } @@ -758,7 +811,8 @@ declare_internal (list, local_var) v = bind_variable_value (var, value, aflags); if (v == 0 && (onref || nameref_p (var))) { - sh_invalidid (value); + if (valid_nameref_value (value, 0) == 0) + sh_invalidid (value); assign_error++; /* XXX - unset this variable? or leave it as normal var? */ delete_var (var->name, mkglobal ? global_variables : shell_variables); @@ -801,9 +855,16 @@ declare_internal (list, local_var) flags_on |= onref; VUNSETATTR (var, offref); flags_off |= offref; - /* Yuck. ksh93 compatibility */ + /* Yuck. ksh93 compatibility. XXX - need to investigate more but + definitely happens when turning off nameref attribute on nameref + (see comments above). Under no circumstances allow this to turn + off readonly attribute on readonly nameref variable. */ if (refvar) - VUNSETATTR (refvar, flags_off); + { + if (flags_off & att_readonly) + flags_off &= ~att_readonly; + VUNSETATTR (refvar, flags_off); + } stupidly_hack_special_variables (name); diff --git a/doc/bash.1 b/doc/bash.1 index a67b1b04..356dfd67 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Wed Apr 27 09:19:58 EDT 2016 +.\" Last Change: Sun May 29 13:48:21 EDT 2016 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2016 April 27" "GNU Bash 4.4" +.TH BASH 1 "2016 May 29" "GNU Bash 4.4" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -1266,7 +1266,8 @@ A variable can be assigned the \fInameref\fP attribute using the to create a \fInameref\fP, or a reference to another variable. This allows variables to be manipulated indirectly. Whenever the nameref variable is referenced, assigned to, unset, or has -its attributes modified (other than the \fInameref\fP attribute itself), the +its attributes modified (other than using or changing the \fInameref\fP +attribute itself), the operation is actually performed on the variable specified by the nameref variable's value. A nameref is commonly used within shell functions to refer to a variable @@ -7723,7 +7724,7 @@ Give each \fIname\fP the \fInameref\fP attribute, making it a name reference to another variable. That other variable is defined by the value of \fIname\fP. All references, assignments, and attribute modifications -to \fIname\fP, except for changing the +to \fIname\fP, except those using or changing the \fB\-n\fP attribute itself, are performed on the variable referenced by \fIname\fP's value. The nameref attribute cannot be applied to array variables. diff --git a/doc/bashref.texi b/doc/bashref.texi index 9e6fa501..e7c60ed5 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -1518,7 +1518,8 @@ A variable can be assigned the @var{nameref} attribute using the to create a @var{nameref}, or a reference to another variable. This allows variables to be manipulated indirectly. Whenever the nameref variable is referenced, assigned to, unset, or has -its attributes modified (other than the nameref attribute itself), the +its attributes modified (other than using or changing the nameref +attribute itself), the operation is actually performed on the variable specified by the nameref variable's value. A nameref is commonly used within shell functions to refer to a variable @@ -4052,7 +4053,7 @@ Give each @var{name} the @var{nameref} attribute, making it a name reference to another variable. That other variable is defined by the value of @var{name}. All references, assignments, and attribute modifications -to @var{name}, except for changing the +to @var{name}, except for those using or changing the @option{-n} attribute itself, are performed on the variable referenced by @var{name}'s value. The nameref attribute cannot be applied to array variables. diff --git a/doc/version.texi b/doc/version.texi index 2b3ce66d..429a155a 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -2,10 +2,10 @@ Copyright (C) 1988-2016 Free Software Foundation, Inc. @end ignore -@set LASTCHANGE Wed Apr 27 09:19:38 EDT 2016 +@set LASTCHANGE Sun May 29 13:48:02 EDT 2016 @set EDITION 4.4 @set VERSION 4.4 -@set UPDATED 27 April 2016 -@set UPDATED-MONTH April 2016 +@set UPDATED 29 May 2016 +@set UPDATED-MONTH May 2016 diff --git a/lib/readline/display.c b/lib/readline/display.c index a409574a..cd9b89f3 100644 --- a/lib/readline/display.c +++ b/lib/readline/display.c @@ -1389,6 +1389,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) int current_invis_chars; int col_lendiff, col_temp; int bytes_to_insert; + int mb_cur_max = MB_CUR_MAX; #if defined (HANDLE_MULTIBYTE) mbstate_t ps_new, ps_old; int new_offset, old_offset; @@ -1399,7 +1400,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) the exact cursor position and cut-and-paste with certain terminal emulators. In this calculation, TEMP is the physical screen position of the cursor. */ - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) temp = _rl_last_c_pos; else temp = _rl_last_c_pos - WRAP_OFFSET (_rl_last_v_pos, visible_wrap_offset); @@ -1407,7 +1408,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) && _rl_last_v_pos == current_line - 1) { #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { wchar_t wc; mbstate_t ps; @@ -1421,7 +1422,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) _rl_clear_to_eol (line_state_visible->wrapped_line[current_line]); memset (&ps, 0, sizeof (mbstate_t)); - ret = mbrtowc (&wc, new, MB_CUR_MAX, &ps); + ret = mbrtowc (&wc, new, mb_cur_max, &ps); if (MB_INVALIDCH (ret)) { tempwidth = 1; @@ -1441,7 +1442,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) _rl_last_c_pos = tempwidth; _rl_last_v_pos++; memset (&ps, 0, sizeof (mbstate_t)); - ret = mbrtowc (&wc, old, MB_CUR_MAX, &ps); + ret = mbrtowc (&wc, old, mb_cur_max, &ps); if (ret != 0 && bytes != 0) { if (MB_INVALIDCH (ret)) @@ -1480,7 +1481,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) /* Find first difference. */ #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { /* See if the old line is a subset of the new line, so that the only change is adding characters. */ @@ -1537,7 +1538,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) return; #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0 && _rl_utf8locale) + if (mb_cur_max > 1 && rl_byte_oriented == 0 && _rl_utf8locale) { wchar_t wc; mbstate_t ps = { 0 }; @@ -1546,7 +1547,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) /* If the first character in the difference is a zero-width character, assume it's a combining character and back one up so the two base characters no longer compare equivalently. */ - t = mbrtowc (&wc, ofd, MB_CUR_MAX, &ps); + t = mbrtowc (&wc, ofd, mb_cur_max, &ps); if (t > 0 && UNICODE_COMBINING_CHAR (wc) && WCWIDTH (wc) == 0) { old_offset = _rl_find_prev_mbchar (old, ofd - old, MB_FIND_ANY); @@ -1560,7 +1561,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) wsatend = 1; /* flag for trailing whitespace */ #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { ols = old + _rl_find_prev_mbchar (old, oe - old, MB_FIND_ANY); nls = new + _rl_find_prev_mbchar (new, ne - new, MB_FIND_ANY); @@ -1618,14 +1619,14 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) { if (*ols) /* don't step past the NUL */ { - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) ols = old + _rl_find_next_mbchar (old, ols - old, 1, MB_FIND_ANY); else ols++; } if (*nls) { - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) nls = new + _rl_find_next_mbchar (new, nls - new, 1, MB_FIND_ANY); else nls++; @@ -1642,7 +1643,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) visible_wrap_offset based on what we know. */ if (current_line == 0) visible_wrap_offset = prompt_invis_chars_first_line; /* XXX */ - if ((MB_CUR_MAX == 1 || rl_byte_oriented) && current_line == 0 && visible_wrap_offset) + if ((mb_cur_max == 1 || rl_byte_oriented) && current_line == 0 && visible_wrap_offset) _rl_last_c_pos += visible_wrap_offset; } @@ -1687,7 +1688,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) if (modmark) _rl_output_some_chars ("*", 1); _rl_output_some_chars (local_prompt, lendiff); - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { /* We take wrap_offset into account here so we can pass correct information to _rl_move_cursor_relative. */ @@ -1723,7 +1724,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) if (temp > 0) { _rl_output_some_chars (nfd, temp); - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) _rl_last_c_pos += _rl_col_width (new, nd, ne - new, 1); else _rl_last_c_pos += temp; @@ -1746,7 +1747,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) /* We need to indicate that the cursor position is correct in the presence of invisible characters in the prompt string. Let's see if setting this when we make sure we're at the end of the drawn prompt string works. */ - if (current_line == 0 && MB_CUR_MAX > 1 && rl_byte_oriented == 0 && + if (current_line == 0 && mb_cur_max > 1 && rl_byte_oriented == 0 && (_rl_last_c_pos > 0 || o_cpos > 0) && _rl_last_c_pos == prompt_physical_chars) cpos_adjusted = 1; @@ -1757,7 +1758,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) col_lendiff == difference on screen (columns) When not using multibyte characters, these are equal */ lendiff = (nls - nfd) - (ols - ofd); - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) col_lendiff = _rl_col_width (new, nfd - new, nls - new, 1) - _rl_col_width (old, ofd - old, ols - old, 1); else col_lendiff = lendiff; @@ -1768,7 +1769,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) if (current_line == 0 && /* !_rl_horizontal_scroll_mode && */ current_invis_chars != visible_wrap_offset) { - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { lendiff += visible_wrap_offset - current_invis_chars; col_lendiff += visible_wrap_offset - current_invis_chars; @@ -1786,7 +1787,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) and writes TEMP bytes. */ /* Insert (diff (len (old), len (new)) ch. */ temp = ne - nfd; - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) col_temp = _rl_col_width (new, nfd - new, ne - new, 1); else col_temp = temp; @@ -1837,7 +1838,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) _rl_last_c_pos == 0 && lendiff > prompt_visible_length && current_invis_chars > 0) == 0) && - (((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && + (((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && wrap_offset && ((nfd - new) <= prompt_last_invisible) && (col_lendiff < prompt_visible_length)) == 0) && @@ -1845,12 +1846,12 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) { open_some_spaces (col_lendiff); _rl_output_some_chars (nfd, bytes_to_insert); - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); else _rl_last_c_pos += bytes_to_insert; } - else if ((MB_CUR_MAX == 1 || rl_byte_oriented != 0) && *ols == 0 && lendiff > 0) + else if ((mb_cur_max == 1 || rl_byte_oriented != 0) && *ols == 0 && lendiff > 0) { /* At the end of a line the characters do not have to be "inserted". They can just be placed on the screen. */ @@ -1865,7 +1866,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) /* If nfd begins before the last invisible character in the prompt, adjust _rl_last_c_pos to account for wrap_offset and set cpos_adjusted to let the caller know. */ - if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) { _rl_last_c_pos -= wrap_offset; cpos_adjusted = 1; @@ -1878,7 +1879,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) /* If nfd begins before the last invisible character in the prompt, adjust _rl_last_c_pos to account for wrap_offset and set cpos_adjusted to let the caller know. */ - if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == 0 && displaying_prompt_first_line && wrap_offset && ((nfd - new) <= prompt_last_invisible)) { _rl_last_c_pos -= wrap_offset; cpos_adjusted = 1; @@ -1898,7 +1899,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) crude attempt to compute how far into the new line buffer we are. It doesn't work well in the face of multibyte characters and needs to be rethought. XXX */ - if ((MB_CUR_MAX > 1 && rl_byte_oriented == 0) && + if ((mb_cur_max > 1 && rl_byte_oriented == 0) && current_line == prompt_last_screen_line && wrap_offset && displaying_prompt_first_line && wrap_offset != prompt_invis_chars_first_line && @@ -1942,7 +1943,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) in a multibyte locale to account for the wrap offset and set cpos_adjusted accordingly. */ _rl_output_some_chars (nfd, bytes_to_insert); - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { _rl_last_c_pos += _rl_col_width (nfd, 0, bytes_to_insert, 1); if (current_line == 0 && wrap_offset && @@ -1977,7 +1978,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) set cpos_adjusted accordingly. */ _rl_output_some_chars (nfd, temp); _rl_last_c_pos += col_temp; /* XXX */ - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { if (current_line == 0 && wrap_offset && displaying_prompt_first_line && @@ -1991,7 +1992,7 @@ update_line (old, new, current_line, omax, nmax, inv_botlin) } clear_rest_of_line: lendiff = (oe - old) - (ne - new); - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) col_lendiff = _rl_col_width (old, 0, oe - old, 1) - _rl_col_width (new, 0, ne - new, 1); else col_lendiff = lendiff; @@ -2001,7 +2002,7 @@ clear_rest_of_line: space_to_eol will insert too many spaces. XXX - maybe we should adjust col_lendiff based on the difference between _rl_last_c_pos and _rl_screenwidth */ - if (col_lendiff && ((MB_CUR_MAX == 1 || rl_byte_oriented) || (_rl_last_c_pos < _rl_screenwidth))) + if (col_lendiff && ((mb_cur_max == 1 || rl_byte_oriented) || (_rl_last_c_pos < _rl_screenwidth))) { if (_rl_term_autowrap && current_line < inv_botlin) space_to_eol (col_lendiff); @@ -2161,6 +2162,7 @@ _rl_move_cursor_relative (new, data) int cpos, dpos; /* current and desired cursor positions */ int adjust; int in_invisline; + int mb_cur_max = MB_CUR_MAX; woff = WRAP_OFFSET (_rl_last_v_pos, wrap_offset); cpos = _rl_last_c_pos; @@ -2174,7 +2176,7 @@ _rl_move_cursor_relative (new, data) this case, NEW's display position is not obvious and must be calculated. We need to account for invisible characters in this line, as long as we are past them and they are counted by _rl_col_width. */ - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { adjust = 1; /* Try to short-circuit common cases and eliminate a bunch of multibyte @@ -2242,7 +2244,7 @@ _rl_move_cursor_relative (new, data) of moving backwards. */ /* i == current physical cursor position. */ #if defined (HANDLE_MULTIBYTE) - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) i = _rl_last_c_pos; else #endif @@ -2277,7 +2279,7 @@ _rl_move_cursor_relative (new, data) in the buffer and we have to go back to the beginning of the screen line. In this case, we can use the terminal sequence to move forward if it's available. */ - if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) + if (mb_cur_max > 1 && rl_byte_oriented == 0) { if (_rl_term_forward_char) { diff --git a/tests/nameref.right b/tests/nameref.right index 5406ae77..c9f7573b 100644 --- a/tests/nameref.right +++ b/tests/nameref.right @@ -27,7 +27,7 @@ changevar: expect argv[1] = expect argv[1] = -./nameref.tests: line 93: bar: readonly variable +./nameref.tests: line 93: foo: readonly variable ./nameref.tests: line 94: foo: readonly variable one one @@ -111,6 +111,7 @@ ref -> two, value: 2 ref -> three, value: 3 final state: ref -> three, value: 3 ./nameref6.sub: line 2: typeset: x: nameref variable self references not allowed +./nameref6.sub: line 5: typeset: x[3]: reference variable cannot be an array ./nameref6.sub: line 12: typeset: x: reference variable cannot be an array the -- 1 42 -- 0 @@ -120,10 +121,19 @@ y -- 0 y -- 0 bar unset +./nameref8.sub: line 3: typeset: warning: v: circular name reference +./nameref8.sub: line 3: warning: v: circular name reference +./nameref8.sub: line 5: warning: v: circular name reference inside inside: two outside: +./nameref8.sub: line 29: typeset: warning: x: circular name reference +./nameref8.sub: line 29: warning: x: circular name reference +./nameref8.sub: line 31: warning: x: circular name reference foo +./nameref8.sub: line 38: typeset: warning: v: circular name reference +./nameref8.sub: line 38: warning: v: circular name reference +./nameref8.sub: line 38: warning: v: circular name reference local ./nameref8.sub: line 47: typeset: v: nameref variable self references not allowed ./nameref8.sub: line 54: warning: x: circular name reference @@ -214,7 +224,7 @@ declare -r RO_PID ./nameref11.sub: line 39: RO: cannot unset: readonly variable declare -r RO="x" ./nameref11.sub: line 39: declare: RO_PID: not found -./nameref11.sub: line 41: ref_PID: readonly variable +./nameref11.sub: line 41: RO2: readonly variable declare -r RO2="a" ./nameref12.sub: line 6: declare: `/': invalid variable name for name reference ./nameref12.sub: line 9: declare: `%': invalid variable name for name reference @@ -280,3 +290,82 @@ after declare -n ref="var" ./nameref14.sub: line 32: typeset: var: not found declare -n ref="var" +./nameref15.sub: line 1: local: warning: a: circular name reference +./nameref15.sub: line 1: `a[0]': not a valid identifier +declare -a a=([0]="0") +./nameref15.sub: line 1: local: warning: a: circular name reference +./nameref15.sub: line 1: warning: a: circular name reference +./nameref15.sub: line 1: warning: a: circular name reference +declare -a a=([0]="X") +declare -a b=([0]="X") +./nameref15.sub: line 1: local: warning: a: circular name reference +./nameref15.sub: line 1: `a[0]': not a valid identifier +declare -a b=([0]="0") +./nameref15.sub: line 19: typeset: warning: ref: circular name reference +./nameref15.sub: line 19: warning: ref: circular name reference +./nameref15.sub: line 20: warning: ref: circular name reference +./nameref15.sub: line 21: warning: ref: circular name reference +inside +outside X +./nameref15.sub: line 29: typeset: ref: nameref variable self references not allowed +./nameref15.sub: line 31: ref: nameref variable self references not allowed +./nameref15.sub: line 32: typeset: ref: not found +declare -- ref="4" +4 +declare -n foo="var[@]" +declare -n ref="var[@]" +./nameref15.sub: line 47: var[@]: bad array subscript +declare -n bar="var[@]" +./nameref15.sub: line 52: var[@]: bad array subscript +declare -n r1="y" +declare -n r2="x" +./nameref16.sub: line 12: typeset: x: not found +./nameref16.sub: line 12: typeset: y: not found +declare -n r1="y" +declare -n r2="x" +./nameref16.sub: line 21: typeset: x: not found +./nameref16.sub: line 21: typeset: y: not found +declare -n r1="y" +declare -n r2="x" +./nameref16.sub: line 33: typeset: x: not found +./nameref16.sub: line 33: typeset: y: not found +declare -n r1="y" +declare -n r2="x" +declare -- x="one" +declare -- y="two" +./nameref17.sub: line 8: declare: bar: not found +./nameref17.sub: line 9: unset: foo0: cannot unset: readonly variable +declare -nr foo0="bar" +declare -nr foo0="bar" +declare -- bar +./nameref17.sub: line 14: declare: foo0: readonly variable +./nameref17.sub: line 15: declare: foo0: readonly variable +declare -nr foo1 +./nameref17.sub: line 24: typeset: foo1: readonly variable +declare -nr foo1 +declare -n foo2="bar" +declare -r bar +./nameref17.sub: line 35: bar: readonly variable +./nameref17.sub: line 36: typeset: bar: readonly variable +declare -n foo2="bar" +declare -r bar +declare -- bar3="three" +./nameref17.sub: line 46: unset: foo3: cannot unset: readonly variable +./nameref17.sub: line 49: declare: bar3: readonly variable +declare -nr foo3="bar3" +declare -r bar3="three" +./nameref17.sub: line 51: declare: foo3: readonly variable +declare -nr foo4="bar4" +declare -- bar4="four" +./nameref17.sub: line 63: typeset: foo4: readonly variable +declare -nr foo4="bar4" +declare -nr foo4="bar4" +declare -- bar4="four" +./nameref17.sub: line 79: typeset: foo4: readonly variable +declare -nr foo4="bar4" +declare -nr foo4="bar4" +declare -- bar4="four" +declare -nr foo5 +declare -r foo5 +declare -nr foo5 +declare -r foo5 diff --git a/tests/nameref.tests b/tests/nameref.tests index 1f8c454e..4114b586 100644 --- a/tests/nameref.tests +++ b/tests/nameref.tests @@ -129,3 +129,6 @@ ${THIS_SH} ./nameref11.sub ${THIS_SH} ./nameref12.sub ${THIS_SH} ./nameref13.sub ${THIS_SH} ./nameref14.sub +${THIS_SH} ./nameref15.sub +${THIS_SH} ./nameref16.sub +${THIS_SH} ./nameref17.sub diff --git a/tests/nameref15.sub b/tests/nameref15.sub new file mode 100644 index 00000000..e493bf67 --- /dev/null +++ b/tests/nameref15.sub @@ -0,0 +1,52 @@ +f() { local -n a=$1; a=X; } + +a=(0); f 'a[0]' +while [[ -v a ]]; do declare -p a; unset a; done + +a=(0); f 'a' +while [[ -v a ]]; do declare -p a; unset a; done + +b=(0); f 'b[0]' +while [[ -v a ]]; do typeset -p a; unset a; done +typeset -p b + +b=(0); f 'a[0]' +while [[ -v a ]]; do typeset -p a; unset a; done +typeset -p b + +add_X_echo() +{ + typeset -n ref=$1 + ref+=X + echo inside $ref +} + +ref= +add_X_echo ref +echo outside "$ref" +unset ref + +typeset -n ref=ref + +typeset -n ref=re ref+=f +typeset -p ref +ref=4 +typeset -p ref + +export ref +printenv ref + +unset ref ; unset -n ref +unset foo; unset -n foo + +typeset -n foo=var[@] +typeset -p foo +typeset -n ref=var ref+=[@] +typeset -p ref + +ref=42 + +typeset -n bar +bar=var[@] +typeset -p bar +bar=7 diff --git a/tests/nameref16.sub b/tests/nameref16.sub new file mode 100644 index 00000000..75c7ba11 --- /dev/null +++ b/tests/nameref16.sub @@ -0,0 +1,44 @@ +# post-bash-4.3 changes for more ksh93 compatibility when following nameref +# chains and using typeset/declare -n to operate on nameref variables rather +# than the variables they reference + +# don't follow nameref chain when using declare -n and at the global scope +unset -n r1 r2 + +typeset -n r1=r2 +typeset -n r2=x +typeset -n r1=y + +typeset -p r1 r2 x y + +# same behavior when in a shell function +foo() +{ + typeset -n r1=r2 + typeset -n r2=x + typeset -n r1=y + + typeset -p r1 r2 x y +} +unset -n r1 r2 +foo +unset -f foo + +# same behavior when namerefs aren't chained +unset -n r1 r2 + +typeset -n r1=z +typeset -n r2=x +typeset -n r1=y +typeset -p r1 r2 x y + +# same behavior when referenced variables have values +unset -n r1 r2 + +x=one +y=two +typeset -n r1=r2 +typeset -n r2=x +typeset -n r1=y + +typeset -p r1 r2 x y diff --git a/tests/nameref17.sub b/tests/nameref17.sub new file mode 100644 index 00000000..0cd5a42d --- /dev/null +++ b/tests/nameref17.sub @@ -0,0 +1,103 @@ +# test behavior of readonly namerefs and namerefs referencing readonly variables + +# readonly nameref variable referencing read-write global variable + +bar=one +declare -rn foo0=bar +unset foo0 # unsets bar +declare -p bar +unset -n foo0 # cannot unset +declare -p foo0 + +declare +r foo0 # modifies bar +declare -p foo0 bar +declare +r -n foo0 # error +declare +n foo0 # error +unset bar + +# readonly nameref variable without a value +typeset -n foo1 +typeset -r foo1 + +typeset -p foo1 + +typeset foo1=bar # error +typeset +r foo1 # no-op, follows nameref chain to nothing +typeset -p foo1 + +# nameref pointing to read-only global variable +foo2=bar +typeset -n foo2 +typeset -r foo2 # changes bar + +typeset -p foo2 bar + +foo2=bar # error? +typeset +r foo2 # attempts to change bar, error +typeset -p foo2 bar # nameref unchanged + +# read-only nameref pointing to read-only global variable +bar3=three +declare -rn foo3=bar3 +unset foo3 # unsets bar3 + +bar3=three +declare -p bar3 +unset -n foo3 # cannot unset + +readonly bar3 +declare +r foo3 # error attempting to reference bar3 +declare -p foo3 bar3 +declare +r -n foo3 # error + +# readonly nameref pointing to read-write local -- can we remove nameref attr? +func() +{ + typeset bar4=four + + # readonly nameref + typeset -n -r foo4=bar4 + + typeset -p foo4 bar4 + + typeset +n foo4 + + typeset -p foo4 +} +func +unset -f func + +# readonly nameref pointing to read-write global -- can we remove nameref attr? +bar4=four +foo4=bar4 +# readonly nameref +typeset -n foo4 +typeset -r -n foo4 + +typeset -p foo4 bar4 + +typeset +n foo4 +typeset -p foo4 + +bar4=four +: ${foo4=bar4} + +typeset -p foo4 bar4 + +# readonly local nameref without a value -- can we remove nameref attribute? +func() +{ + declare -r -n foo5 + declare -p foo5 + declare +n foo5 + declare -p foo5 +} +func +unset -f func + +# readonly global nameref without a value -- can we remove nameref attribute? +declare -n foo5 +declare -r -n foo5 +declare -p foo5 +declare +n foo5 +declare -p foo5 diff --git a/variables.c b/variables.c index 256ddb3c..718b449f 100644 --- a/variables.c +++ b/variables.c @@ -283,6 +283,8 @@ static int variable_in_context __P((SHELL_VAR *)); static int visible_array_vars __P((SHELL_VAR *)); #endif +static SHELL_VAR *find_variable_internal __P((const char *, int)); + static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *)); static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); @@ -2767,7 +2769,7 @@ assign_value: if ((readonly_p (entry) && (aflags & ASS_FORCE) == 0) || noassign_p (entry)) { if (readonly_p (entry)) - err_readonly (name); + err_readonly (name_cell (entry)); return (entry); } @@ -2875,7 +2877,7 @@ bind_variable (name, value, flags) #if 0 return (bind_variable_value (v, value, flags|ASS_NAMEREF)); #else - return (bind_variable_internal (v->name, value, nvc->table, 0, flags)); + v = 0; /* backwards compat */ #endif } else @@ -2887,7 +2889,7 @@ bind_variable (name, value, flags) #if 0 return (bind_variable_value (v, value, flags|ASS_NAMEREF)); #else - return (bind_variable_internal (v->name, value, nvc->table, 0, flags)); + v = 0; /* backwards compat */ #endif } else @@ -2947,7 +2949,20 @@ bind_variable_value (var, value, aflags) else { t = make_variable_value (var, value, aflags); - if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || valid_nameref_value (t, 0) == 0)) + if (STREQ (name_cell (var), t)) + { + if (variable_context) + internal_warning (_("%s: circular name reference"), name_cell (var)); + else + { + internal_error (_("%s: nameref variable self references not allowed"), name_cell (var)); + free (t); + if (invis) + VSETATTR (var, att_invisible); /* XXX */ + return ((SHELL_VAR *)NULL); + } + } + if ((aflags & ASS_NAMEREF) && (valid_nameref_value (t, 0) == 0)) { free (t); if (invis) diff --git a/variables.h b/variables.h index 5a7e44ea..25c9ae07 100644 --- a/variables.h +++ b/variables.h @@ -255,7 +255,7 @@ extern SHELL_VAR *find_global_variable_last_nameref __P((const char *, int)); extern SHELL_VAR *find_variable_nameref __P((SHELL_VAR *)); extern SHELL_VAR *find_variable_nameref_for_create __P((const char *, int)); extern SHELL_VAR *find_variable_nameref_for_assignment __P((const char *, int)); -extern SHELL_VAR *find_variable_internal __P((const char *, int)); +/*extern SHELL_VAR *find_variable_internal __P((const char *, int));*/ extern SHELL_VAR *find_variable_tempenv __P((const char *)); extern SHELL_VAR *find_variable_notempenv __P((const char *)); extern SHELL_VAR *find_global_variable __P((const char *));