/* * Implementation of the ODBC driver installer * * Copyright 2005 Mike McCormack for CodeWeavers * Copyright 2005 Hans Leidekker * Copyright 2007 Bill Medland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #define COBJMACROS #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winreg.h" #include "winnls.h" #include "wine/debug.h" #include "odbcinst.h" WINE_DEFAULT_DEBUG_CHANNEL(odbc); /* Registry key names */ 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}; /* This config mode is known to be process-wide. * MSDN documentation suggests that the value is hidden somewhere in the registry but I haven't found it yet. * Although both the registry and the ODBC.ini files appear to be maintained together they are not maintained automatically through the registry's IniFileMapping. */ static UWORD config_mode = ODBC_BOTH_DSN; /* MSDN documentation suggests that the error subsystem handles errors 1 to 8 * only and experimentation (Windows 2000) shows that the errors are process- * wide so go for the simple solution; static arrays. */ static int num_errors; static int error_code[8]; static const WCHAR *error_msg[8]; static const WCHAR odbc_error_general_err[] = {'G','e','n','e','r','a','l',' ','e','r','r','o','r',0}; 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}; 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}; static const WCHAR odbc_error_out_of_mem[] = {'O','u','t',' ','o','f',' ','m','e','m','o','r','y',0}; 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}; /* Push an error onto the error stack, taking care of ranges etc. */ static void push_error(int code, LPCWSTR msg) { if (num_errors < sizeof error_code/sizeof error_code[0]) { error_code[num_errors] = code; error_msg[num_errors] = msg; num_errors++; } } /* Clear the error stack */ static void clear_errors(void) { num_errors = 0; } BOOL WINAPI ODBCCPlApplet( LONG i, LONG j, LONG * p1, LONG * p2) { clear_errors(); FIXME( "( %d %d %p %p) : stub!\n", i, j, p1, p2); return FALSE; } static LPWSTR SQLInstall_strdup_multi(LPCSTR str) { LPCSTR p; LPWSTR ret = NULL; DWORD len; if (!str) return ret; for (p = str; *p; p += lstrlenA(p) + 1) ; len = MultiByteToWideChar(CP_ACP, 0, str, p - str, NULL, 0 ); ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, str, p - str, ret, len ); ret[len] = 0; return ret; } static LPWSTR SQLInstall_strdup(LPCSTR str) { DWORD len; LPWSTR ret = NULL; if (!str) return ret; len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0 ); ret = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len ); return ret; } /* Convert the wide string or zero-length-terminated list of wide strings to a * narrow string or zero-length-terminated list of narrow strings. * Do not try to emulate windows undocumented excesses (e.g. adding a third \0 * to a list) * Arguments * mode Indicates the sort of string. * 1 denotes that the buffers contain strings terminated by a single nul * character * 2 denotes that the buffers contain zero-length-terminated lists * (frequently erroneously referred to as double-null-terminated) * buffer The narrow-character buffer into which to place the result. This * must be a non-null pointer to the first element of a buffer whose * length is passed in buffer_length. * str The wide-character buffer containing the string or list of strings to * be converted. str_length defines how many wide characters in the * buffer are to be converted, including all desired terminating nul * characters. * str_length Effective length of str * buffer_length Length of buffer * returned_length A pointer to a variable that will receive the number of * narrow characters placed into the buffer. This pointer * may be NULL. */ static BOOL SQLInstall_narrow(int mode, LPSTR buffer, LPCWSTR str, WORD str_length, WORD buffer_length, WORD *returned_length) { LPSTR pbuf; /* allows us to allocate a temporary buffer only if needed */ int len; /* Length of the converted list */ BOOL success = FALSE; assert(mode == 1 || mode == 2); assert(buffer_length); len = WideCharToMultiByte(CP_ACP, 0, str, str_length, 0, 0, NULL, NULL); if (len > 0) { if (len > buffer_length) { pbuf = HeapAlloc(GetProcessHeap(), 0, len); } else { pbuf = buffer; } len = WideCharToMultiByte(CP_ACP, 0, str, str_length, pbuf, len, NULL, NULL); if (len > 0) { if (pbuf != buffer) { if (buffer_length > (mode - 1)) { memcpy (buffer, pbuf, buffer_length-mode); *(buffer+buffer_length-mode) = '\0'; } *(buffer+buffer_length-1) = '\0'; } if (returned_length) { *returned_length = pbuf == buffer ? len : buffer_length; } success = TRUE; } else { ERR("transferring wide to narrow\n"); } if (pbuf != buffer) { HeapFree(GetProcessHeap(), 0, pbuf); } } else { ERR("measuring wide to narrow\n"); } return success; } BOOL WINAPI SQLConfigDataSourceW(HWND hwndParent, WORD fRequest, LPCWSTR lpszDriver, LPCWSTR lpszAttributes) { LPCWSTR p; clear_errors(); FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_w(lpszDriver), debugstr_w(lpszAttributes)); for (p = lpszAttributes; *p; p += lstrlenW(p) + 1) FIXME("%s\n", debugstr_w(p)); return TRUE; } BOOL WINAPI SQLConfigDataSource(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes) { FIXME("%p %d %s %s\n", hwndParent, fRequest, debugstr_a(lpszDriver), debugstr_a(lpszAttributes)); clear_errors(); return TRUE; } BOOL WINAPI SQLConfigDriverW(HWND hwndParent, WORD fRequest, LPCWSTR lpszDriver, LPCWSTR lpszArgs, LPWSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut) { clear_errors(); FIXME("(%p %d %s %s %p %d %p)\n", hwndParent, fRequest, debugstr_w(lpszDriver), debugstr_w(lpszArgs), lpszMsg, cbMsgMax, pcbMsgOut); return TRUE; } BOOL WINAPI SQLConfigDriver(HWND hwndParent, WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszArgs, LPSTR lpszMsg, WORD cbMsgMax, WORD *pcbMsgOut) { clear_errors(); FIXME("(%p %d %s %s %p %d %p)\n", hwndParent, fRequest, debugstr_a(lpszDriver), debugstr_a(lpszArgs), lpszMsg, cbMsgMax, pcbMsgOut); return TRUE; } BOOL WINAPI SQLCreateDataSourceW(HWND hwnd, LPCWSTR lpszDS) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLCreateDataSource(HWND hwnd, LPCSTR lpszDS) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLGetAvailableDriversW(LPCWSTR lpszInfFile, LPWSTR lpszBuf, WORD cbBufMax, WORD *pcbBufOut) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLGetAvailableDrivers(LPCSTR lpszInfFile, LPSTR lpszBuf, WORD cbBufMax, WORD *pcbBufOut) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLGetConfigMode(UWORD *pwConfigMode) { clear_errors(); if (pwConfigMode) *pwConfigMode = config_mode; return TRUE; } /* This is implemented sensibly rather than according to exact conformance to Microsoft's buggy implementations * e.g. The Microsoft one occasionally actually adds a third nul character (possibly beyond the buffer). * 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. */ BOOL WINAPI SQLGetInstalledDriversW(LPWSTR lpszBuf, WORD cbBufMax, WORD *pcbBufOut) { HKEY hDrivers; /* Registry handle to the Drivers key */ LONG reg_ret; /* Return code from registry functions */ BOOL success = FALSE; /* The value we will return */ clear_errors(); if (!lpszBuf || cbBufMax == 0) { push_error(ODBC_ERROR_INVALID_BUFF_LEN, odbc_error_invalid_buff_len); } else if ((reg_ret = RegOpenKeyExW (HKEY_LOCAL_MACHINE /* The drivers does not depend on the config mode */, drivers_key, 0, KEY_READ /* Maybe overkill */, &hDrivers)) == ERROR_SUCCESS) { DWORD index = 0; cbBufMax--; success = TRUE; while (cbBufMax > 0) { DWORD size_name; size_name = cbBufMax; if ((reg_ret = RegEnumValueW(hDrivers, index, lpszBuf, &size_name, NULL, NULL, NULL, NULL)) == ERROR_SUCCESS) { index++; assert (size_name < cbBufMax && *(lpszBuf + size_name) == 0); size_name++; cbBufMax-= size_name; lpszBuf+=size_name; } else { if (reg_ret != ERROR_NO_MORE_ITEMS) { success = FALSE; push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err); } break; } } *lpszBuf = 0; if ((reg_ret = RegCloseKey (hDrivers)) != ERROR_SUCCESS) TRACE ("Error %d closing ODBC Drivers key\n", reg_ret); } else { /* MSDN states that it returns failure with COMPONENT_NOT_FOUND in this case. * Version 3.525.1117.0 (Windows 2000) does not; it actually returns success. * I doubt if it will actually be an issue. */ push_error(ODBC_ERROR_COMPONENT_NOT_FOUND, odbc_error_component_not_found); } return success; } BOOL WINAPI SQLGetInstalledDrivers(LPSTR lpszBuf, WORD cbBufMax, WORD *pcbBufOut) { BOOL ret; int size_wbuf = cbBufMax; LPWSTR wbuf; WORD size_used; wbuf = HeapAlloc(GetProcessHeap(), 0, size_wbuf*sizeof(WCHAR)); if (wbuf) { ret = SQLGetInstalledDriversW(wbuf, size_wbuf, &size_used); if (ret) { if (!(ret = SQLInstall_narrow(2, lpszBuf, wbuf, size_used, cbBufMax, pcbBufOut))) { push_error(ODBC_ERROR_GENERAL_ERR, odbc_error_general_err); } } HeapFree(GetProcessHeap(), 0, wbuf); /* ignore failure; we have achieved the aim */ } else { push_error(ODBC_ERROR_OUT_OF_MEM, odbc_error_out_of_mem); ret = FALSE; } return ret; } int WINAPI SQLGetPrivateProfileStringW(LPCWSTR lpszSection, LPCWSTR lpszEntry, LPCWSTR lpszDefault, LPCWSTR RetBuffer, int cbRetBuffer, LPCWSTR lpszFilename) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } int WINAPI SQLGetPrivateProfileString(LPCSTR lpszSection, LPCSTR lpszEntry, LPCSTR lpszDefault, LPCSTR RetBuffer, int cbRetBuffer, LPCSTR lpszFilename) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLGetTranslatorW(HWND hwndParent, LPWSTR lpszName, WORD cbNameMax, WORD *pcbNameOut, LPWSTR lpszPath, WORD cbPathMax, WORD *pcbPathOut, DWORD *pvOption) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLGetTranslator(HWND hwndParent, LPSTR lpszName, WORD cbNameMax, WORD *pcbNameOut, LPSTR lpszPath, WORD cbPathMax, WORD *pcbPathOut, DWORD *pvOption) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLInstallDriverW(LPCWSTR lpszInfFile, LPCWSTR lpszDriver, LPWSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut) { DWORD usage; clear_errors(); TRACE("%s %s %p %d %p\n", debugstr_w(lpszInfFile), debugstr_w(lpszDriver), lpszPath, cbPathMax, pcbPathOut); if (lpszInfFile) return FALSE; return SQLInstallDriverExW(lpszDriver, NULL, lpszPath, cbPathMax, pcbPathOut, ODBC_INSTALL_COMPLETE, &usage); } BOOL WINAPI SQLInstallDriver(LPCSTR lpszInfFile, LPCSTR lpszDriver, LPSTR lpszPath, WORD cbPathMax, WORD * pcbPathOut) { DWORD usage; clear_errors(); TRACE("%s %s %p %d %p\n", debugstr_a(lpszInfFile), debugstr_a(lpszDriver), lpszPath, cbPathMax, pcbPathOut); if (lpszInfFile) return FALSE; return SQLInstallDriverEx(lpszDriver, NULL, lpszPath, cbPathMax, pcbPathOut, ODBC_INSTALL_COMPLETE, &usage); } BOOL WINAPI SQLInstallDriverExW(LPCWSTR lpszDriver, LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { UINT len; LPCWSTR p; WCHAR path[MAX_PATH]; clear_errors(); TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszDriver), debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); for (p = lpszDriver; *p; p += lstrlenW(p) + 1) TRACE("%s\n", debugstr_w(p)); len = GetSystemDirectoryW(path, MAX_PATH); if (pcbPathOut) *pcbPathOut = len; len = GetSystemDirectoryW(path, MAX_PATH); if (lpszPathOut && cbPathOutMax > len) { lstrcpyW(lpszPathOut, path); return TRUE; } return FALSE; } BOOL WINAPI SQLInstallDriverEx(LPCSTR lpszDriver, LPCSTR lpszPathIn, LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { LPCSTR p; LPWSTR driver, pathin; WCHAR pathout[MAX_PATH]; BOOL ret; WORD cbOut = 0; clear_errors(); TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszDriver), debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); for (p = lpszDriver; *p; p += lstrlenA(p) + 1) TRACE("%s\n", debugstr_a(p)); driver = SQLInstall_strdup_multi(lpszDriver); pathin = SQLInstall_strdup(lpszPathIn); ret = SQLInstallDriverExW(driver, pathin, pathout, MAX_PATH, &cbOut, fRequest, lpdwUsageCount); if (ret) { int len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut, 0, NULL, NULL); if (len) { if (pcbPathOut) *pcbPathOut = len - 1; if (!lpszPathOut || cbPathOutMax < len) { ret = FALSE; goto out; } len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut, cbPathOutMax, NULL, NULL); } } out: HeapFree(GetProcessHeap(), 0, driver); HeapFree(GetProcessHeap(), 0, pathin); return ret; } BOOL WINAPI SQLInstallDriverManagerW(LPWSTR lpszPath, WORD cbPathMax, WORD *pcbPathOut) { UINT len; WCHAR path[MAX_PATH]; TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut); if (cbPathMax < MAX_PATH) return FALSE; clear_errors(); len = GetSystemDirectoryW(path, MAX_PATH); if (pcbPathOut) *pcbPathOut = len; if (lpszPath && cbPathMax > len) { lstrcpyW(lpszPath, path); return TRUE; } return FALSE; } BOOL WINAPI SQLInstallDriverManager(LPSTR lpszPath, WORD cbPathMax, WORD *pcbPathOut) { BOOL ret; WORD len, cbOut = 0; WCHAR path[MAX_PATH]; TRACE("(%p %d %p)\n", lpszPath, cbPathMax, pcbPathOut); if (cbPathMax < MAX_PATH) return FALSE; clear_errors(); ret = SQLInstallDriverManagerW(path, MAX_PATH, &cbOut); if (ret) { len = WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath, 0, NULL, NULL); if (len) { if (pcbPathOut) *pcbPathOut = len - 1; if (!lpszPath || cbPathMax < len) return FALSE; len = WideCharToMultiByte(CP_ACP, 0, path, -1, lpszPath, cbPathMax, NULL, NULL); } } return ret; } BOOL WINAPI SQLInstallODBCW(HWND hwndParent, LPCWSTR lpszInfFile, LPCWSTR lpszSrcPath, LPCWSTR lpszDrivers) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLInstallODBC(HWND hwndParent, LPCSTR lpszInfFile, LPCSTR lpszSrcPath, LPCSTR lpszDrivers) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } SQLRETURN WINAPI SQLInstallerErrorW(WORD iError, DWORD *pfErrorCode, LPWSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg) { TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg, cbErrorMsgMax, pcbErrorMsg); if (iError == 0) { return SQL_ERROR; } else if (iError <= num_errors) { BOOL truncated = FALSE; WORD len; LPCWSTR msg; iError--; if (pfErrorCode) *pfErrorCode = error_code[iError]; msg = error_msg[iError]; len = msg ? lstrlenW(msg) : 0; if (pcbErrorMsg) *pcbErrorMsg = len; len++; if (cbErrorMsgMax < len) { len = cbErrorMsgMax; truncated = TRUE; } if (lpszErrorMsg && len) { if (msg) { memcpy (lpszErrorMsg, msg, len * sizeof(WCHAR)); } else { assert(len==1); *lpszErrorMsg = 0; } } else { /* Yes. If you pass a null pointer and a large length it is not an error! */ truncated = TRUE; } return truncated ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS; } /* 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 */ if (pcbErrorMsg) *pcbErrorMsg = 0; if (lpszErrorMsg && cbErrorMsgMax > 0) *lpszErrorMsg = '\0'; return SQL_NO_DATA; } SQLRETURN WINAPI SQLInstallerError(WORD iError, DWORD *pfErrorCode, LPSTR lpszErrorMsg, WORD cbErrorMsgMax, WORD *pcbErrorMsg) { SQLRETURN ret; LPWSTR wbuf; WORD cbwbuf; TRACE("%d %p %p %d %p\n", iError, pfErrorCode, lpszErrorMsg, cbErrorMsgMax, pcbErrorMsg); wbuf = 0; if (lpszErrorMsg && cbErrorMsgMax) { wbuf = HeapAlloc(GetProcessHeap(), 0, cbErrorMsgMax*sizeof(WCHAR)); if (!wbuf) return SQL_ERROR; } ret = SQLInstallerErrorW(iError, pfErrorCode, wbuf, cbErrorMsgMax, &cbwbuf); if (wbuf) { WORD cbBuf = 0; SQLInstall_narrow(1, lpszErrorMsg, wbuf, cbwbuf+1, cbErrorMsgMax, &cbBuf); HeapFree(GetProcessHeap(), 0, wbuf); if (pcbErrorMsg) *pcbErrorMsg = cbBuf-1; } return ret; } BOOL WINAPI SQLInstallTranslatorExW(LPCWSTR lpszTranslator, LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { UINT len; LPCWSTR p; WCHAR path[MAX_PATH]; clear_errors(); TRACE("%s %s %p %d %p %d %p\n", debugstr_w(lpszTranslator), debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); for (p = lpszTranslator; *p; p += lstrlenW(p) + 1) TRACE("%s\n", debugstr_w(p)); len = GetSystemDirectoryW(path, MAX_PATH); if (pcbPathOut) *pcbPathOut = len; if (lpszPathOut && cbPathOutMax > len) { lstrcpyW(lpszPathOut, path); return TRUE; } return FALSE; } BOOL WINAPI SQLInstallTranslatorEx(LPCSTR lpszTranslator, LPCSTR lpszPathIn, LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { LPCSTR p; LPWSTR translator, pathin; WCHAR pathout[MAX_PATH]; BOOL ret; WORD cbOut = 0; clear_errors(); TRACE("%s %s %p %d %p %d %p\n", debugstr_a(lpszTranslator), debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); for (p = lpszTranslator; *p; p += lstrlenA(p) + 1) TRACE("%s\n", debugstr_a(p)); translator = SQLInstall_strdup_multi(lpszTranslator); pathin = SQLInstall_strdup(lpszPathIn); ret = SQLInstallTranslatorExW(translator, pathin, pathout, MAX_PATH, &cbOut, fRequest, lpdwUsageCount); if (ret) { int len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut, 0, NULL, NULL); if (len) { if (pcbPathOut) *pcbPathOut = len - 1; if (!lpszPathOut || cbPathOutMax < len) { ret = FALSE; goto out; } len = WideCharToMultiByte(CP_ACP, 0, pathout, -1, lpszPathOut, cbPathOutMax, NULL, NULL); } } out: HeapFree(GetProcessHeap(), 0, translator); HeapFree(GetProcessHeap(), 0, pathin); return ret; } BOOL WINAPI SQLInstallTranslator(LPCSTR lpszInfFile, LPCSTR lpszTranslator, LPCSTR lpszPathIn, LPSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { clear_errors(); TRACE("%s %s %s %p %d %p %d %p\n", debugstr_a(lpszInfFile), debugstr_a(lpszTranslator), debugstr_a(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); if (lpszInfFile) return FALSE; return SQLInstallTranslatorEx(lpszTranslator, lpszPathIn, lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); } BOOL WINAPI SQLInstallTranslatorW(LPCWSTR lpszInfFile, LPCWSTR lpszTranslator, LPCWSTR lpszPathIn, LPWSTR lpszPathOut, WORD cbPathOutMax, WORD *pcbPathOut, WORD fRequest, LPDWORD lpdwUsageCount) { clear_errors(); TRACE("%s %s %s %p %d %p %d %p\n", debugstr_w(lpszInfFile), debugstr_w(lpszTranslator), debugstr_w(lpszPathIn), lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); if (lpszInfFile) return FALSE; return SQLInstallTranslatorExW(lpszTranslator, lpszPathIn, lpszPathOut, cbPathOutMax, pcbPathOut, fRequest, lpdwUsageCount); } BOOL WINAPI SQLManageDataSources(HWND hwnd) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } SQLRETURN WINAPI SQLPostInstallerErrorW(DWORD fErrorCode, LPCWSTR szErrorMsg) { FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } SQLRETURN WINAPI SQLPostInstallerError(DWORD fErrorCode, LPCSTR szErrorMsg) { FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLReadFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName, LPCWSTR lpszKeyName, LPWSTR lpszString, WORD cbString, WORD *pcbString) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLReadFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName, LPCSTR lpszKeyName, LPSTR lpszString, WORD cbString, WORD *pcbString) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLRemoveDefaultDataSource(void) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLRemoveDriverW(LPCWSTR lpszDriver, BOOL fRemoveDSN, LPDWORD lpdwUsageCount) { clear_errors(); FIXME("stub\n"); if (lpdwUsageCount) *lpdwUsageCount = 1; return TRUE; } BOOL WINAPI SQLRemoveDriver(LPCSTR lpszDriver, BOOL fRemoveDSN, LPDWORD lpdwUsageCount) { clear_errors(); FIXME("stub\n"); if (lpdwUsageCount) *lpdwUsageCount = 1; return TRUE; } BOOL WINAPI SQLRemoveDriverManager(LPDWORD pdwUsageCount) { clear_errors(); FIXME("stub\n"); if (pdwUsageCount) *pdwUsageCount = 1; return TRUE; } BOOL WINAPI SQLRemoveDSNFromIniW(LPCWSTR lpszDSN) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLRemoveDSNFromIni(LPCSTR lpszDSN) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLRemoveTranslatorW(LPCWSTR lpszTranslator, LPDWORD lpdwUsageCount) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLRemoveTranslator(LPCSTR lpszTranslator, LPDWORD lpdwUsageCount) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLSetConfigMode(UWORD wConfigMode) { clear_errors(); if (wConfigMode > ODBC_SYSTEM_DSN) { push_error(ODBC_ERROR_INVALID_PARAM_SEQUENCE, odbc_error_invalid_param_sequence); return FALSE; } else { config_mode = wConfigMode; return TRUE; } } BOOL WINAPI SQLValidDSNW(LPCWSTR lpszDSN) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLValidDSN(LPCSTR lpszDSN) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLWriteDSNToIniW(LPCWSTR lpszDSN, LPCWSTR lpszDriver) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLWriteDSNToIni(LPCSTR lpszDSN, LPCSTR lpszDriver) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLWriteFileDSNW(LPCWSTR lpszFileName, LPCWSTR lpszAppName, LPCWSTR lpszKeyName, LPCWSTR lpszString) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLWriteFileDSN(LPCSTR lpszFileName, LPCSTR lpszAppName, LPCSTR lpszKeyName, LPCSTR lpszString) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLWritePrivateProfileStringW(LPCWSTR lpszSection, LPCWSTR lpszEntry, LPCWSTR lpszString, LPCWSTR lpszFilename) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; } BOOL WINAPI SQLWritePrivateProfileString(LPCSTR lpszSection, LPCSTR lpszEntry, LPCSTR lpszString, LPCSTR lpszFilename) { clear_errors(); FIXME("\n"); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return FALSE; }