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.
This commit is contained in:
Tim J. Robbins 2002-09-19 12:50:28 +00:00
parent 751ec0537d
commit b9aac30810
2 changed files with 110 additions and 14 deletions

View File

@ -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"

View File

@ -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 *);
@ -328,6 +332,65 @@ __ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs,
return (cp);
}
/*
* 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
*/
@ -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 @@ number: if ((dprec = prec) >= 0)
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 @@ reswitch: switch (ch) {
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;
}
}