On Fri, Apr 29, 2011 at 01:09:56PM +0200, Stefan Sperling wrote:
> Anyone?

Updated diff following the vfwprintf() memory leak fix.
The previous version of this diff had the same bug.

Index: Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/stdio/Makefile.inc,v
retrieving revision 1.16
diff -u -p -r1.16 Makefile.inc
--- Makefile.inc        28 Apr 2011 17:38:46 -0000      1.16
+++ Makefile.inc        29 Apr 2011 12:52:29 -0000
@@ -3,7 +3,7 @@
 # stdio sources
 .PATH: ${LIBCSRCDIR}/stdio
 
-CFLAGS+=-DFLOATING_POINT
+CFLAGS+=-DFLOATING_POINT -DPRINTF_WIDE_CHAR
 
 SRCS+= asprintf.c clrerr.c fclose.c fdopen.c feof.c ferror.c fflush.c fgetc.c \
        fgetln.c fgetpos.c fgets.c fileno.c findfp.c flags.c fopen.c \
Index: vfprintf.c
===================================================================
RCS file: /cvs/src/lib/libc/stdio/vfprintf.c,v
retrieving revision 1.60
diff -u -p -r1.60 vfprintf.c
--- vfprintf.c  22 Dec 2010 14:54:44 -0000      1.60
+++ vfprintf.c  8 May 2011 23:17:09 -0000
@@ -49,6 +49,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <wchar.h>
 
 #include "local.h"
 #include "fvwrite.h"
@@ -79,6 +80,8 @@ union arg {
        double                  doublearg;
        long double             longdoublearg;
 #endif
+       wint_t                  wintarg;
+       wchar_t                 *pwchararg;
 };
 
 static int __find_arguments(const char *fmt0, va_list ap, union arg **argtable,
@@ -138,6 +141,72 @@ __sbprintf(FILE *fp, const char *fmt, va
        return (ret);
 }
 
+#ifdef PRINTF_WIDE_CHAR
+/*
+ * Convert a wide character string argument for the %ls format to a multibyte
+ * string representation. If not -1, prec specifies the maximum number of
+ * bytes to output, and also means that we can't assume that the wide char
+ * string is null-terminated.
+ */
+static char *
+__wcsconv(wchar_t *wcsarg, int prec)
+{
+       mbstate_t mbs;
+       char buf[MB_LEN_MAX];
+       wchar_t *p;
+       char *convbuf;
+       size_t clen, nbytes;
+
+       /* Allocate space for the maximum number of bytes we could output. */
+       if (prec < 0) {
+               memset(&mbs, 0, sizeof(mbs));
+               p = wcsarg;
+               nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
+               if (nbytes == (size_t)-1) {
+                       errno = EILSEQ;
+                       return (NULL);
+               }
+       } else {
+               /*
+                * Optimisation: if the output precision is small enough,
+                * just allocate enough memory for the maximum instead of
+                * scanning the string.
+                */
+               if (prec < 128)
+                       nbytes = prec;
+               else {
+                       nbytes = 0;
+                       p = wcsarg;
+                       memset(&mbs, 0, sizeof(mbs));
+                       for (;;) {
+                               clen = wcrtomb(buf, *p++, &mbs);
+                               if (clen == 0 || clen == (size_t)-1 ||
+                                   nbytes + clen > (size_t)prec)
+                                       break;
+                               nbytes += clen;
+                       }
+                       if (clen == (size_t)-1) {
+                               errno = EILSEQ;
+                               return (NULL);
+                       }
+               }
+       }
+       if ((convbuf = malloc(nbytes + 1)) == NULL)
+               return (NULL);
+
+       /* Fill the output buffer. */
+       p = wcsarg;
+       memset(&mbs, 0, sizeof(mbs));
+       if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
+           nbytes, &mbs)) == (size_t)-1) {
+               free(convbuf);
+               errno = EILSEQ;
+               return (NULL);
+       }
+       convbuf[nbytes] = '\0';
+       return (convbuf);
+}
+#endif
 
 #ifdef FLOATING_POINT
 #include <float.h>
@@ -260,7 +329,9 @@ __vfprintf(FILE *fp, const char *fmt0, _
        size_t argtablesiz;
        int nextarg;            /* 1-based argument index */
        va_list orgap;          /* original argument pointer */
-
+#ifdef PRINTF_WIDE_CHAR
+       char *convbuf;          /* buffer for wide to multi-byte conversion */
+#endif
        /*
         * Choose PADSIZE to trade efficiency vs. size.  If larger printf
         * fields occur frequently, increase PADSIZE and make the initialisers
@@ -402,7 +473,9 @@ __vfprintf(FILE *fp, const char *fmt0, _
        uio.uio_resid = 0;
        uio.uio_iovcnt = 0;
        ret = 0;
-
+#ifdef PRINTF_WIDE_CHAR
+       convbuf = NULL;
+#endif
        memset(&ps, 0, sizeof(ps));
        /*
         * Scan the format for conversions (`%' character).
@@ -553,8 +626,28 @@ reswitch:  switch (ch) {
                        flags |= SIZEINT;
                        goto rflag;
                case 'c':
-                       *(cp = buf) = GETARG(int);
-                       size = 1;
+#ifdef PRINTF_WIDE_CHAR
+                       if (flags & LONGINT) {
+                               mbstate_t mbs;
+                               size_t mbseqlen;
+
+                               memset(&mbs, 0, sizeof(mbs));
+                               mbseqlen = wcrtomb(buf,
+                                   (wchar_t)GETARG(wint_t), &mbs);
+                               if (mbseqlen == (size_t)-1) {
+                                       fp->_flags |= __SERR;
+                                       errno = EILSEQ;
+                                       goto error;
+                               }
+                               cp = buf;
+                               size = (int)mbseqlen;
+                       } else {
+#endif
+                               *(cp = buf) = GETARG(int);
+                               size = 1;
+#ifdef PRINTF_WIDE_CHAR
+                       }
+#endif
                        sign = '\0';
                        break;
                case 'D':
@@ -744,6 +837,26 @@ fp_common:
                        ox[1] = 'x';
                        goto nosign;
                case 's':
+#ifdef PRINTF_WIDE_CHAR
+                       if (flags & LONGINT) {
+                               wchar_t *wcp;
+
+                               if (convbuf != NULL) {
+                                       free(convbuf);
+                                       convbuf = NULL;
+                               }
+                               if ((wcp = GETARG(wchar_t *)) == NULL) {
+                                       cp = "(null)";
+                               } else {
+                                       convbuf = __wcsconv(wcp, prec);
+                                       if (convbuf == NULL) {
+                                               fp->_flags = __SERR;
+                                               goto error;
+                                       }
+                                       cp = convbuf;
+                               }
+                       } else
+#endif /* PRINTF_WIDE_CHAR */
                        if ((cp = GETARG(char *)) == NULL)
                                cp = "(null)";
                        if (prec >= 0) {
@@ -954,6 +1067,10 @@ overflow:
        ret = -1;
 
 finish:
+#ifdef PRINTF_WIDE_CHAR
+       if (convbuf)
+               free(convbuf);
+#endif
 #ifdef FLOATING_POINT
        if (dtoaresult)
                __freedtoa(dtoaresult);
@@ -995,6 +1112,8 @@ finish:
 #define TP_MAXINT      24
 #define T_CHAR         25
 #define T_U_CHAR       26
+#define T_WINT         27
+#define TP_WCHAR       28
 
 /*
  * Find all arguments when a positional parameter is encountered.  Returns a
@@ -1160,7 +1279,12 @@ reswitch:        switch (ch) {
                        flags |= SIZEINT;
                        goto rflag;
                case 'c':
-                       ADDTYPE(T_INT);
+#ifdef PRINTF_WIDE_CHAR
+                       if (flags & LONGINT)
+                               ADDTYPE(T_WINT);
+                       else
+#endif
+                               ADDTYPE(T_INT);
                        break;
                case 'D':
                        flags |= LONGINT;
@@ -1210,7 +1334,12 @@ reswitch:        switch (ch) {
                        ADDTYPE(TP_VOID);
                        break;
                case 's':
-                       ADDTYPE(TP_CHAR);
+#ifdef PRINTF_WIDE_CHAR
+                       if (flags & LONGINT)
+                               ADDTYPE(TP_WCHAR);
+                       else
+#endif
+                               ADDTYPE(TP_CHAR);
                        break;
                case 'U':
                        flags |= LONGINT;
@@ -1311,6 +1440,14 @@ done:
                case TP_MAXINT:
                        (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
                        break;
+#ifdef PRINTF_WIDE_CHAR
+               case T_WINT:
+                       (*argtable)[n].wintarg = va_arg(ap, wint_t);
+                       break;
+               case TP_WCHAR:
+                       (*argtable)[n].pwchararg = va_arg(ap, wchar_t *);
+                       break;
+#endif
                }
        }
        goto finish;

Reply via email to