Bug 1060419 - add %S and %ls support to Smprintf; r?froydnj draft
authorTom Tromey <tom@tromey.com>
Thu, 19 Jan 2017 15:56:09 -0700
changeset 486141 a6594ded90913ed3c81792fa1ce6a0adec87313d
parent 486140 f4fd06af8693cbe51d28fa695c104973bdd8da9a
child 486142 567238bf274382cbebea4519e901f84349464fed
push id45909
push userbmo:ttromey@mozilla.com
push dateFri, 17 Feb 2017 16:00:11 +0000
reviewersfroydnj
bugs1060419
milestone54.0a1
Bug 1060419 - add %S and %ls support to Smprintf; r?froydnj MozReview-Commit-ID: CQMZCkCOXDV
mozglue/misc/Printf.cpp
--- a/mozglue/misc/Printf.cpp
+++ b/mozglue/misc/Printf.cpp
@@ -8,23 +8,28 @@
  * Portable safe sprintf code.
  *
  * Author: Kipp E.B. Hickman
  */
 
 #include "mozilla/AllocPolicy.h"
 #include "mozilla/Printf.h"
 #include "mozilla/Sprintf.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Vector.h"
 
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#if defined(XP_WIN)
+#include <windows.h>
+#endif
+
 /*
  * Note: on some platforms va_list is defined as an array,
  * and requires array notation.
  */
 #ifdef HAVE_VA_COPY
 #define VARARGS_ASSIGN(foo, bar)        VA_COPY(foo, bar)
 #elif defined(HAVE_VA_LIST_AS_ARRAY)
 #define VARARGS_ASSIGN(foo, bar)        foo[0] = bar[0]
@@ -51,16 +56,19 @@ typedef mozilla::Vector<NumArgState, 20,
 #define TYPE_LONG       4
 #define TYPE_ULONG      5
 #define TYPE_LONGLONG   6
 #define TYPE_ULONGLONG  7
 #define TYPE_STRING     8
 #define TYPE_DOUBLE     9
 #define TYPE_INTSTR     10
 #define TYPE_POINTER    11
+#if defined(XP_WIN)
+#define TYPE_WSTRING    12
+#endif
 #define TYPE_UNKNOWN    20
 
 #define FLAG_LEFT       0x1
 #define FLAG_SIGNED     0x2
 #define FLAG_SPACED     0x4
 #define FLAG_ZEROS      0x8
 #define FLAG_NEG        0x10
 
@@ -441,27 +449,42 @@ BuildArgArray(const char* fmt, va_list a
         case 'g':
             nas[cn].type = TYPE_DOUBLE;
             break;
 
         case 'p':
             nas[cn].type = TYPE_POINTER;
             break;
 
+        case 'S':
+#if defined(XP_WIN)
+            nas[cn].type = TYPE_WSTRING;
+            break;
+#endif
+            /* Fall through here when not XP_WIN.  */
         case 'C':
-        case 'S':
         case 'E':
         case 'G':
             // XXX not supported I suppose
             MOZ_ASSERT(0);
             nas[cn].type = TYPE_UNKNOWN;
             break;
 
         case 's':
-            nas[cn].type = TYPE_STRING;
+#if defined(XP_WIN)
+            if (nas[cn].type == TYPE_LONG) {
+                nas[cn].type = TYPE_WSTRING;
+                break;
+            }
+#endif
+            if (nas[cn].type == TYPE_INTN) {
+                nas[cn].type = TYPE_STRING;
+            } else {
+                nas[cn].type = TYPE_UNKNOWN;
+            }
             break;
 
         case 'n':
             nas[cn].type = TYPE_INTSTR;
             break;
 
         default:
             MOZ_ASSERT(0);
@@ -495,16 +518,19 @@ BuildArgArray(const char* fmt, va_list a
         case TYPE_LONG:         (void) va_arg(ap, long);        break;
         case TYPE_ULONG:        (void) va_arg(ap, unsigned long); break;
         case TYPE_LONGLONG:     (void) va_arg(ap, long long);   break;
         case TYPE_ULONGLONG:    (void) va_arg(ap, unsigned long long); break;
         case TYPE_STRING:       (void) va_arg(ap, char*);       break;
         case TYPE_INTSTR:       (void) va_arg(ap, int*);        break;
         case TYPE_DOUBLE:       (void) va_arg(ap, double);      break;
         case TYPE_POINTER:      (void) va_arg(ap, void*);       break;
+#if defined(XP_WIN)
+        case TYPE_WSTRING:      (void) va_arg(ap, wchar_t*);    break;
+#endif
 
         default: MOZ_CRASH();
         }
 
         cn++;
     }
 
     return true;
@@ -519,16 +545,19 @@ mozilla::PrintfTarget::vprint(const char
         char ch;
         int i;
         long l;
         long long ll;
         double d;
         const char* s;
         int* ip;
         void* p;
+#if defined(XP_WIN)
+        const wchar_t* ws;
+#endif
     } u;
     const char* fmt0;
     static const char hex[] = "0123456789abcdef";
     static const char HEX[] = "0123456789ABCDEF";
     const char* hexp;
     int i;
     char pattern[20];
     const char* dolPt = nullptr;  // in "%4$.2f", dolPt will point to '.'
@@ -777,28 +806,63 @@ mozilla::PrintfTarget::vprint(const char
 
           case 'p':
             type = TYPE_POINTER;
             radix = 16;
             goto fetch_and_convert;
 
 #if 0
           case 'C':
-          case 'S':
           case 'E':
           case 'G':
             // XXX not supported I suppose
             MOZ_ASSERT(0);
             break;
 #endif
 
           case 's':
-            u.s = va_arg(ap, const char*);
-            if (!cvt_s(u.s, width, prec, flags))
-                return false;
+            if (type == TYPE_INTN) {
+                u.s = va_arg(ap, const char*);
+                if (!cvt_s(u.s, width, prec, flags))
+                    return false;
+                break;
+            } else if (type == TYPE_LONGLONG) {
+                // This should have asserted during BuildArgArray anyway.
+                MOZ_ASSERT(0);
+                break;
+            }
+            MOZ_ASSERT(type == TYPE_LONG);
+            MOZ_FALLTHROUGH;
+          case 'S':
+#if defined(XP_WIN)
+            {
+                u.ws = va_arg(ap, const wchar_t*);
+
+                int rv = WideCharToMultiByte(CP_ACP, 0, u.ws, -1, NULL, 0, NULL, NULL);
+                if (rv == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION) {
+                    if (!cvt_s("<unicode errors in string>", width, prec, flags)) {
+                        return false;
+                    }
+                } else {
+                    if (rv == 0) {
+                        rv = 1;
+                    }
+                    UniqueFreePtr<char[]> buf((char*)malloc(rv));
+                    WideCharToMultiByte(CP_ACP, 0, u.ws, -1, buf.get(), rv, NULL, NULL);
+                    buf[rv - 1] = '\0';
+
+                    if (!cvt_s(buf.get(), width, prec, flags)) {
+                        return false;
+                    }
+                }
+            }
+#else
+            // This should have asserted during BuildArgArray anyway.
+            MOZ_ASSERT(0);
+#endif
             break;
 
           case 'n':
             u.ip = va_arg(ap, int*);
             if (u.ip) {
                 *u.ip = mEmitted;
             }
             break;
@@ -842,15 +906,16 @@ mozilla::PrintfTarget::print(const char*
 #undef TYPE_LONG
 #undef TYPE_ULONG
 #undef TYPE_LONGLONG
 #undef TYPE_ULONGLONG
 #undef TYPE_STRING
 #undef TYPE_DOUBLE
 #undef TYPE_INTSTR
 #undef TYPE_POINTER
+#undef TYPE_WSTRING
 #undef TYPE_UNKNOWN
 
 #undef FLAG_LEFT
 #undef FLAG_SIGNED
 #undef FLAG_SPACED
 #undef FLAG_ZEROS
 #undef FLAG_NEG