The Unicode string I/O - essentially a *printf directive 'U' that accepts Unicode strings as arguments, but also the ability to produce Unicode strings rather than char* strings - requires some modifications to the vasnprintf code.
The snprintfv code has not been migrating to gnulib (yet? dormant project?), so I have no other choice than using gnulib's vasnprintf.c as code base. Earlier I had the modifications in separate files, but it turns out that this hampers maintainability. And the modified code is only ca. 20% of the vasnprintf code. So, although it's an extension, it makes sense to put the code into vasnprintf.c and its companion files. 2007-06-10 Bruno Haible <[EMAIL PROTECTED]> Prepare vasnprintf code for use with Unicode strings. * lib/printf-args.h (PRINTF_FETCHARGS): New macro. (arg_type) [ENABLE_UNISTDIO]: Define TYPE_U8_STRING, TYPE_U16_STRING, TYPE_U32_STRING. (argument) [ENABLE_UNISTDIO]: Add a_u8_string, a_u16_string, a_u32_string variants. (PRINTF_FETCHARGS): Renamed from printf_fetchargs. * lib/printf-args.c: Don't include config.h and the specification header if PRINTF_FETCHARGS is already defined. (PRINTF_FETCHARGS): Renamed from printf_fetchargs. (PRINTF_FETCHARGS) [ENABLE_UNISTDIO]: Add code for TYPE_U8_STRING, TYPE_U16_STRING, TYPE_U32_STRING. * lib/printf-parse.h [ENABLE_UNISTDIO] (u8_directive, u8_directives, u16_directive, u16_directives, u32_directive, u32_directives): New types. (ulc_printf_parse, u8_printf_parse, u16_printf_parse, u32_printf_parse): New declarations. * lib/printf-parse.c: Don't include config.h and the specification header if PRINTF_PARSE is already defined. Eliminate the set of parameters for WIDE_CHAR_VERSION; the user of this file must provide them now. Include c-ctype.h. (PRINTF_PARSE) [ENABLE_UNISTDIO]: Add code implementing the 'U' directive and CHAR_T_ONLY_ASCII. * lib/vasnprintf.c: Don't include config.h and the specification header if VASNPRINTF is already defined. (DCHAR_IS_TCHAR, DCHAR_CPY): New macros. (VASNPRINTF): Use PRINTF_FETCHARGS instead of printf_fetchargs. Use DCHAR_CPY. Handle the case that DCHAR_T and FCHAR_T are not the same type. Handle the case that TCHAR_T and FCHAR_T are not of the same size. Handle the case that DCHAR_T and TCHAR_T are not the same type, add a conversion from TCHAR_T[] to DCHAR_T[], and rework the padding code accordingly. (VASNPRINTF) [ENABLE_UNISTDIO]: Implement the 'U' directive. Enable pad_ourselves also in this case, with the 'c' and 's' directives, and with a different notion of "width". * m4/vasnprintf.m4 (gl_PREREQ_VASNPRINTF_WITH_EXTRAS): New macros. *** lib/printf-args.h 6 Apr 2007 14:36:56 -0000 1.7 --- lib/printf-args.h 11 Jun 2007 00:46:59 -0000 *************** *** 18,23 **** --- 18,33 ---- #ifndef _PRINTF_ARGS_H #define _PRINTF_ARGS_H + /* This file can be parametrized with the following macros: + ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. + PRINTF_FETCHARGS Name of the function to be declared. + STATIC Set to 'static' to declare the function static. */ + + /* Default parameters. */ + #ifndef PRINTF_FETCHARGS + # define PRINTF_FETCHARGS printf_fetchargs + #endif + /* Get size_t. */ #include <stddef.h> *************** *** 69,74 **** --- 79,90 ---- #if HAVE_LONG_LONG_INT , TYPE_COUNT_LONGLONGINT_POINTER #endif + #if ENABLE_UNISTDIO + /* The unistdio extensions. */ + , TYPE_U8_STRING + , TYPE_U16_STRING + , TYPE_U32_STRING + #endif } arg_type; /* Polymorphic argument */ *************** *** 108,113 **** --- 124,135 ---- #if HAVE_LONG_LONG_INT long long int * a_count_longlongint_pointer; #endif + #if ENABLE_UNISTDIO + /* The unistdio extensions. */ + const uint8_t * a_u8_string; + const uint16_t * a_u16_string; + const uint32_t * a_u32_string; + #endif } a; } *************** *** 127,132 **** #else extern #endif ! int printf_fetchargs (va_list args, arguments *a); #endif /* _PRINTF_ARGS_H */ --- 149,154 ---- #else extern #endif ! int PRINTF_FETCHARGS (va_list args, arguments *a); #endif /* _PRINTF_ARGS_H */ *** lib/printf-args.c 6 Apr 2007 14:36:56 -0000 1.10 --- lib/printf-args.c 11 Jun 2007 00:46:59 -0000 *************** *** 15,30 **** with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ ! #include <config.h> /* Specification. */ ! #include "printf-args.h" #ifdef STATIC STATIC #endif int ! printf_fetchargs (va_list args, arguments *a) { size_t i; argument *ap; --- 15,39 ---- with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ ! /* This file can be parametrized with the following macros: ! ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. ! PRINTF_FETCHARGS Name of the function to be defined. ! STATIC Set to 'static' to declare the function static. */ ! ! #ifndef PRINTF_FETCHARGS ! # include <config.h> ! #endif /* Specification. */ ! #ifndef PRINTF_FETCHARGS ! # include "printf-args.h" ! #endif #ifdef STATIC STATIC #endif int ! PRINTF_FETCHARGS (va_list args, arguments *a) { size_t i; argument *ap; *************** *** 131,136 **** --- 140,184 ---- ap->a.a_count_longlongint_pointer = va_arg (args, long long int *); break; #endif + #if ENABLE_UNISTDIO + /* The unistdio extensions. */ + case TYPE_U8_STRING: + ap->a.a_u8_string = va_arg (args, const uint8_t *); + /* A null pointer is an invalid argument for "%U", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_u8_string == NULL) + { + static const uint8_t u8_null_string[] = + { '(', 'N', 'U', 'L', 'L', 0 }; + ap->a.a_u8_string = u8_null_string; + } + break; + case TYPE_U16_STRING: + ap->a.a_u16_string = va_arg (args, const uint16_t *); + /* A null pointer is an invalid argument for "%lU", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_u16_string == NULL) + { + static const uint16_t u16_null_string[] = + { '(', 'N', 'U', 'L', 'L', 0 }; + ap->a.a_u16_string = u16_null_string; + } + break; + case TYPE_U32_STRING: + ap->a.a_u32_string = va_arg (args, const uint32_t *); + /* A null pointer is an invalid argument for "%llU", but in practice + it occurs quite frequently in printf statements that produce + debug output. Use a fallback in this case. */ + if (ap->a.a_u32_string == NULL) + { + static const uint32_t u32_null_string[] = + { '(', 'N', 'U', 'L', 'L', 0 }; + ap->a.a_u32_string = u32_null_string; + } + break; + #endif default: /* Unknown type. */ return -1; *** lib/printf-parse.h 14 May 2005 06:03:58 -0000 1.5 --- lib/printf-parse.h 11 Jun 2007 00:46:59 -0000 *************** *** 1,5 **** /* Parse printf format string. ! Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. 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 --- 1,5 ---- /* Parse printf format string. ! Copyright (C) 1999, 2002-2003, 2005, 2007 Free Software Foundation, Inc. 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 *************** *** 18,23 **** --- 18,27 ---- #ifndef _PRINTF_PARSE_H #define _PRINTF_PARSE_H + /* This file can be parametrized with the following macros: + ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. + STATIC Set to 'static' to declare the function static. */ + #include "printf-args.h" *************** *** 32,37 **** --- 36,44 ---- /* arg_index value indicating that no argument is consumed. */ #define ARG_NONE (~(size_t)0) + /* xxx_directive: A parsed directive. + xxx_directives: A parsed format string. */ + /* A parsed directive. */ typedef struct { *************** *** 59,74 **** } char_directives; /* Parses the format string. Fills in the number N of directives, and fills in directives[0], ..., directives[N-1], and sets directives[N].dir_start to the end of the format string. Also fills in the arg_type fields of the arguments and the needed count of arguments. */ ! #ifdef STATIC ! STATIC #else extern ! #endif int printf_parse (const char *format, char_directives *d, arguments *a); #endif /* _PRINTF_PARSE_H */ --- 66,179 ---- } char_directives; + #if ENABLE_UNISTDIO + + /* A parsed directive. */ + typedef struct + { + const uint8_t* dir_start; + const uint8_t* dir_end; + int flags; + const uint8_t* width_start; + const uint8_t* width_end; + size_t width_arg_index; + const uint8_t* precision_start; + const uint8_t* precision_end; + size_t precision_arg_index; + uint8_t conversion; /* d i o u x X f e E g G c s p n U % but not C S */ + size_t arg_index; + } + u8_directive; + + /* A parsed format string. */ + typedef struct + { + size_t count; + u8_directive *dir; + size_t max_width_length; + size_t max_precision_length; + } + u8_directives; + + /* A parsed directive. */ + typedef struct + { + const uint16_t* dir_start; + const uint16_t* dir_end; + int flags; + const uint16_t* width_start; + const uint16_t* width_end; + size_t width_arg_index; + const uint16_t* precision_start; + const uint16_t* precision_end; + size_t precision_arg_index; + uint16_t conversion; /* d i o u x X f e E g G c s p n U % but not C S */ + size_t arg_index; + } + u16_directive; + + /* A parsed format string. */ + typedef struct + { + size_t count; + u16_directive *dir; + size_t max_width_length; + size_t max_precision_length; + } + u16_directives; + + /* A parsed directive. */ + typedef struct + { + const uint32_t* dir_start; + const uint32_t* dir_end; + int flags; + const uint32_t* width_start; + const uint32_t* width_end; + size_t width_arg_index; + const uint32_t* precision_start; + const uint32_t* precision_end; + size_t precision_arg_index; + uint32_t conversion; /* d i o u x X f e E g G c s p n U % but not C S */ + size_t arg_index; + } + u32_directive; + + /* A parsed format string. */ + typedef struct + { + size_t count; + u32_directive *dir; + size_t max_width_length; + size_t max_precision_length; + } + u32_directives; + + #endif + /* Parses the format string. Fills in the number N of directives, and fills in directives[0], ..., directives[N-1], and sets directives[N].dir_start to the end of the format string. Also fills in the arg_type fields of the arguments and the needed count of arguments. */ ! #if ENABLE_UNISTDIO ! extern int ! ulc_printf_parse (const char *format, char_directives *d, arguments *a); ! extern int ! u8_printf_parse (const uint8_t *format, u8_directives *d, arguments *a); ! extern int ! u16_printf_parse (const uint16_t *format, u16_directives *d, ! arguments *a); ! extern int ! u32_printf_parse (const uint32_t *format, u32_directives *d, ! arguments *a); #else + # ifdef STATIC + STATIC + # else extern ! # endif int printf_parse (const char *format, char_directives *d, arguments *a); + #endif #endif /* _PRINTF_PARSE_H */ *** lib/printf-parse.c 6 Apr 2007 14:36:56 -0000 1.12 --- lib/printf-parse.c 11 Jun 2007 00:46:59 -0000 *************** *** 15,29 **** with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ ! #include <config.h> /* Specification. */ ! #if WIDE_CHAR_VERSION ! # include "wprintf-parse.h" ! #else # include "printf-parse.h" #endif /* Get size_t, NULL. */ #include <stddef.h> --- 15,50 ---- with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ ! /* This file can be parametrized with the following macros: ! CHAR_T The element type of the format string. ! CHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters ! in the format string are ASCII. ! DIRECTIVE Structure denoting a format directive. ! Depends on CHAR_T. ! DIRECTIVES Structure denoting the set of format directives of a ! format string. Depends on CHAR_T. ! PRINTF_PARSE Function that parses a format string. ! Depends on CHAR_T. ! STATIC Set to 'static' to declare the function static. ! ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. */ ! ! #ifndef PRINTF_PARSE ! # include <config.h> ! #endif /* Specification. */ ! #ifndef PRINTF_PARSE # include "printf-parse.h" #endif + /* Default parameters. */ + #ifndef PRINTF_PARSE + # define PRINTF_PARSE printf_parse + # define CHAR_T char + # define DIRECTIVE char_directive + # define DIRECTIVES char_directives + #endif + /* Get size_t, NULL. */ #include <stddef.h> *************** *** 45,60 **** /* Checked size_t computations. */ #include "xsize.h" ! #if WIDE_CHAR_VERSION ! # define PRINTF_PARSE wprintf_parse ! # define CHAR_T wchar_t ! # define DIRECTIVE wchar_t_directive ! # define DIRECTIVES wchar_t_directives ! #else ! # define PRINTF_PARSE printf_parse ! # define CHAR_T char ! # define DIRECTIVE char_directive ! # define DIRECTIVES char_directives #endif #ifdef STATIC --- 66,74 ---- /* Checked size_t computations. */ #include "xsize.h" ! #if CHAR_T_ONLY_ASCII ! /* c_isascii(). */ ! # include "c-ctype.h" #endif #ifdef STATIC *************** *** 119,125 **** if (c == '%') { size_t arg_index = ARG_NONE; ! DIRECTIVE *dp = &d->dir[d->count];/* pointer to next directive */ /* Initialize the next directive. */ dp->dir_start = cp - 1; --- 133,139 ---- if (c == '%') { size_t arg_index = ARG_NONE; ! DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */ /* Initialize the next directive. */ dp->dir_start = cp - 1; *************** *** 479,484 **** --- 493,509 ---- else type = TYPE_COUNT_INT_POINTER; break; + #if ENABLE_UNISTDIO + /* The unistdio extensions. */ + case 'U': + if (flags >= 16) + type = TYPE_U32_STRING; + else if (flags >= 8) + type = TYPE_U16_STRING; + else + type = TYPE_U8_STRING; + break; + #endif case '%': type = TYPE_NONE; break; *************** *** 522,527 **** --- 547,559 ---- d->dir = memory; } } + #if CHAR_T_ONLY_ASCII + else if (!c_isascii (c)) + { + /* Non-ASCII character. Not supported. */ + goto error; + } + #endif } d->dir[d->count].dir_start = cp; *************** *** 537,543 **** return -1; } #undef DIRECTIVES #undef DIRECTIVE #undef CHAR_T - #undef PRINTF_PARSE --- 569,576 ---- return -1; } + #undef PRINTF_PARSE #undef DIRECTIVES #undef DIRECTIVE + #undef CHAR_T_ONLY_ASCII #undef CHAR_T *** lib/vasnprintf.c 10 Jun 2007 12:02:55 -0000 1.57 --- lib/vasnprintf.c 11 Jun 2007 00:47:00 -0000 *************** *** 15,20 **** --- 15,49 ---- with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + /* This file can be parametrized with the following macros: + VASNPRINTF The name of the function being defined. + FCHAR_T The element type of the format string. + DCHAR_T The element type of the destination (result) string. + FCHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters + in the format string are ASCII. MUST be set if + FCHAR_T and DCHAR_T are not the same type. + DIRECTIVE Structure denoting a format directive. + Depends on FCHAR_T. + DIRECTIVES Structure denoting the set of format directives of a + format string. Depends on FCHAR_T. + PRINTF_PARSE Function that parses a format string. + Depends on FCHAR_T. + DCHAR_CPY memcpy like function for DCHAR_T[] arrays. + DCHAR_SET memset like function for DCHAR_T[] arrays. + DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays. + SNPRINTF The system's snprintf (or similar) function. + This may be either snprintf or swprintf. + TCHAR_T The element type of the argument and result string + of the said SNPRINTF function. This may be either + char or wchar_t. The code exploits that + sizeof (TCHAR_T) | sizeof (DCHAR_T) and + alignof (TCHAR_T) <= alignof (DCHAR_T). + DCHAR_IS_TCHAR Set to 1 if DCHAR_T and TCHAR_T are the same type. + DCHAR_CONV_FROM_ENCODING A function to convert from char[] to DCHAR[]. + DCHAR_IS_UINT8_T Set to 1 if DCHAR_T is uint8_t. + DCHAR_IS_UINT16_T Set to 1 if DCHAR_T is uint16_t. + DCHAR_IS_UINT32_T Set to 1 if DCHAR_T is uint32_t. */ + /* Tell glibc's <stdio.h> to provide a prototype for snprintf(). This must come before <config.h> because <config.h> may include <features.h>, and once <features.h> has been included, it's too late. */ *************** *** 22,37 **** # define _GNU_SOURCE 1 #endif ! #include <config.h> #ifndef IN_LIBINTL # include <alloca.h> #endif /* Specification. */ ! #if WIDE_CHAR_VERSION ! # include "vasnwprintf.h" ! #else ! # include "vasnprintf.h" #endif #include <locale.h> /* localeconv() */ --- 51,70 ---- # define _GNU_SOURCE 1 #endif ! #ifndef VASNPRINTF ! # include <config.h> ! #endif #ifndef IN_LIBINTL # include <alloca.h> #endif /* Specification. */ ! #ifndef VASNPRINTF ! # if WIDE_CHAR_VERSION ! # include "vasnwprintf.h" ! # else ! # include "vasnprintf.h" ! # endif #endif #include <locale.h> /* localeconv() */ *************** *** 44,53 **** #if HAVE_NL_LANGINFO # include <langinfo.h> #endif ! #if WIDE_CHAR_VERSION ! # include "wprintf-parse.h" ! #else ! # include "printf-parse.h" #endif /* Checked size_t computations. */ --- 77,88 ---- #if HAVE_NL_LANGINFO # include <langinfo.h> #endif ! #ifndef VASNPRINTF ! # if WIDE_CHAR_VERSION ! # include "wprintf-parse.h" ! # else ! # include "printf-parse.h" ! # endif #endif /* Checked size_t computations. */ *************** *** 107,135 **** # endif #endif ! /* Define some macros that parametrize the code: ! VASNPRINTF The name of the function being defined. ! FCHAR_T The element type of the format string. ! DCHAR_T The element type of the destination (result) string. ! TCHAR_T The element type of the temporary buffer that is ! filled with a simple format directive, executed by ! the system's sprintf/snprintf (or similar) function. ! DIRECTIVE Structure denoting a format directive. ! Depends on FCHAR_T. ! DIRECTIVES Structure denoting the set of format directives of a ! format string. Depends on FCHAR_T. ! PRINTF_PARSE Function that parses a format string. ! Depends on FCHAR_T. ! SNPRINTF The system's snprintf (or similar) function. ! Depends on DCHAR_T. */ #if WIDE_CHAR_VERSION ! # define VASNPRINTF vasnwprintf ! # define FCHAR_T wchar_t ! # define DCHAR_T wchar_t ! # define TCHAR_T wchar_t ! # define DIRECTIVE wchar_t_directive ! # define DIRECTIVES wchar_t_directives ! # define PRINTF_PARSE wprintf_parse # define USE_SNPRINTF 1 # if HAVE_DECL__SNWPRINTF /* On Windows, the function swprintf() has a different signature than --- 142,173 ---- # endif #endif ! /* Default parameters. */ ! #ifndef VASNPRINTF ! # if WIDE_CHAR_VERSION ! # define VASNPRINTF vasnwprintf ! # define FCHAR_T wchar_t ! # define DCHAR_T wchar_t ! # define TCHAR_T wchar_t ! # define DCHAR_IS_TCHAR 1 ! # define DIRECTIVE wchar_t_directive ! # define DIRECTIVES wchar_t_directives ! # define PRINTF_PARSE wprintf_parse ! # define DCHAR_CPY wmemcpy ! # else ! # define VASNPRINTF vasnprintf ! # define FCHAR_T char ! # define DCHAR_T char ! # define TCHAR_T char ! # define DCHAR_IS_TCHAR 1 ! # define DIRECTIVE char_directive ! # define DIRECTIVES char_directives ! # define PRINTF_PARSE printf_parse ! # define DCHAR_CPY memcpy ! # endif ! #endif #if WIDE_CHAR_VERSION ! /* TCHAR_T is wchar_t. */ # define USE_SNPRINTF 1 # if HAVE_DECL__SNWPRINTF /* On Windows, the function swprintf() has a different signature than *************** *** 140,152 **** # define SNPRINTF swprintf # endif #else ! # define VASNPRINTF vasnprintf ! # define FCHAR_T char ! # define DCHAR_T char ! # define TCHAR_T char ! # define DIRECTIVE char_directive ! # define DIRECTIVES char_directives ! # define PRINTF_PARSE printf_parse # /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. But don't use it on BeOS, since BeOS snprintf produces no output if the size argument is >= 0x3000000. */ --- 178,184 ---- # define SNPRINTF swprintf # endif #else ! /* TCHAR_T is char. */ # /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. But don't use it on BeOS, since BeOS snprintf produces no output if the size argument is >= 0x3000000. */ *************** *** 1157,1163 **** #endif DCHAR_T * ! VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, const FCHAR_T *format, va_list args) { DIRECTIVES d; arguments a; --- 1189,1196 ---- #endif DCHAR_T * ! VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, ! const FCHAR_T *format, va_list args) { DIRECTIVES d; arguments a; *************** *** 1173,1179 **** if (a.arg) \ free (a.arg); ! if (printf_fetchargs (args, &a) < 0) { CLEANUP (); errno = EINVAL; --- 1206,1212 ---- if (a.arg) \ free (a.arg); ! if (PRINTF_FETCHARGS (args, &a) < 0) { CLEANUP (); errno = EINVAL; *************** *** 1250,1256 **** if (memory == NULL) \ goto out_of_memory; \ if (result == resultbuf && length > 0) \ ! memcpy (memory, result, length * sizeof (DCHAR_T)); \ result = memory; \ } --- 1283,1289 ---- if (memory == NULL) \ goto out_of_memory; \ if (result == resultbuf && length > 0) \ ! DCHAR_CPY (memory, result, length); \ result = memory; \ } *************** *** 1262,1269 **** size_t augmented_length = xsum (length, n); ENSURE_ALLOCATION (augmented_length); ! memcpy (result + length, cp, n * sizeof (DCHAR_T)); ! length = augmented_length; } if (i == d.count) break; --- 1295,1314 ---- size_t augmented_length = xsum (length, n); ENSURE_ALLOCATION (augmented_length); ! /* This copies a piece of FCHAR_T[] into a DCHAR_T[]. Here we ! need that the format string contains only ASCII characters ! if FCHAR_T and DCHAR_T are not the same type. */ ! if (sizeof (FCHAR_T) == sizeof (DCHAR_T)) ! { ! DCHAR_CPY (result + length, (const DCHAR_T *) cp, n); ! length = augmented_length; ! } ! else ! { ! do ! result[length++] = (unsigned char) *cp++; ! while (--n > 0); ! } } if (i == d.count) break; *************** *** 1310,1315 **** --- 1355,1824 ---- abort (); } } + #if ENABLE_UNISTDIO + /* The unistdio extensions. */ + else if (dp->conversion == 'U') + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = (unsigned int) (-arg); + } + else + width = arg; + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } + + has_precision = 0; + precision = 0; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + switch (type) + { + case TYPE_U8_STRING: + { + const uint8_t *arg = a.arg[dp->arg_index].a.a_u8_string; + const uint8_t *arg_end; + size_t characters; + + if (has_precision) + { + /* Use only PRECISION characters, from the left. */ + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + int count = u8_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else if (has_width) + { + /* Use the entire string, and count the number of + characters. */ + arg_end = arg; + characters = 0; + for (;;) + { + int count = u8_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else + { + /* Use the entire string. */ + arg_end = arg + u8_strlen (arg); + /* The number of characters doesn't matter. */ + characters = 0; + } + + if (has_width && width > characters + && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + + # if DCHAR_IS_UINT8_T + { + size_t n = arg_end - arg; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_CPY (result + length, arg, n); + length += n; + } + # else + { /* Convert. */ + DCHAR_T *converted = result + length; + size_t converted_len = allocated - length; + # if DCHAR_IS_TCHAR + /* Convert from UTF-8 to locale encoding. */ + if (u8_conv_to_encoding (locale_charset (), + iconveh_question_mark, + arg, arg_end - arg, NULL, + &converted, &converted_len) + < 0) + # else + /* Convert from UTF-8 to UTF-16/UTF-32. */ + converted = + U8_TO_DCHAR (arg, arg_end - arg, + converted, &converted_len); + if (converted == NULL) + # endif + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + if (converted != result + length) + { + ENSURE_ALLOCATION (xsum (length, converted_len)); + DCHAR_CPY (result + length, converted, converted_len); + free (converted); + } + length += converted_len; + } + # endif + + if (has_width && width > characters + && (dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + break; + + case TYPE_U16_STRING: + { + const uint16_t *arg = a.arg[dp->arg_index].a.a_u16_string; + const uint16_t *arg_end; + size_t characters; + + if (has_precision) + { + /* Use only PRECISION characters, from the left. */ + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + int count = u16_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else if (has_width) + { + /* Use the entire string, and count the number of + characters. */ + arg_end = arg; + characters = 0; + for (;;) + { + int count = u16_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else + { + /* Use the entire string. */ + arg_end = arg + u16_strlen (arg); + /* The number of characters doesn't matter. */ + characters = 0; + } + + if (has_width && width > characters + && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + + # if DCHAR_IS_UINT16_T + { + size_t n = arg_end - arg; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_CPY (result + length, arg, n); + length += n; + } + # else + { /* Convert. */ + DCHAR_T *converted = result + length; + size_t converted_len = allocated - length; + # if DCHAR_IS_TCHAR + /* Convert from UTF-16 to locale encoding. */ + if (u16_conv_to_encoding (locale_charset (), + iconveh_question_mark, + arg, arg_end - arg, NULL, + &converted, &converted_len) + < 0) + # else + /* Convert from UTF-16 to UTF-8/UTF-32. */ + converted = + U16_TO_DCHAR (arg, arg_end - arg, + converted, &converted_len); + if (converted == NULL) + # endif + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + if (converted != result + length) + { + ENSURE_ALLOCATION (xsum (length, converted_len)); + DCHAR_CPY (result + length, converted, converted_len); + free (converted); + } + length += converted_len; + } + # endif + + if (has_width && width > characters + && (dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + break; + + case TYPE_U32_STRING: + { + const uint32_t *arg = a.arg[dp->arg_index].a.a_u32_string; + const uint32_t *arg_end; + size_t characters; + + if (has_precision) + { + /* Use only PRECISION characters, from the left. */ + arg_end = arg; + characters = 0; + for (; precision > 0; precision--) + { + int count = u32_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else if (has_width) + { + /* Use the entire string, and count the number of + characters. */ + arg_end = arg; + characters = 0; + for (;;) + { + int count = u32_strmblen (arg_end); + if (count == 0) + break; + if (count < 0) + { + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = EILSEQ; + return NULL; + } + arg_end += count; + characters++; + } + } + else + { + /* Use the entire string. */ + arg_end = arg + u32_strlen (arg); + /* The number of characters doesn't matter. */ + characters = 0; + } + + if (has_width && width > characters + && !(dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + + # if DCHAR_IS_UINT32_T + { + size_t n = arg_end - arg; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_CPY (result + length, arg, n); + length += n; + } + # else + { /* Convert. */ + DCHAR_T *converted = result + length; + size_t converted_len = allocated - length; + # if DCHAR_IS_TCHAR + /* Convert from UTF-32 to locale encoding. */ + if (u32_conv_to_encoding (locale_charset (), + iconveh_question_mark, + arg, arg_end - arg, NULL, + &converted, &converted_len) + < 0) + # else + /* Convert from UTF-32 to UTF-8/UTF-16. */ + converted = + U32_TO_DCHAR (arg, arg_end - arg, + converted, &converted_len); + if (converted == NULL) + # endif + { + int saved_errno = errno; + if (!(result == resultbuf || result == NULL)) + free (result); + if (buf_malloced != NULL) + free (buf_malloced); + CLEANUP (); + errno = saved_errno; + return NULL; + } + if (converted != result + length) + { + ENSURE_ALLOCATION (xsum (length, converted_len)); + DCHAR_CPY (result + length, converted, converted_len); + free (converted); + } + length += converted_len; + } + # endif + + if (has_width && width > characters + && (dp->flags & FLAG_LEFT)) + { + size_t n = width - characters; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + break; + + default: + abort (); + } + } + #endif #if NEED_PRINTF_DIRECTIVE_A && !defined IN_LIBINTL else if (dp->conversion == 'a' || dp->conversion == 'A') { *************** *** 1554,1564 **** { '%', '+', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } - # else - sprintf (p, "%+d", exponent); - # endif while (*p != '\0') p++; } END_LONG_DOUBLE_ROUNDING (); --- 2063,2086 ---- { '%', '+', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } while (*p != '\0') p++; + # else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; ep++) + p++; + } + # endif } END_LONG_DOUBLE_ROUNDING (); *************** *** 1688,1698 **** { '%', '+', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } - # else - sprintf (p, "%+d", exponent); - # endif while (*p != '\0') p++; } } } --- 2210,2233 ---- { '%', '+', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } while (*p != '\0') p++; + # else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; ep++) + p++; + } + # endif } } } *************** *** 2084,2094 **** { '%', '+', '.', '2', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } - # else - sprintf (p, "%+.2d", exponent); - # endif while (*p != '\0') p++; } else if (dp->conversion == 'g' || dp->conversion == 'G') { --- 2619,2642 ---- { '%', '+', '.', '2', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } while (*p != '\0') p++; + # else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+.2d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+.2d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; ep++) + p++; + } + # endif } else if (dp->conversion == 'g' || dp->conversion == 'G') { *************** *** 2228,2238 **** { '%', '+', '.', '2', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } - # else - sprintf (p, "%+.2d", exponent); - # endif while (*p != '\0') p++; } free (digits); --- 2776,2799 ---- { '%', '+', '.', '2', 'd', '\0' }; SNPRINTF (p, 6 + 1, decimal_format, exponent); } while (*p != '\0') p++; + # else + if (sizeof (DCHAR_T) == 1) + { + sprintf ((char *) p, "%+.2d", exponent); + while (*p != '\0') + p++; + } + else + { + char expbuf[6 + 1]; + const char *ep; + sprintf (expbuf, "%+.2d", exponent); + for (ep = expbuf; (*p = *ep) != '\0'; ep++) + p++; + } + # endif } free (digits); *************** *** 2417,2427 **** { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; ! #if !USE_SNPRINTF || NEED_PRINTF_FLAG_ZERO int has_width; size_t width; #endif ! #if NEED_PRINTF_FLAG_ZERO int pad_ourselves; #else # define pad_ourselves 0 --- 2978,2988 ---- { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; ! #if !USE_SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO int has_width; size_t width; #endif ! #if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO int pad_ourselves; #else # define pad_ourselves 0 *************** *** 2435,2441 **** TCHAR_T *tmp; #endif ! #if !USE_SNPRINTF || NEED_PRINTF_FLAG_ZERO has_width = 0; width = 0; if (dp->width_start != dp->width_end) --- 2996,3002 ---- TCHAR_T *tmp; #endif ! #if !USE_SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO has_width = 0; width = 0; if (dp->width_start != dp->width_end) *************** *** 2669,2676 **** --- 3230,3247 ---- abort (); } + # if ENABLE_UNISTDIO + /* Padding considers the number of characters, therefore the + number of elements after padding may be + > max (tmp_length, width) + but is certainly + <= tmp_length + width. */ + tmp_length = xsum (tmp_length, width); + # else + /* Padding considers the number of elements, says POSIX. */ if (tmp_length < width) tmp_length = width; + # endif tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ } *************** *** 2692,2702 **** #endif /* Decide whether to perform the padding ourselves. */ ! #if NEED_PRINTF_FLAG_ZERO switch (dp->conversion) { case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': pad_ourselves = 1; break; default: --- 3263,3282 ---- #endif /* Decide whether to perform the padding ourselves. */ ! #if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO switch (dp->conversion) { + # if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO + /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need + to perform the padding after this conversion. Functions + with unistdio extensions perform the padding based on + character count rather than element count. */ + case 'c': case 's': + # endif + # if NEED_PRINTF_FLAG_ZERO case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': + # endif pad_ourselves = 1; break; default: *************** *** 2732,2746 **** if (dp->width_start != dp->width_end) { size_t n = dp->width_end - dp->width_start; ! memcpy (fbp, dp->width_start, n * sizeof (TCHAR_T)); ! fbp += n; } } if (dp->precision_start != dp->precision_end) { size_t n = dp->precision_end - dp->precision_start; ! memcpy (fbp, dp->precision_start, n * sizeof (TCHAR_T)); ! fbp += n; } switch (type) --- 3312,3350 ---- if (dp->width_start != dp->width_end) { size_t n = dp->width_end - dp->width_start; ! /* The width specification is known to consist only ! of standard ASCII characters. */ ! if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) ! { ! memcpy (fbp, dp->width_start, n * sizeof (TCHAR_T)); ! fbp += n; ! } ! else ! { ! const FCHAR_T *mp = dp->width_start; ! do ! *fbp++ = (unsigned char) *mp++; ! while (--n > 0); ! } } } if (dp->precision_start != dp->precision_end) { size_t n = dp->precision_end - dp->precision_start; ! /* The precision specification is known to consist only ! of standard ASCII characters. */ ! if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) ! { ! memcpy (fbp, dp->precision_start, n * sizeof (TCHAR_T)); ! fbp += n; ! } ! else ! { ! const FCHAR_T *mp = dp->precision_start; ! do ! *fbp++ = (unsigned char) *mp++; ! while (--n > 0); ! } } switch (type) *************** *** 2804,2838 **** } #if USE_SNPRINTF /* Prepare checking whether snprintf returns the count via %n. */ ENSURE_ALLOCATION (xsum (length, 1)); ! result[length] = '\0'; #endif for (;;) { int count = -1; - int retcount = 0; #if USE_SNPRINTF size_t maxlen = allocated - length; ! /* SNPRINTF can fail if maxlen > INT_MAX. */ ! if (maxlen > INT_MAX) goto overflow; # define SNPRINTF_BUF(arg) \ switch (prefix_count) \ { \ case 0: \ ! retcount = SNPRINTF (result + length, maxlen, buf, \ arg, &count); \ break; \ case 1: \ ! retcount = SNPRINTF (result + length, maxlen, buf, \ prefixes[0], arg, &count); \ break; \ case 2: \ ! retcount = SNPRINTF (result + length, maxlen, buf, \ prefixes[0], prefixes[1], arg, \ &count); \ break; \ --- 3408,3453 ---- } #if USE_SNPRINTF + /* The SNPRINTF result is appended after result[0..length]. + The latter is an array of DCHAR_T; SNPRINTF appends an + array of TCHAR_T to it. This is possible because + sizeof (TCHAR_T) divides sizeof (DCHAR_T) and + alignof (TCHAR_T) <= alignof (DCHAR_T). */ + # define TCHARS_PER_DCHAR (sizeof (DCHAR_T) / sizeof (TCHAR_T)) /* Prepare checking whether snprintf returns the count via %n. */ ENSURE_ALLOCATION (xsum (length, 1)); ! *(TCHAR_T *) (result + length) = '\0'; #endif for (;;) { int count = -1; #if USE_SNPRINTF + int retcount = 0; size_t maxlen = allocated - length; ! /* SNPRINTF can fail if its second argument is ! > INT_MAX. */ ! if (maxlen > INT_MAX / TCHARS_PER_DCHAR) goto overflow; + maxlen = maxlen * TCHARS_PER_DCHAR; # define SNPRINTF_BUF(arg) \ switch (prefix_count) \ { \ case 0: \ ! retcount = SNPRINTF ((TCHAR_T *) (result + length), \ ! maxlen, buf, \ arg, &count); \ break; \ case 1: \ ! retcount = SNPRINTF ((TCHAR_T *) (result + length), \ ! maxlen, buf, \ prefixes[0], arg, &count); \ break; \ case 2: \ ! retcount = SNPRINTF ((TCHAR_T *) (result + length), \ ! maxlen, buf, \ prefixes[0], prefixes[1], arg, \ &count); \ break; \ *************** *** 2981,2987 **** { /* Verify that snprintf() has NUL-terminated its result. */ ! if (count < maxlen && result[length + count] != '\0') abort (); /* Portability hack. */ if (retcount > count) --- 3596,3603 ---- { /* Verify that snprintf() has NUL-terminated its result. */ ! if (count < maxlen ! && ((TCHAR_T *) (result + length)) [count] != '\0') abort (); /* Portability hack. */ if (retcount > count) *************** *** 3032,3056 **** } #if USE_SNPRINTF ! /* Make room for the result. */ if (count >= maxlen) { ! /* Need at least count bytes. But allocate ! proportionally, to avoid looping eternally if ! snprintf() reports a too small count. */ size_t n = ! xmax (xsum (length, count), xtimes (allocated, 2)); ENSURE_ALLOCATION (n); continue; } #endif ! #if !USE_SNPRINTF /* Make room for the result. */ if (count > allocated - length) { ! /* Need at least count bytes. But allocate proportionally. */ size_t n = xmax (xsum (length, count), xtimes (allocated, 2)); --- 3648,3767 ---- } #if USE_SNPRINTF ! /* Handle overflow of the allocated buffer. */ if (count >= maxlen) { ! /* Need at least count * sizeof (TCHAR_T) bytes. But ! allocate proportionally, to avoid looping eternally ! if snprintf() reports a too small count. */ size_t n = ! xmax (xsum (length, ! (count + TCHARS_PER_DCHAR - 1) ! / TCHARS_PER_DCHAR), ! xtimes (allocated, 2)); ENSURE_ALLOCATION (n); continue; } #endif ! #if !DCHAR_IS_TCHAR ! # if !USE_SNPRINTF ! if (count >= tmp_length) ! /* tmp_length was incorrectly calculated - fix the ! code above! */ ! abort (); ! # endif ! ! /* Convert from TCHAR_T[] to DCHAR_T[]. */ ! if (dp->conversion == 'c' || dp->conversion == 's') ! { ! /* type = TYPE_CHAR or TYPE_WIDE_CHAR or TYPE_STRING ! TYPE_WIDE_STRING. ! The result string is not certainly ASCII. */ ! const TCHAR_T *tmpsrc; ! DCHAR_T *tmpdst; ! size_t tmpdst_len; ! /* This code assumes that TCHAR_T is 'char'. */ ! typedef int TCHAR_T_verify ! [2 * (sizeof (TCHAR_T) == 1) - 1]; ! # if USE_SNPRINTF ! tmpsrc = (TCHAR_T *) (result + length); ! # else ! tmpsrc = tmp; ! # endif ! tmpdst = NULL; ! tmpdst_len = 0; ! if (DCHAR_CONV_FROM_ENCODING (locale_charset (), ! iconveh_question_mark, ! tmpsrc, count, ! NULL, ! &tmpdst, &tmpdst_len) ! < 0) ! { ! int saved_errno = errno; ! if (!(result == resultbuf || result == NULL)) ! free (result); ! if (buf_malloced != NULL) ! free (buf_malloced); ! CLEANUP (); ! errno = saved_errno; ! return NULL; ! } ! ENSURE_ALLOCATION (xsum (length, tmpdst_len)); ! DCHAR_CPY (result + length, tmpdst, tmpdst_len); ! free (tmpdst); ! count = tmpdst_len; ! } ! else ! { ! /* The result string is ASCII. ! Simple 1:1 conversion. */ ! # if USE_SNPRINTF ! /* If sizeof (DCHAR_T) == sizeof (TCHAR_T), it's a ! no-op conversion, in-place on the array starting ! at (result + length). */ ! if (sizeof (DCHAR_T) != sizeof (TCHAR_T)) ! # endif ! { ! const TCHAR_T *tmpsrc; ! DCHAR_T *tmpdst; ! size_t n; ! ! # if USE_SNPRINTF ! if (result == resultbuf) ! { ! tmpsrc = (TCHAR_T *) (result + length); ! /* ENSURE_ALLOCATION will not move tmpsrc ! (because it's part of resultbuf). */ ! ENSURE_ALLOCATION (xsum (length, count)); ! } ! else ! { ! /* ENSURE_ALLOCATION will move the array ! (because it uses realloc(). */ ! ENSURE_ALLOCATION (xsum (length, count)); ! tmpsrc = (TCHAR_T *) (result + length); ! } ! # else ! tmpsrc = tmp; ! ENSURE_ALLOCATION (xsum (length, count)); ! # endif ! tmpdst = result + length; ! /* Copy backwards, because of overlapping. */ ! tmpsrc += count; ! tmpdst += count; ! for (n = count; n > 0; n--) ! *--tmpdst = (unsigned char) *--tmpsrc; ! } ! } ! #endif ! ! #if DCHAR_IS_TCHAR && !USE_SNPRINTF /* Make room for the result. */ if (count > allocated - length) { ! /* Need at least count elements. But allocate proportionally. */ size_t n = xmax (xsum (length, count), xtimes (allocated, 2)); *************** *** 3062,3138 **** /* Here count <= allocated - length. */ /* Perform padding. */ ! #if NEED_PRINTF_FLAG_ZERO ! if (pad_ourselves && has_width && count < width) { ! # if USE_SNPRINTF ! /* Make room for the result. */ ! if (width > maxlen) ! { ! /* Need at least width bytes. But allocate ! proportionally, to avoid looping eternally if ! snprintf() reports a too small count. */ ! size_t n = ! xmax (xsum (length, width), ! xtimes (allocated, 2)); ! ! length += count; ! ENSURE_ALLOCATION (n); ! length -= count; ! } ! /* Here width <= allocated - length. */ # endif ! { # if USE_SNPRINTF ! DCHAR_T * const rp = result + length; # else ! DCHAR_T * const rp = tmp; # endif ! DCHAR_T *p = rp + count; ! size_t pad = width - count; ! DCHAR_T *end = p + pad; ! DCHAR_T *pad_ptr = (*rp == '-' ? rp + 1 : rp); ! /* No zero-padding of "inf" and "nan". */ ! if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') ! || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) ! pad_ptr = NULL; ! /* The generated string now extends from rp to p, ! with the zero padding insertion point being at ! pad_ptr. */ ! if (flags & FLAG_LEFT) ! { ! /* Pad with spaces on the right. */ ! for (; pad > 0; pad--) ! *p++ = ' '; ! } ! else if ((flags & FLAG_ZERO) && pad_ptr != NULL) ! { ! /* Pad with zeroes. */ ! DCHAR_T *q = end; ! while (p > pad_ptr) ! *--q = *--p; ! for (; pad > 0; pad--) ! *p++ = '0'; ! } ! else ! { ! /* Pad with spaces on the left. */ ! DCHAR_T *q = end; ! while (p > rp) ! *--q = *--p; ! for (; pad > 0; pad--) ! *p++ = ' '; ! } ! count = width; /* = count + pad = end - rp */ ! } } #endif ! #if !USE_SNPRINTF if (count >= tmp_length) /* tmp_length was incorrectly calculated - fix the code above! */ --- 3773,3876 ---- /* Here count <= allocated - length. */ /* Perform padding. */ ! #if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO ! if (pad_ourselves && has_width) { ! size_t w; ! # if ENABLE_UNISTDIO ! /* Outside POSIX, it's preferrable to compare the width ! against the number of _characters_ of the converted ! value. */ ! w = DCHAR_MBSNLEN (result + length, count); ! # else ! /* The width is compared against the number of _bytes_ ! of the converted value, says POSIX. */ ! w = count; # endif ! if (w < width) ! { ! size_t pad = width - w; # if USE_SNPRINTF ! /* Make room for the result. */ ! if (xsum (count, pad) > allocated - length) ! { ! /* Need at least count + pad elements. But ! allocate proportionally. */ ! size_t n = ! xmax (xsum3 (length, count, pad), ! xtimes (allocated, 2)); ! ! length += count; ! ENSURE_ALLOCATION (n); ! length -= count; ! } ! /* Here count + pad <= allocated - length. */ ! # endif ! { ! # if !DCHAR_IS_TCHAR || USE_SNPRINTF ! DCHAR_T * const rp = result + length; # else ! DCHAR_T * const rp = tmp; # endif ! DCHAR_T *p = rp + count; ! DCHAR_T *end = p + pad; ! # if NEED_PRINTF_FLAG_ZERO ! DCHAR_T *pad_ptr; ! # if !DCHAR_IS_TCHAR ! if (dp->conversion == 'c' ! || dp->conversion == 's') ! /* No zero-padding for string directives. */ ! pad_ptr = NULL; ! else ! # endif ! { ! pad_ptr = (*rp == '-' ? rp + 1 : rp); ! /* No zero-padding of "inf" and "nan". */ ! if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') ! || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) ! pad_ptr = NULL; ! } ! # endif ! /* The generated string now extends from rp to p, ! with the zero padding insertion point being at ! pad_ptr. */ ! count = count + pad; /* = end - rp */ ! if (flags & FLAG_LEFT) ! { ! /* Pad with spaces on the right. */ ! for (; pad > 0; pad--) ! *p++ = ' '; ! } ! # if NEED_PRINTF_FLAG_ZERO ! else if ((flags & FLAG_ZERO) && pad_ptr != NULL) ! { ! /* Pad with zeroes. */ ! DCHAR_T *q = end; ! while (p > pad_ptr) ! *--q = *--p; ! for (; pad > 0; pad--) ! *p++ = '0'; ! } ! # endif ! else ! { ! /* Pad with spaces on the left. */ ! DCHAR_T *q = end; ! while (p > rp) ! *--q = *--p; ! for (; pad > 0; pad--) ! *p++ = ' '; ! } ! } ! } } #endif ! #if DCHAR_IS_TCHAR && !USE_SNPRINTF if (count >= tmp_length) /* tmp_length was incorrectly calculated - fix the code above! */ *************** *** 3141,3151 **** /* Here still count <= allocated - length. */ ! #if USE_SNPRINTF /* The snprintf() result did fit. */ #else /* Append the sprintf() result. */ memcpy (result + length, tmp, count * sizeof (DCHAR_T)); if (tmp != tmpbuf) free (tmp); #endif --- 3879,3891 ---- /* Here still count <= allocated - length. */ ! #if !DCHAR_IS_TCHAR || USE_SNPRINTF /* The snprintf() result did fit. */ #else /* Append the sprintf() result. */ memcpy (result + length, tmp, count * sizeof (DCHAR_T)); + #endif + #if !USE_SNPRINTF if (tmp != tmpbuf) free (tmp); #endif *************** *** 3214,3219 **** --- 3954,3960 ---- } } + #undef TCHARS_PER_DCHAR #undef SNPRINTF #undef USE_SNPRINTF #undef PRINTF_PARSE *** m4/vasnprintf.m4 10 Jun 2007 15:05:31 -0000 1.27 --- m4/vasnprintf.m4 11 Jun 2007 00:47:00 -0000 *************** *** 1,4 **** ! # vasnprintf.m4 serial 19 dnl Copyright (C) 2002-2004, 2006-2007 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, --- 1,4 ---- ! # vasnprintf.m4 serial 20 dnl Copyright (C) 2002-2004, 2006-2007 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, *************** *** 178,183 **** --- 178,196 ---- esac ]) + # Prerequisites of lib/vasnprintf.c including all extras for POSIX compliance. + AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_EXTRAS], + [ + AC_REQUIRE([gl_PREREQ_VASNPRINTF]) + gl_PREREQ_VASNPRINTF_LONG_DOUBLE + gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE + gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE + gl_PREREQ_VASNPRINTF_DIRECTIVE_A + gl_PREREQ_VASNPRINTF_DIRECTIVE_F + gl_PREREQ_VASNPRINTF_FLAG_GROUPING + gl_PREREQ_VASNPRINTF_FLAG_ZERO + ]) + # Prerequisites of lib/asnprintf.c. AC_DEFUN([gl_PREREQ_ASNPRINTF], [