[PATCH 2/4] reg: Add wchar/raw data conversion functions

xantares 09 xantares09 at hotmail.com
Thu Jul 30 15:44:27 CDT 2015









>From ec00e5c94d5b3ec65d7e8dbc4220aa71ff97872a Mon Sep 17 00:00:00 2001

From: Jonathan Vollebregt <jnvsor at gmail.com>

Date: Fri, 5 Sep 2014 22:48:23 +0200

Subject: [PATCH 2/4] reg: Add wchar/raw data conversion functions

To: wine-patches at winehq.org



In the case of REG_SZ and the like, it may seem like the functions

perform an unncessary copy of the strings which should already be

in memory ripe for the taking.



However because these functions handle more than one type of data

the calling function needs to be able to free the data afterwards.



Simply returning the input string would result in a function

freeing one of it's own parameters, so we make sure to return

new memory by making a copy.

---

 programs/reg/reg.c       | 145 ++++++++++++++++++++++++++++++++++++++---------

 programs/reg/reg.h       |   1 +

 programs/reg/reg.rc      |   1 +

 programs/reg/tests/reg.c |  57 +++++++++----------

 4 files changed, 149 insertions(+), 55 deletions(-)



diff --git a/programs/reg/reg.c b/programs/reg/reg.c

index cf6425e..b2447fc 100644

--- a/programs/reg/reg.c

+++ b/programs/reg/reg.c

@@ -18,10 +18,15 @@

  
 #include <windows.h>

 #include <wine/unicode.h>

+#include <wine/debug.h>

 #include "reg.h"

  
 #define ARRAY_SIZE(A) (sizeof(A)/sizeof(*A))

  
+WINE_DEFAULT_DEBUG_CHANNEL(reg);

+

+static const WCHAR empty_wstr[] = {0};

+

 static const WCHAR short_hklm[] = {'H','K','L','M',0};

 static const WCHAR short_hkcu[] = {'H','K','C','U',0};

 static const WCHAR short_hkcr[] = {'H','K','C','R',0};

@@ -189,43 +194,126 @@ static DWORD wchar_get_type(const WCHAR *type_name)

     return ~0u;

 }

  
-static LPBYTE get_regdata(LPWSTR data, DWORD reg_type, WCHAR separator, DWORD *reg_count)

+static BYTE *wchar_get_data(const WCHAR *input, const DWORD type, const WCHAR separator,

+    DWORD *size_out)

 {

-    LPBYTE out_data = NULL;

-    *reg_count = 0;

+    DWORD i;

+

+    if (!input)

+        input = empty_wstr;

  
-    switch (reg_type)

+    switch (type)

     {

+        case REG_NONE:

         case REG_SZ:

+        case REG_EXPAND_SZ:

         {

-            *reg_count = (lstrlenW(data) + 1) * sizeof(WCHAR);

-            out_data = HeapAlloc(GetProcessHeap(),0,*reg_count);

-            lstrcpyW((LPWSTR)out_data,data);

-            break;

+            BYTE *data;

+

+            i = (strlenW(input) + 1) * sizeof(WCHAR);

+            *size_out = i;

+            data = HeapAlloc(GetProcessHeap(), 0, i);

+            memcpy(data, input, i);

+            return data;

         }

         case REG_DWORD:

+        case REG_DWORD_BIG_ENDIAN:

         {

-            LPWSTR rest;

-            DWORD val;

-            val = strtolW(data, &rest, 0);

-            if (rest == data) {

-                static const WCHAR nonnumber[] = {'E','r','r','o','r',':',' ','/','d',' ','r','e','q','u','i','r','e','s',' ','n','u','m','b','e','r','.','\n',0};

-                reg_printfW(nonnumber);

-                break;

+            BYTE *data;

+            WCHAR *temp;

+

+            if (input[0] == '0' && (input[1] == 'x' || input[1] == 'X'))

+                i = strtoulW(input, &temp, 16);

+            else

+                i = strtoulW(input, &temp, 10);

+

+            if (input[0] == '-' || temp[0] || temp == input)

+            {

+                reg_message(STRING_INVALID_DWORD);

+                return NULL;

             }

-            *reg_count = sizeof(DWORD);

-            out_data = HeapAlloc(GetProcessHeap(),0,*reg_count);

-            ((LPDWORD)out_data)[0] = val;

-            break;

+

+            if (i == 0xffffffff)

+                WINE_FIXME("Check for integer overflow.\n");

+

+            data = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));

+            *(DWORD *) data = i;

+            *size_out = sizeof(DWORD);

+            return data;

+        }

+        case REG_MULTI_SZ:

+        {

+            WCHAR *data = HeapAlloc(GetProcessHeap(), 0, (strlenW(input) + 1) * sizeof(WCHAR));

+            DWORD p;

+

+            for (i = 0, p = 0; i <= strlenW(input); i++, p++)

+            {

+                /* If this character is the separator, or no separator has been given and these

+                 * characters are "\\0", then add a 0 indicating the end of this string */

+                if ( (separator && input[i] == separator) ||

+                     (!separator && input[i] == '\\' && input[i + 1] == '0') )

+                {

+                    /* If it's the first character or the previous one was a separator */

+                    if (!p || data[p - 1] == 0)

+                    {

+                        HeapFree(GetProcessHeap(), 0, data);

+                        reg_message(STRING_INVALID_CMDLINE);

+                        return NULL;

+                    }

+                    data[p] = 0;

+

+                    if (!separator)

+                        i++;

+                }

+                else

+                    data[p] = input[i];

+            }

+

+            /* Add a 0 to the end if the string wasn't "", and it wasn't

+             * double-0-terminated already (In the case of a trailing separator) */

+            if (p > 1 && data[p - 2])

+                data[p++] = 0;

+

+            *size_out = p * sizeof(WCHAR);

+            return (BYTE *) data;

+        }

+        case REG_BINARY:

+        {

+            BYTE *data = HeapAlloc(GetProcessHeap(), 0, strlenW(input));

+            DWORD p, odd;

+

+            for (i = 0; i < strlenW(input); i++)

+            {

+                if (input[i] >= '0' && input[i] <= '9')

+                    data[i] = input[i] - '0';

+                else if (input[i] >= 'a' && input[i] <= 'f')

+                    data[i] = input[i] - 'a' + 10;

+                else if (input[i] >= 'A' && input[i] <= 'F')

+                    data[i] = input[i] - 'A' + 10;

+                else

+                {

+                    HeapFree(GetProcessHeap(), 0, data);

+                    reg_message(STRING_INVALID_CMDLINE);

+                    return NULL;

+                }

+            }

+

+            odd = i & 1;

+            p = i >> 1;

+            data += odd;

+

+            for (i = 0; i < p; i++)

+                data[i] = (data[i * 2] << 4) | data[i * 2 + 1];

+

+            *size_out = p + odd;

+            return data - odd;

         }

         default:

         {

-            static const WCHAR unhandled[] = {'U','n','h','a','n','d','l','e','d',' ','T','y','p','e',' ','0','x','%','x',' ',' ','d','a','t','a',' ','%','s','\n',0};

-            reg_printfW(unhandled, reg_type,data);

+            WINE_FIXME("Add support for registry type: %u\n", type);

+            return NULL;

         }

     }

-

-    return out_data;

 }

  
 static BOOL sane_path(const WCHAR *key)

@@ -286,7 +374,14 @@ static int reg_add(WCHAR *key_name, WCHAR *value_name, BOOL value_empty,

         }

  
         if (data)

-            reg_data = get_regdata(data,reg_type,separator,®_count);

+        {

+            reg_data = wchar_get_data(data, reg_type, separator, ®_count);

+            if (!reg_data)

+            {

+                RegCloseKey(subkey);

+                return 1;

+            }

+        }

  
         RegSetValueExW(subkey,value_name,0,reg_type,reg_data,reg_count);

         HeapFree(GetProcessHeap(),0,reg_data);

@@ -336,8 +431,6 @@ static int reg_delete(WCHAR *key_name, WCHAR *value_name, BOOL value_empty,

     /* Delete subtree only if no /v* option is given */

     if (!value_name && !value_empty && !value_all)

     {

-        static const WCHAR empty_wstr[] = {0};

-

         err = RegDeleteTreeW(subkey, NULL);

         if (err != ERROR_SUCCESS)

         {

diff --git a/programs/reg/reg.h b/programs/reg/reg.h

index 6fca7eb..c6fb151 100644

--- a/programs/reg/reg.h

+++ b/programs/reg/reg.h

@@ -31,3 +31,4 @@

 #define STRING_NO_REMOTE        108

 #define STRING_CANNOT_FIND      109

 #define STRING_UNSUPPORTED_TYPE 110

+#define STRING_INVALID_DWORD    111

diff --git a/programs/reg/reg.rc b/programs/reg/reg.rc

index 94dddf2..dbd92ea 100644

--- a/programs/reg/reg.rc

+++ b/programs/reg/reg.rc

@@ -36,4 +36,5 @@ STRINGTABLE

     STRING_NO_REMOTE, "Error: Unable to access remote machine\n"

     STRING_CANNOT_FIND, "Error: The system was unable to find the specified registry key or value\n"

     STRING_UNSUPPORTED_TYPE, "Error: Unsupported type\n"

+    STRING_INVALID_DWORD, "Error: /d must be positive number\n"

 }

diff --git a/programs/reg/tests/reg.c b/programs/reg/tests/reg.c

index dbfce89..99b4813 100644

--- a/programs/reg/tests/reg.c

+++ b/programs/reg/tests/reg.c

@@ -154,7 +154,7 @@ static void test_add(void)

     /* REG_NONE */

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v none0 /d deadbeef /t REG_NONE /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %d\n", r);

-    verify_reg(hkey, "none0", REG_NONE, "d\0e\0a\0d\0b\0e\0e\0f\0\0", 18, TODO_REG_SIZE);

+    verify_reg(hkey, "none0", REG_NONE, "d\0e\0a\0d\0b\0e\0e\0f\0\0", 18, 0);

  
     /* REG_SZ */

     run_reg_exe("reg add HKCU\\" KEY_BASE " /d WineTest /f", &r);

@@ -188,11 +188,11 @@ static void test_add(void)

     /* REG_EXPAND_SZ */

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v expand0 /t REG_EXpand_sz /d \"dead%PATH%beef\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "expand0", REG_EXPAND_SZ, "dead%PATH%beef", 15, TODO_REG_SIZE);

+    verify_reg(hkey, "expand0", REG_EXPAND_SZ, "dead%PATH%beef", 15, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /v expand1 /t REG_EXpand_sz /d \"dead^%PATH^%beef\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "expand1", REG_EXPAND_SZ, "dead^%PATH^%beef", 17, TODO_REG_SIZE);

+    verify_reg(hkey, "expand1", REG_EXPAND_SZ, "dead^%PATH^%beef", 17, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_EXPAND_SZ /v expand2 /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

@@ -200,11 +200,11 @@ static void test_add(void)

  
     run_reg_exe("reg add HKEY_CURRENT_USER\\" KEY_BASE " /ve /t REG_EXPAND_SZ /d WineTEST /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "", REG_EXPAND_SZ, "WineTEST", 9, TODO_REG_SIZE);

+    verify_reg(hkey, "", REG_EXPAND_SZ, "WineTEST", 9, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_EXPAND_SZ /v expand3 /f /d \"\"", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "expand3", REG_EXPAND_SZ, "", 1, TODO_REG_SIZE);

+    verify_reg(hkey, "expand3", REG_EXPAND_SZ, "", 1, 0);

  
     /* REG_BINARY */

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin0 /f", &r);

@@ -214,14 +214,14 @@ static void test_add(void)

     run_reg_exe("reg add HKEY_CURRENT_USER\\" KEY_BASE " /ve /t REG_BINARY /d deadbeef /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

     dword = 0xefbeadde;

-    verify_reg(hkey, "", REG_BINARY, &dword, sizeof(DWORD), TODO_REG_SIZE);

+    verify_reg(hkey, "", REG_BINARY, &dword, sizeof(DWORD), 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin1 /f /d 0xDeAdBeEf", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin2 /f /d x01", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin3 /f /d 01x", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin4 /f /d DeAdBeEf0DD", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

@@ -235,8 +235,8 @@ static void test_add(void)

     err = RegQueryValueExA(hkey, "bin4", NULL, &type, (void *) (buffer+12), &size);

     ok(err == ERROR_SUCCESS, "RegQueryValueEx failed: got %d\n", err);

     ok(type == REG_BINARY, "got wrong type %u\n", type);

-    todo_wine ok(size == 6, "got wrong size %u\n", size);

-    todo_wine ok(memcmp(buffer, buffer+12, 6) == 0 ||

+    ok(size == 6, "got wrong size %u\n", size);

+    ok(memcmp(buffer, buffer+12, 6) == 0 ||

         broken(memcmp(buffer+6, buffer+12, 6) == 0 /* WinXP */), "got wrong data\n");

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_BINARY /v bin5 /d \"\" /f", &r);

@@ -257,11 +257,11 @@ static void test_add(void)

     todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */),

        "got exit code %d, expected 0\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword2 /t REG_DWORD /d zzz /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword3 /t REG_DWORD /d deadbeef /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword4 /t REG_DWORD /d 123xyz /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword5 /t reg_dword /d 12345678 /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);

@@ -275,22 +275,21 @@ static void test_add(void)

     ok(err == ERROR_SUCCESS, "RegQueryValueEx failed: got %d\n", err);

     ok(type == REG_DWORD, "got wrong type %d, expected %d\n", type, REG_DWORD);

     ok(size == sizeof(DWORD), "got wrong size %d, expected %d\n", size, (int)sizeof(DWORD));

-    todo_wine ok(dword == 123 || broken(dword == 0123 /* WinXP */),

+    ok(dword == 123 || broken(dword == 0123 /* WinXP */),

                  "got wrong data %d, expected %d\n", dword, 123);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword7 /t reg_dword /d 0xabcdefg /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %d, expected 1\n", r);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword8 /t REG_dword /d 0xdeadbeef /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %d, expected 0\n", r);

     dword = 0xdeadbeef;

-    verify_reg(hkey, "dword8", REG_DWORD, &dword, sizeof(dword),

-               (sizeof(long) > sizeof(DWORD)) ? 0 : TODO_REG_DATA);

+    verify_reg(hkey, "dword8", REG_DWORD, &dword, sizeof(dword), 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_DWORD /v dword9 /f /d -1", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_DWORD /v dword10 /f /d -0x1", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %u\n", r);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /v dword8 /t REG_dword /d 0x01ffffffff /f", &r);

     todo_wine ok(r == REG_EXIT_FAILURE || broken(r == REG_EXIT_SUCCESS /* WinXP */), "got exit code %d\n", r);

@@ -305,7 +304,7 @@ static void test_add(void)

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v DWORD_BE /t REG_DWORD_BIG_ENDIAN /d 456 /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

     dword = 456;

-    verify_reg(hkey, "DWORD_BE", REG_DWORD_BIG_ENDIAN, &dword, sizeof(dword), TODO_REG_SIZE);

+    verify_reg(hkey, "DWORD_BE", REG_DWORD_BIG_ENDIAN, &dword, sizeof(dword), 0);

     /* REG_DWORD_BIG_ENDIAN is broken in every version of windows. It behaves like

      * an ordinary REG_DWORD - that is little endian. GG */

  
@@ -313,15 +312,15 @@ static void test_add(void)

     run_reg_exe("reg add HKCU\\" KEY_BASE " /v multi0 /t REG_MULTI_SZ /d \"three\\0little\\0strings\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

     memcpy(buffer, "three\0little\0strings\0", 22);

-    verify_reg(hkey, "multi0", REG_MULTI_SZ, buffer, 22, TODO_REG_SIZE);

+    verify_reg(hkey, "multi0", REG_MULTI_SZ, buffer, 22, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi1 /s \"#\" /d \"three#little#strings\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "multi1", REG_MULTI_SZ, buffer, 22, TODO_REG_SIZE);

+    verify_reg(hkey, "multi1", REG_MULTI_SZ, buffer, 22, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi2 /d \"\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "multi2", REG_MULTI_SZ, &buffer[21], 1, TODO_REG_SIZE);

+    verify_reg(hkey, "multi2", REG_MULTI_SZ, &buffer[21], 1, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi3 /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

@@ -329,7 +328,7 @@ static void test_add(void)

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi4 /s \"#\" /d \"threelittlestrings\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

-    verify_reg(hkey, "multi4", REG_MULTI_SZ, "threelittlestrings\0", 20, TODO_REG_SIZE);

+    verify_reg(hkey, "multi4", REG_MULTI_SZ, "threelittlestrings\0", 20, 0);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi5 /s \"#randomgibberish\" /d \"three#little#strings\" /f", &r);

     todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

@@ -338,16 +337,16 @@ static void test_add(void)

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi7 /s \"\" /d \"three#little#strings\" /f", &r);

     todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi8 /s \"#\" /d \"##\" /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi9 /s \"#\" /d \"two##strings\" /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi10 /s \"#\" /d \"#a\" /f", &r);

-    todo_wine ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

+    ok(r == REG_EXIT_FAILURE, "got exit code %u\n", r);

  
     run_reg_exe("reg add HKCU\\" KEY_BASE " /t REG_MULTI_SZ /v multi11 /s \"#\" /d \"a#\" /f", &r);

     ok(r == REG_EXIT_SUCCESS, "got exit code %u\n", r);

     buffer[0]='a'; buffer[1]=0; buffer[2]=0;

-    verify_reg(hkey, "multi11", REG_MULTI_SZ, buffer, 3, TODO_REG_SIZE);

+    verify_reg(hkey, "multi11", REG_MULTI_SZ, buffer, 3, 0);

  
     RegCloseKey(hkey);

  
--  
2.5.0



 		 	   		  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20150730/e8ad0eb5/attachment-0001.html>


More information about the wine-patches mailing list