cmd: Use FormatMessage() for better internationalization support.

Francois Gouget fgouget at free.fr
Tue Dec 6 10:40:42 CST 2011


This makes it possible to reorder the format string placeholders in message translations.
Also add a WCMD_format_string() function to mirror the WCMD_output*() ones and so all resources can use the same formatting style.
This also simplifies the padding code for the 'dir /w' command.
---

Yay. This is was the last case of a non-reorderable format string in the 
po files.


 programs/cmd/builtins.c  |   27 +++++++-------
 programs/cmd/cmd.rc      |   28 +++++++-------
 programs/cmd/directory.c |   59 +++++++++++-------------------
 programs/cmd/wcmd.h      |    1 +
 programs/cmd/wcmdmain.c  |   90 +++++++++++++++++++++++++++++++---------------
 5 files changed, 111 insertions(+), 94 deletions(-)

diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 216a28b..ed34585 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -488,9 +488,10 @@ void WCMD_copy (void) {
         else if (!overwrite) {
           attribs = GetFileAttributesW(outname);
           if (attribs != INVALID_FILE_ATTRIBUTES) {
-            WCHAR buffer[MAXSTRING];
-            wsprintfW(buffer, WCMD_LoadMessage(WCMD_OVERWRITE), outname);
-            overwrite = WCMD_ask_confirm(buffer, FALSE, NULL);
+            WCHAR* question;
+            question = WCMD_format_string(WCMD_LoadMessage(WCMD_OVERWRITE), outname);
+            overwrite = WCMD_ask_confirm(question, FALSE, NULL);
+            LocalFree(question);
           }
           else overwrite = TRUE;
         }
@@ -734,11 +735,12 @@ static BOOL WCMD_delete_one (const WCHAR *thisArg) {
 
           /* /P means prompt for each file */
           if (ok && strstrW (quals, parmP) != NULL) {
-            WCHAR  question[MAXSTRING];
+            WCHAR* question;
 
             /* Ask for confirmation */
-            wsprintfW(question, WCMD_LoadMessage(WCMD_DELPROMPT), fpath);
+            question = WCMD_format_string(WCMD_LoadMessage(WCMD_DELPROMPT), fpath);
             ok = WCMD_ask_confirm(question, FALSE, NULL);
+            LocalFree(question);
           }
 
           /* Only proceed if ok to */
@@ -1711,14 +1713,15 @@ void WCMD_move (void)
 
       /* Prompt if overwriting */
       if (!force) {
-        WCHAR  question[MAXSTRING];
+        WCHAR* question;
         WCHAR  yesChar[10];
 
         strcpyW(yesChar, WCMD_LoadMessage(WCMD_YES));
 
         /* Ask for confirmation */
-        wsprintfW(question, WCMD_LoadMessage(WCMD_OVERWRITE), dest);
+        question = WCMD_format_string(WCMD_LoadMessage(WCMD_OVERWRITE), dest);
         ok = WCMD_ask_confirm(question, FALSE, NULL);
+        LocalFree(question);
 
         /* So delete the destination prior to the move */
         if (ok) {
@@ -2519,7 +2522,7 @@ void WCMD_type (WCHAR *command) {
       errorlevel = 1;
     } else {
       if (writeHeaders) {
-        static const WCHAR fmt[] = {'\n','%','s','\n','\n','\0'};
+        static const WCHAR fmt[] = {'\n','%','1','\n','\n','\0'};
         WCMD_output(fmt, thisArg);
       }
       while (WCMD_ReadFile(h, buffer, sizeof(buffer)/sizeof(WCHAR) - 1, &count)) {
@@ -2866,7 +2869,6 @@ void WCMD_assoc (const WCHAR *command, BOOL assoc) {
 
         } else {
           WCHAR  msgbuffer[MAXSTRING];
-          WCHAR  outbuffer[MAXSTRING];
 
           /* Load the translated 'File association not found' */
           if (assoc) {
@@ -2874,8 +2876,7 @@ void WCMD_assoc (const WCHAR *command, BOOL assoc) {
           } else {
             LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer, sizeof(msgbuffer)/sizeof(WCHAR));
           }
-          wsprintfW(outbuffer, msgbuffer, keyValue);
-          WCMD_output_asis_stderr(outbuffer);
+          WCMD_output_stderr(msgbuffer, keyValue);
           errorlevel = 2;
         }
 
@@ -2905,7 +2906,6 @@ void WCMD_assoc (const WCHAR *command, BOOL assoc) {
 
           } else {
             WCHAR  msgbuffer[MAXSTRING];
-            WCHAR  outbuffer[MAXSTRING];
 
             /* Load the translated 'File association not found' */
             if (assoc) {
@@ -2915,8 +2915,7 @@ void WCMD_assoc (const WCHAR *command, BOOL assoc) {
               LoadStringW(hinst, WCMD_NOFTYPE, msgbuffer,
                           sizeof(msgbuffer)/sizeof(WCHAR));
             }
-            wsprintfW(outbuffer, msgbuffer, keyValue);
-            WCMD_output_asis_stderr(outbuffer);
+            WCMD_output_stderr(msgbuffer, keyValue);
             errorlevel = 2;
           }
 
diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc
index 63ae599..7fe9d72 100644
--- a/programs/cmd/cmd.rc
+++ b/programs/cmd/cmd.rc
@@ -291,36 +291,36 @@ Enter HELP <command> for further information on any of the above commands.\n"
   WCMD_CONFIRM, "Are you sure"
   WCMD_YES, "#msgctxt#Yes key#Y"
   WCMD_NO, "#msgctxt#No key#N"
-  WCMD_NOASSOC, "File association missing for extension %s\n"
-  WCMD_NOFTYPE, "No open command associated with file type '%s'\n"
-  WCMD_OVERWRITE, "Overwrite %s"
+  WCMD_NOASSOC, "File association missing for extension %1\n"
+  WCMD_NOFTYPE, "No open command associated with file type '%1'\n"
+  WCMD_OVERWRITE, "Overwrite %1"
   WCMD_MORESTR, "More..."
   WCMD_TRUNCATEDLINE, "Line in Batch processing possibly truncated. Using:\n"
   WCMD_NYI, "Not Yet Implemented\n\n"
   WCMD_NOARG, "Argument missing\n"
   WCMD_SYNTAXERR, "Syntax error\n"
-  WCMD_FILENOTFOUND, "%s: File Not Found\n"
-  WCMD_NOCMDHELP, "No help available for %s\n"
+  WCMD_FILENOTFOUND, "%1: File Not Found\n"
+  WCMD_NOCMDHELP, "No help available for %1\n"
   WCMD_NOTARGET, "Target to GOTO not found\n"
-  WCMD_CURRENTDATE, "Current Date is %s\n"
-  WCMD_CURRENTTIME, "Current Time is %s\n"
+  WCMD_CURRENTDATE, "Current Date is %1\n"
+  WCMD_CURRENTTIME, "Current Time is %1\n"
   WCMD_NEWDATE, "Enter new date: "
   WCMD_NEWTIME, "Enter new time: "
-  WCMD_MISSINGENV, "Environment variable %s not defined\n"
-  WCMD_READFAIL, "Failed to open '%s'\n"
+  WCMD_MISSINGENV, "Environment variable %1 not defined\n"
+  WCMD_READFAIL, "Failed to open '%1'\n"
   WCMD_CALLINSCRIPT, "Cannot call batch label outside of a batch script\n"
   WCMD_ALL, "#msgctxt#All key#A"
-  WCMD_DELPROMPT, "%s, Delete"
-  WCMD_ECHOPROMPT, "Echo is %s\n"
-  WCMD_VERIFYPROMPT, "Verify is %s\n"
+  WCMD_DELPROMPT, "%1, Delete"
+  WCMD_ECHOPROMPT, "Echo is %1\n"
+  WCMD_VERIFYPROMPT, "Verify is %1\n"
   WCMD_VERIFYERR, "Verify must be ON or OFF\n"
   WCMD_ARGERR, "Parameter error\n"
-  WCMD_VOLUMEDETAIL, "Volume in drive %c is %s\nVolume Serial Number is %04x-%04x\n\n"
+  WCMD_VOLUMEDETAIL, "Volume in drive %1!c! is %2\nVolume Serial Number is %3!04x!-%4!04x!\n\n"
   WCMD_VOLUMEPROMPT, "Volume label (11 characters, ENTER for none)?"
   WCMD_NOPATH, "PATH not found\n"
   WCMD_ANYKEY,"Press any key to continue... "
   WCMD_CONSTITLE,"Wine Command Prompt"
-  WCMD_VERSION,"CMD Version %s\n"
+  WCMD_VERSION,"CMD Version %1!S!\n"
   WCMD_MOREPROMPT, "More? "
   WCMD_LINETOOLONG, "The input line is too long.\n"
 }
diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c
index 7fc60a7..1134f23 100644
--- a/programs/cmd/directory.c
+++ b/programs/cmd/directory.c
@@ -254,14 +254,14 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
   int concurrentDirs = 0;
   BOOL done_header = FALSE;
 
-  static const WCHAR fmtDir[]  = {'%','1','0','s',' ',' ','%','8','s',' ',' ',
+  static const WCHAR fmtDir[]  = {'%','1','!','1','0','s','!',' ',' ','%','2','!','8','s','!',' ',' ',
                                   '<','D','I','R','>',' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'};
-  static const WCHAR fmtFile[] = {'%','1','0','s',' ',' ','%','8','s',' ',' ',
-                                  ' ',' ','%','1','0','s',' ',' ','\0'};
-  static const WCHAR fmt2[]  = {'%','-','1','3','s','\0'};
-  static const WCHAR fmt3[]  = {'%','-','2','3','s','\0'};
-  static const WCHAR fmt4[]  = {'%','s','\0'};
-  static const WCHAR fmt5[]  = {'%','s','%','s','\0'};
+  static const WCHAR fmtFile[] = {'%','1','!','1','0','s','!',' ',' ','%','2','!','8','s','!',' ',' ',
+                                  ' ',' ','%','3','!','1','0','s','!',' ',' ','\0'};
+  static const WCHAR fmt2[]  = {'%','1','!','-','1','3','s','!','\0'};
+  static const WCHAR fmt3[]  = {'%','1','!','-','2','3','s','!','\0'};
+  static const WCHAR fmt4[]  = {'%','1','\0'};
+  static const WCHAR fmt5[]  = {'%','1','%','2','\0'};
 
   dir_count = 0;
   file_count = 0;
@@ -319,7 +319,7 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
        if (level != 0 && (entry_count > 0)) WCMD_output_asis (newline);
        if (!recurse || ((entry_count > 0) && done_header==FALSE)) {
            static const WCHAR headerW[] = {'D','i','r','e','c','t','o','r','y',' ','o','f',
-                                           ' ','%','s','\n','\n','\0'};
+                                           ' ','%','1','\n','\n','\0'};
            WCMD_output (headerW, real_path);
            done_header = TRUE;
        }
@@ -391,12 +391,12 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
 
         tmp_width = cur_width;
         if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
-            static const WCHAR fmt[] = {'[','%','s',']','\0'};
+            static const WCHAR fmt[] = {'[','%','1',']','\0'};
             WCMD_output (fmt, (fd+i)->cFileName);
             dir_count++;
             tmp_width = tmp_width + strlenW((fd+i)->cFileName) + 2;
         } else {
-            static const WCHAR fmt[] = {'%','s','\0'};
+            static const WCHAR fmt[] = {'%','1','\0'};
             WCMD_output (fmt, (fd+i)->cFileName);
             tmp_width = tmp_width + strlenW((fd+i)->cFileName) ;
             file_count++;
@@ -409,24 +409,9 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
         if ((cur_width + widest) > max_width) {
             cur_width = 0;
         } else {
-            int padding = cur_width - tmp_width;
-            int toWrite = 0;
-            WCHAR temp[101];
-
-            /* Note: WCMD_output uses wvsprintf which does not allow %*
-                 so manually pad with spaces to appropriate width       */
-            strcpyW(temp, nullW);
-            while (padding > 0) {
-                strcatW(&temp[toWrite], space);
-                toWrite++;
-                if (toWrite > 99) {
-                    WCMD_output_asis(temp);
-                    toWrite = 0;
-                    strcpyW(temp, nullW);
-                }
-                padding--;
-            }
-            WCMD_output_asis(temp);
+            static const WCHAR padfmt[] = {'%','1','!','*','s','!','\0'};
+            static const WCHAR empty[] = {'\0'};
+            WCMD_output(padfmt, cur_width - tmp_width, empty);
         }
 
       } else if ((fd+i)->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
@@ -469,11 +454,11 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
     if (!bare) {
        if (file_count == 1) {
          static const WCHAR fmt[] = {' ',' ',' ',' ',' ',' ',' ','1',' ','f','i','l','e',' ',
-                                     '%','2','5','s',' ','b','y','t','e','s','\n','\0'};
+                                     '%','1','!','2','5','s','!',' ','b','y','t','e','s','\n','\0'};
          WCMD_output (fmt, WCMD_filesize64 (byte_count.QuadPart));
        }
        else {
-         static const WCHAR fmt[] = {'%','8','d',' ','f','i','l','e','s',' ','%','2','4','s',
+         static const WCHAR fmt[] = {'%','1','!','8','d','!',' ','f','i','l','e','s',' ','%','2','!','2','4','s','!',
                                      ' ','b','y','t','e','s','\n','\0'};
          WCMD_output (fmt, file_count, WCMD_filesize64 (byte_count.QuadPart));
        }
@@ -484,11 +469,11 @@ static DIRECTORY_STACK *WCMD_list_directory (DIRECTORY_STACK *inputparms, int le
 
     if (!bare && !recurse) {
        if (dir_count == 1) {
-           static const WCHAR fmt[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','y',
+           static const WCHAR fmt[] = {'%','1','!','8','d','!',' ','d','i','r','e','c','t','o','r','y',
                                        ' ',' ',' ',' ',' ',' ',' ',' ',' ','\0'};
            WCMD_output (fmt, 1);
        } else {
-           static const WCHAR fmt[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','i',
+           static const WCHAR fmt[] = {'%','1','!','8','d','!',' ','d','i','r','e','c','t','o','r','i',
                                        'e','s','\0'};
            WCMD_output (fmt, dir_count);
        }
@@ -588,15 +573,15 @@ static void WCMD_dir_trailer(WCHAR drive) {
     if (errorlevel==0 && !bare) {
       if (recurse) {
         static const WCHAR fmt1[] = {'\n',' ',' ',' ',' ',' ','T','o','t','a','l',' ','f','i','l','e','s',
-                                     ' ','l','i','s','t','e','d',':','\n','%','8','d',' ','f','i','l','e',
-                                     's','%','2','5','s',' ','b','y','t','e','s','\n','\0'};
-        static const WCHAR fmt2[] = {'%','8','d',' ','d','i','r','e','c','t','o','r','i','e','s',' ','%',
-                                     '1','8','s',' ','b','y','t','e','s',' ','f','r','e','e','\n','\n',
+                                     ' ','l','i','s','t','e','d',':','\n','%','1','!','8','d','!',' ','f','i','l','e',
+                                     's','%','2','!','2','5','s','!',' ','b','y','t','e','s','\n','\0'};
+        static const WCHAR fmt2[] = {'%','1','!','8','d','!',' ','d','i','r','e','c','t','o','r','i','e','s',' ','%',
+                                     '2','!','1','8','s','!',' ','b','y','t','e','s',' ','f','r','e','e','\n','\n',
                                      '\0'};
         WCMD_output (fmt1, file_total, WCMD_filesize64 (byte_total));
         WCMD_output (fmt2, dir_total, WCMD_filesize64 (freebytes.QuadPart));
       } else {
-        static const WCHAR fmt[] = {' ','%','1','8','s',' ','b','y','t','e','s',' ','f','r','e','e',
+        static const WCHAR fmt[] = {' ','%','1','!','1','8','s','!',' ','b','y','t','e','s',' ','f','r','e','e',
                                     '\n','\n','\0'};
         WCMD_output (fmt, WCMD_filesize64 (freebytes.QuadPart));
       }
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index fe16805..801c707 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -75,6 +75,7 @@ void WCMD_if (WCHAR *, CMD_LIST **cmdList);
 void WCMD_leave_paged_mode(void);
 void WCMD_more (WCHAR *);
 void WCMD_move (void);
+WCHAR* WCMD_format_string (const WCHAR *format, ...);
 void WCMD_output (const WCHAR *format, ...);
 void WCMD_output_stderr (const WCHAR *format, ...);
 void WCMD_output_asis (const WCHAR *message);
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index df4f9cb..cf2062a 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -123,19 +123,23 @@ static void WCMD_output_asis_len(const WCHAR *message, DWORD len, HANDLE device)
 
 void WCMD_output (const WCHAR *format, ...) {
 
-  va_list ap;
-  WCHAR string[1024];
-  DWORD ret;
-
-  va_start(ap,format);
-  ret = vsnprintfW(string, sizeof(string)/sizeof(WCHAR), format, ap);
-  if( ret >= (sizeof(string)/sizeof(WCHAR))) {
-       WINE_ERR("Output truncated\n" );
-       ret = (sizeof(string)/sizeof(WCHAR)) - 1;
-       string[ret] = '\0';
+  __ms_va_list ap;
+  WCHAR* string;
+  DWORD len;
+
+  __ms_va_start(ap,format);
+  SetLastError(NO_ERROR);
+  string = NULL;
+  len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                       format, 0, 0, (LPWSTR)&string, 0, &ap);
+  __ms_va_end(ap);
+  if (len == 0 && GetLastError() != NO_ERROR)
+    WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
+  else
+  {
+    WCMD_output_asis_len(string, len, GetStdHandle(STD_OUTPUT_HANDLE));
+    LocalFree(string);
   }
-  va_end(ap);
-  WCMD_output_asis_len(string, ret, GetStdHandle(STD_OUTPUT_HANDLE));
 }
 
 /*******************************************************************
@@ -145,19 +149,47 @@ void WCMD_output (const WCHAR *format, ...) {
 
 void WCMD_output_stderr (const WCHAR *format, ...) {
 
-  va_list ap;
-  WCHAR string[1024];
-  DWORD ret;
-
-  va_start(ap,format);
-  ret = vsnprintfW(string, sizeof(string)/sizeof(WCHAR), format, ap);
-  if( ret >= (sizeof(string)/sizeof(WCHAR))) {
-       WINE_ERR("Output truncated\n" );
-       ret = (sizeof(string)/sizeof(WCHAR)) - 1;
-       string[ret] = '\0';
+  __ms_va_list ap;
+  WCHAR* string;
+  DWORD len;
+
+  __ms_va_start(ap,format);
+  SetLastError(NO_ERROR);
+  string = NULL;
+  len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                       format, 0, 0, (LPWSTR)&string, 0, &ap);
+  __ms_va_end(ap);
+  if (len == 0 && GetLastError() != NO_ERROR)
+    WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
+  else
+  {
+    WCMD_output_asis_len(string, len, GetStdHandle(STD_ERROR_HANDLE));
+    LocalFree(string);
+  }
+}
+
+/*******************************************************************
+ * WCMD_format_string - allocate a buffer and format a string
+ *
+ */
+
+WCHAR* WCMD_format_string (const WCHAR *format, ...) {
+
+  __ms_va_list ap;
+  WCHAR* string;
+  DWORD len;
+
+  __ms_va_start(ap,format);
+  SetLastError(NO_ERROR);
+  len = FormatMessageW(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER,
+                       format, 0, 0, (LPWSTR)&string, 0, &ap);
+  __ms_va_end(ap);
+  if (len == 0 && GetLastError() != NO_ERROR) {
+    WINE_FIXME("Could not format string: le=%u, fmt=%s\n", GetLastError(), wine_dbgstr_w(format));
+    string = (WCHAR*)LocalAlloc(LMEM_FIXED, 2);
+    *string = 0;
   }
-  va_end(ap);
-  WCMD_output_asis_len(string, ret, GetStdHandle(STD_ERROR_HANDLE));
+  return string;
 }
 
 void WCMD_enter_paged_mode(const WCHAR *msg)
@@ -2221,23 +2253,23 @@ void WCMD_free_commands(CMD_LIST *cmds) {
 int wmain (int argc, WCHAR *argvW[])
 {
   int     args;
-  WCHAR  *cmd   = NULL;
+  WCHAR  *cmd;
   WCHAR string[1024];
   WCHAR envvar[4];
   BOOL opt_q;
   int opt_t = 0;
   static const WCHAR promptW[] = {'P','R','O','M','P','T','\0'};
   static const WCHAR defaultpromptW[] = {'$','P','$','G','\0'};
-  char ansiVersion[100];
   CMD_LIST *toExecute = NULL;         /* Commands left to be executed */
 
   srand(time(NULL));
 
   /* Pre initialize some messages */
-  strcpy(ansiVersion, PACKAGE_VERSION);
-  MultiByteToWideChar(CP_ACP, 0, ansiVersion, -1, string, 1024);
-  wsprintfW(version_string, WCMD_LoadMessage(WCMD_VERSION), string);
   strcpyW(anykey, WCMD_LoadMessage(WCMD_ANYKEY));
+  cmd = WCMD_format_string(WCMD_LoadMessage(WCMD_VERSION), PACKAGE_VERSION);
+  strcpyW(version_string, cmd);
+  LocalFree(cmd);
+  cmd = NULL;
 
   args  = argc;
   opt_c = opt_k = opt_q = opt_s = FALSE;
-- 
1.7.7.3



More information about the wine-patches mailing list