xref: /reactos/dll/win32/odbccp32/odbccp32.c (revision bae2bac6)
1 /*
2  * Implementation of the ODBC driver installer
3  *
4  * Copyright 2005 Mike McCormack for CodeWeavers
5  * Copyright 2005 Hans Leidekker
6  * Copyright 2007 Bill Medland
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22 
23 #include <assert.h>
24 #include <stdarg.h>
25 
26 #define COBJMACROS
27 
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winnls.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34 #include "wine/heap.h"
35 
36 #include "odbcinst.h"
37 
38 WINE_DEFAULT_DEBUG_CHANNEL(odbc);
39 
40 /* Registry key names */
41 static const WCHAR drivers_key[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C','\\','O','D','B','C','I','N','S','T','.','I','N','I','\\','O','D','B','C',' ','D','r','i','v','e','r','s',0};
42 static const WCHAR odbcW[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C',0};
43 static const WCHAR odbcini[] = {'S','o','f','t','w','a','r','e','\\','O','D','B','C','\\','O','D','B','C','I','N','S','T','.','I','N','I','\\',0};
44 static const WCHAR odbcdrivers[] = {'O','D','B','C',' ','D','r','i','v','e','r','s',0};
45 static const WCHAR odbctranslators[] = {'O','D','B','C',' ','T','r','a','n','s','l','a','t','o','r','s',0};
46 
47 /* This config mode is known to be process-wide.
48  * MSDN documentation suggests that the value is hidden somewhere in the registry but I haven't found it yet.
49  * Although both the registry and the ODBC.ini files appear to be maintained together they are not maintained automatically through the registry's IniFileMapping.
50  */
51 static UWORD config_mode = ODBC_BOTH_DSN;
52 
53 /* MSDN documentation suggests that the error subsystem handles errors 1 to 8
54  * only and experimentation (Windows 2000) shows that the errors are process-
55  * wide so go for the simple solution; static arrays.
56  */
57 static int num_errors;
58 static int error_code[8];
59 static const WCHAR *error_msg[8];
60 static const WCHAR odbc_error_general_err[] = {'G','e','n','e','r','a','l',' ','e','r','r','o','r',0};
61 static const WCHAR odbc_error_invalid_buff_len[] = {'I','n','v','a','l','i','d',' ','b','u','f','f','e','r',' ','l','e','n','g','t','h',0};
62 static const WCHAR odbc_error_component_not_found[] = {'C','o','m','p','o','n','e','n','t',' ','n','o','t',' ','f','o','u','n','d',0};
63 static const WCHAR odbc_error_out_of_mem[] = {'O','u','t',' ','o','f',' ','m','e','m','o','r','y',0};
64 static const WCHAR odbc_error_invalid_param_sequence[] = {'I','n','v','a','l','i','d',' ','p','a','r','a','m','e','t','e','r',' ','s','e','q','u','e','n','c','e',0};
65 static const WCHAR odbc_error_invalid_param_string[] = {'I','n','v','a','l','i','d',' ','p','a','r','a','m','e','t','e','r',' ','s','t','r','i','n','g',0};
66 static const WCHAR odbc_error_invalid_dsn[] = {'I','n','v','a','l','i','d',' ','D','S','N',0};
67 static const WCHAR odbc_error_load_lib_failed[] = {'L','o','a','d',' ','L','i','b','r','a','r','y',' ','F','a','i','l','e','d',0};
68 static const WCHAR odbc_error_request_failed[] = {'R','e','q','u','e','s','t',' ','F','a','i','l','e','d',0};
69 static const WCHAR odbc_error_invalid_keyword[] = {'I','n','v','a','l','i','d',' ','k','e','y','w','o','r','d',' ','v','a','l','u','e',0};
70 
71 /* Push an error onto the error stack, taking care of ranges etc. */
72 static void push_error(int code, LPCWSTR msg)
73 {
74     if (num_errors < sizeof error_code/sizeof error_code[0])
75     {
76         error_code[num_errors] = code;
77         error_msg[num_errors] = msg;
78         num_errors++;
79     }
80 }
81 
82 /* Clear the error stack */
83 static void clear_errors(void)
84 {
85     num_errors = 0;
86 }
87 
88 static inline WCHAR *heap_strdupAtoW(const char *str)
89 {
90     LPWSTR ret = NULL;
91 
92     if(str) {
93         DWORD len;
94 
95         len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
96         ret = heap_alloc(len*sizeof(WCHAR));
97         if(ret)
98             MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
99     }
100 
101     return ret;
102 }
103 
104 
105 BOOL WINAPI ODBCCPlApplet( LONG i, LONG j, LONG * p1, LONG * p2)
106 {
107     clear_errors();
108     FIXME( "( %d %d %p %p) : stub!\n", i, j, p1, p2);
109     return FALSE;
110 }
111 
112 static LPWSTR SQLInstall_strdup_multi(LPCSTR str)
113 {
114     LPCSTR p;
115     LPWSTR ret = NULL;
116     DWORD len;
117 
118     if (!str)
119         return ret;
120 
121     for (p = str; *p; p += lstrlenA(p) + 1)
122         ;
123 
124     len = MultiByteToWideChar(CP_ACP, 0, str, p - str, NULL, 0 );
125     ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
126     MultiByteToWideChar(CP_ACP, 0, str, p - str, ret, len );
127     ret[len] = 0;
128 
129     return ret;
130 }
131 
132 static LPWSTR SQLInstall_strdup(LPCSTR str)
133 {
134     DWORD len;
135     LPWSTR ret = NULL;
136 
137     if (!str)
138         return ret;
139 
140     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0 );
141     ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
142     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len );
143 
144     return ret;
145 }
146 
147 /* Convert the wide string or zero-length-terminated list of wide strings to a
148  * narrow string or zero-length-terminated list of narrow strings.
149  * Do not try to emulate windows undocumented excesses (e.g. adding a third \0
150  * to a list)
151  * Arguments
152  *   mode Indicates the sort of string.
153  *     1 denotes that the buffers contain strings terminated by a single nul
154  *       character
155  *     2 denotes that the buffers contain zero-length-terminated lists
156  *       (frequently erroneously referred to as double-null-terminated)
157  *   buffer The narrow-character buffer into which to place the result.  This
158  *          must be a non-null pointer to the first element of a buffer whose
159  *          length is passed in buffer_length.
160  *   str The wide-character buffer containing the string or list of strings to
161  *       be converted.  str_length defines how many wide characters in the
162  *       buffer are to be converted, including all desired terminating nul
163  *       characters.
164  *   str_length Effective length of str
165  *   buffer_length Length of buffer
166  *   returned_length A pointer to a variable that will receive the number of
167  *                   narrow characters placed into the buffer.  This pointer
168  *                   may be NULL.
169  */
170 static BOOL SQLInstall_narrow(int mode, LPSTR buffer, LPCWSTR str, WORD str_length, WORD buffer_length, WORD *returned_length)
171 {
172     LPSTR pbuf; /* allows us to allocate a temporary buffer only if needed */
173     int len; /* Length of the converted list */
174     BOOL success = FALSE;
175     assert(mode == 1 || mode == 2);
176     assert(buffer_length);
177     len = WideCharToMultiByte(CP_ACP, 0, str, str_length, 0, 0, NULL, NULL);
178     if (len > 0)
179     {
180         if (len > buffer_length)
181         {
182             pbuf = HeapAlloc(GetProcessHeap(), 0, len);
183         }
184         else
185         {
186             pbuf = buffer;
187         }
188         len = WideCharToMultiByte(CP_ACP, 0, str, str_length, pbuf, len, NULL, NULL);
189         if (len > 0)
190         {
191             if (pbuf != buffer)
192             {
193                 if (buffer_length > (mode - 1))
194                 {
195                     memcpy (buffer, pbuf, buffer_length-mode);
196                     *(buffer+buffer_length-mode) = '\0';
197                 }
198                 *(buffer+buffer_length-1) = '\0';
199             }
200             if (returned_length)
201             {
202                 *returned_length = pbuf == buffer ? len : buffer_length;
203             }
204             success = TRUE;
205         }
206         else
207         {
208             ERR("transferring wide to narrow\n");
209         }
210         if (pbuf != buffer)
211         {
212             HeapFree(GetProcessHeap(), 0, pbuf);
213         }
214     }
215     else
216     {
217         ERR("measuring wide to narrow\n");
218     }
219     return success;
220 }
221 
222 BOOL WINAPI SQLConfigDataSourceW(HWND hwndParent, WORD fRequest,
223                LPCWSTR lpszDriver, LPCWSTR lpszAttributes)
224 {
225     LPCWSTR p;
226 
227     clear_errors();
228     FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_w(lpszDriver),
229           debugstr_w(lpszAttributes));
230 
231     for (p = lpszAttributes; *p; p += lstrlenW(p) + 1)
232         FIXME("%s\n", debugstr_w(p));
233 
234     return TRUE;
235 }
236 
237 BOOL WINAPI SQLConfigDataSource(HWND hwndParent, WORD fRequest,
238                LPCSTR lpszDriver, LPCSTR lpszAttributes)
239 {
240     FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_a(lpszDriver),
241           debugstr_a(lpszAttributes));
242     clear_errors();
243     return TRUE;
244 }
245 
246 static HMODULE load_config_driver(const WCHAR *driver)
247 {
248     static WCHAR reg_driver[] = {'d','r','i','v','e','r',0};
249     long ret;
250     HMODULE hmod;
251     WCHAR *filename = NULL;
252     DWORD size = 0, type;
253     HKEY hkey;
254 
255     if ((ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey)) == ERROR_SUCCESS)
256     {
257         HKEY hkeydriver;
258 
259         if ((ret = RegOpenKeyW(hkey, driver, &hkeydriver)) == ERROR_SUCCESS)
260         {
261             ret = RegGetValueW(hkeydriver, NULL, reg_driver, RRF_RT_REG_SZ, &type, NULL, &size);
262             if(ret == ERROR_MORE_DATA)
263             {
264                 filename = HeapAlloc(GetProcessHeap(), 0, size);
265                 if(!filename)
266                 {
267                     RegCloseKey(hkeydriver);
268                     RegCloseKey(hkey);
269                     push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
270 
271                     return NULL;
272                 }
273                 ret = RegGetValueW(hkeydriver, NULL, driver, RRF_RT_REG_SZ, &type, filename, &size);
274             }
275 
276             RegCloseKey(hkeydriver);
277         }
278 
279         RegCloseKey(hkey);
280     }
281 
282     if(ret != ERROR_SUCCESS)
283     {
284         HeapFree(GetProcessHeap(), 0, filename);
285         push_error(ODBC_ERROR_INVALID_DSN, odbc_error_invalid_dsn);
286         return NULL;
287     }
288 
289     hmod = LoadLibraryW(filename);
290     HeapFree(GetProcessHeap(), 0, filename);
291 
292     if(!hmod)
293         push_error(ODBC_ERROR_LOAD_LIB_FAILED, odbc_error_load_lib_failed);
294 
295     return hmod;
296 }
297 
298 static BOOL write_config_value(const WCHAR *driver, const WCHAR *args)
299 {
300     long ret;
301     HKEY hkey, hkeydriver;
302     WCHAR *name = NULL;
303 
304     if(!args)
305         return FALSE;
306 
307     if((ret = RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey)) == ERROR_SUCCESS)
308     {
309         if((ret = RegOpenKeyW(hkey, driver, &hkeydriver)) == ERROR_SUCCESS)
310         {
311             WCHAR *divider, *value;
312 
313             name = heap_alloc( (strlenW(args) + 1) * sizeof(WCHAR));
314             if(!name)
315             {
316                 push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
317                 goto fail;
318             }
319             lstrcpyW(name, args);
320 
321             divider = strchrW(name,'=');
322             if(!divider)
323             {
324                 push_error(ODBC_ERROR_INVALID_KEYWORD_VALUE, odbc_error_invalid_keyword);
325                 goto fail;
326             }
327 
328             value = divider + 1;
329             *divider = '\0';
330 
331             TRACE("Write pair: %s = %s\n", debugstr_w(name), debugstr_w(value));
332             if(RegSetValueExW(hkeydriver, name, 0, REG_SZ, (BYTE*)value,
333                                (strlenW(value)+1) * sizeof(WCHAR)) != ERROR_SUCCESS)
334                 ERR("Failed to write registry installed key\n");
335             heap_free(name);
336 
337             RegCloseKey(hkeydriver);
338         }
339 
340         RegCloseKey(hkey);
341     }
342 
343     if(ret != ERROR_SUCCESS)
344         push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
345 
346     return ret == ERROR_SUCCESS;
347 
348 fail:
349     RegCloseKey(hkeydriver);
350     RegCloseKey(hkey);
351     heap_free(name);
352 
353     return FALSE;
354 }
355 
356 BOOL WINAPI SQLConfigDriverW(HWND hwnd, WORD request, LPCWSTR driver,
357                LPCWSTR args, LPWSTR msg, WORD msgmax, WORD *msgout)
358 {
359     BOOL (WINAPI *pConfigDriverW)(HWND hwnd, WORD request, const WCHAR *driver, const WCHAR *args, const WCHAR *msg, WORD msgmax, WORD *msgout);
360     HMODULE hmod;
361     BOOL funcret = FALSE;
362 
363     clear_errors();
364     TRACE("(%p %d %s %s %p %d %p)\n", hwnd, request, debugstr_w(driver),
365           debugstr_w(args), msg, msgmax, msgout);
366 
367     if(request == ODBC_CONFIG_DRIVER)
368     {
369         return write_config_value(driver, args);
370     }
371 
372     hmod = load_config_driver(driver);
373     if(!hmod)
374         return FALSE;
375 
376     pConfigDriverW = (void*)GetProcAddress(hmod, "ConfigDriverW");
377     if(pConfigDriverW)
378         funcret = pConfigDriverW(hwnd, request, driver, args, msg, msgmax, msgout);
379 
380     if(!funcret)
381         push_error(ODBC_ERROR_REQUEST_FAILED, odbc_error_request_failed);
382 
383     FreeLibrary(hmod);
384 
385     return funcret;
386 }
387 
388 BOOL WINAPI SQLConfigDriver(HWND hwnd, WORD request, LPCSTR driver,
389                LPCSTR args, LPSTR msg, WORD msgmax, WORD *msgout)
390 {
391     BOOL (WINAPI *pConfigDriverA)(HWND hwnd, WORD request, const char *driver, const char *args, const char *msg, WORD msgmax, WORD *msgout);
392     HMODULE hmod;
393     WCHAR *driverW;
394     BOOL funcret = FALSE;
395 
396     clear_errors();
397     TRACE("(%p %d %s %s %p %d %p)\n", hwnd, request, debugstr_a(driver),
398           debugstr_a(args), msg, msgmax, msgout);
399 
400     driverW = heap_strdupAtoW(driver);
401     if(!driverW)
402     {
403         push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
404         return FALSE;
405     }
406     if(request == ODBC_CONFIG_DRIVER)
407     {
408         BOOL ret = FALSE;
409         WCHAR *argsW = heap_strdupAtoW(args);
410         if(argsW)
411         {
412             ret = write_config_value(driverW, argsW);
413             HeapFree(GetProcessHeap(), 0, argsW);
414         }
415         else
416         {
417             push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
418         }
419 
420         HeapFree(GetProcessHeap(), 0, driverW);
421 
422         return ret;
423     }
424 
425     hmod = load_config_driver(driverW);
426     HeapFree(GetProcessHeap(), 0, driverW);
427     if(!hmod)
428         return FALSE;
429 
430     pConfigDriverA = (void*)GetProcAddress(hmod, "ConfigDriver");
431     if(pConfigDriverA)
432         funcret = pConfigDriverA(hwnd, request, driver, args, msg, msgmax, msgout);
433 
434     if(!funcret)
435         push_error(ODBC_ERROR_REQUEST_FAILED, odbc_error_request_failed);
436 
437     FreeLibrary(hmod);
438 
439     return funcret;
440 }
441 
442 BOOL WINAPI SQLCreateDataSourceW(HWND hwnd, LPCWSTR lpszDS)
443 {
444     clear_errors();
445     FIXME("%p %s\n", hwnd, debugstr_w(lpszDS));
446     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
447     return FALSE;
448 }
449 
450 BOOL WINAPI SQLCreateDataSource(HWND hwnd, LPCSTR lpszDS)
451 {
452     clear_errors();
453     FIXME("%p %s\n", hwnd, debugstr_a(lpszDS));
454     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
455     return FALSE;
456 }
457 
458 BOOL WINAPI SQLGetAvailableDriversW(LPCWSTR lpszInfFile, LPWSTR lpszBuf,
459                WORD cbBufMax, WORD *pcbBufOut)
460 {
461     clear_errors();
462     FIXME("%s %p %d %p\n", debugstr_w(lpszInfFile), lpszBuf, cbBufMax, pcbBufOut);
463     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
464     return FALSE;
465 }
466 
467 BOOL WINAPI SQLGetAvailableDrivers(LPCSTR lpszInfFile, LPSTR lpszBuf,
468                WORD cbBufMax, WORD *pcbBufOut)
469 {
470     clear_errors();
471     FIXME("%s %p %d %p\n", debugstr_a(lpszInfFile), lpszBuf, cbBufMax, pcbBufOut);
472     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
473     return FALSE;
474 }
475 
476 BOOL WINAPI SQLGetConfigMode(UWORD *pwConfigMode)
477 {
478     clear_errors();
479     TRACE("%p\n", pwConfigMode);
480     if (pwConfigMode)
481         *pwConfigMode = config_mode;
482     return TRUE;
483 }
484 
485 /* This is implemented sensibly rather than according to exact conformance to Microsoft's buggy implementations
486  * e.g. The Microsoft one occasionally actually adds a third nul character (possibly beyond the buffer).
487  * e.g. If the key has no drivers then version 3.525.1117.0 does not modify the buffer at all, not even a nul character.
488  */
489 BOOL WINAPI SQLGetInstalledDriversW(LPWSTR lpszBuf, WORD cbBufMax,
490                WORD *pcbBufOut)
491 {
492     HKEY hDrivers; /* Registry handle to the Drivers key */
493     LONG reg_ret; /* Return code from registry functions */
494     BOOL success = FALSE; /* The value we will return */
495 
496     clear_errors();
497 
498     TRACE("%p %d %p\n", lpszBuf, cbBufMax, pcbBufOut);
499 
500     if (!lpszBuf || cbBufMax == 0)
501     {
502         push_error(ODBC_ERROR_INVALID_BUFF_LEN, odbc_error_invalid_buff_len);
503     }
504     else if ((reg_ret = RegOpenKeyExW (HKEY_LOCAL_MACHINE /* The drivers does not depend on the config mode */,
505             drivers_key, 0, KEY_READ /* Maybe overkill */,
506             &hDrivers)) == ERROR_SUCCESS)
507     {
508         DWORD index = 0;
509         cbBufMax--;
510         success = TRUE;
511         while (cbBufMax > 0)
512         {
513             DWORD size_name;
514             size_name = cbBufMax;
515             if ((reg_ret = RegEnumValueW(hDrivers, index, lpszBuf, &size_name, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS)
516             {
517                 index++;
518                 assert (size_name < cbBufMax && *(lpszBuf + size_name) == 0);
519                 size_name++;
520                 cbBufMax-= size_name;
521                 lpszBuf+=size_name;
522             }
523             else
524             {
525                 if (reg_ret != ERROR_NO_MORE_ITEMS)
526                 {
527                     success = FALSE;
528                     push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err);
529                 }
530                 break;
531             }
532         }
533         *lpszBuf = 0;
534         if ((reg_ret = RegCloseKey (hDrivers)) != ERROR_SUCCESS)
535             TRACE ("Error %d closing ODBC Drivers key\n", reg_ret);
536     }
537     else
538     {
539         /* MSDN states that it returns failure with COMPONENT_NOT_FOUND in this case.
540          * Version 3.525.1117.0 (Windows 2000) does not; it actually returns success.
541          * I doubt if it will actually be an issue.
542          */
543         push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
544     }
545     return success;
546 }
547 
548 BOOL WINAPI SQLGetInstalledDrivers(LPSTR lpszBuf, WORD cbBufMax,
549                WORD *pcbBufOut)
550 {
551     BOOL ret;
552     int size_wbuf = cbBufMax;
553     LPWSTR wbuf;
554     WORD size_used;
555 
556     TRACE("%p %d %p\n", lpszBuf, cbBufMax, pcbBufOut);
557 
558     wbuf = HeapAlloc(GetProcessHeap(), 0, size_wbuf*sizeof(WCHAR));
559     if (wbuf)
560     {
561         ret = SQLGetInstalledDriversW(wbuf, size_wbuf, &size_used);
562         if (ret)
563         {
564             if (!(ret = SQLInstall_narrow(2, lpszBuf, wbuf, size_used, cbBufMax, pcbBufOut)))
565             {
566                 push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err);
567             }
568         }
569         HeapFree(GetProcessHeap(), 0, wbuf);
570         /* ignore failure; we have achieved the aim */
571     }
572     else
573     {
574         push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem);
575         ret = FALSE;
576     }
577     return ret;
578 }
579 
580 static HKEY get_privateprofile_sectionkey(LPCWSTR section, LPCWSTR filename)
581 {
582     HKEY hkey, hkeyfilename, hkeysection;
583     LONG ret;
584 
585     if (RegOpenKeyW(HKEY_CURRENT_USER, odbcW, &hkey))
586         return NULL;
587 
588     ret = RegOpenKeyW(hkey, filename, &hkeyfilename);
589     RegCloseKey(hkey);
590     if (ret)
591         return NULL;
592 
593     ret = RegOpenKeyW(hkeyfilename, section, &hkeysection);
594     RegCloseKey(hkeyfilename);
595 
596     return ret ? NULL : hkeysection;
597 }
598 
599 int WINAPI SQLGetPrivateProfileStringW(LPCWSTR section, LPCWSTR entry,
600     LPCWSTR defvalue, LPWSTR buff, int buff_len, LPCWSTR filename)
601 {
602     BOOL usedefault = TRUE;
603     HKEY sectionkey;
604     LONG ret = 0;
605 
606     TRACE("%s %s %s %p %d %s\n", debugstr_w(section), debugstr_w(entry),
607                debugstr_w(defvalue), buff, buff_len, debugstr_w(filename));
608 
609     clear_errors();
610 
611     if (buff_len <= 0 || !section)
612         return 0;
613 
614     if(buff)
615         buff[0] = 0;
616 
617     if (!defvalue || !buff)
618         return 0;
619 
620     sectionkey = get_privateprofile_sectionkey(section, filename);
621     if (sectionkey)
622     {
623         DWORD type, size;
624 
625         if (entry)
626         {
627             size = buff_len * sizeof(*buff);
628             if (RegGetValueW(sectionkey, NULL, entry, RRF_RT_REG_SZ, &type, buff, &size) == ERROR_SUCCESS)
629             {
630                 usedefault = FALSE;
631                 ret = (size / sizeof(*buff)) - 1;
632             }
633         }
634         else
635         {
636             WCHAR name[MAX_PATH];
637             DWORD index = 0;
638             DWORD namelen;
639 
640             usedefault = FALSE;
641 
642             memset(buff, 0, buff_len);
643 
644             namelen = sizeof(name);
645             while (RegEnumValueW(sectionkey, index, name, &namelen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
646             {
647                 if ((ret +  namelen+1) > buff_len)
648                     break;
649 
650                 lstrcpyW(buff+ret, name);
651                 ret += namelen+1;
652                 namelen = sizeof(name);
653                 index++;
654             }
655         }
656 
657         RegCloseKey(sectionkey);
658     }
659     else
660         usedefault = entry != NULL;
661 
662     if (usedefault)
663     {
664         lstrcpynW(buff, defvalue, buff_len);
665         ret = lstrlenW(buff);
666     }
667 
668     return ret;
669 }
670 
671 int WINAPI SQLGetPrivateProfileString(LPCSTR section, LPCSTR entry,
672     LPCSTR defvalue, LPSTR buff, int buff_len, LPCSTR filename)
673 {
674     WCHAR *sectionW, *filenameW;
675     BOOL usedefault = TRUE;
676     HKEY sectionkey;
677     LONG ret = 0;
678 
679     TRACE("%s %s %s %p %d %s\n", debugstr_a(section), debugstr_a(entry),
680                debugstr_a(defvalue), buff, buff_len, debugstr_a(filename));
681 
682     clear_errors();
683 
684     if (buff_len <= 0)
685         return 0;
686 
687     if (buff)
688         buff[0] = 0;
689 
690     if (!section || !defvalue || !buff)
691         return 0;
692 
693     sectionW = heap_strdupAtoW(section);
694     filenameW = heap_strdupAtoW(filename);
695 
696     sectionkey = get_privateprofile_sectionkey(sectionW, filenameW);
697 
698     heap_free(sectionW);
699     heap_free(filenameW);
700 
701     if (sectionkey)
702     {
703         DWORD type, size;
704 
705         if (entry)
706         {
707             size = buff_len * sizeof(*buff);
708             if (RegGetValueA(sectionkey, NULL, entry, RRF_RT_REG_SZ, &type, buff, &size) == ERROR_SUCCESS)
709             {
710                 usedefault = FALSE;
711                 ret = (size / sizeof(*buff)) - 1;
712             }
713         }
714         else
715         {
716             char name[MAX_PATH] = {0};
717             DWORD index = 0;
718             DWORD namelen;
719 
720             usedefault = FALSE;
721 
722             memset(buff, 0, buff_len);
723 
724             namelen = sizeof(name);
725             while (RegEnumValueA(sectionkey, index, name, &namelen, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
726             {
727                 if ((ret +  namelen+1) > buff_len)
728                     break;
729 
730                 lstrcpyA(buff+ret, name);
731 
732                 ret += namelen+1;
733                 namelen = sizeof(name);
734                 index++;
735             }
736         }
737 
738         RegCloseKey(sectionkey);
739     }
740     else
741         usedefault = entry != NULL;
742 
743     if (usedefault)
744     {
745         lstrcpynA(buff, defvalue, buff_len);
746         ret = strlen(buff);
747     }
748 
749     return ret;
750 }
751 
752 BOOL WINAPI SQLGetTranslatorW(HWND hwndParent, LPWSTR lpszName, WORD cbNameMax,
753                WORD *pcbNameOut, LPWSTR lpszPath, WORD cbPathMax,
754                WORD *pcbPathOut, DWORD *pvOption)
755 {
756     clear_errors();
757     FIXME("%p %s %d %p %p %d %p %p\n", hwndParent, debugstr_w(lpszName), cbNameMax,
758                pcbNameOut, lpszPath, cbPathMax, pcbPathOut, pvOption);
759     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
760     return FALSE;
761 }
762 
763 BOOL WINAPI SQLGetTranslator(HWND hwndParent, LPSTR lpszName, WORD cbNameMax,
764                WORD *pcbNameOut, LPSTR lpszPath, WORD cbPathMax,
765                WORD *pcbPathOut, DWORD *pvOption)
766 {
767     clear_errors();
768     FIXME("%p %s %d %p %p %d %p %p\n", hwndParent, debugstr_a(lpszName), cbNameMax,
769                pcbNameOut, lpszPath, cbPathMax, pcbPathOut, pvOption);
770     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
771     return FALSE;
772 }
773 
774 BOOL WINAPI SQLInstallDriverW(LPCWSTR lpszInfFile, LPCWSTR lpszDriver,
775                LPWSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut)
776 {
777     DWORD usage;
778 
779     clear_errors();
780     TRACE("%s %s %p %d %p\n", debugstr_w(lpszInfFile),
781           debugstr_w(lpszDriver), lpszPath, cbPathMax, pcbPathOut);
782 
783     if (lpszInfFile)
784         return FALSE;
785 
786     return SQLInstallDriverExW(lpszDriver, NULL, lpszPath, cbPathMax,
787                                pcbPathOut, ODBC_INSTALL_COMPLETE, &usage);
788 }
789 
790 BOOL WINAPI SQLInstallDriver(LPCSTR lpszInfFile, LPCSTR lpszDriver,
791                LPSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut)
792 {
793     DWORD usage;
794 
795     clear_errors();
796     TRACE("%s %s %p %d %p\n", debugstr_a(lpszInfFile),
797           debugstr_a(lpszDriver), lpszPath, cbPathMax, pcbPathOut);
798 
799     if (lpszInfFile)
800         return FALSE;
801 
802     return SQLInstallDriverEx(lpszDriver, NULL, lpszPath, cbPathMax,
803                               pcbPathOut, ODBC_INSTALL_COMPLETE, &usage);
804 }
805 
806 static void write_registry_values(const WCHAR *regkey, const WCHAR *driver, const  WCHAR *path_in, WCHAR *path,
807                                   DWORD *usage_count)
808 {
809     static const WCHAR installed[] = {'I','n','s','t','a','l','l','e','d',0};
810     static const WCHAR slash[] = {'\\', 0};
811     static const WCHAR driverW[] = {'D','r','i','v','e','r',0};
812     static const WCHAR setupW[] = {'S','e','t','u','p',0};
813     static const WCHAR translator[] = {'T','r','a','n','s','l','a','t','o','r',0};
814     HKEY hkey, hkeydriver;
815 
816     if (RegCreateKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey) == ERROR_SUCCESS)
817     {
818         if (RegCreateKeyW(hkey, regkey, &hkeydriver) == ERROR_SUCCESS)
819         {
820             if(RegSetValueExW(hkeydriver, driver, 0, REG_SZ, (BYTE*)installed, sizeof(installed)) != ERROR_SUCCESS)
821                 ERR("Failed to write registry installed key\n");
822 
823             RegCloseKey(hkeydriver);
824         }
825 
826         if (RegCreateKeyW(hkey, driver, &hkeydriver) == ERROR_SUCCESS)
827         {
828             WCHAR entry[1024];
829             const WCHAR *p;
830             DWORD usagecount = 0;
831             DWORD type, size;
832 
833             /* Skip name entry */
834             p = driver;
835             p += lstrlenW(p) + 1;
836 
837             if (!path_in)
838                 GetSystemDirectoryW(path, MAX_PATH);
839             else
840                 lstrcpyW(path, path_in);
841 
842             /* Store Usage */
843             size = sizeof(usagecount);
844             RegGetValueA(hkeydriver, NULL, "UsageCount", RRF_RT_DWORD, &type, &usagecount, &size);
845             TRACE("Usage count %d\n", usagecount);
846 
847             for (; *p; p += lstrlenW(p) + 1)
848             {
849                 WCHAR *divider = strchrW(p,'=');
850 
851                 if (divider)
852                 {
853                     WCHAR *value;
854                     int len;
855 
856                     /* Write pair values to the registry. */
857                     lstrcpynW(entry, p, divider - p + 1);
858 
859                     divider++;
860                     TRACE("Writing pair %s,%s\n", debugstr_w(entry), debugstr_w(divider));
861 
862                     /* Driver, Setup, Translator entries use the system path unless a path is specified. */
863                     if(lstrcmpiW(driverW, entry) == 0 || lstrcmpiW(setupW, entry) == 0 ||
864                        lstrcmpiW(translator, entry) == 0)
865                     {
866                         len = lstrlenW(path) + lstrlenW(slash) + lstrlenW(divider) + 1;
867                         value = heap_alloc(len * sizeof(WCHAR));
868                         if(!value)
869                         {
870                             ERR("Out of memory\n");
871                             return;
872                         }
873 
874                         lstrcpyW(value, path);
875                         lstrcatW(value, slash);
876                         lstrcatW(value, divider);
877                     }
878                     else
879                     {
880                         len = lstrlenW(divider) + 1;
881                         value = heap_alloc(len * sizeof(WCHAR));
882                         lstrcpyW(value, divider);
883                     }
884 
885                     if (RegSetValueExW(hkeydriver, entry, 0, REG_SZ, (BYTE*)value,
886                                     (lstrlenW(value)+1)*sizeof(WCHAR)) != ERROR_SUCCESS)
887                         ERR("Failed to write registry data %s %s\n", debugstr_w(entry), debugstr_w(value));
888                     heap_free(value);
889                 }
890                 else
891                 {
892                     ERR("No pair found. %s\n", debugstr_w(p));
893                     break;
894                 }
895             }
896 
897             /* Set Usage Count */
898             usagecount++;
899             if (RegSetValueExA(hkeydriver, "UsageCount", 0, REG_DWORD, (BYTE*)&usagecount, sizeof(usagecount)) != ERROR_SUCCESS)
900                 ERR("Failed to write registry UsageCount key\n");
901 
902             if (usage_count)
903                 *usage_count = usagecount;
904 
905             RegCloseKey(hkeydriver);
906         }
907 
908         RegCloseKey(hkey);
909     }
910 }
911 
912 BOOL WINAPI SQLInstallDriverExW(LPCWSTR lpszDriver, LPCWSTR lpszPathIn,
913                LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
914                WORD fRequest, LPDWORD lpdwUsageCount)
915 {
916     UINT len;
917     WCHAR path[MAX_PATH];
918 
919     clear_errors();
920     TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszDriver),
921           debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
922           fRequest, lpdwUsageCount);
923 
924     write_registry_values(odbcdrivers, lpszDriver, lpszPathIn, path, lpdwUsageCount);
925 
926     len = lstrlenW(path);
927 
928     if (pcbPathOut)
929         *pcbPathOut = len;
930 
931     if (lpszPathOut && cbPathOutMax > len)
932     {
933         lstrcpyW(lpszPathOut, path);
934         return TRUE;
935     }
936     return FALSE;
937 }
938 
939 BOOL WINAPI SQLInstallDriverEx(LPCSTR lpszDriver, LPCSTR lpszPathIn,
940                LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
941                WORD fRequest, LPDWORD lpdwUsageCount)
942 {
943     LPWSTR driver, pathin;
944     WCHAR pathout[MAX_PATH];
945     BOOL ret;
946     WORD cbOut = 0;
947 
948     clear_errors();
949     TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszDriver),
950           debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
951           fRequest, lpdwUsageCount);
952 
953     driver = SQLInstall_strdup_multi(lpszDriver);
954     pathin = SQLInstall_strdup(lpszPathIn);
955 
956     ret = SQLInstallDriverExW(driver, pathin, pathout, MAX_PATH, &cbOut,
957                               fRequest, lpdwUsageCount);
958     if (ret)
959     {
960         int len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
961                                        0, NULL, NULL);
962         if (len)
963         {
964             if (pcbPathOut)
965                 *pcbPathOut = len - 1;
966 
967             if (!lpszPathOut || cbPathOutMax < len)
968             {
969                 ret = FALSE;
970                 goto out;
971             }
972             len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
973                                        cbPathOutMax, NULL, NULL);
974         }
975     }
976 
977 out:
978     HeapFree(GetProcessHeap(), 0, driver);
979     HeapFree(GetProcessHeap(), 0, pathin);
980     return ret;
981 }
982 
983 BOOL WINAPI SQLInstallDriverManagerW(LPWSTR lpszPath, WORD cbPathMax,
984                WORD *pcbPathOut)
985 {
986     UINT len;
987     WCHAR path[MAX_PATH];
988 
989     TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut);
990 
991     if (cbPathMax < MAX_PATH)
992         return FALSE;
993 
994     clear_errors();
995 
996     len = GetSystemDirectoryW(path, MAX_PATH);
997 
998     if (pcbPathOut)
999         *pcbPathOut = len;
1000 
1001     if (lpszPath && cbPathMax > len)
1002     {
1003     	lstrcpyW(lpszPath, path);
1004     	return TRUE;
1005     }
1006     return FALSE;
1007 }
1008 
1009 BOOL WINAPI SQLInstallDriverManager(LPSTR lpszPath, WORD cbPathMax,
1010                WORD *pcbPathOut)
1011 {
1012     BOOL ret;
1013     WORD len, cbOut = 0;
1014     WCHAR path[MAX_PATH];
1015 
1016     TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut);
1017 
1018     if (cbPathMax < MAX_PATH)
1019         return FALSE;
1020 
1021     clear_errors();
1022 
1023     ret = SQLInstallDriverManagerW(path, MAX_PATH, &cbOut);
1024     if (ret)
1025     {
1026         len =  WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath, 0,
1027                                    NULL, NULL);
1028         if (len)
1029         {
1030             if (pcbPathOut)
1031                 *pcbPathOut = len - 1;
1032 
1033             if (!lpszPath || cbPathMax < len)
1034                 return FALSE;
1035 
1036             len =  WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath,
1037                                        cbPathMax, NULL, NULL);
1038         }
1039     }
1040     return ret;
1041 }
1042 
1043 BOOL WINAPI SQLInstallODBCW(HWND hwndParent, LPCWSTR lpszInfFile,
1044                LPCWSTR lpszSrcPath, LPCWSTR lpszDrivers)
1045 {
1046     clear_errors();
1047     FIXME("%p %s %s %s\n", hwndParent, debugstr_w(lpszInfFile),
1048                debugstr_w(lpszSrcPath), debugstr_w(lpszDrivers));
1049     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1050     return FALSE;
1051 }
1052 
1053 BOOL WINAPI SQLInstallODBC(HWND hwndParent, LPCSTR lpszInfFile,
1054                LPCSTR lpszSrcPath, LPCSTR lpszDrivers)
1055 {
1056     clear_errors();
1057     FIXME("%p %s %s %s\n", hwndParent, debugstr_a(lpszInfFile),
1058                debugstr_a(lpszSrcPath), debugstr_a(lpszDrivers));
1059     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1060     return FALSE;
1061 }
1062 
1063 SQLRETURN WINAPI SQLInstallerErrorW(WORD iError, DWORD *pfErrorCode,
1064                LPWSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
1065 {
1066     TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg,
1067           cbErrorMsgMax, pcbErrorMsg);
1068 
1069     if (iError == 0)
1070     {
1071         return SQL_ERROR;
1072     }
1073     else if (iError <= num_errors)
1074     {
1075         BOOL truncated = FALSE;
1076         WORD len;
1077         LPCWSTR msg;
1078         iError--;
1079         if (pfErrorCode)
1080             *pfErrorCode = error_code[iError];
1081         msg = error_msg[iError];
1082         len = msg ? lstrlenW(msg) : 0;
1083         if (pcbErrorMsg)
1084             *pcbErrorMsg = len;
1085         len++;
1086         if (cbErrorMsgMax < len)
1087         {
1088             len = cbErrorMsgMax;
1089             truncated = TRUE;
1090         }
1091         if (lpszErrorMsg && len)
1092         {
1093             if (msg)
1094             {
1095                 memcpy (lpszErrorMsg, msg, len * sizeof(WCHAR));
1096             }
1097             else
1098             {
1099                 assert(len==1);
1100                 *lpszErrorMsg = 0;
1101             }
1102         }
1103         else
1104         {
1105             /* Yes.  If you pass a null pointer and a large length it is not an error! */
1106             truncated = TRUE;
1107         }
1108 
1109         return truncated ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
1110     }
1111 
1112     /* At least on Windows 2000 , the buffers are not altered in this case.  However that is a little too dangerous a test for just now */
1113     if (pcbErrorMsg)
1114         *pcbErrorMsg = 0;
1115 
1116     if (lpszErrorMsg && cbErrorMsgMax > 0)
1117         *lpszErrorMsg = '\0';
1118 
1119     return SQL_NO_DATA;
1120 }
1121 
1122 SQLRETURN WINAPI SQLInstallerError(WORD iError, DWORD *pfErrorCode,
1123                LPSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg)
1124 {
1125     SQLRETURN ret;
1126     LPWSTR wbuf;
1127     WORD cbwbuf;
1128     TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg,
1129           cbErrorMsgMax, pcbErrorMsg);
1130 
1131     wbuf = 0;
1132     if (lpszErrorMsg && cbErrorMsgMax)
1133     {
1134         wbuf = HeapAlloc(GetProcessHeap(), 0, cbErrorMsgMax*sizeof(WCHAR));
1135         if (!wbuf)
1136             return SQL_ERROR;
1137     }
1138     ret = SQLInstallerErrorW(iError, pfErrorCode, wbuf, cbErrorMsgMax, &cbwbuf);
1139     if (wbuf)
1140     {
1141         WORD cbBuf = 0;
1142         SQLInstall_narrow(1, lpszErrorMsg, wbuf, cbwbuf+1, cbErrorMsgMax, &cbBuf);
1143         HeapFree(GetProcessHeap(), 0, wbuf);
1144         if (pcbErrorMsg)
1145             *pcbErrorMsg = cbBuf-1;
1146     }
1147     return ret;
1148 }
1149 
1150 BOOL WINAPI SQLInstallTranslatorExW(LPCWSTR lpszTranslator, LPCWSTR lpszPathIn,
1151                LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
1152                WORD fRequest, LPDWORD lpdwUsageCount)
1153 {
1154     UINT len;
1155     WCHAR path[MAX_PATH];
1156 
1157     clear_errors();
1158     TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszTranslator),
1159           debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
1160           fRequest, lpdwUsageCount);
1161 
1162     write_registry_values(odbctranslators, lpszTranslator, lpszPathIn, path, lpdwUsageCount);
1163 
1164     len = lstrlenW(path);
1165 
1166     if (pcbPathOut)
1167         *pcbPathOut = len;
1168 
1169     if (lpszPathOut && cbPathOutMax > len)
1170     {
1171         lstrcpyW(lpszPathOut, path);
1172         return TRUE;
1173     }
1174     return FALSE;
1175 }
1176 
1177 BOOL WINAPI SQLInstallTranslatorEx(LPCSTR lpszTranslator, LPCSTR lpszPathIn,
1178                LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut,
1179                WORD fRequest, LPDWORD lpdwUsageCount)
1180 {
1181     LPCSTR p;
1182     LPWSTR translator, pathin;
1183     WCHAR pathout[MAX_PATH];
1184     BOOL ret;
1185     WORD cbOut = 0;
1186 
1187     clear_errors();
1188     TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszTranslator),
1189           debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut,
1190           fRequest, lpdwUsageCount);
1191 
1192     for (p = lpszTranslator; *p; p += lstrlenA(p) + 1)
1193         TRACE("%s\n", debugstr_a(p));
1194 
1195     translator = SQLInstall_strdup_multi(lpszTranslator);
1196     pathin = SQLInstall_strdup(lpszPathIn);
1197 
1198     ret = SQLInstallTranslatorExW(translator, pathin, pathout, MAX_PATH,
1199                                   &cbOut, fRequest, lpdwUsageCount);
1200     if (ret)
1201     {
1202         int len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
1203                                        0, NULL, NULL);
1204         if (len)
1205         {
1206             if (pcbPathOut)
1207                 *pcbPathOut = len - 1;
1208 
1209             if (!lpszPathOut || cbPathOutMax < len)
1210             {
1211                 ret = FALSE;
1212                 goto out;
1213             }
1214             len =  WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut,
1215                                        cbPathOutMax, NULL, NULL);
1216         }
1217     }
1218 
1219 out:
1220     HeapFree(GetProcessHeap(), 0, translator);
1221     HeapFree(GetProcessHeap(), 0, pathin);
1222     return ret;
1223 }
1224 
1225 BOOL WINAPI SQLInstallTranslator(LPCSTR lpszInfFile, LPCSTR lpszTranslator,
1226                LPCSTR lpszPathIn, LPSTR lpszPathOut, WORD cbPathOutMax,
1227                WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount)
1228 {
1229     clear_errors();
1230     TRACE("%s %s %s %p %d %p %d %p\n", debugstr_a(lpszInfFile),
1231           debugstr_a(lpszTranslator), debugstr_a(lpszPathIn), lpszPathOut,
1232           cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1233 
1234     if (lpszInfFile)
1235         return FALSE;
1236 
1237     return SQLInstallTranslatorEx(lpszTranslator, lpszPathIn, lpszPathOut,
1238                        cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1239 }
1240 
1241 BOOL WINAPI SQLInstallTranslatorW(LPCWSTR lpszInfFile, LPCWSTR lpszTranslator,
1242               LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax,
1243               WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount)
1244 {
1245     clear_errors();
1246     TRACE("%s %s %s %p %d %p %d %p\n", debugstr_w(lpszInfFile),
1247           debugstr_w(lpszTranslator), debugstr_w(lpszPathIn), lpszPathOut,
1248           cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1249 
1250     if (lpszInfFile)
1251         return FALSE;
1252 
1253     return SQLInstallTranslatorExW(lpszTranslator, lpszPathIn, lpszPathOut,
1254                         cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount);
1255 }
1256 
1257 BOOL WINAPI SQLManageDataSources(HWND hwnd)
1258 {
1259     clear_errors();
1260     FIXME("%p\n", hwnd);
1261     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1262     return FALSE;
1263 }
1264 
1265 SQLRETURN WINAPI SQLPostInstallerErrorW(DWORD fErrorCode, LPCWSTR szErrorMsg)
1266 {
1267     FIXME("%u %s\n", fErrorCode, debugstr_w(szErrorMsg));
1268     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1269     return FALSE;
1270 }
1271 
1272 SQLRETURN WINAPI SQLPostInstallerError(DWORD fErrorCode, LPCSTR szErrorMsg)
1273 {
1274     FIXME("%u %s\n", fErrorCode, debugstr_a(szErrorMsg));
1275     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1276     return FALSE;
1277 }
1278 
1279 BOOL WINAPI SQLReadFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName,
1280                LPCWSTR lpszKeyName, LPWSTR lpszString, WORD cbString,
1281                WORD *pcbString)
1282 {
1283     clear_errors();
1284     FIXME("%s %s %s %s %d %p\n", debugstr_w(lpszFileName), debugstr_w(lpszAppName),
1285                debugstr_w(lpszKeyName), debugstr_w(lpszString), cbString, pcbString);
1286     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1287     return FALSE;
1288 }
1289 
1290 BOOL WINAPI SQLReadFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName,
1291                LPCSTR lpszKeyName, LPSTR lpszString, WORD cbString,
1292                WORD *pcbString)
1293 {
1294     clear_errors();
1295     FIXME("%s %s %s %s %d %p\n", debugstr_a(lpszFileName), debugstr_a(lpszAppName),
1296                debugstr_a(lpszKeyName), debugstr_a(lpszString), cbString, pcbString);
1297     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1298     return FALSE;
1299 }
1300 
1301 BOOL WINAPI SQLRemoveDefaultDataSource(void)
1302 {
1303     clear_errors();
1304     FIXME("\n");
1305     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1306     return FALSE;
1307 }
1308 
1309 BOOL WINAPI SQLRemoveDriverW(LPCWSTR drivername, BOOL remove_dsn, LPDWORD usage_count)
1310 {
1311     HKEY hkey;
1312     DWORD usagecount = 1;
1313 
1314     clear_errors();
1315     TRACE("%s %d %p\n", debugstr_w(drivername), remove_dsn, usage_count);
1316 
1317     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey) == ERROR_SUCCESS)
1318     {
1319         HKEY hkeydriver;
1320 
1321         if (RegOpenKeyW(hkey, drivername, &hkeydriver) == ERROR_SUCCESS)
1322         {
1323             DWORD size, type;
1324             DWORD count;
1325 
1326             size = sizeof(usagecount);
1327             RegGetValueA(hkeydriver, NULL, "UsageCount", RRF_RT_DWORD, &type, &usagecount, &size);
1328             TRACE("Usage count %d\n", usagecount);
1329             count = usagecount - 1;
1330             if (count)
1331             {
1332                  if (RegSetValueExA(hkeydriver, "UsageCount", 0, REG_DWORD, (BYTE*)&count, sizeof(count)) != ERROR_SUCCESS)
1333                     ERR("Failed to write registry UsageCount key\n");
1334             }
1335 
1336             RegCloseKey(hkeydriver);
1337         }
1338 
1339         if (usagecount)
1340             usagecount--;
1341 
1342         if (!usagecount)
1343         {
1344             if (RegDeleteKeyW(hkey, drivername) != ERROR_SUCCESS)
1345                 ERR("Failed to delete registry key: %s\n", debugstr_w(drivername));
1346 
1347             if (RegOpenKeyW(hkey, odbcdrivers, &hkeydriver) == ERROR_SUCCESS)
1348             {
1349                 if(RegDeleteValueW(hkeydriver, drivername) != ERROR_SUCCESS)
1350                     ERR("Failed to delete registry value: %s\n", debugstr_w(drivername));
1351                 RegCloseKey(hkeydriver);
1352             }
1353         }
1354 
1355         RegCloseKey(hkey);
1356     }
1357 
1358     if (usage_count)
1359         *usage_count = usagecount;
1360 
1361     return TRUE;
1362 }
1363 
1364 BOOL WINAPI SQLRemoveDriver(LPCSTR lpszDriver, BOOL fRemoveDSN,
1365                LPDWORD lpdwUsageCount)
1366 {
1367     WCHAR *driver;
1368     BOOL ret;
1369 
1370     clear_errors();
1371     TRACE("%s %d %p\n", debugstr_a(lpszDriver), fRemoveDSN, lpdwUsageCount);
1372 
1373     driver = SQLInstall_strdup(lpszDriver);
1374 
1375     ret =  SQLRemoveDriverW(driver, fRemoveDSN, lpdwUsageCount);
1376 
1377     HeapFree(GetProcessHeap(), 0, driver);
1378     return ret;
1379 }
1380 
1381 BOOL WINAPI SQLRemoveDriverManager(LPDWORD pdwUsageCount)
1382 {
1383     clear_errors();
1384     FIXME("%p\n", pdwUsageCount);
1385     if (pdwUsageCount) *pdwUsageCount = 1;
1386     return TRUE;
1387 }
1388 
1389 BOOL WINAPI SQLRemoveDSNFromIniW(LPCWSTR lpszDSN)
1390 {
1391     clear_errors();
1392     FIXME("%s\n", debugstr_w(lpszDSN));
1393     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1394     return FALSE;
1395 }
1396 
1397 BOOL WINAPI SQLRemoveDSNFromIni(LPCSTR lpszDSN)
1398 {
1399     clear_errors();
1400     FIXME("%s\n", debugstr_a(lpszDSN));
1401     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1402     return FALSE;
1403 }
1404 
1405 BOOL WINAPI SQLRemoveTranslatorW(const WCHAR *translator, DWORD *usage_count)
1406 {
1407     HKEY hkey;
1408     DWORD usagecount = 1;
1409     BOOL ret = TRUE;
1410 
1411     clear_errors();
1412     TRACE("%s %p\n", debugstr_w(translator), usage_count);
1413 
1414     if (RegOpenKeyW(HKEY_LOCAL_MACHINE, odbcini, &hkey) == ERROR_SUCCESS)
1415     {
1416         HKEY hkeydriver;
1417 
1418         if (RegOpenKeyW(hkey, translator, &hkeydriver) == ERROR_SUCCESS)
1419         {
1420             DWORD size, type;
1421             DWORD count;
1422 
1423             size = sizeof(usagecount);
1424             RegGetValueA(hkeydriver, NULL, "UsageCount", RRF_RT_DWORD, &type, &usagecount, &size);
1425             TRACE("Usage count %d\n", usagecount);
1426             count = usagecount - 1;
1427             if (count)
1428             {
1429                  if (RegSetValueExA(hkeydriver, "UsageCount", 0, REG_DWORD, (BYTE*)&count, sizeof(count)) != ERROR_SUCCESS)
1430                     ERR("Failed to write registry UsageCount key\n");
1431             }
1432 
1433             RegCloseKey(hkeydriver);
1434         }
1435 
1436         if (usagecount)
1437             usagecount--;
1438 
1439         if (!usagecount)
1440         {
1441             if(RegDeleteKeyW(hkey, translator) != ERROR_SUCCESS)
1442             {
1443                 push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
1444                 WARN("Failed to delete registry key: %s\n", debugstr_w(translator));
1445                 ret = FALSE;
1446             }
1447 
1448             if (ret && RegOpenKeyW(hkey, odbctranslators, &hkeydriver) == ERROR_SUCCESS)
1449             {
1450                 if(RegDeleteValueW(hkeydriver, translator) != ERROR_SUCCESS)
1451                 {
1452                     push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found);
1453                     WARN("Failed to delete registry key: %s\n", debugstr_w(translator));
1454                     ret = FALSE;
1455                 }
1456 
1457                 RegCloseKey(hkeydriver);
1458             }
1459         }
1460 
1461         RegCloseKey(hkey);
1462     }
1463 
1464     if (ret && usage_count)
1465         *usage_count = usagecount;
1466 
1467     return ret;
1468 }
1469 
1470 BOOL WINAPI SQLRemoveTranslator(LPCSTR lpszTranslator, LPDWORD lpdwUsageCount)
1471 {
1472     WCHAR *translator;
1473     BOOL ret;
1474 
1475     clear_errors();
1476     TRACE("%s %p\n", debugstr_a(lpszTranslator), lpdwUsageCount);
1477 
1478     translator = SQLInstall_strdup(lpszTranslator);
1479     ret =  SQLRemoveTranslatorW(translator, lpdwUsageCount);
1480 
1481     HeapFree(GetProcessHeap(), 0, translator);
1482     return ret;
1483 }
1484 
1485 BOOL WINAPI SQLSetConfigMode(UWORD wConfigMode)
1486 {
1487     clear_errors();
1488     TRACE("%u\n", wConfigMode);
1489 
1490     if (wConfigMode > ODBC_SYSTEM_DSN)
1491     {
1492         push_error(ODBC_ERROR_INVALID_PARAM_SEQUENCE, odbc_error_invalid_param_sequence);
1493         return FALSE;
1494     }
1495     else
1496     {
1497         config_mode = wConfigMode;
1498         return TRUE;
1499     }
1500 }
1501 
1502 BOOL WINAPI SQLValidDSNW(LPCWSTR lpszDSN)
1503 {
1504     clear_errors();
1505     FIXME("%s\n", debugstr_w(lpszDSN));
1506     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1507     return FALSE;
1508 }
1509 
1510 BOOL WINAPI SQLValidDSN(LPCSTR lpszDSN)
1511 {
1512     clear_errors();
1513     FIXME("%s\n", debugstr_a(lpszDSN));
1514     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1515     return FALSE;
1516 }
1517 
1518 BOOL WINAPI SQLWriteDSNToIniW(LPCWSTR lpszDSN, LPCWSTR lpszDriver)
1519 {
1520     clear_errors();
1521     FIXME("%s %s\n", debugstr_w(lpszDSN), debugstr_w(lpszDriver));
1522     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1523     return FALSE;
1524 }
1525 
1526 BOOL WINAPI SQLWriteDSNToIni(LPCSTR lpszDSN, LPCSTR lpszDriver)
1527 {
1528     clear_errors();
1529     FIXME("%s %s\n", debugstr_a(lpszDSN), debugstr_a(lpszDriver));
1530     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1531     return FALSE;
1532 }
1533 
1534 BOOL WINAPI SQLWriteFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName,
1535                LPCWSTR lpszKeyName, LPCWSTR lpszString)
1536 {
1537     clear_errors();
1538     FIXME("%s %s %s %s\n", debugstr_w(lpszFileName), debugstr_w(lpszAppName),
1539                  debugstr_w(lpszKeyName), debugstr_w(lpszString));
1540     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1541     return FALSE;
1542 }
1543 
1544 BOOL WINAPI SQLWriteFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName,
1545                LPCSTR lpszKeyName, LPCSTR lpszString)
1546 {
1547     clear_errors();
1548     FIXME("%s %s %s %s\n", debugstr_a(lpszFileName), debugstr_a(lpszAppName),
1549                  debugstr_a(lpszKeyName), debugstr_a(lpszString));
1550     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1551     return FALSE;
1552 }
1553 
1554 BOOL WINAPI SQLWritePrivateProfileStringW(LPCWSTR lpszSection, LPCWSTR lpszEntry,
1555                LPCWSTR lpszString, LPCWSTR lpszFilename)
1556 {
1557     LONG ret;
1558     HKEY hkey;
1559 
1560     clear_errors();
1561     TRACE("%s %s %s %s\n", debugstr_w(lpszSection), debugstr_w(lpszEntry),
1562                 debugstr_w(lpszString), debugstr_w(lpszFilename));
1563 
1564     if(!lpszFilename || !*lpszFilename)
1565     {
1566         push_error(ODBC_ERROR_INVALID_STR, odbc_error_invalid_param_string);
1567         return FALSE;
1568     }
1569 
1570     if ((ret = RegCreateKeyW(HKEY_CURRENT_USER, odbcW, &hkey)) == ERROR_SUCCESS)
1571     {
1572          HKEY hkeyfilename;
1573 
1574          if ((ret = RegCreateKeyW(hkey, lpszFilename, &hkeyfilename)) == ERROR_SUCCESS)
1575          {
1576               HKEY hkey_section;
1577 
1578               if ((ret = RegCreateKeyW(hkeyfilename, lpszSection, &hkey_section)) == ERROR_SUCCESS)
1579               {
1580                   ret = RegSetValueExW(hkey_section, lpszEntry, 0, REG_SZ, (BYTE*)lpszString, (lstrlenW(lpszString)+1)*sizeof(WCHAR));
1581                   RegCloseKey(hkey_section);
1582               }
1583 
1584               RegCloseKey(hkeyfilename);
1585          }
1586 
1587          RegCloseKey(hkey);
1588     }
1589 
1590     return ret == ERROR_SUCCESS;
1591 }
1592 
1593 BOOL WINAPI SQLWritePrivateProfileString(LPCSTR lpszSection, LPCSTR lpszEntry,
1594                LPCSTR lpszString, LPCSTR lpszFilename)
1595 {
1596     BOOL ret;
1597     WCHAR *sect, *entry, *string, *file;
1598     clear_errors();
1599     TRACE("%s %s %s %s\n", lpszSection, lpszEntry, lpszString, lpszFilename);
1600 
1601     sect = heap_strdupAtoW(lpszSection);
1602     entry = heap_strdupAtoW(lpszEntry);
1603     string = heap_strdupAtoW(lpszString);
1604     file = heap_strdupAtoW(lpszFilename);
1605 
1606     ret = SQLWritePrivateProfileStringW(sect, entry, string, file);
1607 
1608     heap_free(sect);
1609     heap_free(entry);
1610     heap_free(string);
1611     heap_free(file);
1612 
1613     return ret;
1614 }
1615