diff options
author | tjr <tjr@FreeBSD.org> | 2002-09-19 12:50:28 +0000 |
---|---|---|
committer | tjr <tjr@FreeBSD.org> | 2002-09-19 12:50:28 +0000 |
commit | a7450cd28a6c40436b33b9e2d50e79e86248466b (patch) | |
tree | 6718a52633a6aaa02eb8129c4686ebbbc379916e /lib/libc/stdio | |
parent | 1564160a1a36ac25621d820696fea9a4d347e8d5 (diff) | |
download | FreeBSD-src-a7450cd28a6c40436b33b9e2d50e79e86248466b.zip FreeBSD-src-a7450cd28a6c40436b33b9e2d50e79e86248466b.tar.gz |
Implement the %ls and %lc conversions for printing wide character strings
and wide characters. These were already documented in the manual page,
with an entry mentioning that they were not implemented yet. The XSI
%S and %C synoyms have not been added.
Diffstat (limited to 'lib/libc/stdio')
-rw-r--r-- | lib/libc/stdio/printf.3 | 8 | ||||
-rw-r--r-- | lib/libc/stdio/vfprintf.c | 116 |
2 files changed, 110 insertions, 14 deletions
diff --git a/lib/libc/stdio/printf.3 b/lib/libc/stdio/printf.3 index 7fde56a..8a29269 100644 --- a/lib/libc/stdio/printf.3 +++ b/lib/libc/stdio/printf.3 @@ -844,14 +844,6 @@ and .Cm A conversion specifiers have not yet been implemented. The -.Cm l -(ell) modifier for the -.Cm c -and -.Cm s -conversion specifiers, for wide characters and strings, have not yet -been implemented. -The .Cm L modifier for floating point formats simply round the .Vt "long double" diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c index 45ee3c1..23914b1 100644 --- a/lib/libc/stdio/vfprintf.c +++ b/lib/libc/stdio/vfprintf.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <wchar.h> #include <stdarg.h> #include "un-namespace.h" @@ -93,6 +94,8 @@ union arg { double doublearg; long double longdoublearg; #endif + wint_t wintarg; + wchar_t *pwchararg; }; /* @@ -103,7 +106,7 @@ enum typeid { T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG, T_PTRDIFFT, TP_PTRDIFFT, T_SIZET, TP_SIZET, T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR, - T_DOUBLE, T_LONG_DOUBLE + T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR }; static int __sprint(FILE *, struct __suio *); @@ -112,6 +115,7 @@ static char *__ujtoa(uintmax_t, char *, int, int, char *, int, char, const char *); static char *__ultoa(u_long, char *, int, int, char *, int, char, const char *); +static char *__wcsconv(wchar_t *, int); static void __find_arguments(const char *, va_list, union arg **); static void __grow_type_table(int, enum typeid **, int *); @@ -329,6 +333,65 @@ __ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs, } /* + * Convert a wide character string argument for the %ls format to a multibyte + * string representation. ``prec'' specifies the maximum number of bytes + * to output. If ``prec'' is greater than or equal to zero, we can't assume + * that the wide char. string ends in a null character. + */ +static char * +__wcsconv(wchar_t *wcsarg, int prec) +{ + char buf[MB_LEN_MAX]; + wchar_t *p; + char *convbuf, *mbp; + size_t clen, nbytes; + mbstate_t mbs; + + /* + * Determine the number of bytes to output and allocate space for + * the output. + */ + memset(&mbs, 0, sizeof(mbs)); + if (prec >= 0) { + nbytes = 0; + p = wcsarg; + for (;;) { + clen = wcrtomb(buf, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1 || + nbytes + clen > prec) + break; + nbytes += clen; + } + } else { + p = wcsarg; + nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); + if (nbytes == (size_t)-1) + return (NULL); + } + if ((convbuf = malloc(nbytes + 1)) == NULL) + return (NULL); + + /* + * Fill the output buffer with the multibyte representations of as + * many wide characters as will fit. + */ + mbp = convbuf; + p = wcsarg; + memset(&mbs, 0, sizeof(mbs)); + while (mbp - convbuf < nbytes) { + clen = wcrtomb(mbp, *p++, &mbs); + if (clen == 0 || clen == (size_t)-1) + break; + mbp += clen; + } + *mbp = '\0'; + if (clen == (size_t)-1) + return (NULL); + + return (convbuf); +} + +/* * MT-safe version */ int @@ -425,6 +488,7 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) union arg statargtable [STATIC_ARG_TBL_SIZE]; int nextarg; /* 1-based argument index */ va_list orgap; /* original argument pointer */ + char *convbuf; /* wide to multibyte conversion result */ /* * Choose PADSIZE to trade efficiency vs. size. If larger printf @@ -530,6 +594,7 @@ __vfprintf(FILE *fp, const char *fmt0, va_list ap) thousands_sep = '\0'; grouping = NULL; + convbuf = NULL; #ifdef FLOATING_POINT dtoaresult = NULL; decimal_point = localeconv()->decimal_point; @@ -684,8 +749,20 @@ reswitch: switch (ch) { flags |= SIZET; goto rflag; case 'c': - *(cp = buf) = GETARG(int); - size = 1; + if (flags & LONGINT) { + mbstate_t mbs; + size_t mbseqlen; + + memset(&mbs, 0, sizeof(mbs)); + mbseqlen = wcrtomb(cp = buf, + (wchar_t)GETARG(wint_t), &mbs); + if (mbseqlen == (size_t)-1) + goto error; + size = (int)mbseqlen; + } else { + *(cp = buf) = GETARG(int); + size = 1; + } sign = '\0'; break; case 'D': @@ -842,7 +919,20 @@ fp_begin: if (prec == -1) ch = 'x'; goto nosign; case 's': - if ((cp = GETARG(char *)) == NULL) + if (flags & LONGINT) { + wchar_t *wcp; + + if (convbuf != NULL) + free(convbuf); + if ((wcp = GETARG(wchar_t *)) == NULL) + cp = "(null)"; + else { + convbuf = __wcsconv(wcp, prec); + if (convbuf == NULL) + goto error; + cp = convbuf; + } + } else if ((cp = GETARG(char *)) == NULL) cp = "(null)"; if (prec >= 0) { /* @@ -1040,6 +1130,8 @@ error: if (dtoaresult != NULL) free(dtoaresult); #endif + if (convbuf != NULL) + free(convbuf); if (__sferror(fp)) ret = EOF; if ((argtable != NULL) && (argtable != statargtable)) @@ -1199,7 +1291,10 @@ reswitch: switch (ch) { flags |= SIZET; goto rflag; case 'c': - ADDTYPE(T_INT); + if (flags & LONGINT) + ADDTYPE(T_WINT); + else + ADDTYPE(T_INT); break; case 'D': flags |= LONGINT; @@ -1252,7 +1347,10 @@ reswitch: switch (ch) { ADDTYPE(TP_VOID); break; case 's': - ADDTYPE(TP_CHAR); + if (flags & LONGINT) + ADDTYPE(TP_WCHAR); + else + ADDTYPE(TP_CHAR); break; case 'U': flags |= LONGINT; @@ -1351,6 +1449,12 @@ done: case TP_VOID: (*argtable) [n].pvoidarg = va_arg (ap, void *); break; + case T_WINT: + (*argtable) [n].wintarg = va_arg (ap, wint_t); + break; + case TP_WCHAR: + (*argtable) [n].pwchararg = va_arg (ap, wchar_t *); + break; } } |