]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
printf changes: %q/%Q altform, %ls/%lc wide character strings; posix mode changes...
authorChet Ramey <chet.ramey@case.edu>
Mon, 6 Feb 2023 15:02:16 +0000 (10:02 -0500)
committerChet Ramey <chet.ramey@case.edu>
Mon, 6 Feb 2023 15:02:16 +0000 (10:02 -0500)
19 files changed:
CWRU/CWRU.chlog
MANIFEST
aclocal.m4
builtins/enable.def
builtins/printf.def
builtins/set.def
config.h.in
configure
configure.ac
doc/bashref.texi
execute_cmd.c
general.c
general.h
parse.y
print_cmd.c
tests/intl.right
tests/intl.tests
tests/intl4.sub [new file with mode: 0644]
tests/printf.tests

index 78caa961169b191859dc26ea11af9d89eab53d70..88185633dae1eb4ef2ccd10860f7e1f9ad9e8ef3 100644 (file)
@@ -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 <torreemanuele6@gmail.com>
+
+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
index 534429fc6714370a2e4fd20ac57a35583f14175b..e5bbafb17eacfbf9412c3a2216ada25544f09c51 100644 (file)
--- 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
index cc97bd4b8461528958dbd009c7c563aa97945527..c5e8ab15575e40afcbb65d1cf4ee06af1c0c9df6 100644 (file)
@@ -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)
 
index c1c5e673b91289445b74048d1bcdedc8404748fd..b6d6a8fe244e47ec5de1522f4cd8d2eaef135d3a 100644 (file)
@@ -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);
index 5ec874ae2ddc5fc2bcbd2120262683d8cc62ef1d..6a4dac075a0f156f4ecabeeac03f67679a189e77 100644 (file)
@@ -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
index b3e898c96387c2f82f543ee27407fa2f8cc9cf6a..a6f5c25396ebe7bc34630612faa7da54cff7a5d7 100644 (file)
@@ -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;
        }
index d6d52930bde22335cf74cee08a436c1691e8e172..5a221ba22b9c25c4e2574f64e4f23fbc36fdc615 100644 (file)
 /* 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
 
index 4fa2335765710c2e6ee3dbc8e82b8df844f267d8..03008d0393a9f445859d8426bcfa6a13dbff33f8 100755 (executable)
--- 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
index d0f7054140a7d70dde79dd1fc8041a89134278f1..da2bd9449d179cefe48202e8916d1d8e902d7242 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.
 
-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)
index 3968342e9ae553bdce2dd8cd5f96f453d032f994..2eff91ec2f658f15f3e1b725f1cf85db4e4ad767 100644 (file)
@@ -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.
index 947eb098be3a788aa6e4d7e989db96f7040a2922..d4c082d044591a933f08bdb6a2c4fa0c9c528163 100644 (file)
@@ -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)))
     {
index e8bf5f3a0d907535e7070ea9e62f5bb75b952520..da59f9c0fbc6eb3251f5b4a77d17199ffbd1faa9 100644 (file)
--- 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
index 87ff7a4008bdb684bea6f9f15b345b0704b0a588..fa57251a56656192350f89901a2b7a169388bd72 100644 (file)
--- 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 178b2181d802f84a48827934d49641a6508e4df7..f5199f151d4f7c3d22bedc60c6fc0b8e6f7ddd9c 100644 (file)
--- 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;
            }
index a1180f24f8a5e29e506919b55e16c8b65d13ee64..3368647806817d00c30232170d1f54eb4632f504 100644 (file)
@@ -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);
     }
index 7da99192dec7ee2defa09ea0487bba4821e480b3..d5d7b929d8769df3cab042d87cfb118191a2d116 100644 (file)
@@ -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
+ಇಳಿಕೆಗಳು
+ಇಳ
+ಇ
+ಇಳ
+  ಇಳ
+ಇಳ  ---
+ಇ
+ಇ
+   ಇ
+ಇ   ---
index c4ff02c3c2777a57e86e618f401152eef978bbb9..990d782dd0d202f65b602e14e590b2925f0ca695 100644 (file)
@@ -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 (file)
index 0000000..9e58145
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+#
+
+# 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"
index df37e47eeaf7a0604a3263b7897df466d888feec..3947e7d6de73c7a0dc15722752c3f5de03edfb4e 100644 (file)
@@ -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