printf changes: %q/%Q altform, %ls/%lc wide character strings; posix mode changes for function execution; allow <( and >) in function names

This commit is contained in:
Chet Ramey
2023-02-06 10:02:16 -05:00
parent de195f9483
commit 315095ad7f
19 changed files with 455 additions and 64 deletions
+69 -1
View 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
+1
View File
@@ -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
Vendored
+1
View 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)
+5
View 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);
+233 -30
View 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
+30 -5
View 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;
}
+3
View File
@@ -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
Vendored
+8 -1
View File
@@ -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
+2 -2
View 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)
+5
View 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.
+4 -12
View 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)))
{
+40 -5
View File
@@ -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
+1
View File
@@ -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);
+1 -1
View File
@@ -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;
}
+5 -4
View 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);
}
+10
View 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
ಇಳಿಕೆಗಳು
ಇಳ
ಇಳ
ಇಳ
ಇಳ ---
ಇ ---
+2
View 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
+35
View File
@@ -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"
-3
View 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