From: Chet Ramey Date: Mon, 6 Feb 2023 15:02:16 +0000 (-0500) Subject: printf changes: %q/%Q altform, %ls/%lc wide character strings; posix mode changes... X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=315095ad7f518761020bc956376ab0e006e6b92d;p=thirdparty%2Fbash.git printf changes: %q/%Q altform, %ls/%lc wide character strings; posix mode changes for function execution; allow <( and >) in function names --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 78caa9611..88185633d 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -5135,7 +5135,7 @@ subst.c lib/readline/histfile.c - history_write_slow: a fallback function that uses stdio to write the history list to a supplied file descriptor - - history_do_write: call history_write_slow if ftrucate/mmap/malloc + - history_do_write: call history_write_slow if ftruncate/mmap/malloc fail; hope the simpler approach works 1/27 @@ -5183,3 +5183,71 @@ parse.y subst.c - use locale_mb_cur_max instead of MB_CUR_MAX consistently + + 2/1 + --- +builtins/printf.def + - printf_builtin: %q and %Q use the `alternate form' flag to force + single quoting instead of backslash quoting. $'...' is still + preferred if there are characters that require it + - getwidestr, getwidechar: functions to take a possibly multibyte + character string and convert to a wide character string or a wide + character, respectively + - convwidestr, convwidechar: functions to take a wide character string + or single character, apply any precision, convert back to a multibyte + character string, and return the result for printing + - printf_builtin: consistently print an error message if printstr() + returns < 0, since it will only do that if ferror(stdout) is true; + let the PRETURN macro do that if appropriate, so we don't duplicate + code + + 2/2 + --- +builtins/printf.def + - printwidestr: wide-character version of printstr; understands + fieldwidth and precision as characters instead of bytes + - printf_builtin: interpret %ls and %lc as referring to wide strings + and characters, respectively; use printwidestr to print the wide- + character string obtained from the (presumably multibyte) argument. + Fieldwidth and precision are characters, not bytes + + 2/3 + --- +builtins/set.def + - unset_builtin: since tokenize_array_reference modifies its NAME + argument, we need to restore the original word if there is no + array variable found with that name, so we can do ahead and try to + unset a function with that wonky name. Inspired by a report from + Emanuele Torre + +parse.y + - read_token_word: don't set the W_HASDOLLAR flag in the returned WORD + for <(command) and >(command). That way they can be used in function + names. + +execute_cmd.c + - execute_simple_command: if in posix mode, don't perform function + lookup for command names that contain a slash + +builtins/enable.def + - dyn_load_builtins: don't allow dynamically loaded builtins to have + slashes in their name + + 2/4 + --- +general.c + - valid_function_word: new function, used to check whether a word can + be used as a function name. Replaces call to check_identifier(); + incorporates POSIX interp 383 check for special builtin so we can + move it out of execute_intern_function(). + - valid_function_name: use the FLAGS argument instead of POSIXLY_CORRECT; + only reject reserved words if FLAGS&1 + +execute_cmd.c + - execute_intern_function: call valid_function_word() instead of + check_identifier(); remove check for special builtin that + valid_function_word performs + +print_cmd.c + - print_function_def,named_function_string: pass POSIXLY_CORRECT to + valid_function_name as the FLAGS argument diff --git a/MANIFEST b/MANIFEST index 534429fc6..e5bbafb17 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1216,6 +1216,7 @@ tests/intl.tests f tests/intl1.sub f tests/intl2.sub f tests/intl3.sub f +tests/intl4.sub f tests/intl.right f tests/iquote.tests f tests/iquote.right f diff --git a/aclocal.m4 b/aclocal.m4 index cc97bd4b8..c5e8ab155 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1770,6 +1770,7 @@ AC_CHECK_FUNC(wcscoll, AC_DEFINE(HAVE_WCSCOLL)) AC_CHECK_FUNC(wcsdup, AC_DEFINE(HAVE_WCSDUP)) AC_CHECK_FUNC(wcwidth, AC_DEFINE(HAVE_WCWIDTH)) AC_CHECK_FUNC(wctype, AC_DEFINE(HAVE_WCTYPE)) +AC_CHECK_FUNC(wcsnrtombs, AC_DEFINE(HAVE_WCSNRTOMBS)) AC_REPLACE_FUNCS(wcswidth) diff --git a/builtins/enable.def b/builtins/enable.def index c1c5e673b..b6d6a8fe2 100644 --- a/builtins/enable.def +++ b/builtins/enable.def @@ -384,6 +384,11 @@ dyn_load_builtin (WORD_LIST *list, int flags, char *filename) for (replaced = 0, new = 0; list; list = list->next) { name = list->word->word; + if (absolute_program (name)) + { + builtin_error (_("%s: builtin names may not contain slashes"), name); + continue; + } size = strlen (name); struct_name = (char *)xmalloc (size + 8); diff --git a/builtins/printf.def b/builtins/printf.def index 5ec874ae2..6a4dac075 100644 --- a/builtins/printf.def +++ b/builtins/printf.def @@ -1,7 +1,7 @@ This file is printf.def, from which is created printf.c. It implements the builtin "printf" in Bash. -Copyright (C) 1997-2022 Free Software Foundation, Inc. +Copyright (C) 1997-2023 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -232,6 +232,9 @@ static intmax_t asciicode (void); #if defined (HANDLE_MULTIBYTE) static wchar_t *getwidestr (size_t *); static wint_t getwidechar (void); +static char *convwidestr (wchar_t *, int); +static char *convwidechar (wint_t, int); +static int printwidestr (char *, wchar_t *, size_t, int, int); #endif static WORD_LIST *garglist, *orig_arglist; @@ -250,11 +253,22 @@ static intmax_t tw; static char *conv_buf; static size_t conv_bufsize; +static inline int +decodeprec (char *ps) +{ + int mpr; + + mpr = *ps++ - '0'; + while (DIGIT (*ps)) + mpr = (mpr * 10) + (*ps++ - '0'); + return mpr; +} + int printf_builtin (WORD_LIST *list) { int ch, fieldwidth, precision; - int have_fieldwidth, have_precision, use_Lmod, altform; + int have_fieldwidth, have_precision, use_Lmod, altform, longform; char convch, thisch, nextch, *format, *modstart, *precstart, *fmt, *start; #if defined (HANDLE_MULTIBYTE) char mbch[25]; /* 25 > MB_LEN_MAX, plus can handle 4-byte UTF-8 and large Unicode characters*/ @@ -345,7 +359,7 @@ printf_builtin (WORD_LIST *list) for (fmt = format; *fmt; fmt++) { precision = fieldwidth = 0; - have_fieldwidth = have_precision = altform = 0; + have_fieldwidth = have_precision = altform = longform = 0; precstart = 0; if (*fmt == '\\') @@ -435,6 +449,7 @@ printf_builtin (WORD_LIST *list) while (*fmt && strchr (LENMODS, *fmt)) { use_Lmod |= USE_LONG_DOUBLE && *fmt == 'L'; + longform |= *fmt == 'l'; fmt++; } @@ -457,6 +472,22 @@ printf_builtin (WORD_LIST *list) { char p; +#if defined (HANDLE_MULTIBYTE) + if (longform) + { + wchar_t wc, ws[2]; + int r; + + wc = getwidechar (); + ws[0] = wc; + ws[1] = L'\0'; + + r = printwidestr (start, ws, 1, fieldwidth, precision); + if (r < 0) + PRETURN (EXECUTION_FAILURE); + break; + } +#endif p = getchr (); PF(start, p); break; @@ -465,7 +496,21 @@ printf_builtin (WORD_LIST *list) case 's': { char *p; +#if defined (HANDLE_MULTIBYTE) + if (longform) + { + wchar_t *wp; + size_t slen; + int r; + wp = getwidestr (&slen); + r = printwidestr (start, wp, slen, fieldwidth, precision); + free (wp); + if (r < 0) + PRETURN (EXECUTION_FAILURE); + break; + } +#endif p = getstr (); PF(start, p); break; @@ -537,14 +582,7 @@ printf_builtin (WORD_LIST *list) modstart[1] = '\0'; r = printstr (start, timebuf, strlen (timebuf), fieldwidth, precision); /* XXX - %s for now */ if (r < 0) - { - if (ferror (stdout) == 0) - { - sh_wrerror (); - clearerr (stdout); - } - PRETURN (EXECUTION_FAILURE); - } + PRETURN (EXECUTION_FAILURE); break; } @@ -581,14 +619,7 @@ printf_builtin (WORD_LIST *list) in XP -- printf does not handle that well. */ r = printstr (start, xp, rlen, fieldwidth, precision); if (r < 0) - { - if (ferror (stdout) == 0) - { - sh_wrerror (); - clearerr (stdout); - } - retval = EXECUTION_FAILURE; - } + retval = EXECUTION_FAILURE; free (xp); } @@ -609,9 +640,7 @@ printf_builtin (WORD_LIST *list) /* Decode precision and apply it to the unquoted string. */ if (convch == 'Q' && precstart) { - mpr = *precstart++ - '0'; - while (DIGIT (*precstart)) - mpr = (mpr * 10) + (*precstart++ - '0'); + mpr = decodeprec (precstart); /* Error if precision > INT_MAX here? */ precision = (mpr < 0 || mpr > INT_MAX) ? INT_MAX : mpr; slen = strlen (p); @@ -623,23 +652,21 @@ printf_builtin (WORD_LIST *list) xp = savestring ("''"); else if (ansic_shouldquote (p)) xp = ansic_quote (p, 0, (int *)0); + else if (altform) + xp = sh_single_quote (p); else xp = sh_backslash_quote (p, 0, 3); if (xp) { + slen = strlen (xp); if (convch == 'Q') { - slen = strlen (xp); if (slen > precision) precision = slen; } /* Use printstr to get fieldwidth and precision right. */ - r = printstr (start, xp, strlen (xp), fieldwidth, precision); - if (r < 0) - { - sh_wrerror (); - clearerr (stdout); - } + r = printstr (start, xp, slen, fieldwidth, precision); + /* Let PRETURN print the error message. */ free (xp); } @@ -765,7 +792,10 @@ printf_erange (char *s) STRING: expanded string argument LEN: length of expanded string FIELDWIDTH: argument for width of `*' - PRECISION: argument for precision of `*' */ + PRECISION: argument for precision of `*' + + Returns -1 on detectable write error, 0 otherwise. */ + static int printstr (char *fmt, char *string, int len, int fieldwidth, int precision) { @@ -873,6 +903,114 @@ printstr (char *fmt, char *string, int len, int fieldwidth, int precision) return (ferror (stdout) ? -1 : 0); } + +#if defined (HANDLE_MULTIBYTE) +/* A wide-character version of printstr */ +static int +printwidestr (char *fmt, wchar_t *wstring, size_t len, int fieldwidth, int precision) +{ +#if 0 + char *s; +#endif + char *string; + int padlen, nc, ljust, i; + int fw, pr; /* fieldwidth and precision */ + intmax_t mfw, mpr; + + if (wstring == 0) + wstring = L""; + +#if 0 + s = fmt; +#endif + if (*fmt == '%') + fmt++; + + ljust = fw = 0; + pr = -1; + mfw = 0; + mpr = -1; + + /* skip flags */ + while (strchr (SKIP1, *fmt)) + { + if (*fmt == '-') + ljust = 1; + fmt++; + } + + /* get fieldwidth, if present. rely on caller to clamp fieldwidth at INT_MAX */ + if (*fmt == '*') + { + fmt++; + fw = fieldwidth; + if (fw < 0) + { + fw = -fw; + ljust = 1; + } + } + else if (DIGIT (*fmt)) + { + mfw = *fmt++ - '0'; + while (DIGIT (*fmt)) + mfw = (mfw * 10) + (*fmt++ - '0'); + /* Error if fieldwidth > INT_MAX here? */ + fw = (mfw < 0 || mfw > INT_MAX) ? INT_MAX : mfw; + } + + /* get precision, if present. doesn't handle negative precisions */ + if (*fmt == '.') + { + fmt++; + if (*fmt == '*') + { + fmt++; + pr = precision; + } + else if (DIGIT (*fmt)) + { + mpr = *fmt++ - '0'; + while (DIGIT (*fmt)) + mpr = (mpr * 10) + (*fmt++ - '0'); + /* Error if precision > INT_MAX here? */ + pr = (mpr < 0 || mpr > INT_MAX) ? INT_MAX : mpr; + if (pr < precision && precision < INT_MAX) + pr = precision; /* XXX */ + } + else + pr = 0; /* "a null digit string is treated as zero" */ + } + + /* chars from wide string to print */ + nc = (pr >= 0 && pr <= len) ? pr : len; + + padlen = fw - nc; + if (padlen < 0) + padlen = 0; + if (ljust) + padlen = -padlen; + + /* leading pad characters */ + for (; padlen > 0; padlen--) + PC (' '); + + /* convert WSTRING to multibyte character STRING, honoring PRECISION */ + string = convwidestr (wstring, pr); + + /* output STRING, assuming that convwidestr has taken care of the precision + and returned only the necessary bytes. */ + for (i = 0; string[i]; i++) + PC (string[i]); + + /* output any necessary trailing padding */ + for (; padlen < 0; padlen++) + PC (' '); + + free (string); + return (ferror (stdout) ? -1 : 0); +} +#endif /* Convert STRING by expanding the escape sequences specified by the POSIX standard for printf's `%b' format string. If SAWC is non-null, @@ -1387,4 +1525,69 @@ getwidechar (void) garglist = garglist->next; return (wc); } + +/* The basic approach is to take the wide character string, apply any + precision in terms of characters (otherwise the precision is useless), + compute the size of the output buffer, call wcsrtombs to convert back + to multibyte characters, and return the result. */ +static char * +convwidestr (wchar_t *ws, int prec) +{ + const wchar_t *ts; + wchar_t wc; + char *ret; + size_t rlen, rsize; + DECLARE_MBSTATE; + + ts = (const wchar_t *)ws; + + if (prec > 0) + { + rsize = prec * MB_CUR_MAX; + ret = (char *)xmalloc (rsize + 1); +#if defined (HAVE_WCSNRTOMBS) + rlen = wcsnrtombs (ret, &ts, prec, rsize, &state); +#else + wc = ws[prec]; + ws[prec] = L'\0'; + rlen = wcsrtombs (ret, &ts, rsize, &state); + ws[prec] = wc; +#endif + } + else + { + rlen = wcsrtombs (NULL, &ts, 0, &state); + if (rlen != (size_t)-1) + { + memset (&state, '\0', sizeof (mbstate_t)); + ret = (char *)xmalloc (rlen + 1); + rlen = wcsrtombs (ret, &ts, rlen, &state); + } + else + ret = (char *)xmalloc (1); + } + if (MB_INVALIDCH (rlen)) + rlen = 0; + + ret[rlen] = '\0'; + return ret; +} + +static char * +convwidechar (wint_t wi, int prec) +{ + wchar_t wc; + char *ret; + size_t rlen; + DECLARE_MBSTATE; + + wc = (wchar_t)wi; + ret = (char *)xmalloc (MB_LEN_MAX + 1); + rlen = wcrtomb (ret, wc, &state); + if (MB_INVALIDCH (rlen)) + rlen = 0; + + ret[rlen] = '\0'; + return ret; +} #endif diff --git a/builtins/set.def b/builtins/set.def index b3e898c96..a6f5c2539 100644 --- a/builtins/set.def +++ b/builtins/set.def @@ -812,6 +812,16 @@ $END #define NEXT_VARIABLE() any_failed++; list = list->next; continue; +#define RESTORE_NAME() \ +do { \ + tname = name + strlen (name); \ + if (tname == t - 1) /* probably a paranoid check */ \ + { \ + tname[0] = '['; \ + t[strlen (t)] = ']'; \ + } \ +} while (0) + int unset_builtin (WORD_LIST *list) { @@ -874,14 +884,13 @@ unset_builtin (WORD_LIST *list) #if defined (ARRAY_VARS) vflags = builtin_arrayref_flags (list->word, base_vflags); -#endif - -#if defined (ARRAY_VARS) unset_array = 0; /* XXX valid array reference second arg was 0 */ - if (!unset_function && nameref == 0 && tokenize_array_reference (name, vflags, &t)) + /* XXX tokenize_array_reference modifies NAME if it succeeds */ + if (unset_function == 0 && nameref == 0 && tokenize_array_reference (name, vflags, &t)) unset_array = 1; #endif + /* Get error checking out of the way first. The low-level functions just perform the unset, relying on the caller to verify. */ valid_id = legal_identifier (name); @@ -891,6 +900,10 @@ unset_builtin (WORD_LIST *list) treat them as potential shell function names. */ if (global_unset_func == 0 && global_unset_var == 0 && valid_id == 0) { +#if defined (ARRAY_VARS) + if (unset_array) + RESTORE_NAME (); +#endif unset_variable = unset_array = 0; unset_function = 1; } @@ -926,8 +939,20 @@ unset_builtin (WORD_LIST *list) find a function after unsuccessfully searching for a variable, note that we're acting on a function now as if -f were supplied. The readonly check below takes care of it. */ - if (var == 0 && nameref == 0 && unset_variable == 0 && unset_function == 0) + if (var == 0 && nameref == 0 && unset_variable == 0 && unset_function == 0) { +#if defined (ARRAY_VARS) + /* We modified NAME in the call to tokenize_array_reference, so we + need to restore it here. We turned the original `[' and `]' into + NULL, to isolate the array name and subscript. This only happens + if tokenize_array_reference succeeds with a non-NULL subscript + pointer, and UNSET_ARRAY is set to 1 only in this case. */ + if (unset_array) + { + RESTORE_NAME(); + unset_array = 0; + } +#endif if (var = find_function (name)) unset_function = 1; } diff --git a/config.h.in b/config.h.in index d6d52930b..5a221ba22 100644 --- a/config.h.in +++ b/config.h.in @@ -948,6 +948,9 @@ /* Define if you have the wcsdup function. */ #undef HAVE_WCSDUP +/* Define if you have the wcsnrtombs function. */ +#undef HAVE_WCSNRTOMBS + /* Define if you have the wctype function. */ #undef HAVE_WCTYPE diff --git a/configure b/configure index 4fa233576..03008d039 100755 --- a/configure +++ b/configure @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac for Bash 5.2, version 5.047. +# From configure.ac for Bash 5.2, version 5.048. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.71 for bash 5.2-maint. # @@ -16365,6 +16365,13 @@ then : fi +ac_fn_c_check_func "$LINENO" "wcsnrtombs" "ac_cv_func_wcsnrtombs" +if test "x$ac_cv_func_wcsnrtombs" = xyes +then : + printf "%s\n" "#define HAVE_WCSNRTOMBS 1" >>confdefs.h + +fi + ac_fn_c_check_func "$LINENO" "wcswidth" "ac_cv_func_wcswidth" if test "x$ac_cv_func_wcswidth" = xyes diff --git a/configure.ac b/configure.ac index d0f705414..da2bd9449 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ dnl report bugs to chet@po.cwru.edu dnl dnl Process this file with autoconf to produce a configure script. -# Copyright (C) 1987-2022 Free Software Foundation, Inc. +# Copyright (C) 1987-2023 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify @@ -21,7 +21,7 @@ dnl Process this file with autoconf to produce a configure script. # You should have received a copy of the GNU General Public License # along with this program. If not, see . -AC_REVISION([for Bash 5.2, version 5.047])dnl +AC_REVISION([for Bash 5.2, version 5.048])dnl define(bashvers, 5.2) define(relstatus, maint) diff --git a/doc/bashref.texi b/doc/bashref.texi index 3968342e9..2eff91ec2 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -8218,6 +8218,11 @@ causes a fatal syntax error in non-interactive shells. Function names may not be the same as one of the @sc{posix} special builtins. +@item +Even if a shell function whose name contains a slash was defined before +entering @sc{posix} mode, the shell will not execute a function whose name +contains one or more slashes. + @item @sc{posix} special builtins are found before shell functions during command lookup. diff --git a/execute_cmd.c b/execute_cmd.c index 947eb098b..d4c082d04 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -4480,7 +4480,7 @@ execute_simple_command (SIMPLE_COM *simple_command, int pipe_in, int pipe_out, i builtin_is_special = 1; } if (builtin == 0) - func = find_function (words->word->word); + func = (posixly_correct == 0 || absolute_program (words->word->word) == 0) ? find_function (words->word->word) : 0; } /* What happens in posix mode when an assignment preceding a command name @@ -6022,12 +6022,12 @@ execute_intern_function (WORD_DESC *name, FUNCTION_DEF *funcdef) SHELL_VAR *var; char *t; - if (check_identifier (name, posixly_correct) == 0) + if (valid_function_word (name, posixly_correct) == 0) { - if (posixly_correct && interactive_shell == 0) + if (posixly_correct) { last_command_exit_value = EX_BADUSAGE; - jump_to_top_level (ERREXIT); + jump_to_top_level (interactive_shell ? DISCARD : ERREXIT); } return (EXECUTION_FAILURE); } @@ -6039,14 +6039,6 @@ execute_intern_function (WORD_DESC *name, FUNCTION_DEF *funcdef) name->word = t; } - /* Posix interpretation 383 */ - if (posixly_correct && find_special_builtin (name->word)) - { - internal_error (_("`%s': is a special builtin"), name->word); - last_command_exit_value = EX_BADUSAGE; - jump_to_top_level (interactive_shell ? DISCARD : ERREXIT); - } - var = find_function (name->word); if (var && (readonly_p (var) || noassign_p (var))) { diff --git a/general.c b/general.c index e8bf5f3a0..da59f9c0f 100644 --- a/general.c +++ b/general.c @@ -404,20 +404,55 @@ legal_alias_name (const char *string, int flags) } /* Return 1 if this is a valid identifer that can be used to declare a function - without the `function' reserved word. FLAGS is currently unused; a - placeholder for the future. */ + without the `function' reserved word. FLAGS&1 means to reject POSIX + non-identifiers and reserved words; FLAGS&2 means to suppress the check + for ASSIGNMENT_WORD. So you can pass posixly_correct as FLAGS and get + POSIX checks. */ int valid_function_name (const char *name, int flags) { - if (find_reserved_word (name) >= 0) + if ((flags & 1) && find_reserved_word (name) >= 0) return 0; - if (posixly_correct && (all_digits (name) || legal_identifier (name) == 0)) + if ((flags & 1) && (all_digits (name) || legal_identifier (name) == 0)) return 0; - if (assignment (name, 0)) /* difference between WORD and ASSIGNMENT_WORD */ + /* pass flags & 2 to suppress this check */ + if ((flags & 2) == 0 && assignment (name, 0)) /* difference between WORD and ASSIGNMENT_WORD */ return 0; return 1; } +/* Return 1 if this is an identifier that can be used as a function name + when declaring a function. We don't allow `$' for historical reasons. + We allow quotes (for now), slashes, and pretty much everything else. + If FLAGS is non-zero (it's usually posixly_correct), we check the name + for additional posix restrictions using valid_function_name(). We pass + flags|2 to valid_function_name to suppress the check for an assignment + word, since we want to allow those here. */ +int +valid_function_word (WORD_DESC *word, int flags) +{ + char *name; + + name = word->word; + if ((word->flags & W_HASDOLLAR)) /* allow quotes for now */ + { + internal_error (_("`%s': not a valid identifier"), name); + return (0); + } + /* POSIX interpretation 383 */ + if (flags && find_special_builtin (name)) + { + internal_error (_("`%s': is a special builtin"), name); + return (0); + } + if (flags && valid_function_name (name, flags|2) == 0) + { + internal_error (_("`%s': not a valid identifier"), name); + return (0); + } + return 1; +} + /* Returns non-zero if STRING is an assignment statement. The returned value is the index of the `=' sign. If FLAGS&1 we are expecting a compound assignment and require an array subscript before the `=' to denote an assignment diff --git a/general.h b/general.h index 87ff7a400..fa57251a5 100644 --- a/general.h +++ b/general.h @@ -322,6 +322,7 @@ extern int valid_nameref_value (const char *, int); extern int check_selfref (const char *, char *, int); extern int legal_alias_name (const char *, int); extern int valid_function_name (const char *, int); +extern int valid_function_word (WORD_DESC *, int); extern int line_isblank (const char *); extern int assignment (const char *, int); diff --git a/parse.y b/parse.y index 178b2181d..f5199f151 100644 --- a/parse.y +++ b/parse.y @@ -5176,7 +5176,7 @@ read_token_word (int character) strcpy (token + token_index, ttok); token_index += ttoklen; FREE (ttok); - dollar_present = 1; + dollar_present |= character == '$'; all_digit_token = 0; goto next_character; } diff --git a/print_cmd.c b/print_cmd.c index a1180f24f..336864780 100644 --- a/print_cmd.c +++ b/print_cmd.c @@ -1265,10 +1265,12 @@ print_function_def (FUNCTION_DEF *func) `function' to words that are not valid POSIX identifiers. */ if (posixly_correct == 0) cprintf ("function %s () \n", func->name->word); - else if (valid_function_name (func->name->word, 0) == 0) + else if (valid_function_name (func->name->word, posixly_correct) == 0) cprintf ("function %s () \n", func->name->word); else cprintf ("%s () \n", func->name->word); + + begin_unwind_frame ("function-def"); add_unwind_protect (reset_locals, 0); indent (indentation); @@ -1305,8 +1307,7 @@ print_function_def (FUNCTION_DEF *func) was_heredoc = 0; /* not printing any here-documents now */ } - remove_unwind_protect (); /* unwind_protect_pointer */ - remove_unwind_protect (); /* reset_locals */ + discard_unwind_frame ("function-def"); } /* Return the string representation of the named function. @@ -1331,7 +1332,7 @@ named_function_string (char *name, COMMAND *command, int flags) if (name && *name) { - if (valid_function_name (name, 0) == 0) + if (valid_function_name (name, posixly_correct) == 0) cprintf ("function "); cprintf ("%s ", name); } diff --git a/tests/intl.right b/tests/intl.right index 7da99192d..d5d7b929d 100644 --- a/tests/intl.right +++ b/tests/intl.right @@ -55,3 +55,13 @@ Passed all 1378 Unicode tests $'5\247@3\231+\306S8\237\242\352\263' + : $'5\247@3\231+\306S8\237\242\352\263' + set +x +ಇಳಿಕೆಗಳು +ಇಳ +ಇ +ಇಳ + ಇಳ +ಇಳ --- +ಇ +ಇ + ಇ +ಇ --- diff --git a/tests/intl.tests b/tests/intl.tests index c4ff02c3c..990d782dd 100644 --- a/tests/intl.tests +++ b/tests/intl.tests @@ -66,3 +66,5 @@ ${THIS_SH} ./unicode1.sub # 2>/dev/null ${THIS_SH} ./unicode2.sub ${THIS_SH} ./unicode3.sub 2>&1 + +${THIS_SH} ./intl4.sub diff --git a/tests/intl4.sub b/tests/intl4.sub new file mode 100644 index 000000000..9e58145dc --- /dev/null +++ b/tests/intl4.sub @@ -0,0 +1,35 @@ +# 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 . +# + +# printf "%ls" and "%lc" format specifiers for multibyte characters with +# field width and precision in characters instead of bytes +LC_ALL=en_US.UTF-8 + +V=ಇಳಿಕೆಗಳು +V2=${V:0:2} +V3=${V:0:1} + +printf "%ls\n" "$V" +printf "%ls\n" "$V2" +printf "%lc\n" "$V" +printf "%.2ls\n" "$V" + +printf "%4.2ls\n" "$V" +printf "%-4.2ls---\n" "$V" + +printf "%ls\n" "$V3" +printf "%lc\n" "$V3" + +printf "%4.2lc\n" "$V3" +printf "%-4.2lc---\n" "$V3" diff --git a/tests/printf.tests b/tests/printf.tests index df37e47ee..3947e7d6d 100644 --- a/tests/printf.tests +++ b/tests/printf.tests @@ -326,9 +326,6 @@ printf "<%3s><%3b>\n" # tests variable assignment with -v ${THIS_SH} ./printf1.sub - ${THIS_SH} ./printf2.sub - ${THIS_SH} ./printf3.sub - ${THIS_SH} ./printf4.sub