Yet more printf de-duplication.
Bug: http://b/67371539
Test: ran tests
Change-Id: Iae38055426596919c0de4f5080c99eba95fac3a9
diff --git a/libc/stdio/printf_common.h b/libc/stdio/printf_common.h
index 13b4246..050120e 100644
--- a/libc/stdio/printf_common.h
+++ b/libc/stdio/printf_common.h
@@ -681,3 +681,148 @@
*tablesize = new_size;
return 0;
}
+
+struct helpers {
+ // Flush out all the vectors defined by the given uio,
+ // then reset it so that it can be reused.
+ static int sprint(FILE* fp, struct __suio* uio) {
+ if (uio->uio_resid == 0) {
+ uio->uio_iovcnt = 0;
+ return 0;
+ }
+ int result = __sfvwrite(fp, uio);
+ uio->uio_resid = 0;
+ uio->uio_iovcnt = 0;
+ return result;
+ }
+
+ // 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) 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) return NULL;
+ }
+ }
+ if ((convbuf = static_cast<char*>(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);
+ return NULL;
+ }
+ convbuf[nbytes] = '\0';
+ return convbuf;
+ }
+
+ // Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
+ // File must already be locked.
+ static wint_t xfputwc(wchar_t wc, FILE* fp) {
+ if ((fp->_flags & __SSTR) == 0) return __fputwc_unlock(wc, fp);
+
+ char buf[MB_LEN_MAX];
+ mbstate_t mbs = {};
+ size_t len = wcrtomb(buf, wc, &mbs);
+ if (len == (size_t)-1) {
+ fp->_flags |= __SERR;
+ errno = EILSEQ;
+ return WEOF;
+ }
+
+ struct __siov iov;
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ struct __suio uio;
+ uio.uio_iov = &iov;
+ uio.uio_resid = len;
+ uio.uio_iovcnt = 1;
+ return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
+ }
+
+ // Convert a multibyte character string argument for the %s format to a wide
+ // 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 multibyte character string ends in a null character.
+ //
+ // Returns NULL on failure.
+ // To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
+ static wchar_t* mbsconv(char* mbsarg, int prec) {
+ mbstate_t mbs;
+ const char* p;
+ size_t insize, nchars, nconv;
+
+ if (mbsarg == NULL) return NULL;
+
+ // Supplied argument is a multibyte string; convert it to wide characters first.
+ if (prec >= 0) {
+ // String is not guaranteed to be NUL-terminated. Find the number of characters to print.
+ p = mbsarg;
+ insize = nchars = nconv = 0;
+ bzero(&mbs, sizeof(mbs));
+ while (nchars != (size_t)prec) {
+ nconv = mbrlen(p, MB_CUR_MAX, &mbs);
+ if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
+ p += nconv;
+ nchars++;
+ insize += nconv;
+ }
+ if (nconv == (size_t)-1 || nconv == (size_t)-2) return (NULL);
+ } else {
+ insize = strlen(mbsarg);
+ }
+
+ // Allocate buffer for the result and perform the conversion,
+ // converting at most `size' bytes of the input multibyte string to
+ // wide characters for printing.
+ wchar_t* convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf)));
+ if (convbuf == NULL) return NULL;
+ wchar_t* wcp = convbuf;
+ p = mbsarg;
+ bzero(&mbs, sizeof(mbs));
+ nconv = 0;
+ while (insize != 0) {
+ nconv = mbrtowc(wcp, p, insize, &mbs);
+ if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
+ wcp++;
+ p += nconv;
+ insize -= nconv;
+ }
+ if (nconv == (size_t)-1 || nconv == (size_t)-2) {
+ free(convbuf);
+ return NULL;
+ }
+ *wcp = '\0';
+
+ return convbuf;
+ }
+
+};