From: Steven Edwards Date: Sat, 24 Jan 2004 15:39:47 +0000 (+0000) Subject: Started porting Setupapi.dll from WINE to ReactOS. X-Git-Tag: backups/avendor@12434^2~181 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=ed4bb6a2cd9fb07b782c4837c9e9fe0d9ad74fac Started porting Setupapi.dll from WINE to ReactOS. svn path=/trunk/; revision=7863 --- diff --git a/reactos/include/wine/setupapi.h b/reactos/include/wine/setupapi.h new file mode 100644 index 00000000000..0100b00b0db --- /dev/null +++ b/reactos/include/wine/setupapi.h @@ -0,0 +1,22 @@ +/* + * Compatibility header + * + * This header is wrapper to allow compilation of Wine DLLs under ReactOS + * build system. It contains definitions commonly refered to as Wineisms + * and definitions that are missing in w32api. + */ + +#ifndef __WINE_SETUPAPI_H +#define __WINE_SETUPAPI_H + +#include_next + +#define FLG_ADDREG_DELREG_BIT 0x00008000 +#define FLG_DELREG_KEYONLY_COMMON FLG_ADDREG_KEYONLY_COMMON +#define FLG_DELREG_MULTI_SZ_DELSTRING (FLG_DELREG_TYPE_MULTI_SZ | FLG_ADDREG_DELREG_BIT | 0x00000002) +#define FLG_DELREG_TYPE_MULTI_SZ FLG_ADDREG_TYPE_MULTI_SZ +#define FLG_ADDREG_KEYONLY_COMMON 0x00002000 +#define FLG_ADDREG_DELREG_BIT 0x00008000 +#define SPINST_COPYINF 0x00000200 + +#endif /* __WINE_SETUPAPI_H */ diff --git a/reactos/lib/setupapi/Makefile.in b/reactos/lib/setupapi/Makefile.in new file mode 100644 index 00000000000..198ddafecfc --- /dev/null +++ b/reactos/lib/setupapi/Makefile.in @@ -0,0 +1,32 @@ +EXTRADEFS = -D_SETUPAPI_ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = setupapi.dll +IMPORTS = user32 version advapi32 kernel32 ntdll +ALTNAMES = setupx.dll +EXTRALIBS = $(LIBUNICODE) + +SPEC_SRCS16 = $(ALTNAMES:.dll=.spec) + +C_SRCS = \ + devinst.c \ + dirid.c \ + install.c \ + parser.c \ + queue.c \ + setupcab.c \ + stubs.c + +C_SRCS16 = \ + devinst16.c \ + infparse.c \ + setupx_main.c \ + virtcopy.c + +RC_SRCS= setupapi.rc + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/lib/setupapi/Makefile.ros-template b/reactos/lib/setupapi/Makefile.ros-template new file mode 100644 index 00000000000..a8d0fc15781 --- /dev/null +++ b/reactos/lib/setupapi/Makefile.ros-template @@ -0,0 +1,21 @@ +# $Id: Makefile.ros-template,v 1.1 2004/01/24 15:39:18 sedwards Exp $ + +TARGET_NAME = setupapi + +TARGET_OBJECTS = @C_SRCS@ + +TARGET_CFLAGS = @EXTRADEFS@ -D__REACTOS__ + +TARGET_SDKLIBS = @IMPORTS@ libwine.a ntdll.a libwine_unicode.a + +TARGET_BASE = 0x76160000 + +TARGET_RC_SRCS = @RC_SRCS@ +TARGET_RC_BINSRC = @RC_BINSRC@ +TARGET_RC_BINARIES = @RC_BINARIES@ + +default: all + +DEP_OBJECTS = $(TARGET_OBJECTS) + +include $(TOOLS_PATH)/depend.mk diff --git a/reactos/lib/setupapi/devinst.c b/reactos/lib/setupapi/devinst.c new file mode 100644 index 00000000000..7d84bf699f6 --- /dev/null +++ b/reactos/lib/setupapi/devinst.c @@ -0,0 +1,149 @@ +/* + * SetupAPI device installer + * + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "setupapi.h" +#include "wine/debug.h" + + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInterfaceDetailA( + HDEVINFO DeviceInfoSet, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, + DWORD DeviceInterfaceDetailDataSize, + PDWORD RequiredSize, + PSP_DEVINFO_DATA DeviceInfoData ) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInterfaceDetailW( + HDEVINFO DeviceInfoSet, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, + PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, + DWORD DeviceInterfaceDetailDataSize, + PDWORD RequiredSize, + PSP_DEVINFO_DATA DeviceInfoData ) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiEnumDeviceInterfaces (SETUPAPI.@) + */ +BOOL WINAPI SetupDiEnumDeviceInterfaces( + HDEVINFO DeviceInfoSet, + PSP_DEVINFO_DATA DeviceInfoData, + CONST GUID * InterfaceClassGuid, + DWORD MemberIndex, + PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetClassDevsA (SETUPAPI.@) + */ +HDEVINFO WINAPI SetupDiGetClassDevsA( + CONST GUID *class, + LPCSTR enumstr, + HWND parent, + DWORD flags) +{ + FIXME("%s %s %p %08lx\n",debugstr_guid(class),enumstr,parent,flags); + + return (HDEVINFO) INVALID_HANDLE_VALUE; +} + +/*********************************************************************** + * SetupDiGetClassDevsW (SETUPAPI.@) + */ +HDEVINFO WINAPI SetupDiGetClassDevsW( + CONST GUID *class, + LPCWSTR enumstr, + HWND parent, + DWORD flags) +{ + FIXME("%s %s %p %08lx\n",debugstr_guid(class),debugstr_w(enumstr),parent,flags); + + return (HDEVINFO) INVALID_HANDLE_VALUE; +} +/*********************************************************************** + * SetupDiEnumDeviceInfo (SETUPAPI.@) + */ +BOOL WINAPI SetupDiEnumDeviceInfo( + HDEVINFO devinfo, + DWORD index, + PSP_DEVINFO_DATA info) +{ + FIXME("%p %ld %p\n", devinfo, index, info ); + + if(info==NULL) + return FALSE; + if(info->cbSize < sizeof(*info)) + return FALSE; + + return FALSE; +} + +/*********************************************************************** + * SetupDiDestroyDeviceInfoList (SETUPAPI.@) + */ +BOOL WINAPI SetupDiDestroyDeviceInfoList( HDEVINFO devinfo ) +{ + FIXME("%04lx\n", (DWORD)devinfo); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceRegistryPropertyA( + HDEVINFO devinfo, + PSP_DEVINFO_DATA DeviceInfoData, + DWORD Property, + PDWORD PropertyRegDataType, + PBYTE PropertyBuffer, + DWORD PropertyBufferSize, + PDWORD RequiredSize) +{ + FIXME("%04lx %p %ld %p %p %ld %p\n", (DWORD)devinfo, DeviceInfoData, + Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize, + RequiredSize); + return FALSE; +} diff --git a/reactos/lib/setupapi/devinst16.c b/reactos/lib/setupapi/devinst16.c new file mode 100644 index 00000000000..0c402621410 --- /dev/null +++ b/reactos/lib/setupapi/devinst16.c @@ -0,0 +1,124 @@ +/* + * SetupAPI device installer + * + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "setupx16.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * DiGetClassDevs (SETUPX.304) + * Return a list of installed system devices. + * Uses HKLM\\ENUM to list devices. + */ +RETERR16 WINAPI DiGetClassDevs16(LPLPDEVICE_INFO16 lplpdi, + LPCSTR lpszClassName, HWND16 hwndParent, INT16 iFlags) +{ + LPDEVICE_INFO16 lpdi; + + FIXME("(%p, '%s', %04x, %04x), semi-stub.\n", + lplpdi, lpszClassName, hwndParent, iFlags); + lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16)); + lpdi->cbSize = sizeof(DEVICE_INFO16); + *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi); + return OK; +} + +/*********************************************************************** + * DiBuildCompatDrvList (SETUPX.300) + */ +RETERR16 WINAPI DiBuildCompatDrvList16(LPDEVICE_INFO16 lpdi) +{ + FIXME("(%p): stub\n", lpdi); + lpdi->lpCompatDrvList = NULL; + return FALSE; +} + +/*********************************************************************** + * DiCallClassInstaller (SETUPX.308) + */ +RETERR16 WINAPI DiCallClassInstaller16(/*DI_FUNCTIONS*/WORD diFctn, LPDEVICE_INFO16 lpdi) +{ + FIXME("(%x, %p): stub\n", diFctn, lpdi); + return FALSE; +} + +/*********************************************************************** + * DiCreateDevRegKey (SETUPX.318) + */ +RETERR16 WINAPI DiCreateDevRegKey16(LPDEVICE_INFO16 lpdi, + VOID* p2, WORD w3, + LPCSTR s4, WORD w5) +{ + FIXME("(%p, %p, %x, %s, %x): stub\n", lpdi, p2, w3, debugstr_a(s4), w5); + return FALSE; +} + +/*********************************************************************** + * DiDeleteDevRegKey (SETUPX.344) + */ +RETERR16 WINAPI DiDeleteDevRegKey16(LPDEVICE_INFO16 lpdi, INT16 iFlags) +{ + FIXME("(%p, %x): stub\n", lpdi, iFlags); + return FALSE; +} + +/*********************************************************************** + * DiCreateDeviceInfo (SETUPX.303) + */ +RETERR16 WINAPI DiCreateDeviceInfo16(LPLPDEVICE_INFO16 lplpdi, + LPCSTR lpszDescription, DWORD dnDevnode, + HKEY16 hkey, LPCSTR lpszRegsubkey, + LPCSTR lpszClassName, HWND16 hwndParent) +{ + LPDEVICE_INFO16 lpdi; + FIXME("(%p %s %08lx %x %s %s %x): stub\n", lplpdi, + debugstr_a(lpszDescription), dnDevnode, hkey, + debugstr_a(lpszRegsubkey), debugstr_a(lpszClassName), hwndParent); + lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16)); + lpdi->cbSize = sizeof(DEVICE_INFO16); + strcpy(lpdi->szClassName, lpszClassName); + lpdi->hwndParent = hwndParent; + *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi); + return OK; +} + +/*********************************************************************** + * DiDestroyDeviceInfoList (SETUPX.305) + */ +RETERR16 WINAPI DiDestroyDeviceInfoList16(LPDEVICE_INFO16 lpdi) +{ + FIXME("(%p): stub\n", lpdi); + return FALSE; +} + +/*********************************************************************** + * DiOpenDevRegKey (SETUPX.319) + */ +RETERR16 WINAPI DiOpenDevRegKey16(LPDEVICE_INFO16 lpdi, + LPHKEY16 lphk,INT16 iFlags) +{ + FIXME("(%p %p %d): stub\n", lpdi, lphk, iFlags); + return FALSE; +} diff --git a/reactos/lib/setupapi/dirid.c b/reactos/lib/setupapi/dirid.c new file mode 100644 index 00000000000..98fd99e0a32 --- /dev/null +++ b/reactos/lib/setupapi/dirid.c @@ -0,0 +1,260 @@ +/* + * Directory id handling + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "wine/unicode.h" +#include "setupapi_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define MAX_SYSTEM_DIRID DIRID_PRINTPROCESSOR + +struct user_dirid +{ + int id; + WCHAR *str; +}; + +static int nb_user_dirids; /* number of user dirids in use */ +static int alloc_user_dirids; /* number of allocated user dirids */ +static struct user_dirid *user_dirids; +static const WCHAR *system_dirids[MAX_SYSTEM_DIRID+1]; + +/* retrieve the string for unknown dirids */ +static const WCHAR *get_unknown_dirid(void) +{ + static WCHAR *unknown_dirid; + static const WCHAR unknown_str[] = {'\\','u','n','k','n','o','w','n',0}; + + if (!unknown_dirid) + { + UINT len = GetSystemDirectoryW( NULL, 0 ) + strlenW(unknown_str); + if (!(unknown_dirid = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL; + GetSystemDirectoryW( unknown_dirid, len ); + strcatW( unknown_dirid, unknown_str ); + } + return unknown_dirid; +} + +/* create the string for a system dirid */ +static const WCHAR *create_system_dirid( int dirid ) +{ + static const WCHAR Null[] = {0}; + static const WCHAR C_Root[] = {'C',':','\\',0}; + static const WCHAR Drivers[] = {'\\','d','r','i','v','e','r','s',0}; + static const WCHAR Inf[] = {'\\','i','n','f',0}; + static const WCHAR Help[] = {'\\','h','e','l','p',0}; + static const WCHAR Fonts[] = {'\\','f','o','n','t','s',0}; + static const WCHAR Viewers[] = {'\\','v','i','e','w','e','r','s',0}; + static const WCHAR System[] = {'\\','s','y','s','t','e','m',0}; + static const WCHAR Spool[] = {'\\','s','p','o','o','l',0}; + + WCHAR buffer[MAX_PATH+16], *str; + int len; + + switch(dirid) + { + case DIRID_NULL: + return Null; + case DIRID_WINDOWS: + GetWindowsDirectoryW( buffer, MAX_PATH ); + break; + case DIRID_SYSTEM: + GetSystemDirectoryW( buffer, MAX_PATH ); + break; + case DIRID_DRIVERS: + GetSystemDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Drivers ); + break; + case DIRID_INF: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Inf ); + break; + case DIRID_HELP: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Help ); + break; + case DIRID_FONTS: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Fonts ); + break; + case DIRID_VIEWERS: + GetSystemDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Viewers ); + break; + case DIRID_APPS: + return C_Root; /* FIXME */ + case DIRID_SHARED: + GetWindowsDirectoryW( buffer, MAX_PATH ); + break; + case DIRID_BOOT: + return C_Root; /* FIXME */ + case DIRID_SYSTEM16: + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, System ); + break; + case DIRID_SPOOL: + case DIRID_SPOOLDRIVERS: /* FIXME */ + GetWindowsDirectoryW( buffer, MAX_PATH ); + strcatW( buffer, Spool ); + break; + case DIRID_LOADER: + return C_Root; /* FIXME */ + case DIRID_USERPROFILE: /* FIXME */ + case DIRID_COLOR: /* FIXME */ + case DIRID_PRINTPROCESSOR: /* FIXME */ + default: + FIXME( "unknown dirid %d\n", dirid ); + return get_unknown_dirid(); + } + len = (strlenW(buffer) + 1) * sizeof(WCHAR); + if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len ); + return str; +} + +/* retrieve the string corresponding to a dirid, or NULL if none */ +const WCHAR *DIRID_get_string( HINF hinf, int dirid ) +{ + int i; + + if (dirid == DIRID_ABSOLUTE || dirid == DIRID_ABSOLUTE_16BIT) dirid = DIRID_NULL; + + if (dirid >= DIRID_USER) + { + for (i = 0; i < nb_user_dirids; i++) + if (user_dirids[i].id == dirid) return user_dirids[i].str; + ERR("user id %d not found\n", dirid ); + return NULL; + } + else + { + if (dirid > MAX_SYSTEM_DIRID) return get_unknown_dirid(); + if (dirid == DIRID_SRCPATH) return PARSER_get_src_root( hinf ); + if (!system_dirids[dirid]) system_dirids[dirid] = create_system_dirid( dirid ); + return system_dirids[dirid]; + } +} + +/* store a user dirid string */ +static BOOL store_user_dirid( HINF hinf, int id, WCHAR *str ) +{ + int i; + + for (i = 0; i < nb_user_dirids; i++) if (user_dirids[i].id == id) break; + + if (i < nb_user_dirids) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); + else + { + if (nb_user_dirids >= alloc_user_dirids) + { + int new_size = max( 32, alloc_user_dirids * 2 ); + + struct user_dirid *new; + + if (user_dirids) + new = HeapReAlloc( GetProcessHeap(), 0, user_dirids, + new_size * sizeof(*new) ); + else + new = HeapAlloc( GetProcessHeap(), 0, + new_size * sizeof(*new) ); + + if (!new) return FALSE; + user_dirids = new; + alloc_user_dirids = new_size; + } + nb_user_dirids++; + } + user_dirids[i].id = id; + user_dirids[i].str = str; + TRACE("id %d -> %s\n", id, debugstr_w(str) ); + return TRUE; +} + + +/*********************************************************************** + * SetupSetDirectoryIdA (SETUPAPI.@) + */ +BOOL WINAPI SetupSetDirectoryIdA( HINF hinf, DWORD id, PCSTR dir ) +{ + UNICODE_STRING dirW; + int i; + + if (!id) /* clear everything */ + { + for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); + nb_user_dirids = 0; + return TRUE; + } + if (id < DIRID_USER) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + /* duplicate the string */ + if (!RtlCreateUnicodeStringFromAsciiz( &dirW, dir )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + return store_user_dirid( hinf, id, dirW.Buffer ); +} + + +/*********************************************************************** + * SetupSetDirectoryIdW (SETUPAPI.@) + */ +BOOL WINAPI SetupSetDirectoryIdW( HINF hinf, DWORD id, PCWSTR dir ) +{ + int i, len; + WCHAR *str; + + if (!id) /* clear everything */ + { + for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str ); + nb_user_dirids = 0; + return TRUE; + } + if (id < DIRID_USER) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + + /* duplicate the string */ + len = (strlenW(dir)+1) * sizeof(WCHAR); + if (!(str = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE; + memcpy( str, dir, len ); + return store_user_dirid( hinf, id, str ); +} diff --git a/reactos/lib/setupapi/infparse.c b/reactos/lib/setupapi/infparse.c new file mode 100644 index 00000000000..c508db613ae --- /dev/null +++ b/reactos/lib/setupapi/infparse.c @@ -0,0 +1,198 @@ +/* + * SetupX .inf file parsing functions + * + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FIXME: + * - return values ??? + * - this should be reimplemented at some point to have its own + * file parsing instead of using profile functions, + * as some SETUPX exports probably demand that + * (IpSaveRestorePosition, IpFindNextMatchLine, ...). + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupx16.h" +#include "setupapi_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define MAX_HANDLES 16384 +#define FIRST_HANDLE 32 + +static HINF handles[MAX_HANDLES]; + + +static RETERR16 alloc_hinf16( HINF hinf, HINF16 *hinf16 ) +{ + int i; + for (i = 0; i < MAX_HANDLES; i++) + { + if (!handles[i]) + { + handles[i] = hinf; + *hinf16 = i + FIRST_HANDLE; + return OK; + } + } + return ERR_IP_OUT_OF_HANDLES; +} + +static HINF get_hinf( HINF16 hinf16 ) +{ + int idx = hinf16 - FIRST_HANDLE; + if (idx < 0 || idx >= MAX_HANDLES) return 0; + return handles[idx]; +} + + +static HINF free_hinf16( HINF16 hinf16 ) +{ + HINF ret; + int idx = hinf16 - FIRST_HANDLE; + + if (idx < 0 || idx >= MAX_HANDLES) return 0; + ret = handles[idx]; + handles[idx] = 0; + return ret; +} + +/* convert last error code to a RETERR16 value */ +static RETERR16 get_last_error(void) +{ + switch(GetLastError()) + { + case ERROR_EXPECTED_SECTION_NAME: + case ERROR_BAD_SECTION_NAME_LINE: + case ERROR_SECTION_NAME_TOO_LONG: return ERR_IP_INVALID_SECT_NAME; + case ERROR_SECTION_NOT_FOUND: return ERR_IP_SECT_NOT_FOUND; + case ERROR_LINE_NOT_FOUND: return ERR_IP_LINE_NOT_FOUND; + default: return IP_ERROR; /* FIXME */ + } +} + + +/*********************************************************************** + * IpOpen (SETUPX.2) + * + */ +RETERR16 WINAPI IpOpen16( LPCSTR filename, HINF16 *hinf16 ) +{ + HINF hinf = SetupOpenInfFileA( filename, NULL, INF_STYLE_WIN4, NULL ); + if (hinf == (HINF)INVALID_HANDLE_VALUE) return get_last_error(); + return alloc_hinf16( hinf, hinf16 ); +} + + +/*********************************************************************** + * IpClose (SETUPX.4) + */ +RETERR16 WINAPI IpClose16( HINF16 hinf16 ) +{ + HINF hinf = free_hinf16( hinf16 ); + if (!hinf) return ERR_IP_INVALID_HINF; + SetupCloseInfFile( hinf ); + return OK; +} + + +/*********************************************************************** + * IpGetProfileString (SETUPX.210) + */ +RETERR16 WINAPI IpGetProfileString16( HINF16 hinf16, LPCSTR section, LPCSTR entry, + LPSTR buffer, WORD buflen ) +{ + DWORD required_size; + HINF hinf = get_hinf( hinf16 ); + + if (!hinf) return ERR_IP_INVALID_HINF; + if (!SetupGetLineTextA( NULL, hinf, section, entry, buffer, buflen, &required_size )) + return get_last_error(); + TRACE("%p: section %s entry %s ret %s\n", + hinf, debugstr_a(section), debugstr_a(entry), debugstr_a(buffer) ); + return OK; +} + + +/*********************************************************************** + * GenFormStrWithoutPlaceHolders (SETUPX.103) + * + * ought to be pretty much implemented, I guess... + */ +void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR dst, LPCSTR src, HINF16 hinf16 ) +{ + UNICODE_STRING srcW; + HINF hinf = get_hinf( hinf16 ); + + if (!hinf) return; + + if (!RtlCreateUnicodeStringFromAsciiz( &srcW, src )) return; + PARSER_string_substA( hinf, srcW.Buffer, dst, MAX_INF_STRING_LENGTH ); + RtlFreeUnicodeString( &srcW ); + TRACE( "%s -> %s\n", debugstr_a(src), debugstr_a(dst) ); +} + +/*********************************************************************** + * GenInstall (SETUPX.101) + * + * generic installer function for .INF file sections + * + * This is not perfect - patch whenever you can ! + * + * wFlags == GENINSTALL_DO_xxx + * e.g. NetMeeting: + * first call GENINSTALL_DO_REGSRCPATH | GENINSTALL_DO_FILES, + * second call GENINSTALL_DO_LOGCONFIG | CFGAUTO | INI2REG | REG | INI + */ +RETERR16 WINAPI GenInstall16( HINF16 hinf16, LPCSTR section, WORD genflags ) +{ + UINT flags = 0; + HINF hinf = get_hinf( hinf16 ); + RETERR16 ret = OK; + void *context; + + if (!hinf) return ERR_IP_INVALID_HINF; + + if (genflags & GENINSTALL_DO_FILES) flags |= SPINST_FILES; + if (genflags & GENINSTALL_DO_INI) flags |= SPINST_INIFILES; + if (genflags & GENINSTALL_DO_REG) flags |= SPINST_REGISTRY; + if (genflags & GENINSTALL_DO_INI2REG) flags |= SPINST_INI2REG; + if (genflags & GENINSTALL_DO_LOGCONFIG) flags |= SPINST_LOGCONFIG; + if (genflags & GENINSTALL_DO_REGSRCPATH) FIXME( "unsupported flag: GENINSTALL_DO_REGSRCPATH\n" ); + if (genflags & GENINSTALL_DO_CFGAUTO) FIXME( "unsupported flag: GENINSTALL_DO_CFGAUTO\n" ); + if (genflags & GENINSTALL_DO_PERUSER) FIXME( "unsupported flag: GENINSTALL_DO_PERUSER\n" ); + + context = SetupInitDefaultQueueCallback( 0 ); + if (!SetupInstallFromInfSectionA( 0, hinf, section, flags, 0, NULL, + SP_COPY_NEWER_OR_SAME, SetupDefaultQueueCallbackA, + context, 0, 0 )) + ret = get_last_error(); + + SetupTermDefaultQueueCallback( context ); + return ret; +} diff --git a/reactos/lib/setupapi/install.c b/reactos/lib/setupapi/install.c new file mode 100644 index 00000000000..abf7645666f --- /dev/null +++ b/reactos/lib/setupapi/install.c @@ -0,0 +1,675 @@ +/* + * Setupapi install routines + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupapi_private.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/* info passed to callback functions dealing with files */ +struct files_callback_info +{ + HSPFILEQ queue; + PCWSTR src_root; + UINT copy_flags; + HINF layout; +}; + +/* info passed to callback functions dealing with the registry */ +struct registry_callback_info +{ + HKEY default_root; + BOOL delete; +}; + +typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg ); + +/* Unicode constants */ +static const WCHAR CopyFiles[] = {'C','o','p','y','F','i','l','e','s',0}; +static const WCHAR DelFiles[] = {'D','e','l','F','i','l','e','s',0}; +static const WCHAR RenFiles[] = {'R','e','n','F','i','l','e','s',0}; +static const WCHAR Ini2Reg[] = {'I','n','i','2','R','e','g',0}; +static const WCHAR LogConf[] = {'L','o','g','C','o','n','f',0}; +static const WCHAR AddReg[] = {'A','d','d','R','e','g',0}; +static const WCHAR DelReg[] = {'D','e','l','R','e','g',0}; +static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0}; +static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0}; + + +/*********************************************************************** + * get_field_string + * + * Retrieve the contents of a field, dynamically growing the buffer if necessary. + */ +static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer, + WCHAR *static_buffer, DWORD *size ) +{ + DWORD required; + + if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer; + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + { + /* now grow the buffer */ + if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL; + *size = required; + if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer; + } + if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + return NULL; +} + + +/*********************************************************************** + * copy_files_callback + * + * Called once for each CopyFiles entry in a given section. + */ +static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct files_callback_info *info = arg; + + if (field[0] == '@') /* special case: copy single file */ + SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags ); + else + SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags ); + return TRUE; +} + + +/*********************************************************************** + * delete_files_callback + * + * Called once for each DelFiles entry in a given section. + */ +static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct files_callback_info *info = arg; + SetupQueueDeleteSectionW( info->queue, hinf, 0, field ); + return TRUE; +} + + +/*********************************************************************** + * rename_files_callback + * + * Called once for each RenFiles entry in a given section. + */ +static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct files_callback_info *info = arg; + SetupQueueRenameSectionW( info->queue, hinf, 0, field ); + return TRUE; +} + + +/*********************************************************************** + * get_root_key + * + * Retrieve the registry root key from its name. + */ +static HKEY get_root_key( const WCHAR *name, HKEY def_root ) +{ + static const WCHAR HKCR[] = {'H','K','C','R',0}; + static const WCHAR HKCU[] = {'H','K','C','U',0}; + static const WCHAR HKLM[] = {'H','K','L','M',0}; + static const WCHAR HKU[] = {'H','K','U',0}; + static const WCHAR HKR[] = {'H','K','R',0}; + + if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT; + if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER; + if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE; + if (!strcmpiW( name, HKU )) return HKEY_USERS; + if (!strcmpiW( name, HKR )) return def_root; + return 0; +} + + +/*********************************************************************** + * append_multi_sz_value + * + * Append a multisz string to a multisz registry value. + */ +static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings, + DWORD str_size ) +{ + DWORD size, type, total; + WCHAR *buffer, *p; + + if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return; + if (type != REG_MULTI_SZ) return; + + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return; + if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done; + + /* compare each string against all the existing ones */ + total = size; + while (*strings) + { + int len = strlenW(strings) + 1; + + for (p = buffer; *p; p += strlenW(p) + 1) + if (!strcmpiW( p, strings )) break; + + if (!*p) /* not found, need to append it */ + { + memcpy( p, strings, len * sizeof(WCHAR) ); + p[len] = 0; + total += len; + } + strings += len; + } + if (total != size) + { + TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) ); + RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total ); + } + done: + HeapFree( GetProcessHeap(), 0, buffer ); +} + + +/*********************************************************************** + * delete_multi_sz_value + * + * Remove a string from a multisz registry value. + */ +static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string ) +{ + DWORD size, type; + WCHAR *buffer, *src, *dst; + + if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return; + if (type != REG_MULTI_SZ) return; + /* allocate double the size, one for value before and one for after */ + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return; + if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done; + src = buffer; + dst = buffer + size; + while (*src) + { + int len = strlenW(src) + 1; + if (strcmpiW( src, string )) + { + memcpy( dst, src, len * sizeof(WCHAR) ); + dst += len; + } + src += len; + } + *dst++ = 0; + if (dst != buffer + 2*size) /* did we remove something? */ + { + TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) ); + RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, + (BYTE *)(buffer + size), dst - (buffer + size) ); + } + done: + HeapFree( GetProcessHeap(), 0, buffer ); +} + + +/*********************************************************************** + * do_reg_operation + * + * Perform an add/delete registry operation depending on the flags. + */ +static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags ) +{ + DWORD type, size; + + if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL)) /* deletion */ + { + if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON)) + { + if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING) + { + WCHAR *str; + + if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE; + if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; + SetupGetStringFieldW( context, 5, str, size, NULL ); + delete_multi_sz_value( hkey, value, str ); + HeapFree( GetProcessHeap(), 0, str ); + } + else RegDeleteValueW( hkey, value ); + } + else RegDeleteKeyW( hkey, NULL ); + return TRUE; + } + + if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE; + + if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY)) + { + BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL ); + if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE; + if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE; + } + + switch(flags & FLG_ADDREG_TYPE_MASK) + { + case FLG_ADDREG_TYPE_SZ: type = REG_SZ; break; + case FLG_ADDREG_TYPE_MULTI_SZ: type = REG_MULTI_SZ; break; + case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break; + case FLG_ADDREG_TYPE_BINARY: type = REG_BINARY; break; + case FLG_ADDREG_TYPE_DWORD: type = REG_DWORD; break; + case FLG_ADDREG_TYPE_NONE: type = REG_NONE; break; + default: type = flags >> 16; break; + } + + if (!(flags & FLG_ADDREG_BINVALUETYPE) || + (type == REG_DWORD && SetupGetFieldCount(context) == 5)) + { + static const WCHAR empty; + WCHAR *str = NULL; + + if (type == REG_MULTI_SZ) + { + if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0; + if (size) + { + if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; + SetupGetMultiSzFieldW( context, 5, str, size, NULL ); + } + if (flags & FLG_ADDREG_APPEND) + { + if (!str) return TRUE; + append_multi_sz_value( hkey, value, str, size ); + HeapFree( GetProcessHeap(), 0, str ); + return TRUE; + } + /* else fall through to normal string handling */ + } + else + { + if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0; + if (size) + { + if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE; + SetupGetStringFieldW( context, 5, str, size, NULL ); + } + } + + if (type == REG_DWORD) + { + DWORD dw = str ? strtoulW( str, NULL, 16 ) : 0; + TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw ); + RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) ); + } + else + { + TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) ); + if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) ); + else RegSetValueExW( hkey, value, 0, type, (BYTE *)&empty, sizeof(WCHAR) ); + } + HeapFree( GetProcessHeap(), 0, str ); + return TRUE; + } + else /* get the binary data */ + { + BYTE *data = NULL; + + if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0; + if (size) + { + if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE; + TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size ); + SetupGetBinaryField( context, 5, data, size, NULL ); + } + RegSetValueExW( hkey, value, 0, type, data, size ); + HeapFree( GetProcessHeap(), 0, data ); + return TRUE; + } +} + + +/*********************************************************************** + * registry_callback + * + * Called once for each AddReg and DelReg entry in a given section. + */ +static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg ) +{ + struct registry_callback_info *info = arg; + INFCONTEXT context; + HKEY root_key, hkey; + + BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); + + for (; ok; ok = SetupFindNextLine( &context, &context )) + { + WCHAR buffer[MAX_INF_STRING_LENGTH]; + INT flags; + + /* get root */ + if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + continue; + if (!(root_key = get_root_key( buffer, info->default_root ))) + continue; + + /* get key */ + if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + *buffer = 0; + + /* get flags */ + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; + + if (!info->delete) + { + if (flags & FLG_ADDREG_DELREG_BIT) continue; /* ignore this entry */ + } + else + { + if (!flags) flags = FLG_ADDREG_DELREG_BIT; + else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue; /* ignore this entry */ + } + + if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY)) + { + if (RegOpenKeyW( root_key, buffer, &hkey )) continue; /* ignore if it doesn't exist */ + } + else if (RegCreateKeyW( root_key, buffer, &hkey )) + { + ERR( "could not create key %p %s\n", root_key, debugstr_w(buffer) ); + continue; + } + TRACE( "key %p %s\n", root_key, debugstr_w(buffer) ); + + /* get value name */ + if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + *buffer = 0; + + /* and now do it */ + if (!do_reg_operation( hkey, buffer, &context, flags )) + { + RegCloseKey( hkey ); + return FALSE; + } + RegCloseKey( hkey ); + } + return TRUE; +} + + +static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg ) +{ + INFCONTEXT context; + + BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context ); + + for (; ok; ok = SetupFindNextLine( &context, &context )) + { + WCHAR buffer[MAX_INF_STRING_LENGTH]; + WCHAR filename[MAX_INF_STRING_LENGTH]; + WCHAR section[MAX_INF_STRING_LENGTH]; + WCHAR entry[MAX_INF_STRING_LENGTH]; + WCHAR string[MAX_INF_STRING_LENGTH]; + LPWSTR divider; + + if (!SetupGetStringFieldW( &context, 1, filename, + sizeof(filename)/sizeof(WCHAR), NULL )) + continue; + + if (!SetupGetStringFieldW( &context, 2, section, + sizeof(section)/sizeof(WCHAR), NULL )) + continue; + + if (!SetupGetStringFieldW( &context, 4, buffer, + sizeof(buffer)/sizeof(WCHAR), NULL )) + continue; + + divider = strchrW(buffer,'='); + if (divider) + { + *divider = 0; + strcpyW(entry,buffer); + divider++; + strcpyW(string,divider); + } + else + { + strcpyW(entry,buffer); + string[0]=0; + } + + TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry), + debugstr_w(string),debugstr_w(section),debugstr_w(filename)); + WritePrivateProfileStringW(section,entry,string,filename); + + } + return TRUE; +} + +static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should update ini fields %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do ini2reg %s\n", debugstr_w(field) ); + return TRUE; +} + +static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg ) +{ + FIXME( "should do logconf %s\n", debugstr_w(field) ); + return TRUE; +} + + +/*********************************************************************** + * iterate_section_fields + * + * Iterate over all fields of a certain key of a certain section + */ +static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key, + iterate_fields_func callback, void *arg ) +{ + WCHAR static_buffer[200]; + WCHAR *buffer = static_buffer; + DWORD size = sizeof(static_buffer)/sizeof(WCHAR); + INFCONTEXT context; + BOOL ret = FALSE; + + BOOL ok = SetupFindFirstLineW( hinf, section, key, &context ); + while (ok) + { + UINT i, count = SetupGetFieldCount( &context ); + for (i = 1; i <= count; i++) + { + if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size ))) + goto done; + if (!callback( hinf, buffer, arg )) + { + ERR("callback failed for %s %s\n", debugstr_w(section), debugstr_w(buffer) ); + goto done; + } + } + ok = SetupFindNextMatchLineW( &context, key, &context ); + } + ret = TRUE; + done: + if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer ); + return ret; +} + + +/*********************************************************************** + * SetupInstallFilesFromInfSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue, + PCSTR section, PCSTR src_root, UINT flags ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (!src_root) + ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer, + NULL, flags ); + else + { + UNICODE_STRING srcW; + if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root )) + { + ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer, + srcW.Buffer, flags ); + RtlFreeUnicodeString( &srcW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + RtlFreeUnicodeString( §ionW ); + return ret; +} + + +/*********************************************************************** + * SetupInstallFilesFromInfSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue, + PCWSTR section, PCWSTR src_root, UINT flags ) +{ + struct files_callback_info info; + + info.queue = queue; + info.src_root = src_root; + info.copy_flags = flags; + info.layout = hlayout; + return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ); +} + + +/*********************************************************************** + * SetupInstallFromInfSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags, + HKEY key_root, PCSTR src_root, UINT copy_flags, + PSP_FILE_CALLBACK_A callback, PVOID context, + HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data ) +{ + UNICODE_STRING sectionW, src_rootW; + struct callback_WtoA_context ctx; + BOOL ret = FALSE; + + src_rootW.Buffer = NULL; + if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + ctx.orig_context = context; + ctx.orig_handler = callback; + ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root, + src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA, + &ctx, devinfo, devinfo_data ); + RtlFreeUnicodeString( §ionW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + + RtlFreeUnicodeString( &src_rootW ); + return ret; +} + + +/*********************************************************************** + * SetupInstallFromInfSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags, + HKEY key_root, PCWSTR src_root, UINT copy_flags, + PSP_FILE_CALLBACK_W callback, PVOID context, + HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data ) +{ + if (flags & SPINST_FILES) + { + struct files_callback_info info; + HSPFILEQ queue; + BOOL ret; + + if (!(queue = SetupOpenFileQueue())) return FALSE; + info.queue = queue; + info.src_root = src_root; + info.copy_flags = copy_flags; + info.layout = hinf; + ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) && + iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) && + iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) && + SetupCommitFileQueueW( owner, queue, callback, context )); + SetupCloseFileQueue( queue ); + if (!ret) return FALSE; + } + if (flags & SPINST_INIFILES) + { + if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) || + !iterate_section_fields( hinf, section, UpdateIniFields, + update_ini_fields_callback, NULL )) + return FALSE; + } + if (flags & SPINST_INI2REG) + { + if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL )) + return FALSE; + } + + if (flags & SPINST_LOGCONFIG) + { + if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL )) + return FALSE; + } + + if (flags & SPINST_REGISTRY) + { + struct registry_callback_info info; + + info.default_root = key_root; + info.delete = TRUE; + if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info )) + return FALSE; + info.delete = FALSE; + if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info )) + return FALSE; + } + if (flags & (SPINST_BITREG|SPINST_REGSVR|SPINST_UNREGSVR|SPINST_PROFILEITEMS|SPINST_COPYINF)) + FIXME( "unsupported flags %x\n", flags ); + return TRUE; +} diff --git a/reactos/lib/setupapi/makefile b/reactos/lib/setupapi/makefile new file mode 100644 index 00000000000..18c58b85489 --- /dev/null +++ b/reactos/lib/setupapi/makefile @@ -0,0 +1,9 @@ +# $Id: makefile,v 1.1 2004/01/24 15:39:18 sedwards Exp $ + +PATH_TO_TOP = ../.. + +TARGET_TYPE = winedll + +include $(PATH_TO_TOP)/rules.mak + +include $(TOOLS_PATH)/helper.mk diff --git a/reactos/lib/setupapi/parser.c b/reactos/lib/setupapi/parser.c new file mode 100644 index 00000000000..87066aa678e --- /dev/null +++ b/reactos/lib/setupapi/parser.c @@ -0,0 +1,1797 @@ +/* + * INF file parsing + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include +#include +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "setupapi.h" + +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +#define CONTROL_Z '\x1a' +#define MAX_SECTION_NAME_LEN 255 +#define MAX_FIELD_LEN 511 /* larger fields get silently truncated */ +/* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */ +#define MAX_STRING_LEN (MAX_INF_STRING_LENGTH+1) + +/* inf file structure definitions */ + +struct field +{ + const WCHAR *text; /* field text */ +}; + +struct line +{ + int first_field; /* index of first field in field array */ + int nb_fields; /* number of fields in line */ + int key_field; /* index of field for key or -1 if no key */ +}; + +struct section +{ + const WCHAR *name; /* section name */ + unsigned int nb_lines; /* number of used lines */ + unsigned int alloc_lines; /* total number of allocated lines in array below */ + struct line lines[16]; /* lines information (grown dynamically, 16 is initial size) */ +}; + +struct inf_file +{ + struct inf_file *next; /* next appended file */ + WCHAR *strings; /* buffer for string data (section names and field values) */ + WCHAR *string_pos; /* position of next available string in buffer */ + unsigned int nb_sections; /* number of used sections */ + unsigned int alloc_sections; /* total number of allocated section pointers */ + struct section **sections; /* section pointers array */ + unsigned int nb_fields; + unsigned int alloc_fields; + struct field *fields; + int strings_section; /* index of [Strings] section or -1 if none */ + WCHAR *src_root; /* source root directory */ +}; + +/* parser definitions */ + +enum parser_state +{ + LINE_START, /* at beginning of a line */ + SECTION_NAME, /* parsing a section name */ + KEY_NAME, /* parsing a key name */ + VALUE_NAME, /* parsing a value name */ + EOL_BACKSLASH, /* backslash at end of line */ + QUOTES, /* inside quotes */ + LEADING_SPACES, /* leading spaces */ + TRAILING_SPACES, /* trailing spaces */ + COMMENT, /* inside a comment */ + NB_PARSER_STATES +}; + +struct parser +{ + const WCHAR *start; /* start position of item being parsed */ + const WCHAR *end; /* end of buffer */ + struct inf_file *file; /* file being built */ + enum parser_state state; /* current parser state */ + enum parser_state stack[4]; /* state stack */ + int stack_pos; /* current pos in stack */ + + int cur_section; /* index of section being parsed*/ + struct line *line; /* current line */ + unsigned int line_pos; /* current line position in file */ + unsigned int error; /* error code */ + unsigned int token_len; /* current token len */ + WCHAR token[MAX_FIELD_LEN+1]; /* current token */ +}; + +typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos ); + +/* parser state machine functions */ +static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ); +static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ); + +static const parser_state_func parser_funcs[NB_PARSER_STATES] = +{ + line_start_state, /* LINE_START */ + section_name_state, /* SECTION_NAME */ + key_name_state, /* KEY_NAME */ + value_name_state, /* VALUE_NAME */ + eol_backslash_state, /* EOL_BACKSLASH */ + quotes_state, /* QUOTES */ + leading_spaces_state, /* LEADING_SPACES */ + trailing_spaces_state, /* TRAILING_SPACES */ + comment_state /* COMMENT */ +}; + + +/* Unicode string constants */ +static const WCHAR Version[] = {'V','e','r','s','i','o','n',0}; +static const WCHAR Signature[] = {'S','i','g','n','a','t','u','r','e',0}; +static const WCHAR Chicago[] = {'$','C','h','i','c','a','g','o','$',0}; +static const WCHAR WindowsNT[] = {'$','W','i','n','d','o','w','s',' ','N','T','$',0}; +static const WCHAR Windows95[] = {'$','W','i','n','d','o','w','s',' ','9','5','$',0}; +static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0}; + +/* extend an array, allocating more memory if necessary */ +static void *grow_array( void *array, unsigned int *count, size_t elem ) +{ + void *new_array; + unsigned int new_count = *count + *count / 2; + if (new_count < 32) new_count = 32; + + if (array) + new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem ); + else + new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * elem ); + + if (new_array) + *count = new_count; + else + if (array) + HeapFree( GetProcessHeap(), 0, array ); + return new_array; +} + + +/* find a section by name */ +static int find_section( struct inf_file *file, const WCHAR *name ) +{ + int i; + + for (i = 0; i < file->nb_sections; i++) + if (!strcmpiW( name, file->sections[i]->name )) return i; + return -1; +} + + +/* find a line by name */ +static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name ) +{ + struct section *section; + struct line *line; + int i; + + if (section_index < 0 || section_index >= file->nb_sections) return NULL; + section = file->sections[section_index]; + for (i = 0, line = section->lines; i < section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (!strcmpiW( name, file->fields[line->key_field].text )) return line; + } + return NULL; +} + + +/* add a section to the file and return the section index */ +static int add_section( struct inf_file *file, const WCHAR *name ) +{ + struct section *section; + + if (file->nb_sections >= file->alloc_sections) + { + if (!(file->sections = grow_array( file->sections, &file->alloc_sections, + sizeof(file->sections[0]) ))) return -1; + } + if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1; + section->name = name; + section->nb_lines = 0; + section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]); + file->sections[file->nb_sections] = section; + return file->nb_sections++; +} + + +/* add a line to a given section */ +static struct line *add_line( struct inf_file *file, int section_index ) +{ + struct section *section; + struct line *line; + + assert( section_index >= 0 && section_index < file->nb_sections ); + + section = file->sections[section_index]; + if (section->nb_lines == section->alloc_lines) /* need to grow the section */ + { + int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line); + if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL; + section->alloc_lines *= 2; + file->sections[section_index] = section; + } + line = §ion->lines[section->nb_lines++]; + line->first_field = file->nb_fields; + line->nb_fields = 0; + line->key_field = -1; + return line; +} + + +/* retrieve a given line from section/line index */ +inline static struct line *get_line( struct inf_file *file, unsigned int section_index, + unsigned int line_index ) +{ + struct section *section; + + if (section_index >= file->nb_sections) return NULL; + section = file->sections[section_index]; + if (line_index >= section->nb_lines) return NULL; + return §ion->lines[line_index]; +} + + +/* retrieve a given field from section/line/field index */ +static struct field *get_field( struct inf_file *file, int section_index, int line_index, + int field_index ) +{ + struct line *line = get_line( file, section_index, line_index ); + + if (!line) return NULL; + if (!field_index) /* get the key */ + { + if (line->key_field == -1) return NULL; + return &file->fields[line->key_field]; + } + field_index--; + if (field_index >= line->nb_fields) return NULL; + return &file->fields[line->first_field + field_index]; +} + + +/* allocate a new field, growing the array if necessary */ +static struct field *add_field( struct inf_file *file, const WCHAR *text ) +{ + struct field *field; + + if (file->nb_fields >= file->alloc_fields) + { + if (!(file->fields = grow_array( file->fields, &file->alloc_fields, + sizeof(file->fields[0]) ))) return NULL; + } + field = &file->fields[file->nb_fields++]; + field->text = text; + return field; +} + + +/* retrieve the string substitution for a directory id */ +static const WCHAR *get_dirid_subst( int dirid, unsigned int *len ) +{ + extern const WCHAR *DIRID_get_string( HINF hinf, int dirid ); + const WCHAR *ret = DIRID_get_string( 0, dirid ); + if (ret) *len = strlenW(ret); + return ret; +} + + +/* retrieve the string substitution for a given string, or NULL if not found */ +/* if found, len is set to the substitution length */ +static const WCHAR *get_string_subst( struct inf_file *file, const WCHAR *str, unsigned int *len ) +{ + static const WCHAR percent = '%'; + + struct section *strings_section; + struct line *line; + struct field *field; + int i, dirid; + WCHAR *dirid_str, *end; + const WCHAR *ret = NULL; + + if (!*len) /* empty string (%%) is replaced by single percent */ + { + *len = 1; + return &percent; + } + if (file->strings_section == -1) goto not_found; + strings_section = file->sections[file->strings_section]; + for (i = 0, line = strings_section->lines; i < strings_section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue; + if (!file->fields[line->key_field].text[*len]) break; + } + if (i == strings_section->nb_lines || !line->nb_fields) goto not_found; + field = &file->fields[line->first_field]; + *len = strlenW( field->text ); + return field->text; + + not_found: /* check for integer id */ + if ((dirid_str = HeapAlloc( GetProcessHeap(), 0, (*len+1) * sizeof(WCHAR) ))) + { + memcpy( dirid_str, str, *len * sizeof(WCHAR) ); + dirid_str[*len] = 0; + dirid = strtolW( dirid_str, &end, 10 ); + if (!*end) ret = get_dirid_subst( dirid, len ); + HeapFree( GetProcessHeap(), 0, dirid_str ); + return ret; + } + return NULL; +} + + +/* do string substitutions on the specified text */ +/* the buffer is assumed to be large enough */ +/* returns necessary length not including terminating null */ +unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, WCHAR *buffer, + unsigned int size ) +{ + const WCHAR *start, *subst, *p; + unsigned int len, total = 0; + int inside = 0; + + if (!buffer) size = MAX_STRING_LEN + 1; + for (p = start = text; *p; p++) + { + if (*p != '%') continue; + inside = !inside; + if (inside) /* start of a %xx% string */ + { + len = p - start; + if (len > size - 1) len = size - 1; + if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) ); + total += len; + size -= len; + start = p; + } + else /* end of the %xx% string, find substitution */ + { + len = p - start - 1; + subst = get_string_subst( file, start + 1, &len ); + if (!subst) + { + subst = start; + len = p - start + 1; + } + if (len > size - 1) len = size - 1; + if (buffer) memcpy( buffer + total, subst, len * sizeof(WCHAR) ); + total += len; + size -= len; + start = p + 1; + } + } + + if (start != p) /* unfinished string, copy it */ + { + len = p - start; + if (len > size - 1) len = size - 1; + if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) ); + total += len; + } + if (buffer && size) buffer[total] = 0; + return total; +} + + +/* do string substitutions on the specified text */ +/* the buffer is assumed to be large enough */ +/* returns necessary length not including terminating null */ +unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, char *buffer, + unsigned int size ) +{ + WCHAR buffW[MAX_STRING_LEN+1]; + DWORD ret; + + unsigned int len = PARSER_string_substW( file, text, buffW, sizeof(buffW)/sizeof(WCHAR) ); + if (!buffer) RtlUnicodeToMultiByteSize( &ret, buffW, len * sizeof(WCHAR) ); + else + { + RtlUnicodeToMultiByteN( buffer, size-1, &ret, buffW, len * sizeof(WCHAR) ); + buffer[ret] = 0; + } + return ret; +} + + +/* push some string data into the strings buffer */ +static WCHAR *push_string( struct inf_file *file, const WCHAR *string ) +{ + WCHAR *ret = file->string_pos; + strcpyW( ret, string ); + file->string_pos += strlenW( ret ) + 1; + return ret; +} + + +/* push the current state on the parser stack */ +inline static void push_state( struct parser *parser, enum parser_state state ) +{ + assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) ); + parser->stack[parser->stack_pos++] = state; +} + + +/* pop the current state */ +inline static void pop_state( struct parser *parser ) +{ + assert( parser->stack_pos ); + parser->state = parser->stack[--parser->stack_pos]; +} + + +/* set the parser state and return the previous one */ +inline static enum parser_state set_state( struct parser *parser, enum parser_state state ) +{ + enum parser_state ret = parser->state; + parser->state = state; + return ret; +} + + +/* check if the pointer points to an end of file */ +inline static int is_eof( struct parser *parser, const WCHAR *ptr ) +{ + return (ptr >= parser->end || *ptr == CONTROL_Z); +} + + +/* check if the pointer points to an end of line */ +inline static int is_eol( struct parser *parser, const WCHAR *ptr ) +{ + return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == '\n'); +} + + +/* push data from current token start up to pos into the current token */ +static int push_token( struct parser *parser, const WCHAR *pos ) +{ + int len = pos - parser->start; + const WCHAR *src = parser->start; + WCHAR *dst = parser->token + parser->token_len; + + if (len > MAX_FIELD_LEN - parser->token_len) len = MAX_FIELD_LEN - parser->token_len; + + parser->token_len += len; + for ( ; len > 0; len--, dst++, src++) *dst = *src ? *src : ' '; + *dst = 0; + parser->start = pos; + return 0; +} + + +/* add a section with the current token as name */ +static int add_section_from_token( struct parser *parser ) +{ + int section_index; + + if (parser->token_len > MAX_SECTION_NAME_LEN) + { + parser->error = ERROR_SECTION_NAME_TOO_LONG; + return -1; + } + if ((section_index = find_section( parser->file, parser->token )) == -1) + { + /* need to create a new one */ + const WCHAR *name = push_string( parser->file, parser->token ); + if ((section_index = add_section( parser->file, name )) == -1) + { + parser->error = ERROR_NOT_ENOUGH_MEMORY; + return -1; + } + } + parser->token_len = 0; + parser->cur_section = section_index; + return section_index; +} + + +/* add a field containing the current token to the current line */ +static struct field *add_field_from_token( struct parser *parser, int is_key ) +{ + struct field *field; + WCHAR *text; + + if (!parser->line) /* need to start a new line */ + { + if (parser->cur_section == -1) /* got a line before the first section */ + { + parser->error = ERROR_WRONG_INF_STYLE; + return NULL; + } + if (!(parser->line = add_line( parser->file, parser->cur_section ))) goto error; + } + else assert(!is_key); + + text = push_string( parser->file, parser->token ); + if ((field = add_field( parser->file, text ))) + { + if (!is_key) parser->line->nb_fields++; + else + { + /* replace first field by key field */ + parser->line->key_field = parser->line->first_field; + parser->line->first_field++; + } + parser->token_len = 0; + return field; + } + error: + parser->error = ERROR_NOT_ENOUGH_MEMORY; + return NULL; +} + + +/* close the current line and prepare for parsing a new one */ +static void close_current_line( struct parser *parser ) +{ + struct line *cur_line = parser->line; + + if (cur_line) + { + /* if line has a single field and no key, the field is the key too */ + if (cur_line->nb_fields == 1 && cur_line->key_field == -1) + cur_line->key_field = cur_line->first_field; + } + parser->line = NULL; +} + + +/* handler for parser LINE_START state */ +static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eof( parser, p ); p++) + { + switch(*p) + { + case '\n': + parser->line_pos++; + close_current_line( parser ); + break; + case ';': + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); + return p + 1; + case '[': + parser->start = p + 1; + set_state( parser, SECTION_NAME ); + return p + 1; + default: + if (!isspaceW(*p)) + { + parser->start = p; + set_state( parser, KEY_NAME ); + return p; + } + break; + } + } + close_current_line( parser ); + return NULL; +} + + +/* handler for parser SECTION_NAME state */ +static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == ']') + { + push_token( parser, p ); + if (add_section_from_token( parser ) == -1) return NULL; + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); /* ignore everything else on the line */ + return p + 1; + } + } + parser->error = ERROR_BAD_SECTION_NAME_LINE; /* unfinished section name */ + return NULL; +} + + +/* handler for parser KEY_NAME state */ +static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p, *token_end = parser->start; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == ',') break; + switch(*p) + { + + case '=': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 1 )) return NULL; + parser->start = p + 1; + push_state( parser, VALUE_NAME ); + set_state( parser, LEADING_SPACES ); + return p + 1; + case ';': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); + return p + 1; + case '"': + push_token( parser, token_end ); + parser->start = p + 1; + push_state( parser, KEY_NAME ); + set_state( parser, QUOTES ); + return p + 1; + case '\\': + push_token( parser, token_end ); + parser->start = p; + push_state( parser, KEY_NAME ); + set_state( parser, EOL_BACKSLASH ); + return p; + default: + if (!isspaceW(*p)) token_end = p + 1; + else + { + push_token( parser, p ); + push_state( parser, KEY_NAME ); + set_state( parser, TRAILING_SPACES ); + return p; + } + break; + } + } + push_token( parser, token_end ); + set_state( parser, VALUE_NAME ); + return p; +} + + +/* handler for parser VALUE_NAME state */ +static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p, *token_end = parser->start; + + for (p = pos; !is_eol( parser, p ); p++) + { + switch(*p) + { + case ';': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + push_state( parser, LINE_START ); + set_state( parser, COMMENT ); + return p + 1; + case ',': + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + parser->start = p + 1; + push_state( parser, VALUE_NAME ); + set_state( parser, LEADING_SPACES ); + return p + 1; + case '"': + push_token( parser, token_end ); + parser->start = p + 1; + push_state( parser, VALUE_NAME ); + set_state( parser, QUOTES ); + return p + 1; + case '\\': + push_token( parser, token_end ); + parser->start = p; + push_state( parser, VALUE_NAME ); + set_state( parser, EOL_BACKSLASH ); + return p; + default: + if (!isspaceW(*p)) token_end = p + 1; + else + { + push_token( parser, p ); + push_state( parser, VALUE_NAME ); + set_state( parser, TRAILING_SPACES ); + return p; + } + break; + } + } + push_token( parser, token_end ); + if (!add_field_from_token( parser, 0 )) return NULL; + set_state( parser, LINE_START ); + return p; +} + + +/* handler for parser EOL_BACKSLASH state */ +static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eof( parser, p ); p++) + { + switch(*p) + { + case '\n': + parser->line_pos++; + parser->start = p + 1; + set_state( parser, LEADING_SPACES ); + return p + 1; + case '\\': + continue; + case ';': + push_state( parser, EOL_BACKSLASH ); + set_state( parser, COMMENT ); + return p + 1; + default: + if (isspaceW(*p)) continue; + push_token( parser, p ); + pop_state( parser ); + return p; + } + } + parser->start = p; + pop_state( parser ); + return p; +} + + +/* handler for parser QUOTES state */ +static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p, *token_end = parser->start; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == '"') + { + if (p+1 < parser->end && p[1] == '"') /* double quotes */ + { + push_token( parser, p + 1 ); + parser->start = token_end = p + 2; + p++; + } + else /* end of quotes */ + { + push_token( parser, p ); + parser->start = p + 1; + pop_state( parser ); + return p + 1; + } + } + } + push_token( parser, p ); + pop_state( parser ); + return p; +} + + +/* handler for parser LEADING_SPACES state */ +static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == '\\') + { + parser->start = p; + set_state( parser, EOL_BACKSLASH ); + return p; + } + if (!isspaceW(*p)) break; + } + parser->start = p; + pop_state( parser ); + return p; +} + + +/* handler for parser TRAILING_SPACES state */ +static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p; + + for (p = pos; !is_eol( parser, p ); p++) + { + if (*p == '\\') + { + set_state( parser, EOL_BACKSLASH ); + return p; + } + if (!isspaceW(*p)) break; + } + pop_state( parser ); + return p; +} + + +/* handler for parser COMMENT state */ +static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos ) +{ + const WCHAR *p = pos; + + while (!is_eol( parser, p )) p++; + pop_state( parser ); + return p; +} + + +/* parse a complete buffer */ +static DWORD parse_buffer( struct inf_file *file, const WCHAR *buffer, const WCHAR *end, + UINT *error_line ) +{ + static const WCHAR Strings[] = {'S','t','r','i','n','g','s',0}; + + struct parser parser; + const WCHAR *pos = buffer; + + parser.start = buffer; + parser.end = end; + parser.file = file; + parser.line = NULL; + parser.state = LINE_START; + parser.stack_pos = 0; + parser.cur_section = -1; + parser.line_pos = 1; + parser.error = 0; + parser.token_len = 0; + + /* parser main loop */ + while (pos) pos = (parser_funcs[parser.state])( &parser, pos ); + + /* trim excess buffer space */ + if (file->alloc_sections > file->nb_sections) + { + file->sections = HeapReAlloc( GetProcessHeap(), 0, file->sections, + file->nb_sections * sizeof(file->sections[0]) ); + file->alloc_sections = file->nb_sections; + } + if (file->alloc_fields > file->nb_fields) + { + file->fields = HeapReAlloc( GetProcessHeap(), 0, file->fields, + file->nb_fields * sizeof(file->fields[0]) ); + file->alloc_fields = file->nb_fields; + } + file->strings = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, file->strings, + (file->string_pos - file->strings) * sizeof(WCHAR) ); + + if (parser.error) + { + if (error_line) *error_line = parser.line_pos; + return parser.error; + } + + /* find the [strings] section */ + file->strings_section = find_section( file, Strings ); + return 0; +} + + +/* append a child INF file to its parent list, in a thread-safe manner */ +static void append_inf_file( struct inf_file *parent, struct inf_file *child ) +{ + struct inf_file **ppnext = &parent->next; + child->next = NULL; + + for (;;) + { + struct inf_file *next = InterlockedCompareExchangePointer( (void **)ppnext, child, NULL ); + if (!next) return; + ppnext = &next->next; + } +} + + +/*********************************************************************** + * parse_file + * + * parse an INF file. + */ +static struct inf_file *parse_file( HANDLE handle, const WCHAR *class, UINT *error_line ) +{ + void *buffer; + DWORD err = 0; + struct inf_file *file; + + DWORD size = GetFileSize( handle, NULL ); + HANDLE mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, size, NULL ); + if (!mapping) return NULL; + buffer = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, size ); + NtClose( mapping ); + if (!buffer) return NULL; + + if (class) FIXME( "class %s not supported yet\n", debugstr_w(class) ); + + if (!(file = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*file) ))) + { + err = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + + /* we won't need more strings space than the size of the file, + * so we can preallocate it here + */ + if (!(file->strings = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) + { + err = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } + file->string_pos = file->strings; + file->strings_section = -1; + + if (!RtlIsTextUnicode( buffer, size, NULL )) + { + WCHAR *new_buff = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ); + if (new_buff) + { + DWORD len = MultiByteToWideChar( CP_ACP, 0, buffer, size, new_buff, + size * sizeof(WCHAR) ); + err = parse_buffer( file, new_buff, new_buff + len, error_line ); + HeapFree( GetProcessHeap(), 0, new_buff ); + } + } + else err = parse_buffer( file, buffer, (WCHAR *)((char *)buffer + size), error_line ); + + if (!err) /* now check signature */ + { + int version_index = find_section( file, Version ); + if (version_index != -1) + { + struct line *line = find_line( file, version_index, Signature ); + if (line && line->nb_fields > 0) + { + struct field *field = file->fields + line->first_field; + if (!strcmpiW( field->text, Chicago )) goto done; + if (!strcmpiW( field->text, WindowsNT )) goto done; + if (!strcmpiW( field->text, Windows95 )) goto done; + } + } + err = ERROR_WRONG_INF_STYLE; + } + + done: + UnmapViewOfFile( buffer ); + if (err) + { + HeapFree( GetProcessHeap(), 0, file ); + SetLastError( err ); + file = NULL; + } + return file; +} + + +/*********************************************************************** + * PARSER_get_src_root + * + * Retrieve the source directory of an inf file. + */ +const WCHAR *PARSER_get_src_root( HINF hinf ) +{ + struct inf_file *file = hinf; + return file->src_root; +} + + +/*********************************************************************** + * SetupOpenInfFileA (SETUPAPI.@) + */ +HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error ) +{ + UNICODE_STRING nameW, classW; + HINF ret = (HINF)INVALID_HANDLE_VALUE; + + classW.Buffer = NULL; + if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; + } + if (RtlCreateUnicodeStringFromAsciiz( &nameW, name )) + { + ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error ); + RtlFreeUnicodeString( &nameW ); + } + RtlFreeUnicodeString( &classW ); + return ret; +} + + +/*********************************************************************** + * SetupOpenInfFileW (SETUPAPI.@) + */ +HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error ) +{ + struct inf_file *file = NULL; + HANDLE handle; + WCHAR *path, *p; + UINT len; + + if (strchrW( name, '\\' ) || strchrW( name, '/' )) + { + if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return (HINF)INVALID_HANDLE_VALUE; + if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return (HINF)INVALID_HANDLE_VALUE; + } + GetFullPathNameW( name, len, path, NULL ); + handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + } + else /* try Windows directory */ + { + static const WCHAR Inf[] = {'\\','i','n','f','\\',0}; + static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0}; + + len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12; + if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return (HINF)INVALID_HANDLE_VALUE; + } + GetWindowsDirectoryW( path, len ); + p = path + strlenW(path); + strcpyW( p, Inf ); + strcatW( p, name ); + handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + if (handle == INVALID_HANDLE_VALUE) + { + strcpyW( p, System32 ); + strcatW( p, name ); + handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + } + } + + if (handle != INVALID_HANDLE_VALUE) + { + file = parse_file( handle, class, error ); + CloseHandle( handle ); + } + if (!file) + { + HeapFree( GetProcessHeap(), 0, path ); + return (HINF)INVALID_HANDLE_VALUE; + } + TRACE( "%s -> %p\n", debugstr_w(path), file ); + file->src_root = path; + if ((p = strrchrW( path, '\\' ))) p[1] = 0; /* remove file name */ + SetLastError( 0 ); + return (HINF)file; +} + + +/*********************************************************************** + * SetupOpenAppendInfFileA (SETUPAPI.@) + */ +BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error ) +{ + HINF child_hinf; + + if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error ); + child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error ); + if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; + append_inf_file( parent_hinf, child_hinf ); + TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf ); + return TRUE; +} + + +/*********************************************************************** + * SetupOpenAppendInfFileW (SETUPAPI.@) + */ +BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error ) +{ + HINF child_hinf; + + if (!name) + { + INFCONTEXT context; + WCHAR filename[MAX_PATH]; + int idx = 1; + + if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE; + while (SetupGetStringFieldW( &context, idx++, filename, + sizeof(filename)/sizeof(WCHAR), NULL )) + { + child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error ); + if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; + append_inf_file( parent_hinf, child_hinf ); + TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf ); + } + return TRUE; + } + child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error ); + if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE; + append_inf_file( parent_hinf, child_hinf ); + TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf ); + return TRUE; +} + + +/*********************************************************************** + * SetupCloseInfFile (SETUPAPI.@) + */ +void WINAPI SetupCloseInfFile( HINF hinf ) +{ + struct inf_file *file = hinf; + int i; + + for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] ); + HeapFree( GetProcessHeap(), 0, file->src_root ); + HeapFree( GetProcessHeap(), 0, file->sections ); + HeapFree( GetProcessHeap(), 0, file->fields ); + HeapFree( GetProcessHeap(), 0, file->strings ); + HeapFree( GetProcessHeap(), 0, file ); +} + + +/*********************************************************************** + * SetupGetLineCountA (SETUPAPI.@) + */ +LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name ) +{ + UNICODE_STRING sectionW; + LONG ret = -1; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, name )) + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + ret = SetupGetLineCountW( hinf, sectionW.Buffer ); + RtlFreeUnicodeString( §ionW ); + } + return ret; +} + + +/*********************************************************************** + * SetupGetLineCountW (SETUPAPI.@) + */ +LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section ) +{ + struct inf_file *file = hinf; + int section_index; + LONG ret = -1; + + for (file = hinf; file; file = file->next) + { + if ((section_index = find_section( file, section )) == -1) continue; + if (ret == -1) ret = 0; + ret += file->sections[section_index]->nb_lines; + } + TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret ); + SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 ); + return ret; +} + + +/*********************************************************************** + * SetupGetLineByIndexA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context ); + RtlFreeUnicodeString( §ionW ); + } + return ret; +} + + +/*********************************************************************** + * SetupGetLineByIndexW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context ) +{ + struct inf_file *file = hinf; + int section_index; + + SetLastError( ERROR_SECTION_NOT_FOUND ); + for (file = hinf; file; file = file->next) + { + if ((section_index = find_section( file, section )) == -1) continue; + SetLastError( ERROR_LINE_NOT_FOUND ); + if (index < file->sections[section_index]->nb_lines) + { + context->Inf = hinf; + context->CurrentInf = file; + context->Section = section_index; + context->Line = index; + SetLastError( 0 ); + TRACE( "(%p,%s): returning %d/%ld\n", + hinf, debugstr_w(section), section_index, index ); + return TRUE; + } + index -= file->sections[section_index]->nb_lines; + } + TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) ); + return FALSE; +} + + +/*********************************************************************** + * SetupFindFirstLineA (SETUPAPI.@) + */ +BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context ) +{ + UNICODE_STRING sectionW, keyW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + + if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context ); + else + { + if (RtlCreateUnicodeStringFromAsciiz( &keyW, key )) + { + ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context ); + RtlFreeUnicodeString( &keyW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + RtlFreeUnicodeString( §ionW ); + return ret; +} + + +/*********************************************************************** + * SetupFindFirstLineW (SETUPAPI.@) + */ +BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context ) +{ + struct inf_file *file; + int section_index; + + SetLastError( ERROR_SECTION_NOT_FOUND ); + for (file = hinf; file; file = file->next) + { + if ((section_index = find_section( file, section )) == -1) continue; + if (key) + { + INFCONTEXT ctx; + ctx.Inf = hinf; + ctx.CurrentInf = file; + ctx.Section = section_index; + ctx.Line = -1; + return SetupFindNextMatchLineW( &ctx, key, context ); + } + SetLastError( ERROR_LINE_NOT_FOUND ); /* found at least one section */ + if (file->sections[section_index]->nb_lines) + { + context->Inf = hinf; + context->CurrentInf = file; + context->Section = section_index; + context->Line = 0; + SetLastError( 0 ); + TRACE( "(%p,%s,%s): returning %d/0\n", + hinf, debugstr_w(section), debugstr_w(key), section_index ); + return TRUE; + } + } + TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) ); + return FALSE; +} + + +/*********************************************************************** + * SetupFindNextLine (SETUPAPI.@) + */ +BOOL WINAPI SetupFindNextLine( PINFCONTEXT context_in, PINFCONTEXT context_out ) +{ + struct inf_file *file = context_in->CurrentInf; + struct section *section; + + if (context_in->Section >= file->nb_sections) goto error; + + section = file->sections[context_in->Section]; + if (context_in->Line+1 < section->nb_lines) + { + if (context_out != context_in) *context_out = *context_in; + context_out->Line++; + SetLastError( 0 ); + return TRUE; + } + + /* now search the appended files */ + + for (file = file->next; file; file = file->next) + { + int section_index = find_section( file, section->name ); + if (section_index == -1) continue; + if (file->sections[section_index]->nb_lines) + { + context_out->Inf = context_in->Inf; + context_out->CurrentInf = file; + context_out->Section = section_index; + context_out->Line = 0; + SetLastError( 0 ); + return TRUE; + } + } + error: + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; +} + + +/*********************************************************************** + * SetupFindNextMatchLineA (SETUPAPI.@) + */ +BOOL WINAPI SetupFindNextMatchLineA( PINFCONTEXT context_in, PCSTR key, + PINFCONTEXT context_out ) +{ + UNICODE_STRING keyW; + BOOL ret = FALSE; + + if (!key) return SetupFindNextLine( context_in, context_out ); + + if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key )) + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + else + { + ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out ); + RtlFreeUnicodeString( &keyW ); + } + return ret; +} + + +/*********************************************************************** + * SetupFindNextMatchLineW (SETUPAPI.@) + */ +BOOL WINAPI SetupFindNextMatchLineW( PINFCONTEXT context_in, PCWSTR key, + PINFCONTEXT context_out ) +{ + struct inf_file *file = context_in->CurrentInf; + struct section *section; + struct line *line; + unsigned int i; + + if (!key) return SetupFindNextLine( context_in, context_out ); + + if (context_in->Section >= file->nb_sections) goto error; + + section = file->sections[context_in->Section]; + + for (i = context_in->Line+1, line = §ion->lines[i]; i < section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (!strcmpiW( key, file->fields[line->key_field].text )) + { + if (context_out != context_in) *context_out = *context_in; + context_out->Line = i; + SetLastError( 0 ); + TRACE( "(%p,%s,%s): returning %d\n", + file, debugstr_w(section->name), debugstr_w(key), i ); + return TRUE; + } + } + + /* now search the appended files */ + + for (file = file->next; file; file = file->next) + { + int section_index = find_section( file, section->name ); + if (section_index == -1) continue; + section = file->sections[section_index]; + for (i = 0, line = section->lines; i < section->nb_lines; i++, line++) + { + if (line->key_field == -1) continue; + if (!strcmpiW( key, file->fields[line->key_field].text )) + { + context_out->Inf = context_in->Inf; + context_out->CurrentInf = file; + context_out->Section = section_index; + context_out->Line = i; + SetLastError( 0 ); + TRACE( "(%p,%s,%s): returning %d/%d\n", + file, debugstr_w(section->name), debugstr_w(key), section_index, i ); + return TRUE; + } + } + } + TRACE( "(%p,%s,%s): not found\n", + context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) ); + error: + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; +} + + +/*********************************************************************** + * SetupGetLineTextW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineTextW( PINFCONTEXT context, HINF hinf, PCWSTR section_name, + PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD required ) +{ + struct inf_file *file; + struct line *line; + struct field *field; + int i; + DWORD total = 0; + + if (!context) + { + INFCONTEXT new_context; + if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE; + file = new_context.CurrentInf; + line = get_line( file, new_context.Section, new_context.Line ); + } + else + { + file = context->CurrentInf; + if (!(line = get_line( file, context->Section, context->Line ))) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + } + + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1; + + if (required) *required = total; + if (buffer) + { + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + { + unsigned int len = PARSER_string_substW( file, field->text, buffer, size ); + if (i+1 < line->nb_fields) buffer[len] = ','; + buffer += len + 1; + } + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetLineTextA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetLineTextA( PINFCONTEXT context, HINF hinf, PCSTR section_name, + PCSTR key_name, PSTR buffer, DWORD size, PDWORD required ) +{ + struct inf_file *file; + struct line *line; + struct field *field; + int i; + DWORD total = 0; + + if (!context) + { + INFCONTEXT new_context; + if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE; + file = new_context.CurrentInf; + line = get_line( file, new_context.Section, new_context.Line ); + } + else + { + file = context->CurrentInf; + if (!(line = get_line( file, context->Section, context->Line ))) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + } + + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1; + + if (required) *required = total; + if (buffer) + { + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++) + { + unsigned int len = PARSER_string_substA( file, field->text, buffer, size ); + if (i+1 < line->nb_fields) buffer[len] = ','; + buffer += len + 1; + } + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetFieldCount (SETUPAPI.@) + */ +DWORD WINAPI SetupGetFieldCount( PINFCONTEXT context ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + + if (!line) return 0; + return line->nb_fields; +} + + +/*********************************************************************** + * SetupGetStringFieldA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetStringFieldA( PINFCONTEXT context, DWORD index, PSTR buffer, + DWORD size, PDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct field *field = get_field( file, context->Section, context->Line, index ); + unsigned int len; + + SetLastError(0); + if (!field) return FALSE; + len = PARSER_string_substA( file, field->text, NULL, 0 ); + if (required) *required = len + 1; + if (buffer) + { + if (size <= len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + PARSER_string_substA( file, field->text, buffer, size ); + + TRACE( "context %p/%p/%d/%d index %ld returning %s\n", + context->Inf, context->CurrentInf, context->Section, context->Line, + index, debugstr_a(buffer) ); + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetStringFieldW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetStringFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer, + DWORD size, PDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct field *field = get_field( file, context->Section, context->Line, index ); + unsigned int len; + + SetLastError(0); + if (!field) return FALSE; + len = PARSER_string_substW( file, field->text, NULL, 0 ); + if (required) *required = len + 1; + if (buffer) + { + if (size <= len) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + PARSER_string_substW( file, field->text, buffer, size ); + + TRACE( "context %p/%p/%d/%d index %ld returning %s\n", + context->Inf, context->CurrentInf, context->Section, context->Line, + index, debugstr_w(buffer) ); + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetIntField (SETUPAPI.@) + */ +BOOL WINAPI SetupGetIntField( PINFCONTEXT context, DWORD index, PINT result ) +{ + char localbuff[20]; + char *end, *buffer = localbuff; + DWORD required; + INT res; + BOOL ret = FALSE; + + if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required )) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE; + if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE; + if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done; + } + res = strtol( buffer, &end, 0 ); + if (end != buffer && !*end) + { + *result = res; + ret = TRUE; + } + else SetLastError( ERROR_INVALID_DATA ); + + done: + if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer ); + return ret; +} + + +/*********************************************************************** + * SetupGetBinaryField (SETUPAPI.@) + */ +BOOL WINAPI SetupGetBinaryField( PINFCONTEXT context, DWORD index, BYTE *buffer, + DWORD size, LPDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + struct field *field; + int i; + + if (!line) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + if (!index || index >= line->nb_fields) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + index--; /* fields start at 0 */ + if (required) *required = line->nb_fields - index; + if (!buffer) return TRUE; + if (size < line->nb_fields - index) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + const WCHAR *p; + DWORD value = 0; + for (p = field->text; *p && isxdigitW(*p); p++) + { + if ((value <<= 4) > 255) + { + SetLastError( ERROR_INVALID_DATA ); + return FALSE; + } + if (*p <= '9') value |= (*p - '0'); + else value |= (tolowerW(*p) - 'a' + 10); + } + buffer[i - index] = value; + } + if (TRACE_ON(setupapi)) + { + TRACE( "%p/%p/%d/%d index %ld returning", + context->Inf, context->CurrentInf, context->Section, context->Line, index ); + for (i = index; i < line->nb_fields; i++) TRACE( " %02x", buffer[i - index] ); + TRACE( "\n" ); + } + return TRUE; +} + + +/*********************************************************************** + * SetupGetMultiSzFieldA (SETUPAPI.@) + */ +BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer, + DWORD size, LPDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + struct field *field; + unsigned int len; + int i; + DWORD total = 1; + + if (!line) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + if (!index || index >= line->nb_fields) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + index--; /* fields start at 0 */ + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break; + total += len + 1; + } + + if (required) *required = total; + if (!buffer) return TRUE; + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break; + buffer += len + 1; + } + *buffer = 0; /* add final null */ + return TRUE; +} + + +/*********************************************************************** + * SetupGetMultiSzFieldW (SETUPAPI.@) + */ +BOOL WINAPI SetupGetMultiSzFieldW( PINFCONTEXT context, DWORD index, PWSTR buffer, + DWORD size, LPDWORD required ) +{ + struct inf_file *file = context->CurrentInf; + struct line *line = get_line( file, context->Section, context->Line ); + struct field *field; + unsigned int len; + int i; + DWORD total = 1; + + if (!line) + { + SetLastError( ERROR_LINE_NOT_FOUND ); + return FALSE; + } + if (!index || index >= line->nb_fields) + { + SetLastError( ERROR_INVALID_PARAMETER ); + return FALSE; + } + index--; /* fields start at 0 */ + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break; + total += len + 1; + } + + if (required) *required = total; + if (!buffer) return TRUE; + if (total > size) + { + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + field = &file->fields[line->first_field + index]; + for (i = index; i < line->nb_fields; i++, field++) + { + if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break; + buffer += len + 1; + } + *buffer = 0; /* add final null */ + return TRUE; +} diff --git a/reactos/lib/setupapi/queue.c b/reactos/lib/setupapi/queue.c new file mode 100644 index 00000000000..d7578683ba1 --- /dev/null +++ b/reactos/lib/setupapi/queue.c @@ -0,0 +1,1376 @@ +/* + * Setupapi file queue routines + * + * Copyright 2002 Alexandre Julliard for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "wine/unicode.h" +#include "setupapi_private.h" +#include "winver.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/* context structure for the default queue callback */ +struct default_callback_context +{ + HWND owner; + HWND progress; + UINT message; +}; + +struct file_op +{ + struct file_op *next; + UINT style; + WCHAR *src_root; + WCHAR *src_path; + WCHAR *src_file; + WCHAR *src_descr; + WCHAR *src_tag; + WCHAR *dst_path; + WCHAR *dst_file; +}; + +struct file_op_queue +{ + struct file_op *head; + struct file_op *tail; + unsigned int count; +}; + +struct file_queue +{ + struct file_op_queue copy_queue; + struct file_op_queue delete_queue; + struct file_op_queue rename_queue; + DWORD flags; +}; + + +inline static WCHAR *strdupW( const WCHAR *str ) +{ + WCHAR *ret = NULL; + if (str) + { + int len = (strlenW(str) + 1) * sizeof(WCHAR); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( ret, str, len ); + } + return ret; +} + + +inline static WCHAR *strdupAtoW( const char *str ) +{ + WCHAR *ret = NULL; + if (str) + { + DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) + MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); + } + return ret; +} + +inline static char *strdupWtoA( const WCHAR *str ) +{ + char *ret = NULL; + if (str) + { + DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); + if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) + WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); + } + return ret; +} + +/* append a file operation to a queue */ +inline static void queue_file_op( struct file_op_queue *queue, struct file_op *op ) +{ + op->next = NULL; + if (queue->tail) queue->tail->next = op; + else queue->head = op; + queue->tail = op; + queue->count++; +} + +/* free all the file operations on a given queue */ +static void free_file_op_queue( struct file_op_queue *queue ) +{ + struct file_op *t, *op = queue->head; + + while( op ) + { + HeapFree( GetProcessHeap(), 0, op->src_root ); + HeapFree( GetProcessHeap(), 0, op->src_path ); + HeapFree( GetProcessHeap(), 0, op->src_file ); + HeapFree( GetProcessHeap(), 0, op->src_descr ); + HeapFree( GetProcessHeap(), 0, op->src_tag ); + HeapFree( GetProcessHeap(), 0, op->dst_path ); + if (op->dst_file != op->src_file) HeapFree( GetProcessHeap(), 0, op->dst_file ); + t = op; + op = op->next; + HeapFree( GetProcessHeap(), 0, t ); + } +} + +/* concat 3 strings to make a path, handling separators correctly */ +static void concat_W( WCHAR *buffer, const WCHAR *src1, const WCHAR *src2, const WCHAR *src3 ) +{ + *buffer = 0; + if (src1 && *src1) + { + strcpyW( buffer, src1 ); + buffer += strlenW(buffer ); + if (buffer[-1] != '\\') *buffer++ = '\\'; + if (src2) while (*src2 == '\\') src2++; + } + + if (src2) + { + strcpyW( buffer, src2 ); + buffer += strlenW(buffer ); + if (buffer[-1] != '\\') *buffer++ = '\\'; + if (src3) while (*src3 == '\\') src3++; + } + if (src3) + { + strcpyW( buffer, src3 ); + buffer += strlenW(buffer ); + } +} + + +/*********************************************************************** + * build_filepathsW + * + * Build a FILEPATHS_W structure for a given file operation. + */ +static BOOL build_filepathsW( const struct file_op *op, FILEPATHS_W *paths ) +{ + int src_len = 1, dst_len = 1; + WCHAR *source = (PWSTR)paths->Source, *target = (PWSTR)paths->Target; + + if (op->src_root) src_len += strlenW(op->src_root) + 1; + if (op->src_path) src_len += strlenW(op->src_path) + 1; + if (op->src_file) src_len += strlenW(op->src_file) + 1; + if (op->dst_path) dst_len += strlenW(op->dst_path) + 1; + if (op->dst_file) dst_len += strlenW(op->dst_file) + 1; + src_len *= sizeof(WCHAR); + dst_len *= sizeof(WCHAR); + + if (!source || HeapSize( GetProcessHeap(), 0, source ) < src_len ) + { + HeapFree( GetProcessHeap(), 0, source ); + paths->Source = source = HeapAlloc( GetProcessHeap(), 0, src_len ); + } + if (!target || HeapSize( GetProcessHeap(), 0, target ) < dst_len ) + { + HeapFree( GetProcessHeap(), 0, target ); + paths->Target = target = HeapAlloc( GetProcessHeap(), 0, dst_len ); + } + if (!source || !target) return FALSE; + concat_W( source, op->src_root, op->src_path, op->src_file ); + concat_W( target, NULL, op->dst_path, op->dst_file ); + paths->Win32Error = 0; + paths->Flags = 0; + return TRUE; +} + + +/*********************************************************************** + * QUEUE_callback_WtoA + * + * Map a file callback parameters from W to A and call the A callback. + */ +UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, + UINT_PTR param1, UINT_PTR param2 ) +{ + struct callback_WtoA_context *callback_ctx = context; + char buffer[MAX_PATH]; + UINT ret; + UINT_PTR old_param2 = param2; + + switch(notification) + { + case SPFILENOTIFY_COPYERROR: + param2 = (UINT_PTR)&buffer; + /* fall through */ + case SPFILENOTIFY_STARTDELETE: + case SPFILENOTIFY_ENDDELETE: + case SPFILENOTIFY_DELETEERROR: + case SPFILENOTIFY_STARTRENAME: + case SPFILENOTIFY_ENDRENAME: + case SPFILENOTIFY_RENAMEERROR: + case SPFILENOTIFY_STARTCOPY: + case SPFILENOTIFY_ENDCOPY: + { + FILEPATHS_W *pathsW = (FILEPATHS_W *)param1; + FILEPATHS_A pathsA; + + pathsA.Source = strdupWtoA( pathsW->Source ); + pathsA.Target = strdupWtoA( pathsW->Target ); + pathsA.Win32Error = pathsW->Win32Error; + pathsA.Flags = pathsW->Flags; + ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, + (UINT_PTR)&pathsA, param2 ); + HeapFree( GetProcessHeap(), 0, (void *)pathsA.Source ); + HeapFree( GetProcessHeap(), 0, (void *)pathsA.Target ); + } + if (notification == SPFILENOTIFY_COPYERROR) + MultiByteToWideChar( CP_ACP, 0, buffer, -1, (WCHAR *)old_param2, MAX_PATH ); + break; + + case SPFILENOTIFY_NEEDMEDIA: + case SPFILENOTIFY_QUEUESCAN: + FIXME("mapping for %d not implemented\n",notification); + case SPFILENOTIFY_STARTQUEUE: + case SPFILENOTIFY_ENDQUEUE: + case SPFILENOTIFY_STARTSUBQUEUE: + case SPFILENOTIFY_ENDSUBQUEUE: + default: + ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, param1, param2 ); + break; + } + return ret; +} + + +/*********************************************************************** + * get_src_file_info + * + * Retrieve the source file information for a given file. + */ +static void get_src_file_info( HINF hinf, struct file_op *op ) +{ + static const WCHAR SourceDisksNames[] = + {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0}; + static const WCHAR SourceDisksFiles[] = + {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0}; + + INFCONTEXT file_ctx, disk_ctx; + INT id, diskid; + DWORD len, len2; + + /* find the SourceDisksFiles entry */ + if (!SetupFindFirstLineW( hinf, SourceDisksFiles, op->src_file, &file_ctx )) + { + const WCHAR *dir; + + if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return; + /* no specific info, use .inf file source directory */ + if (!op->src_root && (dir = DIRID_get_string( hinf, DIRID_SRCPATH ))) + op->src_root = strdupW( dir ); + return; + } + if (!SetupGetIntField( &file_ctx, 1, &diskid )) return; + + /* now find the diskid in the SourceDisksNames section */ + if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return; + for (;;) + { + if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break; + if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return; + } + + /* and fill in the missing info */ + + if (!op->src_descr) + { + if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) && + (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) ))) + SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL ); + } + if (!op->src_tag) + { + if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) && + (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) ))) + SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL ); + } + if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE)) + { + if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE)) + { + /* retrieve relative path for this disk */ + if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0; + } + /* retrieve relative path for this file */ + if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0; + + if ((len || len2) && + (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) ))) + { + WCHAR *ptr = op->src_path; + if (len) + { + SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL ); + ptr = op->src_path + strlenW(op->src_path); + if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\'; + } + if (!SetupGetStringFieldW( &disk_ctx, 4, ptr, len2, NULL )) *ptr = 0; + } + } + if (!op->src_root) op->src_root = strdupW( PARSER_get_src_root(hinf) ); +} + + +/*********************************************************************** + * get_destination_dir + * + * Retrieve the destination dir for a given section. + */ +static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section ) +{ + static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0}; + static const WCHAR Def[] = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0}; + + const WCHAR *dir; + WCHAR *ptr, *ret; + INFCONTEXT context; + INT dirid; + DWORD len1, len2; + + if (!SetupFindFirstLineW( hinf, Dest, section, &context ) && + !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL; + if (!SetupGetIntField( &context, 1, &dirid )) return NULL; + if (!(dir = DIRID_get_string( hinf, dirid ))) return NULL; + len1 = strlenW(dir) + 1; + if (!SetupGetStringFieldW( &context, 2, NULL, 0, &len2 )) len2 = 0; + if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2) * sizeof(WCHAR) ))) return NULL; + strcpyW( ret, dir ); + ptr = ret + strlenW(ret); + if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\'; + if (!SetupGetStringFieldW( &context, 2, ptr, len2, NULL )) *ptr = 0; + return ret; +} + + +static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD ); + +/*********************************************************************** + * extract_cabinet_file + * + * Extract a file from a .cab file. + */ +static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root, + const WCHAR *src, const WCHAR *dst ) +{ + static const WCHAR extW[] = {'.','c','a','b',0}; + static HMODULE advpack; + + char *cab_path, *cab_file; + int len = strlenW( cabinet ); + + /* make sure the cabinet file has a .cab extension */ + if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE; + if (!pExtractFiles) + { + if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" ))) + { + ERR( "could not load advpack.dll\n" ); + return FALSE; + } + if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" ))) + { + ERR( "could not find ExtractFiles in advpack.dll\n" ); + return FALSE; + } + } + + if (!(cab_path = strdupWtoA( root ))) return FALSE; + len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL ); + if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 ))) + { + HeapFree( GetProcessHeap(), 0, cab_path ); + return FALSE; + } + strcpy( cab_file, cab_path ); + if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" ); + WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL ); + FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) ); + pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 ); + HeapFree( GetProcessHeap(), 0, cab_file ); + HeapFree( GetProcessHeap(), 0, cab_path ); + return CopyFileW( src, dst, FALSE /*FIXME*/ ); +} + + +/*********************************************************************** + * SetupOpenFileQueue (SETUPAPI.@) + */ +HSPFILEQ WINAPI SetupOpenFileQueue(void) +{ + struct file_queue *queue; + + if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue)))) + return (HSPFILEQ)INVALID_HANDLE_VALUE; + return queue; +} + + +/*********************************************************************** + * SetupCloseFileQueue (SETUPAPI.@) + */ +BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle ) +{ + struct file_queue *queue = handle; + + free_file_op_queue( &queue->copy_queue ); + free_file_op_queue( &queue->rename_queue ); + free_file_op_queue( &queue->delete_queue ); + HeapFree( GetProcessHeap(), 0, queue ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopyIndirectA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params ) +{ + struct file_queue *queue = params->QueueHandle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = params->CopyStyle; + op->src_root = strdupAtoW( params->SourceRootPath ); + op->src_path = strdupAtoW( params->SourcePath ); + op->src_file = strdupAtoW( params->SourceFilename ); + op->src_descr = strdupAtoW( params->SourceDescription ); + op->src_tag = strdupAtoW( params->SourceTagfile ); + op->dst_path = strdupAtoW( params->TargetDirectory ); + op->dst_file = strdupAtoW( params->TargetFilename ); + + /* some defaults */ + if (!op->src_file) op->src_file = op->dst_file; + if (params->LayoutInf) + { + get_src_file_info( params->LayoutInf, op ); + if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file ); + } + + TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n", + debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file), + debugstr_w(op->dst_path), debugstr_w(op->dst_file), + debugstr_w(op->src_descr), debugstr_w(op->src_tag) ); + + queue_file_op( &queue->copy_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopyIndirectW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params ) +{ + struct file_queue *queue = params->QueueHandle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = params->CopyStyle; + op->src_root = strdupW( params->SourceRootPath ); + op->src_path = strdupW( params->SourcePath ); + op->src_file = strdupW( params->SourceFilename ); + op->src_descr = strdupW( params->SourceDescription ); + op->src_tag = strdupW( params->SourceTagfile ); + op->dst_path = strdupW( params->TargetDirectory ); + op->dst_file = strdupW( params->TargetFilename ); + + /* some defaults */ + if (!op->src_file) op->src_file = op->dst_file; + if (params->LayoutInf) + { + get_src_file_info( params->LayoutInf, op ); + if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file ); + } + + TRACE( "root=%s path=%s file=%s -> dir=%s file=%s descr=%s tag=%s\n", + debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file), + debugstr_w(op->dst_path), debugstr_w(op->dst_file), + debugstr_w(op->src_descr), debugstr_w(op->src_tag) ); + + queue_file_op( &queue->copy_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopyA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file, + PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file, + DWORD style ) +{ + SP_FILE_COPY_PARAMS_A params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = src_path; + params.SourceFilename = src_file; + params.SourceDescription = src_descr; + params.SourceTagfile = src_tag; + params.TargetDirectory = dst_dir; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = 0; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectA( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueCopyW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file, + PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file, + DWORD style ) +{ + SP_FILE_COPY_PARAMS_W params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = src_path; + params.SourceFilename = src_file; + params.SourceDescription = src_descr; + params.SourceTagfile = src_tag; + params.TargetDirectory = dst_dir; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = 0; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectW( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueDefaultCopyA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file, + PCSTR dst_file, DWORD style ) +{ + SP_FILE_COPY_PARAMS_A params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = NULL; + params.SourceFilename = src_file; + params.SourceDescription = NULL; + params.SourceTagfile = NULL; + params.TargetDirectory = NULL; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = hinf; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectA( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueDefaultCopyW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file, + PCWSTR dst_file, DWORD style ) +{ + SP_FILE_COPY_PARAMS_W params; + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = NULL; + params.SourceFilename = src_file; + params.SourceDescription = NULL; + params.SourceTagfile = NULL; + params.TargetDirectory = NULL; + params.TargetFilename = dst_file; + params.CopyStyle = style; + params.LayoutInf = hinf; + params.SecurityDescriptor = NULL; + return SetupQueueCopyIndirectW( ¶ms ); +} + + +/*********************************************************************** + * SetupQueueDeleteA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = NULL; + op->src_file = NULL; + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupAtoW( part1 ); + op->dst_file = strdupAtoW( part2 ); + queue_file_op( &queue->delete_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueDeleteW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = NULL; + op->src_file = NULL; + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupW( part1 ); + op->dst_file = strdupW( part2 ); + queue_file_op( &queue->delete_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueRenameA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename, + PCSTR TargetPath, PCSTR TargetFilename ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = strdupAtoW( SourcePath ); + op->src_file = strdupAtoW( SourceFilename ); + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupAtoW( TargetPath ); + op->dst_file = strdupAtoW( TargetFilename ); + queue_file_op( &queue->rename_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueRenameW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename, + PCWSTR TargetPath, PCWSTR TargetFilename ) +{ + struct file_queue *queue = handle; + struct file_op *op; + + if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE; + op->style = 0; + op->src_root = NULL; + op->src_path = strdupW( SourcePath ); + op->src_file = strdupW( SourceFilename ); + op->src_descr = NULL; + op->src_tag = NULL; + op->dst_path = strdupW( TargetPath ); + op->dst_file = strdupW( TargetFilename ); + queue_file_op( &queue->rename_queue, op ); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueCopySectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist, + PCSTR section, DWORD style ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (!RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return FALSE; + } + if (!src_root) + ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style ); + else + { + UNICODE_STRING srcW; + if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root )) + { + ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style ); + RtlFreeUnicodeString( &srcW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + } + RtlFreeUnicodeString( §ionW ); + return ret; +} + + +/*********************************************************************** + * SetupQueueCopySectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist, + PCWSTR section, DWORD style ) +{ + SP_FILE_COPY_PARAMS_W params; + INFCONTEXT context; + WCHAR dest[MAX_PATH], src[MAX_PATH]; + INT flags; + + TRACE( "hinf=%p/%p section=%s root=%s\n", + hinf, hlist, debugstr_w(section), debugstr_w(src_root) ); + + params.cbSize = sizeof(params); + params.QueueHandle = queue; + params.SourceRootPath = src_root; + params.SourcePath = NULL; + params.SourceDescription = NULL; + params.SourceTagfile = NULL; + params.TargetFilename = dest; + params.CopyStyle = style; + params.LayoutInf = hinf; + params.SecurityDescriptor = NULL; + + if (!hlist) hlist = hinf; + if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; + if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE; + do + { + if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL )) + return FALSE; + if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0; + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; /* FIXME */ + + params.SourceFilename = *src ? src : NULL; + if (!SetupQueueCopyIndirectW( ¶ms )) return FALSE; + } while (SetupFindNextLine( &context, &context )); + return TRUE; +} + + +/*********************************************************************** + * SetupQueueDeleteSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer ); + RtlFreeUnicodeString( §ionW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; +} + + +/*********************************************************************** + * SetupQueueDeleteSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section ) +{ + INFCONTEXT context; + WCHAR *dest_dir; + WCHAR buffer[MAX_PATH]; + BOOL ret = FALSE; + INT flags; + + TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) ); + + if (!hlist) hlist = hinf; + if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; + if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE; + do + { + if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL )) + goto done; + if (!SetupGetIntField( &context, 4, &flags )) flags = 0; + if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done; + } while (SetupFindNextLine( &context, &context )); + + ret = TRUE; + done: + HeapFree( GetProcessHeap(), 0, dest_dir ); + return ret; +} + + +/*********************************************************************** + * SetupQueueRenameSectionA (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section ) +{ + UNICODE_STRING sectionW; + BOOL ret = FALSE; + + if (RtlCreateUnicodeStringFromAsciiz( §ionW, section )) + { + ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer ); + RtlFreeUnicodeString( §ionW ); + } + else SetLastError( ERROR_NOT_ENOUGH_MEMORY ); + return ret; +} + + +/*********************************************************************** + * SetupQueueRenameSectionW (SETUPAPI.@) + */ +BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section ) +{ + INFCONTEXT context; + WCHAR *dest_dir; + WCHAR src[MAX_PATH], dst[MAX_PATH]; + BOOL ret = FALSE; + + TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) ); + + if (!hlist) hlist = hinf; + if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE; + if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE; + do + { + if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL )) + goto done; + if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) + goto done; + if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done; + } while (SetupFindNextLine( &context, &context )); + + ret = TRUE; + done: + HeapFree( GetProcessHeap(), 0, dest_dir ); + return ret; +} + + +/*********************************************************************** + * SetupCommitFileQueueA (SETUPAPI.@) + */ +BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler, + PVOID context ) +{ + struct callback_WtoA_context ctx; + + ctx.orig_context = context; + ctx.orig_handler = handler; + return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx ); +} + + +/*********************************************************************** + * create_full_pathW + * + * Recursively create all directories in the path. + */ +static BOOL create_full_pathW(const WCHAR *path) +{ + BOOL ret = TRUE; + int len; + WCHAR *new_path; + + new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR)); + strcpyW(new_path, path); + + while((len = strlenW(new_path)) && new_path[len - 1] == '\\') + new_path[len - 1] = 0; + + while(!CreateDirectoryW(new_path, NULL)) + { + WCHAR *slash; + DWORD last_error = GetLastError(); + + if(last_error == ERROR_ALREADY_EXISTS) + break; + + if(last_error != ERROR_PATH_NOT_FOUND) + { + ret = FALSE; + break; + } + + if(!(slash = strrchrW(new_path, '\\'))) + { + ret = FALSE; + break; + } + + len = slash - new_path; + new_path[len] = 0; + if(!create_full_pathW(new_path)) + { + ret = FALSE; + break; + } + new_path[len] = '\\'; + } + + HeapFree(GetProcessHeap(), 0, new_path); + return ret; +} + +BOOL static do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style) +{ + BOOL rc = FALSE; + BOOL docopy = TRUE; + + TRACE("copy %s to %s style 0x%lx\n",debugstr_w(source),debugstr_w(target),style); + + /* before copy processing */ + if (style & SP_COPY_REPLACEONLY) + { + if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES) + docopy = FALSE; + } + if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER)) + { + DWORD VersionSizeSource=0; + DWORD VersionSizeTarget=0; + DWORD zero=0; + + /* + * This is sort of an interesting workaround. You see, calling + * GetVersionInfoSize on a builtin dll loads that dll into memory + * and we do not properly unload builtin dlls.. so we effectively + * lock into memory all the targets we are replacing. This leads + * to problems when we try to register the replaced dlls. + * + * So I will test for the existence of the files first so that + * we just basically unconditionally replace the builtin versions. + */ + if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) && + (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES)) + { + VersionSizeSource = GetFileVersionInfoSizeW(source,&zero); + VersionSizeTarget = GetFileVersionInfoSizeW(target,&zero); + } + + TRACE("SizeTarget %li ... SizeSource %li\n",VersionSizeTarget, + VersionSizeSource); + + if (VersionSizeSource && VersionSizeTarget) + { + LPVOID VersionSource; + LPVOID VersionTarget; + VS_FIXEDFILEINFO *TargetInfo; + VS_FIXEDFILEINFO *SourceInfo; + INT length; + WCHAR SubBlock[2]={'\\',0}; + DWORD ret; + + VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource); + VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget); + + ret = GetFileVersionInfoW(source,0,VersionSizeSource,VersionSource); + if (ret) + ret = GetFileVersionInfoW(target, 0, VersionSizeTarget, + VersionTarget); + + if (ret) + { + ret = VerQueryValueW(VersionSource, SubBlock, + (LPVOID*)&SourceInfo, &length); + if (ret) + ret = VerQueryValueW(VersionTarget, SubBlock, + (LPVOID*)&TargetInfo, &length); + + if (ret) + { + TRACE("Versions: Source %li.%li target %li.%li\n", + SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS, + TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS); + + if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS) + { + FIXME("Notify that target version is greater..\n"); + docopy = FALSE; + } + else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS) + && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS)) + { + FIXME("Notify that target version is greater..\n"); + docopy = FALSE; + } + else if ((style & SP_COPY_NEWER_ONLY) && + (TargetInfo->dwFileVersionMS == + SourceInfo->dwFileVersionMS) + &&(TargetInfo->dwFileVersionLS == + SourceInfo->dwFileVersionLS)) + { + FIXME("Notify that target version is greater..\n"); + docopy = FALSE; + } + } + } + HeapFree(GetProcessHeap(),0,VersionSource); + HeapFree(GetProcessHeap(),0,VersionTarget); + } + } + if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE)) + { + if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) + { + FIXME("Notify user target file exists\n"); + docopy = FALSE; + } + } + if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE | + SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP)) + { + ERR("Unsupported style(s) 0x%lx\n",style); + } + + if (docopy) + { + rc = CopyFileW(source,target,FALSE); + TRACE("Did copy... rc was %i\n",rc); + } + + /* after copy processing */ + if (style & SP_COPY_DELETESOURCE) + { + if (rc) + DeleteFileW(source); + } + + return rc; +} + +/*********************************************************************** + * SetupCommitFileQueueW (SETUPAPI.@) + */ +BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler, + PVOID context ) +{ + struct file_queue *queue = handle; + struct file_op *op; + BOOL result = FALSE; + FILEPATHS_W paths; + UINT op_result; + + paths.Source = paths.Target = NULL; + + if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count) + return TRUE; /* nothing to do */ + + if (!handler( context, SPFILENOTIFY_STARTQUEUE, (UINT)owner, 0 )) return FALSE; + + /* perform deletes */ + + if (queue->delete_queue.count) + { + if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE, + queue->delete_queue.count ))) goto done; + for (op = queue->delete_queue.head; op; op = op->next) + { + build_filepathsW( op, &paths ); + op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE); + if (op_result == FILEOP_ABORT) goto done; + while (op_result == FILEOP_DOIT) + { + TRACE( "deleting file %s\n", debugstr_w(paths.Target) ); + if (DeleteFileW( paths.Target )) break; /* success */ + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 ); + if (op_result == FILEOP_ABORT) goto done; + } + handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 ); + } + handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 ); + } + + /* perform renames */ + + if (queue->rename_queue.count) + { + if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME, + queue->rename_queue.count ))) goto done; + for (op = queue->rename_queue.head; op; op = op->next) + { + build_filepathsW( op, &paths ); + op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME); + if (op_result == FILEOP_ABORT) goto done; + while (op_result == FILEOP_DOIT) + { + TRACE( "renaming file %s -> %s\n", + debugstr_w(paths.Source), debugstr_w(paths.Target) ); + if (MoveFileW( paths.Source, paths.Target )) break; /* success */ + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 ); + if (op_result == FILEOP_ABORT) goto done; + } + handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 ); + } + handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 ); + } + + /* perform copies */ + + if (queue->copy_queue.count) + { + if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY, + queue->copy_queue.count ))) goto done; + for (op = queue->copy_queue.head; op; op = op->next) + { + WCHAR newpath[MAX_PATH]; + + build_filepathsW( op, &paths ); + op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY ); + if (op_result == FILEOP_ABORT) goto done; + if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT; + while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH) + { + TRACE( "copying file %s -> %s\n", + debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ), + debugstr_w(paths.Target) ); + if (op->dst_path) + { + if (!create_full_pathW( op->dst_path )) + { + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_COPYERROR, + (UINT_PTR)&paths, (UINT_PTR)newpath ); + if (op_result == FILEOP_ABORT) goto done; + } + } + if (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source, + paths.Target, op->style )) break; /* success */ + /* try to extract it from the cabinet file */ + if (op->src_tag) + { + if (extract_cabinet_file( op->src_tag, op->src_root, + paths.Source, paths.Target )) break; + } + paths.Win32Error = GetLastError(); + op_result = handler( context, SPFILENOTIFY_COPYERROR, + (UINT_PTR)&paths, (UINT_PTR)newpath ); + if (op_result == FILEOP_ABORT) goto done; + } + handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 ); + } + handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 ); + } + + + result = TRUE; + + done: + handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 ); + HeapFree( GetProcessHeap(), 0, (void *)paths.Source ); + HeapFree( GetProcessHeap(), 0, (void *)paths.Target ); + return result; +} + + +/*********************************************************************** + * SetupScanFileQueueA (SETUPAPI.@) + */ +BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window, + PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result ) +{ + FIXME("stub\n"); + return FALSE; +} + + +/*********************************************************************** + * SetupScanFileQueueW (SETUPAPI.@) + */ +BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window, + PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result ) +{ + FIXME("stub\n"); + return FALSE; +} + + +/*********************************************************************** + * SetupGetFileQueueCount (SETUPAPI.@) + */ +BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result ) +{ + struct file_queue *queue = handle; + + switch(op) + { + case FILEOP_COPY: + *result = queue->copy_queue.count; + return TRUE; + case FILEOP_RENAME: + *result = queue->rename_queue.count; + return TRUE; + case FILEOP_DELETE: + *result = queue->delete_queue.count; + return TRUE; + } + return FALSE; +} + + +/*********************************************************************** + * SetupGetFileQueueFlags (SETUPAPI.@) + */ +BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags ) +{ + struct file_queue *queue = handle; + *flags = queue->flags; + return TRUE; +} + + +/*********************************************************************** + * SetupSetFileQueueFlags (SETUPAPI.@) + */ +BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags ) +{ + struct file_queue *queue = handle; + queue->flags = (queue->flags & ~mask) | flags; + return TRUE; +} + + +/*********************************************************************** + * SetupInitDefaultQueueCallback (SETUPAPI.@) + */ +PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner ) +{ + return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL ); +} + + +/*********************************************************************** + * SetupInitDefaultQueueCallbackEx (SETUPAPI.@) + */ +PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg, + DWORD reserved1, PVOID reserved2 ) +{ + struct default_callback_context *context; + + if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) ))) + { + context->owner = owner; + context->progress = progress; + context->message = msg; + } + return context; +} + + +/*********************************************************************** + * SetupTermDefaultQueueCallback (SETUPAPI.@) + */ +void WINAPI SetupTermDefaultQueueCallback( PVOID context ) +{ + HeapFree( GetProcessHeap(), 0, context ); +} + + +/*********************************************************************** + * SetupDefaultQueueCallbackA (SETUPAPI.@) + */ +UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification, + UINT_PTR param1, UINT_PTR param2 ) +{ + FILEPATHS_A *paths = (FILEPATHS_A *)param1; + + switch(notification) + { + case SPFILENOTIFY_STARTQUEUE: + TRACE( "start queue\n" ); + return TRUE; + case SPFILENOTIFY_ENDQUEUE: + TRACE( "end queue\n" ); + return 0; + case SPFILENOTIFY_STARTSUBQUEUE: + TRACE( "start subqueue %d count %d\n", param1, param2 ); + return TRUE; + case SPFILENOTIFY_ENDSUBQUEUE: + TRACE( "end subqueue %d\n", param1 ); + return 0; + case SPFILENOTIFY_STARTDELETE: + TRACE( "start delete %s\n", debugstr_a(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDDELETE: + TRACE( "end delete %s\n", debugstr_a(paths->Target) ); + return 0; + case SPFILENOTIFY_DELETEERROR: + ERR( "delete error %d %s\n", paths->Win32Error, debugstr_a(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_STARTRENAME: + TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDRENAME: + TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return 0; + case SPFILENOTIFY_RENAMEERROR: + ERR( "rename error %d %s -> %s\n", paths->Win32Error, + debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_STARTCOPY: + TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_DOIT; + case SPFILENOTIFY_ENDCOPY: + TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return 0; + case SPFILENOTIFY_COPYERROR: + ERR( "copy error %d %s -> %s\n", paths->Win32Error, + debugstr_a(paths->Source), debugstr_a(paths->Target) ); + return FILEOP_SKIP; + case SPFILENOTIFY_NEEDMEDIA: + TRACE( "need media\n" ); + return FILEOP_SKIP; + default: + FIXME( "notification %d params %x,%x\n", notification, param1, param2 ); + break; + } + return 0; +} + + +/*********************************************************************** + * SetupDefaultQueueCallbackW (SETUPAPI.@) + */ +UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification, + UINT_PTR param1, UINT_PTR param2 ) +{ + FIXME( "notification %d params %x,%x\n", notification, param1, param2 ); + return FILEOP_SKIP; +} diff --git a/reactos/lib/setupapi/setupapi.rc b/reactos/lib/setupapi/setupapi.rc new file mode 100644 index 00000000000..e218c3c1910 --- /dev/null +++ b/reactos/lib/setupapi/setupapi.rc @@ -0,0 +1,38 @@ +#include +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD + PRODUCTVERSION RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", RES_STR_COMPANY_NAME + VALUE "FileDescription", "WINE IMM32 API Client DLL\0" + VALUE "FileVersion", RES_STR_FILE_VERSION + VALUE "InternalName", "imm32\0" + VALUE "LegalCopyright", RES_STR_LEGAL_COPYRIGHT + VALUE "OriginalFilename", "imm32.dll\0" + VALUE "ProductName", RES_STR_PRODUCT_NAME + VALUE "ProductVersion", RES_STR_PRODUCT_VERSION + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + diff --git a/reactos/lib/setupapi/setupapi.spec b/reactos/lib/setupapi/setupapi.spec new file mode 100644 index 00000000000..fbd58dd929d --- /dev/null +++ b/reactos/lib/setupapi/setupapi.spec @@ -0,0 +1,329 @@ +@ stdcall CM_Connect_MachineW(wstr ptr) +@ stdcall CM_Disconnect_Machine(long) +@ stub CM_Free_Log_Conf_Handle +@ stub CM_Free_Res_Des_Handle +@ stub CM_Get_DevNode_Status_Ex +@ stub CM_Get_Device_ID_ExW +@ stub CM_Get_First_Log_Conf_Ex +@ stub CM_Get_Next_Res_Des_Ex +@ stub CM_Get_Res_Des_Data_Ex +@ stub CM_Get_Res_Des_Data_Size_Ex +@ stub CM_Locate_DevNode_ExW +@ stub CM_Reenumerate_DevNode_Ex +@ stub CaptureAndConvertAnsiArg +@ stub CaptureStringArg +@ stub CenterWindowRelativeToParent +@ stub ConcatenatePaths +@ stub DelayedMove +@ stub DelimStringToMultiSz +@ stub DestroyTextFileReadBuffer +@ stub DoesUserHavePrivilege +@ stub DuplicateString +@ stub EnablePrivilege +@ stub ExtensionPropSheetPageProc +@ stub FileExists +@ stub FreeStringArray +@ stub GetNewInfName +@ stub GetSetFileTimestamp +@ stub GetVersionInfoFromImage +@ stub InfIsFromOemLocation +@ stdcall InstallHinfSection(long long str long) +@ stub InstallHinfSectionA +@ stub InstallHinfSectionW +@ stub InstallStop +@ stub IsUserAdmin +@ stub LookUpStringInTable +@ stub MemoryInitialize +@ stub MultiByteToUnicode +@ stub MultiSzFromSearchControl +@ stub MyFree +@ stub MyGetFileTitle +@ stub MyMalloc +@ stub MyRealloc +@ stub OpenAndMapFileForRead +@ stub OutOfMemory +@ stub QueryMultiSzValueToArray +@ stub QueryRegistryValue +@ stub ReadAsciiOrUnicodeTextFile +@ stub RegistryDelnode +@ stub RetreiveFileSecurity +@ stub RetrieveServiceConfig +@ stub SearchForInfFile +@ stub SetArrayToMultiSzValue +@ stub SetupAddInstallSectionToDiskSpaceListA +@ stub SetupAddInstallSectionToDiskSpaceListW +@ stub SetupAddSectionToDiskSpaceListA +@ stub SetupAddSectionToDiskSpaceListW +@ stub SetupAddToDiskSpaceListA +@ stub SetupAddToDiskSpaceListW +@ stub SetupAddToSourceListA +@ stub SetupAddToSourceListW +@ stub SetupAdjustDiskSpaceListA +@ stub SetupAdjustDiskSpaceListW +@ stub SetupCancelTemporarySourceList +@ stdcall SetupCloseFileQueue(ptr) +@ stdcall SetupCloseInfFile(long) +@ stub SetupCommitFileQueue +@ stdcall SetupCommitFileQueueA(long long ptr ptr) +@ stdcall SetupCommitFileQueueW(long long ptr ptr) +@ stub SetupCopyErrorA +@ stub SetupCopyErrorW +@ stdcall SetupCopyOEMInfA(str str long long ptr long ptr ptr) +@ stub SetupCopyOEMInfW +@ stub SetupCreateDiskSpaceListA +@ stub SetupCreateDiskSpaceListW +@ stub SetupDecompressOrCopyFileA +@ stub SetupDecompressOrCopyFileW +@ stub SetupDefaultQueueCallback +@ stdcall SetupDefaultQueueCallbackA(ptr long long long) +@ stdcall SetupDefaultQueueCallbackW(ptr long long long) +@ stub SetupDeleteErrorA +@ stub SetupDeleteErrorW +@ stub SetupDestroyDiskSpaceList +@ stub SetupDiAskForOEMDisk +@ stub SetupDiBuildClassInfoList +@ stdcall SetupDiBuildClassInfoListExW(long ptr long ptr wstr ptr) +@ stub SetupDiBuildDriverInfoList +@ stub SetupDiCallClassInstaller +@ stub SetupDiCancelDriverInfoSearch +@ stub SetupDiChangeState +@ stub SetupDiClassGuidsFromNameA +@ stdcall SetupDiClassGuidsFromNameExW(wstr ptr long ptr wstr ptr) +@ stub SetupDiClassGuidsFromNameW +@ stub SetupDiClassNameFromGuidA +@ stdcall SetupDiClassNameFromGuidExW(ptr ptr long ptr wstr ptr) +@ stub SetupDiClassNameFromGuidW +@ stub SetupDiCreateDevRegKeyA +@ stub SetupDiCreateDevRegKeyW +@ stub SetupDiCreateDeviceInfoA +@ stdcall SetupDiCreateDeviceInfoList(ptr ptr) +@ stdcall SetupDiCreateDeviceInfoListExW(ptr long str ptr) +@ stub SetupDiCreateDeviceInfoW +@ stub SetupDiDeleteDevRegKey +@ stub SetupDiDeleteDeviceInfo +@ stub SetupDiDestroyClassImageList +@ stdcall SetupDiDestroyDeviceInfoList(long) +@ stub SetupDiDestroyDriverInfoList +@ stub SetupDiDrawMiniIcon +@ stdcall SetupDiEnumDeviceInfo(long long ptr) +@ stdcall SetupDiEnumDeviceInterfaces(long ptr ptr long ptr) +@ stub SetupDiEnumDriverInfoA +@ stub SetupDiEnumDriverInfoW +@ stub SetupDiGetActualSectionToInstallA +@ stub SetupDiGetActualSectionToInstallW +@ stub SetupDiGetClassBitmapIndex +@ stub SetupDiGetClassDescriptionA +@ stdcall SetupDiGetClassDescriptionExW(ptr ptr long ptr wstr ptr) +@ stub SetupDiGetClassDescriptionW +@ stub SetupDiGetClassDevPropertySheetsA +@ stub SetupDiGetClassDevPropertySheetsW +@ stdcall SetupDiGetClassDevsA(ptr ptr long long) +@ stdcall SetupDiGetClassDevsExA(ptr str ptr long ptr str ptr) +@ stdcall SetupDiGetClassDevsExW(ptr wstr ptr long ptr wstr ptr) +@ stdcall SetupDiGetClassDevsW(ptr ptr long long) +@ stub SetupDiGetClassImageIndex +@ stub SetupDiGetClassImageList +@ stub SetupDiGetClassImageListExW +@ stub SetupDiGetClassInstallParamsA +@ stub SetupDiGetClassInstallParamsW +@ stub SetupDiGetDeviceInfoListClass +@ stdcall SetupDiGetDeviceInfoListDetailA(ptr ptr) +@ stdcall SetupDiGetDeviceInfoListDetailW(ptr ptr) +@ stub SetupDiGetDeviceInstallParamsA +@ stub SetupDiGetDeviceInstallParamsW +@ stub SetupDiGetDeviceInstanceIdA +@ stub SetupDiGetDeviceInstanceIdW +@ stdcall SetupDiGetDeviceRegistryPropertyA(long ptr long ptr ptr long ptr) +@ stub SetupDiGetDeviceRegistryPropertyW +@ stub SetupDiGetDriverInfoDetailA +@ stub SetupDiGetDriverInfoDetailW +@ stub SetupDiGetDriverInstallParamsA +@ stub SetupDiGetDriverInstallParamsW +@ stub SetupDiGetDeviceInterfaceAlias +@ stdcall SetupDiGetDeviceInterfaceDetailA(long ptr ptr long ptr ptr) +@ stdcall SetupDiGetDeviceInterfaceDetailW(long ptr ptr long ptr ptr) +@ stub SetupDiGetHwProfileFriendlyNameA +@ stub SetupDiGetHwProfileFriendlyNameW +@ stub SetupDiGetHwProfileList +@ stub SetupDiGetINFClassA +@ stub SetupDiGetINFClassW +@ stub SetupDiGetSelectedDevice +@ stub SetupDiGetSelectedDriverA +@ stub SetupDiGetSelectedDriverW +@ stub SetupDiGetWizardPage +@ stub SetupDiInstallClassA +@ stub SetupDiInstallClassW +@ stub SetupDiInstallDevice +@ stub SetupDiInstallDriverFiles +@ stub SetupDiLoadClassIcon +@ stub SetupDiMoveDuplicateDevice +@ stub SetupDiOpenClassRegKey +@ stdcall SetupDiOpenClassRegKeyExW(ptr long long wstr ptr) +@ stub SetupDiOpenDevRegKey +@ stub SetupDiOpenDeviceInfoA +@ stub SetupDiOpenDeviceInfoW +@ stub SetupDiOpenDeviceInterfaceRegKey +@ stub SetupDiRegisterDeviceInfo +@ stub SetupDiRemoveDevice +@ stub SetupDiSelectDevice +@ stub SetupDiSelectOEMDrv +@ stub SetupDiSetClassInstallParamsA +@ stub SetupDiSetClassInstallParamsW +@ stub SetupDiSetDeviceInstallParamsA +@ stub SetupDiSetDeviceInstallParamsW +@ stub SetupDiSetDeviceRegistryPropertyA +@ stub SetupDiSetDeviceRegistryPropertyW +@ stub SetupDiSetDriverInstallParamsA +@ stub SetupDiSetDriverInstallParamsW +@ stub SetupDiSetSelectedDevice +@ stub SetupDiSetSelectedDriverA +@ stub SetupDiSetSelectedDriverW +@ stub SetupDuplicateDiskSpaceListA +@ stub SetupDuplicateDiskSpaceListW +@ stdcall SetupFindFirstLineA(long str str ptr) +@ stdcall SetupFindFirstLineW(long wstr wstr ptr) +@ stdcall SetupFindNextLine(ptr ptr) +@ stdcall SetupFindNextMatchLineA(ptr str ptr) +@ stdcall SetupFindNextMatchLineW(ptr wstr ptr) +@ stub SetupFreeSourceListA +@ stub SetupFreeSourceListW +@ stdcall SetupGetBinaryField(ptr long ptr long ptr) +@ stdcall SetupGetFieldCount(ptr) +@ stub SetupGetFileCompressionInfoA +@ stub SetupGetFileCompressionInfoW +@ stdcall SetupGetFileQueueCount(long long ptr) +@ stdcall SetupGetFileQueueFlags(long ptr) +@ stub SetupGetInfFileListA +@ stub SetupGetInfFileListW +@ stub SetupGetInfInformationA +@ stub SetupGetInfInformationW +@ stdcall SetupGetIntField(ptr long ptr) +@ stdcall SetupGetLineByIndexA(long str long ptr) +@ stdcall SetupGetLineByIndexW(long wstr long ptr) +@ stdcall SetupGetLineCountA(long str) +@ stdcall SetupGetLineCountW(long wstr) +@ stdcall SetupGetLineTextA(ptr long str str ptr long ptr) +@ stdcall SetupGetLineTextW(ptr long wstr wstr ptr long ptr) +@ stdcall SetupGetMultiSzFieldA(ptr long ptr long ptr) +@ stdcall SetupGetMultiSzFieldW(ptr long ptr long ptr) +@ stub SetupGetSourceFileLocationA +@ stub SetupGetSourceFileLocationW +@ stub SetupGetSourceFileSizeA +@ stub SetupGetSourceFileSizeW +@ stub SetupGetSourceInfoA +@ stub SetupGetSourceInfoW +@ stdcall SetupGetStringFieldA(ptr long ptr long ptr) +@ stdcall SetupGetStringFieldW(ptr long ptr long ptr) +@ stub SetupGetTargetPathA +@ stub SetupGetTargetPathW +@ stdcall SetupInitDefaultQueueCallback(long) +@ stdcall SetupInitDefaultQueueCallbackEx(long long long long ptr) +@ stub SetupInitializeFileLogA +@ stub SetupInitializeFileLogW +@ stub SetupInstallFileA +@ stub SetupInstallFileExA +@ stub SetupInstallFileExW +@ stub SetupInstallFileW +@ stdcall SetupInstallFilesFromInfSectionA(long long long str str long) +@ stdcall SetupInstallFilesFromInfSectionW(long long long wstr wstr long) +@ stdcall SetupInstallFromInfSectionA(long long str long long str long ptr ptr long ptr) +@ stdcall SetupInstallFromInfSectionW(long long wstr long long wstr long ptr ptr long ptr) +@ stub SetupInstallServicesFromInfSectionA +@ stub SetupInstallServicesFromInfSectionW +@ stdcall SetupIterateCabinetA(str long ptr ptr) +@ stdcall SetupIterateCabinetW(wstr long ptr ptr) +@ stub SetupLogFileA +@ stub SetupLogFileW +@ stdcall SetupOpenAppendInfFileA(str long ptr) +@ stdcall SetupOpenAppendInfFileW(wstr long ptr) +@ stdcall SetupOpenFileQueue() +@ stdcall SetupOpenInfFileA(str str long ptr) +@ stdcall SetupOpenInfFileW(wstr wstr long ptr) +@ stub SetupOpenMasterInf +@ stub SetupPromptForDiskA +@ stub SetupPromptForDiskW +@ stub SetupPromptReboot +@ stub SetupQueryDrivesInDiskSpaceListA +@ stub SetupQueryDrivesInDiskSpaceListW +@ stub SetupQueryFileLogA +@ stub SetupQueryFileLogW +@ stub SetupQueryInfFileInformationA +@ stub SetupQueryInfFileInformationW +@ stub SetupQueryInfVersionInformationA +@ stub SetupQueryInfVersionInformationW +@ stub SetupQueryInfOriginalFileInformationW +@ stub SetupQuerySourceListA +@ stub SetupQuerySourceListW +@ stub SetupQuerySpaceRequiredOnDriveA +@ stub SetupQuerySpaceRequiredOnDriveW +@ stdcall SetupQueueCopyA(long str str str str str str str long) +@ stdcall SetupQueueCopyIndirectA(ptr) +@ stdcall SetupQueueCopyIndirectW(ptr) +@ stdcall SetupQueueCopySectionA(long str long long str long) +@ stdcall SetupQueueCopySectionW(long wstr long long wstr long) +@ stdcall SetupQueueCopyW(long wstr wstr wstr wstr wstr wstr wstr long) +@ stdcall SetupQueueDefaultCopyA(long long str str str long) +@ stdcall SetupQueueDefaultCopyW(long long wstr wstr wstr long) +@ stdcall SetupQueueDeleteA(long str str) +@ stdcall SetupQueueDeleteSectionA(long long long str) +@ stdcall SetupQueueDeleteSectionW(long long long wstr) +@ stdcall SetupQueueDeleteW(long wstr wstr) +@ stdcall SetupQueueRenameA(long str str str str) +@ stdcall SetupQueueRenameSectionA(long long long str) +@ stdcall SetupQueueRenameSectionW(long long long wstr) +@ stdcall SetupQueueRenameW(long wstr wstr wstr wstr) +@ stub SetupRemoveFileLogEntryA +@ stub SetupRemoveFileLogEntryW +@ stub SetupRemoveFromDiskSpaceListA +@ stub SetupRemoveFromDiskSpaceListW +@ stub SetupRemoveFromSourceListA +@ stub SetupRemoveFromSourceListW +@ stub SetupRemoveInstallSectionFromDiskSpaceListA +@ stub SetupRemoveInstallSectionFromDiskSpaceListW +@ stub SetupRemoveSectionFromDiskSpaceListA +@ stub SetupRemoveSectionFromDiskSpaceListW +@ stub SetupRenameErrorA +@ stub SetupRenameErrorW +@ stub SetupScanFileQueue +@ stdcall SetupScanFileQueueA(long long long ptr ptr ptr) +@ stdcall SetupScanFileQueueW(long long long ptr ptr ptr) +@ stdcall SetupSetDirectoryIdA(long long str) +@ stub SetupSetDirectoryIdExA +@ stub SetupSetDirectoryIdExW +@ stdcall SetupSetDirectoryIdW(long long wstr) +@ stdcall SetupSetFileQueueFlags(long long long) +@ stub SetupSetPlatformPathOverrideA +@ stub SetupSetPlatformPathOverrideW +@ stub SetupSetSourceListA +@ stub SetupSetSourceListW +@ stdcall SetupTermDefaultQueueCallback(ptr) +@ stub SetupTerminateFileLog +@ stub ShouldDeviceBeExcluded +@ stub StampFileSecurity +@ stub StringTableAddString +@ stub StringTableAddStringEx +@ stub StringTableDestroy +@ stub StringTableDuplicate +@ stub StringTableEnum +@ stub StringTableGetExtraData +@ stub StringTableInitialize +@ stub StringTableInitializeEx +@ stub StringTableLookUpString +@ stub StringTableLookUpStringEx +@ stub StringTableSetExtraData +@ stub StringTableStringFromId +@ stub StringTableTrim +@ stub TakeOwnershipOfFile +@ stub UnicodeToMultiByte +@ stub UnmapAndCloseFile +@ stub pSetupAddMiniIconToList +@ stub pSetupAddTagToGroupOrderListEntry +@ stub pSetupAppendStringToMultiSz +@ stub pSetupDirectoryIdToPath +@ stub pSetupGetField +@ stub pSetupGetOsLoaderDriveAndPath +@ stub pSetupGetVersionDatum +@ stub pSetupGuidFromString +@ stub pSetupIsGuidNull +@ stub pSetupMakeSurePathExists +@ stub pSetupStringFromGuid diff --git a/reactos/lib/setupapi/setupapi_private.h b/reactos/lib/setupapi/setupapi_private.h new file mode 100644 index 00000000000..443c3e4e73a --- /dev/null +++ b/reactos/lib/setupapi/setupapi_private.h @@ -0,0 +1,56 @@ +/* + * Copyright 2001 Andreas Mohr + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SETUPAPI_PRIVATE_H +#define __SETUPAPI_PRIVATE_H + +#define COPYFILEDLGORD 1000 +#define SOURCESTRORD 500 +#define DESTSTRORD 501 +#define PROGRESSORD 502 + + +#define REG_INSTALLEDFILES "System\\CurrentControlSet\\Control\\InstalledFiles" +#define REGPART_RENAME "\\Rename" +#define REG_VERSIONCONFLICT "Software\\Microsoft\\VersionConflictManager" + +/* string substitutions */ + +struct inf_file; +extern const WCHAR *DIRID_get_string( HINF hinf, int dirid ); +extern unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, + char *buffer, unsigned int size ); +extern unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, + WCHAR *buffer, unsigned int size ); +extern const WCHAR *PARSER_get_src_root( HINF hinf ); + +/* support for Ascii queue callback functions */ + +struct callback_WtoA_context +{ + void *orig_context; + PSP_FILE_CALLBACK_A orig_handler; +}; + +UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, UINT_PTR, UINT_PTR ); + +/* from msvcrt/sys/stat.h */ +#define _S_IWRITE 0x0080 +#define _S_IREAD 0x0100 + +#endif /* __SETUPAPI_PRIVATE_H */ diff --git a/reactos/lib/setupapi/setupcab.c b/reactos/lib/setupapi/setupcab.c new file mode 100644 index 00000000000..c13503a6a30 --- /dev/null +++ b/reactos/lib/setupapi/setupcab.c @@ -0,0 +1,686 @@ +/* + * Setupapi cabinet routines + * + * Copyright 2003 Gregory M. Turner + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Many useful traces are commented in code, uncomment them if you have + * trouble and run with --debugmsg +setupapi + * + */ + +#include +#include "string.h" +#include "stdlib.h" + +#include "wine/debug.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "setupapi.h" +#include "setupapi_private.h" +#include "fdi.h" +#include "wine/unicode.h" + +#include "fcntl.h" +#include "share.h" + +#include "wine/debug.h" + +static HINSTANCE CABINET_hInstance = 0; + +static HFDI (__cdecl *sc_FDICreate)(PFNALLOC, PFNFREE, PFNOPEN, + PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF); + +static BOOL (__cdecl *sc_FDICopy)(HFDI, char *, char *, int, + PFNFDINOTIFY, PFNFDIDECRYPT, void *); + +static BOOL (__cdecl *sc_FDIDestroy)(HFDI); + +#define SC_HSC_A_MAGIC 0xACABFEED +typedef struct { + UINT magic; + HFDI hfdi; + PSP_FILE_CALLBACK_A msghandler; + PVOID context; + CHAR most_recent_cabinet_name[MAX_PATH]; +} SC_HSC_A, *PSC_HSC_A; + +#define SC_HSC_W_MAGIC 0x0CABFEED +typedef struct { + UINT magic; + HFDI hfdi; + PSP_FILE_CALLBACK_W msghandler; + PVOID context; + WCHAR most_recent_cabinet_name[MAX_PATH]; +} SC_HSC_W, *PSC_HSC_W; + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +static BOOL LoadCABINETDll(void) +{ + if (!CABINET_hInstance) { + CABINET_hInstance = LoadLibraryA("cabinet.dll"); + if (CABINET_hInstance) { + sc_FDICreate = (void *)GetProcAddress(CABINET_hInstance, "FDICreate"); + sc_FDICopy = (void *)GetProcAddress(CABINET_hInstance, "FDICopy"); + sc_FDIDestroy = (void *)GetProcAddress(CABINET_hInstance, "FDIDestroy"); + return TRUE; + } else { + ERR("load cabinet dll failed.\n"); + return FALSE; + } + } else + return TRUE; +} + +static void UnloadCABINETDll(void) +{ + if (CABINET_hInstance) { + FreeLibrary(CABINET_hInstance); + CABINET_hInstance = 0; + } +} + +/* FDICreate callbacks */ + +static void *sc_cb_alloc(ULONG cb) +{ + return malloc(cb); +} + +static void sc_cb_free(void *pv) +{ + free(pv); +} + +static INT_PTR sc_cb_open(char *pszFile, int oflag, int pmode) +{ + DWORD creation = 0, sharing = 0; + int ioflag = 0; + INT_PTR ret = 0; + SECURITY_ATTRIBUTES sa; + + /* TRACE("(pszFile == %s, oflag == %d, pmode == %d)\n", debugstr_a(pszFile), oflag, pmode); */ + + switch(oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { + case _O_RDONLY: + ioflag |= GENERIC_READ; + break; + case _O_WRONLY: + ioflag |= GENERIC_WRITE; + break; + case _O_RDWR: + ioflag |= GENERIC_READ & GENERIC_WRITE; + break; + case _O_WRONLY | _O_RDWR: /* hmmm.. */ + ERR("_O_WRONLY & _O_RDWR in oflag?\n"); + return -1; + } + + if (oflag & _O_CREAT) { + if (oflag & _O_EXCL) + creation = CREATE_NEW; + else if (oflag & _O_TRUNC) + creation = CREATE_ALWAYS; + else + creation = OPEN_ALWAYS; + } else /* no _O_CREAT */ { + if (oflag & _O_TRUNC) + creation = TRUNCATE_EXISTING; + else + creation = OPEN_EXISTING; + } + + switch( pmode & 0x70 ) { + case _SH_DENYRW: + sharing = 0L; + break; + case _SH_DENYWR: + sharing = FILE_SHARE_READ; + break; + case _SH_DENYRD: + sharing = FILE_SHARE_WRITE; + break; + case _SH_COMPAT: + case _SH_DENYNO: + sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; + break; + default: + ERR("<-- -1 (Unhandled pmode 0x%x)\n", pmode); + return -1; + } + + if (oflag & ~(_O_BINARY | _O_TRUNC | _O_EXCL | _O_CREAT | _O_RDWR | _O_WRONLY | _O_NOINHERIT)) + WARN("unsupported oflag 0x%04x\n",oflag); + + sa.nLength = sizeof( SECURITY_ATTRIBUTES ); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = (ioflag & _O_NOINHERIT) ? FALSE : TRUE; + + ret = (INT_PTR) CreateFileA(pszFile, ioflag, sharing, &sa, creation, FILE_ATTRIBUTE_NORMAL, NULL); + + /* TRACE("<-- %d\n", ret); */ + + return ret; +} + +static UINT sc_cb_read(INT_PTR hf, void *pv, UINT cb) +{ + DWORD num_read; + BOOL rslt; + + /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */ + + rslt = ReadFile((HANDLE) hf, pv, cb, &num_read, NULL); + + + /* eof and failure both give "-1" return */ + if ((! rslt) || ((cb > 0) && (num_read == 0))) { + /* TRACE("<-- -1\n"); */ + return -1; + } + + /* TRACE("<-- %lu\n", num_read); */ + return num_read; +} + +static UINT sc_cb_write(INT_PTR hf, void *pv, UINT cb) +{ + DWORD num_written; + /* BOOL rv; */ + + /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */ + + if ( /* (rv = */ WriteFile((HANDLE) hf, pv, cb, &num_written, NULL) /* ) */ + && (num_written == cb)) { + /* TRACE("<-- %lu\n", num_written); */ + return num_written; + } else { + /* TRACE("rv == %d, num_written == %lu, cb == %u\n", rv, num_written,cb); */ + /* TRACE("<-- -1\n"); */ + return -1; + } +} + +static int sc_cb_close(INT_PTR hf) +{ + /* TRACE("(hf == %d)\n", hf); */ + + if (CloseHandle((HANDLE) hf)) + return 0; + else + return -1; +} + +static long sc_cb_lseek(INT_PTR hf, long dist, int seektype) +{ + DWORD ret; + + /* TRACE("(hf == %d, dist == %ld, seektype == %d)\n", hf, dist, seektype); */ + + if (seektype < 0 || seektype > 2) + return -1; + + if (((ret = SetFilePointer((HANDLE) hf, dist, NULL, seektype)) != INVALID_SET_FILE_POINTER) || !GetLastError()) { + /* TRACE("<-- %lu\n", ret); */ + return ret; + } else { + /* TRACE("<-- -1\n"); */ + return -1; + } +} + +#define SIZEOF_MYSTERIO (MAX_PATH*3) + +/* FDICopy callbacks */ + +static INT_PTR sc_FNNOTIFY_A(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + FILE_IN_CABINET_INFO_A fici; + PSC_HSC_A phsc; + CABINET_INFO_A ci; + FILEPATHS_A fp; + UINT err; + + CHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! probably 256... */ + + memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO); + + TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin); + + if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_A_MAGIC)) + phsc = (PSC_HSC_A) pfdin->pv; + else { + ERR("pv %p is not an SC_HSC_A.\n", (pfdin) ? pfdin->pv : NULL); + return -1; + } + + switch (fdint) { + case fdintCABINET_INFO: + TRACE("Cabinet info notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n"); + ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); + ci.CabinetPath = pfdin->psz3; + ci.DiskName = pfdin->psz2; + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0); + return 0; + case fdintPARTIAL_FILE: + TRACE("Partial file notification\n"); + /* TRACE(" Partial file name: %s\n", debugstr_a(pfdin->psz1)); */ + return 0; + case fdintCOPY_FILE: + TRACE("Copy file notification\n"); + TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + /* TRACE(" File size: %ld\n", pfdin->cb); + TRACE(" File date: %u\n", pfdin->date); + TRACE(" File time: %u\n", pfdin->time); + TRACE(" File attr: %u\n", pfdin->attribs); */ + fici.NameInCabinet = pfdin->psz1; + fici.FileSize = pfdin->cb; + fici.Win32Error = 0; + fici.DosDate = pfdin->date; + fici.DosTime = pfdin->time; + fici.DosAttribs = pfdin->attribs; + memset(&(fici.FullTargetName[0]), 0, MAX_PATH); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, + (UINT) &fici, (UINT) pfdin->psz1); + if (err == FILEOP_DOIT) { + TRACE(" Callback specified filename: %s\n", debugstr_a(&(fici.FullTargetName[0]))); + if (!fici.FullTargetName[0]) { + WARN(" Empty return string causing abort."); + SetLastError(ERROR_PATH_NOT_FOUND); + return -1; + } + return sc_cb_open(&(fici.FullTargetName[0]), _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); + } else { + TRACE(" Callback skipped file.\n"); + return 0; + } + case fdintCLOSE_FILE_INFO: + TRACE("Close file notification\n"); + /* TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Exec file? %s\n", (pfdin->cb) ? "Yes" : "No"); + TRACE(" File hndl: %d\n", pfdin->hf); */ + fp.Source = &(phsc->most_recent_cabinet_name[0]); + fp.Target = pfdin->psz1; + fp.Win32Error = 0; + fp.Flags = 0; + /* the following should be a fixme -- but it occurs too many times */ + WARN("Should set file date/time/attribs (and execute files?)\n"); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0); + if (sc_cb_close(pfdin->hf)) + WARN("_close failed.\n"); + if (err) { + SetLastError(err); + return FALSE; + } else + return TRUE; + case fdintNEXT_CABINET: + TRACE("Next cabinet notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + ci.CabinetFile = pfdin->psz1; + ci.CabinetPath = pfdin->psz3; + ci.DiskName = pfdin->psz2; + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + /* remember the new cabinet name */ + strcpy(&(phsc->most_recent_cabinet_name[0]), pfdin->psz1); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0])); + if (err) { + SetLastError(err); + return -1; + } else { + if (mysterio[0]) { + /* some easy paranoia. no such carefulness exists on the wide API IIRC */ + mysterio[SIZEOF_MYSTERIO - 1] = '\0'; + strncpy(pfdin->psz3, &(mysterio[0]), 255); + mysterio[255] = '\0'; + } + return 0; + } + default: + FIXME("Unknown notification type %d.\n", fdint); + return 0; + } +} + +static INT_PTR sc_FNNOTIFY_W(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + FILE_IN_CABINET_INFO_W fici; + PSC_HSC_W phsc; + CABINET_INFO_W ci; + FILEPATHS_W fp; + UINT err; + int len; + + WCHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! */ + WCHAR buf[MAX_PATH], buf2[MAX_PATH]; + CHAR charbuf[MAX_PATH]; + + memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO * sizeof(WCHAR)); + memset(&(buf[0]), 0, MAX_PATH * sizeof(WCHAR)); + memset(&(buf2[0]), 0, MAX_PATH * sizeof(WCHAR)); + memset(&(charbuf[0]), 0, MAX_PATH); + + TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin); + + if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_W_MAGIC)) + phsc = (PSC_HSC_W) pfdin->pv; + else { + ERR("pv %p is not an SC_HSC_W.\n", (pfdin) ? pfdin->pv : NULL); + return -1; + } + + switch (fdint) { + case fdintCABINET_INFO: + TRACE("Cabinet info notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n"); + ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf[0] = '\0'; + ci.CabinetPath = &(buf[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf2[0] = '\0'; + ci.DiskName = &(buf2[0]); + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0); + return 0; + case fdintPARTIAL_FILE: + TRACE("Partial file notification\n"); + /* TRACE(" Partial file name: %s\n", debugstr_a(pfdin->psz1)); */ + return 0; + case fdintCOPY_FILE: + TRACE("Copy file notification\n"); + TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + /* TRACE(" File size: %ld\n", pfdin->cb); + TRACE(" File date: %u\n", pfdin->date); + TRACE(" File time: %u\n", pfdin->time); + TRACE(" File attr: %u\n", pfdin->attribs); */ + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf2[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf2[0] = '\0'; + fici.NameInCabinet = &(buf2[0]); + fici.FileSize = pfdin->cb; + fici.Win32Error = 0; + fici.DosDate = pfdin->date; + fici.DosTime = pfdin->time; + fici.DosAttribs = pfdin->attribs; + memset(&(fici.FullTargetName[0]), 0, MAX_PATH * sizeof(WCHAR)); + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET, + (UINT) &fici, (UINT) pfdin->psz1); + if (err == FILEOP_DOIT) { + TRACE(" Callback specified filename: %s\n", debugstr_w(&(fici.FullTargetName[0]))); + if (fici.FullTargetName[0]) { + len = strlenW(&(fici.FullTargetName[0])) + 1; + if ((len > MAX_PATH ) || (len <= 1)) + return 0; + if (!WideCharToMultiByte(CP_ACP, 0, &(fici.FullTargetName[0]), len, &(charbuf[0]), MAX_PATH, 0, 0)) + return 0; + } else { + WARN("Empty buffer string caused abort.\n"); + SetLastError(ERROR_PATH_NOT_FOUND); + return -1; + } + return sc_cb_open(&(charbuf[0]), _O_BINARY | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE); + } else { + TRACE(" Callback skipped file.\n"); + return 0; + } + case fdintCLOSE_FILE_INFO: + TRACE("Close file notification\n"); + /* TRACE(" File name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Exec file? %s\n", (pfdin->cb) ? "Yes" : "No"); + TRACE(" File hndl: %d\n", pfdin->hf); */ + fp.Source = &(phsc->most_recent_cabinet_name[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf[0] = '\0'; + fp.Target = &(buf[0]); + fp.Win32Error = 0; + fp.Flags = 0; + /* a valid fixme -- but occurs too many times */ + /* FIXME("Should set file date/time/attribs (and execute files?)\n"); */ + err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0); + if (sc_cb_close(pfdin->hf)) + WARN("_close failed.\n"); + if (err) { + SetLastError(err); + return FALSE; + } else + return TRUE; + case fdintNEXT_CABINET: + TRACE("Next cabinet notification\n"); + /* TRACE(" Cabinet name: %s\n", debugstr_a(pfdin->psz1)); + TRACE(" Cabinet disk: %s\n", debugstr_a(pfdin->psz2)); + TRACE(" Cabinet path: %s\n", debugstr_a(pfdin->psz3)); + TRACE(" Cabinet Set#: %d\n", pfdin->setID); + TRACE(" Cabinet Cab#: %d\n", pfdin->iCabinet); */ + /* remember the new cabinet name */ + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(phsc->most_recent_cabinet_name[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + phsc->most_recent_cabinet_name[0] = '\0'; + ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf[0] = '\0'; + ci.CabinetPath = &(buf[0]); + len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH); + if ((len > MAX_PATH) || (len <= 1)) + buf2[0] = '\0'; + ci.DiskName = &(buf2[0]); + ci.SetId = pfdin->setID; + ci.CabinetNumber = pfdin->iCabinet; + err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0])); + if (err) { + SetLastError(err); + return -1; + } else { + if (mysterio[0]) { + len = strlenW(&(mysterio[0])) + 1; + if ((len > 255) || (len <= 1)) + return 0; + if (!WideCharToMultiByte(CP_ACP, 0, &(mysterio[0]), len, pfdin->psz3, 255, 0, 0)) + return 0; + } + return 0; + } + default: + FIXME("Unknown notification type %d.\n", fdint); + return 0; + } +} + +/*********************************************************************** + * SetupIterateCabinetA (SETUPAPI.@) + */ +BOOL WINAPI SetupIterateCabinetA(PCSTR CabinetFile, DWORD Reserved, + PSP_FILE_CALLBACK_A MsgHandler, PVOID Context) +{ + + SC_HSC_A my_hsc; + ERF erf; + CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *p; + DWORD fpnsize; + BOOL ret; + + + TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n", + debugstr_a(CabinetFile), Reserved, MsgHandler, Context); + + if (! LoadCABINETDll()) + return FALSE; + + memset(&my_hsc, 0, sizeof(SC_HSC_A)); + pszCabinet[0] = '\0'; + pszCabPath[0] = '\0'; + + fpnsize = strlen(CabinetFile); + if (fpnsize >= MAX_PATH) { + SetLastError(ERROR_BAD_PATHNAME); + return FALSE; + } + + fpnsize = GetFullPathNameA(CabinetFile, MAX_PATH, &(pszCabPath[0]), &p); + if (fpnsize > MAX_PATH) { + SetLastError(ERROR_BAD_PATHNAME); + return FALSE; + } + + if (p) { + strcpy(pszCabinet, p); + *p = '\0'; + } else { + strcpy(pszCabinet, CabinetFile); + pszCabPath[0] = '\0'; + } + + TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet)); + + /* remember the cabinet name */ + strcpy(&(my_hsc.most_recent_cabinet_name[0]), pszCabinet); + + my_hsc.magic = SC_HSC_A_MAGIC; + my_hsc.msghandler = MsgHandler; + my_hsc.context = Context; + my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, + sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf ); + + if (!my_hsc.hfdi) return FALSE; + + ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath, + 0, sc_FNNOTIFY_A, NULL, &my_hsc) ) ? TRUE : FALSE; + + sc_FDIDestroy(my_hsc.hfdi); + return ret; +} + +/*********************************************************************** + * SetupIterateCabinetW (SETUPAPI.@) + */ +BOOL WINAPI SetupIterateCabinetW(PCWSTR CabinetFile, DWORD Reserved, + PSP_FILE_CALLBACK_W MsgHandler, PVOID Context) +{ + CHAR CabinetFile_A[MAX_PATH]; + unsigned int len; + SC_HSC_W my_hsc; + ERF erf; + CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *p; + DWORD fpnsize; + BOOL ret; + + TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n", + debugstr_w(CabinetFile), Reserved, MsgHandler, Context); + + if (!LoadCABINETDll()) + return FALSE; + + if (!CabinetFile) return FALSE; + if (!WideCharToMultiByte(CP_ACP, 0, CabinetFile, -1, CabinetFile_A, MAX_PATH, 0, 0)) + return FALSE; + + memset(&my_hsc, 0, sizeof(SC_HSC_W)); + pszCabinet[0] = '\0'; + pszCabPath[0] = '\0'; + + fpnsize = GetFullPathNameA(CabinetFile_A, MAX_PATH, &(pszCabPath[0]), &p); + if (fpnsize > MAX_PATH) { + SetLastError(ERROR_BAD_PATHNAME); + return FALSE; + } + + if (p) { + strcpy(pszCabinet, p); + *p = '\0'; + } else { + strcpy(pszCabinet, CabinetFile_A); + pszCabPath[0] = '\0'; + } + + TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet)); + + /* remember the cabinet name */ + len = 1 + MultiByteToWideChar(CP_ACP, 0, pszCabinet, -1, + &(my_hsc.most_recent_cabinet_name[0]), MAX_PATH); + if (len > MAX_PATH) + return FALSE; + else if (len <= 1) + my_hsc.most_recent_cabinet_name[0] = '\0'; + my_hsc.magic = SC_HSC_W_MAGIC; + my_hsc.msghandler = MsgHandler; + my_hsc.context = Context; + my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read, + sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf ); + + if (!my_hsc.hfdi) return FALSE; + + ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath, + 0, sc_FNNOTIFY_W, NULL, &my_hsc) ) ? TRUE : FALSE; + + sc_FDIDestroy(my_hsc.hfdi); + return ret; +} + + +/*********************************************************************** + * DllMain + * + * PARAMS + * hinstDLL [I] handle to the DLL's instance + * fdwReason [I] + * lpvReserved [I] reserved, must be NULL + * + * RETURNS + * Success: TRUE + * Failure: FALSE + */ + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + break; + case DLL_PROCESS_DETACH: + UnloadCABINETDll(); + break; + } + + return TRUE; +} diff --git a/reactos/lib/setupapi/setupx16.h b/reactos/lib/setupapi/setupx16.h new file mode 100644 index 00000000000..a7382127a00 --- /dev/null +++ b/reactos/lib/setupapi/setupx16.h @@ -0,0 +1,562 @@ +/* + * Copyright 2000 Andreas Mohr for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SETUPX16_H +#define __SETUPX16_H + +#include "wine/windef16.h" + +typedef UINT16 HINF16; +typedef UINT16 LOGDISKID16; +typedef UINT16 VHSTR; + +#define LINE_LEN 256 + +/* error codes stuff */ + +typedef UINT16 RETERR16; +#define OK 0 +#define IP_ERROR (UINT16)100 +#define TP_ERROR (UINT16)200 +#define VCP_ERROR (UINT16)300 +#define GEN_ERROR (UINT16)400 +#define DI_ERROR (UINT16)500 + +enum { + ERR_IP_INVALID_FILENAME = IP_ERROR+1, + ERR_IP_ALLOC_ERR, + ERR_IP_INVALID_SECT_NAME, + ERR_IP_OUT_OF_HANDLES, + ERR_IP_INF_NOT_FOUND, + ERR_IP_INVALID_INFFILE, + ERR_IP_INVALID_HINF, + ERR_IP_INVALID_FIELD, + ERR_IP_SECT_NOT_FOUND, + ERR_IP_END_OF_SECTION, + ERR_IP_PROFILE_NOT_FOUND, + ERR_IP_LINE_NOT_FOUND, + ERR_IP_FILEREAD, + ERR_IP_TOOMANYINFFILES, + ERR_IP_INVALID_SAVERESTORE, + ERR_IP_INVALID_INFTYPE +}; + +/****** virtual copy operations ******/ + +typedef DWORD LPEXPANDVTBL; + +typedef struct { + DWORD dwSoFar; + DWORD dwTotal; +} VCPPROGRESS, *LPVCPPROGRESS; + +typedef struct { + WORD cbSize; + LOGDISKID16 ldid; + VHSTR vhstrRoot; + VHSTR vhstrVolumeLabel; + VHSTR vhstrDiskName; + WORD wVolumeTime; + WORD wVolumeDate; + DWORD dwSerialNumber; + WORD fl; + LPARAM lparamRef; + + VCPPROGRESS prgFileRead; + VCPPROGRESS prgByteRead; + + VCPPROGRESS prgFileWrite; + VCPPROGRESS prgByteWrite; +} VCPDISKINFO, *LPVCPDISKINFO; + +typedef struct { + LOGDISKID16 ldid; + VHSTR vhstrDir; + VHSTR vhstrFileName; +} VCPFILESPEC, *LPVCPFILESPEC; + +typedef struct { + UINT16 uiMDate; + UINT16 uiMTime; + UINT16 uiADate; + UINT16 uiATime; + UINT16 uiAttr; + DWORD llenIn; + DWORD llenOut; +} VCPFATTR, *LPVCPFATTR; + +typedef struct { + UINT16 uDate; + UINT16 uTime; + DWORD dwSize; +} VCPFILESTAT, *LPVCPFILESTAT; + +typedef struct +{ + HFILE16 hFileSrc; + HFILE16 hFileDst; + VCPFATTR fAttr; + WORD dosError; + VHSTR vhstrFileName; + WPARAM vcpm; +} VIRTNODEEX, *LPVIRTNODEEX; + +typedef struct { + WORD cbSize; + VCPFILESPEC vfsSrc; + VCPFILESPEC vfsDst; + WORD fl; + LPARAM lParam; + LPEXPANDVTBL lpExpandVtbl; + LPVIRTNODEEX lpvnex; + VHSTR vhstrDstFinalName; + VCPFILESTAT vFileStat; +} VIRTNODE, *LPVIRTNODE; + +typedef struct { + WORD cbSize; + VCPPROGRESS prgDiskRead; + VCPPROGRESS prgFileRead; + VCPPROGRESS prgByteRead; + + VCPPROGRESS prgDiskWrite; + VCPPROGRESS prgFileWrite; + VCPPROGRESS prgByteWrite; + + LPVCPDISKINFO lpvdiIn; + LPVCPDISKINFO lpvdiOut; + LPVIRTNODE lpvn; +} VCPSTATUS, *LPVCPSTATUS; + +#define CNFL_BACKUP 0x0001 +#define CNFL_DELETEONFAILURE 0x0002 +#define CNFL_RENAMEONSUCCESS 0x0004 +#define CNFL_CONTINUATION 0x0008 +#define CNFL_SKIPPED 0x0010 +#define CNFL_IGNOREERRORS 0x0020 +#define CNFL_RETRYFILE 0x0040 +#define CNFL_COPIED 0x0080 +#define VNFL_UNIQUE 0x0000 +#define VNFL_MULTIPLEOK 0x0100 +#define VNFL_DESTROYOLD 0x0200 +#define VNFL_COPY 0x0000 +#define VNFL_DELETE 0x0800 +#define VNFL_RENAME 0x1000 +#define VNFL_NODE_TYPE (VNFL_RENAME|VNFL_DELETE|VNFL_COPY) +#define VNFL_CREATED 0x2000 +#define VNFL_REJECTED 0x4000 +#define VNFL_DEVICEINSTALLER 0x8000 + +enum { + ERR_VCP_IOFAIL = VCP_ERROR+1, + ERR_VCP_STRINGTOOLONG, + ERR_VCP_NOMEM, + ERR_VCP_QUEUEFULL, + ERR_VCP_NOVHSTR, + ERR_VCP_OVERFLOW, + ERR_VCP_BADARG, + ERR_VCP_UNINIT, + ERR_VCP_NOTFOUND, + ERR_VCP_BUSY, + ERR_VCP_INTERRUPTED, + ERR_VCP_BADDEST, + ERR_VCP_SKIPPED, + ERR_VCP_IO, + ERR_VCP_LOCKED, + ERR_VCP_WRONGDISK, + ERR_VCP_CHANGEMODE, + ERR_VCP_LDDINVALID, + ERR_VCP_LDDFIND, + ERR_VCP_LDDUNINIT, + ERR_VCP_LDDPATH_INVALID, + ERR_VCP_NOEXPANSION, + ERR_VCP_NOTOPEN, + ERR_VCP_NO_DIGITAL_SIGNATURE_CATALOG, + ERR_VCP_NO_DIGITAL_SIGNATURE_FILE +}; + +#define VCPN_OK 0 +#define VCPN_PROCEED 0 +#define VCPN_ABORT -1 +#define VCPN_RETRY -2 +#define VCPN_IGNORE -3 +#define VCPN_SKIP -4 +#define VCPN_FORCE -5 +#define VCPN_DEFER -6 +#define VCPN_FAIL -7 +#define VCPN_RETRYFILE -8 + +#define VCPFL_ABANDON 0x00 +#define VCPFL_BACKUP 0x01 +#define VCPFL_COPY 0x02 +#define VCPFL_BACKUPANDCOPY (VCPFL_BACKUP|VCPFL_COPY) +#define VCPFL_INSPECIFIEDORDER 0x04 +#define VCPFL_DELETE 0x08 +#define VCPFL_RENAME 0x10 +#define VCPFL_ALL (VCPFL_COPY|VCPFL_DELETE|VCPFL_RENAME) + +#define CFNL_BACKUP 0x0001 +#define CFNL_DELETEONFAILURE 0x0002 +#define CFNL_RENAMEONSUCCESS 0x0004 +#define CFNL_CONTINUATION 0x0008 +#define CFNL_SKIPPED 0x0010 +#define CFNL_IGNOREERRORS 0x0020 +#define CFNL_RETRYFILE 0x0040 +#define CFNL_COPIED 0x0080 +#define VFNL_MULTIPLEOK 0x0100 +#define VFNL_DESTROYOLD 0x0200 +#define VFNL_NOW 0x0400 +#define VFNL_COPY 0x0000 +#define VFNL_DELETE 0x0800 +#define VFNL_RENAME 0x1000 +#define VFNL_CREATED 0x2000 +#define VFNL_REJECTED 0x4000 +#define VCPM_DISKCLASS 0x01 +#define VCPM_DISKFIRST 0x0100 +#define VCPM_DISKLAST 0x01ff + +enum { + VCPM_DISKCREATEINFO = VCPM_DISKFIRST, + VCPM_DISKGETINFO, + VCPM_DISKDESTROYINFO, + VCPM_DISKPREPINFO, + VCPM_DISKENSURE, + VCPM_DISKPROMPT, + VCPM_DISKFORMATBEGIN, + VCPM_DISKFORMATTING, + VCPM_DISKFORMATEND +}; + +#define VCPM_FILEINCLASS 0x02 +#define VCPM_FILEOUTCLASS 0x03 +#define VCPM_FILEFIRSTIN 0x0200 +#define VCPM_FILEFIRSTOUT 0x0300 +#define VCPM_FILELAST 0x03ff + +enum { + VCPM_FILEOPENIN = VCPM_FILEFIRSTIN, + VCPM_FILEGETFATTR, + VCPM_FILECLOSEIN, + VCPM_FILECOPY, + VCPM_FILENEEDED, + + VCPM_FILEOPENOUT = VCPM_FILEFIRSTOUT, + VCPM_FILESETFATTR, + VCPM_FILECLOSEOUT, + VCPM_FILEFINALIZE, + VCPM_FILEDELETE, + VCPM_FILERENAME +}; + +#define VCPM_NODECLASS 0x04 +#define VCPM_NODEFIRST 0x0400 +#define VCPM_NODELAST 0x04ff + +enum { + VCPM_NODECREATE = VCPM_NODEFIRST, + VCPM_NODEACCEPT, + VCPM_NODEREJECT, + VCPM_NODEDESTROY, + VCPM_NODECHANGEDESTDIR, + VCPM_NODECOMPARE +}; + +#define VCPM_TALLYCLASS 0x05 +#define VCPM_TALLYFIRST 0x0500 +#define VCPM_TALLYLAST 0x05ff + +enum { + VCPM_TALLYSTART = VCPM_TALLYFIRST, + VCPM_TALLYEND, + VCPM_TALLYFILE, + VCPM_TALLYDISK +}; + +#define VCPM_VERCLASS 0x06 +#define VCPM_VERFIRST 0x0600 +#define VCPM_VERLAST 0x06ff + +enum { + VCPM_VERCHECK = VCPM_VERFIRST, + VCPM_VERCHECKDONE, + VCPM_VERRESOLVECONFLICT +}; + +#define VCPM_VSTATCLASS 0x07 +#define VCPM_VSTATFIRST 0x0700 +#define VCPM_VSTATLAST 0x07ff + +enum { + VCPM_VSTATSTART = VCPM_VSTATFIRST, + VCPM_VSTATEND, + VCPM_VSTATREAD, + VCPM_VSTATWRITE, + VCPM_VSTATNEWDISK, + VCPM_VSTATCLOSESTART, + VCPM_VSTATCLOSEEND, + VCPM_VSTATBACKUPSTART, + VCPM_VSTATBACKUPEND, + VCPM_VSTATRENAMESTART, + VCPM_VSTATRENAMEEND, + VCPM_VSTATCOPYSTART, + VCPM_VSTATCOPYEND, + VCPM_VSTATDELETESTART, + VCPM_VSTATDELETEEND, + VCPM_VSTATPATHCHECKSTART, + VCPM_VSTATPATHCHECKEND, + VCPM_VSTATCERTIFYSTART, + VCPM_VSTATCERTIFYEND, + VCPM_VSTATUSERABORT, + VCPM_VSTATYIELD +}; + +#define VCPM_PATHCLASS 0x08 +#define VCPM_PATHFIRST 0x0800 +#define VCPM_PATHLAST 0x08ff + +enum { + VCPM_BUILDPATH = VCPM_PATHFIRST, + VCPM_UNIQUEPATH, + VCPM_CHECKPATH +}; + +#define VCPM_PATCHCLASS 0x09 +#define VCPM_PATCHFIRST 0x0900 +#define VCPM_PATCHLAST 0x09ff + +enum { + VCPM_FILEPATCHBEFORECPY = VCPM_PATCHFIRST, + VCPM_FILEPATCHAFTERCPY, + VCPM_FILEPATCHINFOPEN, + VCPM_FILEPATCHINFCLOSE +}; + +#define VCPM_CERTCLASS 0x0a +#define VCPM_CERTFIRST 0x0a00 +#define VCPM_CERTLAST 0x0aff + +enum { + VCPM_FILECERTIFY = VCPM_CERTFIRST, + VCPM_FILECERTIFYWARN +}; + +typedef LRESULT (CALLBACK *VIFPROC)(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, LPARAM lParam, LPARAM lparamRef); + +typedef int (CALLBACK *VCPENUMPROC)(LPVIRTNODE lpvn, LPARAM lparamRef); + +RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef); + +/* VcpQueueCopy flags */ +#define VNLP_SYSCRITICAL 0x0001 +#define VNLP_SETUPCRITICAL 0x0002 +#define VNLP_NOVERCHECK 0x0004 +#define VNLP_FORCETEMP 0x0008 +#define VNLP_IFEXISTS 0x0010 +#define VNLP_KEEPNEWER 0x0020 +#define VNLP_PATCHIFEXIST 0x0040 +#define VNLP_NOPATCH 0x0080 +#define VNLP_CATALOGCERT 0x0100 +#define VNLP_NEEDCERTIFY 0x0200 +#define VNLP_COPYIFEXISTS 0x0400 + +RETERR16 WINAPI VcpQueueCopy16( + LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, + LPCSTR lpszSrcDir, LPCSTR lpszDstDir, + LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, + LPEXPANDVTBL lpExpandVtbl, + WORD fl, LPARAM lParam +); +RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest); +RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest); + +/* VcpExplain flags */ +enum { + VCPEX_SRC_DISK, + VCPEX_SRC_CABINET, + VCPEX_SRC_LOCN, + VCPEX_DST_LOCN, + VCPEX_SRC_FILE, + VCPEX_DST_FILE, + VCPEX_DST_FILE_FINAL, + VCPEX_DOS_ERROR, + VCPEX_MESSAGE, + VCPEX_DOS_SOLUTION, + VCPEX_SRC_FULL, + VCPEX_DST_FULL, + VCPEX_DST_FULL_FINAL +}; + +LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat); + +/****** logical disk management ******/ + +typedef struct _LOGDISKDESC_S { /* ldd */ + WORD cbSize; /* struct size */ + LOGDISKID16 ldid; /* logical disk ID */ + LPSTR pszPath; /* path this descriptor points to */ + LPSTR pszVolLabel; /* volume label of the disk related to it */ + LPSTR pszDiskName; /* name of this disk */ + WORD wVolTime; /* modification time of volume label */ + WORD wVolDate; /* modification date */ + DWORD dwSerNum; /* serial number of disk */ + WORD wFlags; +} LOGDISKDESC_S, *LPLOGDISKDESC; + +/** logical disk identifiers (LDID) **/ + +/* predefined LDIDs */ +#define LDID_PREDEF_START 0x0001 +#define LDID_PREDEF_END 0x7fff + +/* registry-assigned LDIDs */ +#define LDID_VAR_START 0x7000 +#define LDID_VAR_END 0x7fff + +/* dynamically assigned LDIDs */ +#define LDID_ASSIGN_START 0x8000 +#define LDID_ASSIGN_END 0xbfff + +#define LDID_NULL 0 +#define LDID_ABSOLUTE ((UINT)-1) +#define LDID_SRCPATH 1 /* setup source path */ +#define LDID_SETUPTEMP 2 /* setup temp dir */ +#define LDID_UNINSTALL 3 /* uninstall dir */ +#define LDID_BACKUP 4 /* backup dir */ +#define LDID_SETUPSCRATCH 5 /* setup scratch dir */ +#define LDID_WIN 10 /* win dir */ +#define LDID_SYS 11 /* win system dir */ +#define LDID_IOS 12 /* win Iosubsys dir */ +#define LDID_CMD 13 /* win command dir */ +#define LDID_CPL 14 /* win control panel dir */ +#define LDID_PRINT 15 /* win printer dir */ +#define LDID_MAIL 16 /* win mail dir */ +#define LDID_INF 17 /* win inf dir */ +#define LDID_HELP 18 /* win help dir */ +#define LDID_WINADMIN 19 /* admin dir */ +#define LDID_FONTS 20 /* win fonts dir */ +#define LDID_VIEWERS 21 /* win viewers dir */ +#define LDID_VMM32 22 /* win VMM32 dir */ +#define LDID_COLOR 23 /* win color mngment dir */ +#define LDID_APPS 24 /* win apps dir */ +#define LDID_SHARED 25 /* win shared dir */ +#define LDID_WINBOOT 26 /* guaranteed win boot drive */ +#define LDID_MACHINE 27 /* machine specific files */ +#define LDID_HOST_WINBOOT 28 +#define LDID_BOOT 30 /* boot drive root dir */ +#define LDID_BOOT_HOST 31 /* boot drive host root dir */ +#define LDID_OLD_WINBOOT 32 /* root subdir */ +#define LDID_OLD_WIN 33 /* old windows dir */ + +/* flags for GenInstall() */ +#define GENINSTALL_DO_FILES 1 +#define GENINSTALL_DO_INI 2 +#define GENINSTALL_DO_REG 4 +#define GENINSTALL_DO_INI2REG 8 +#define GENINSTALL_DO_CFGAUTO 16 +#define GENINSTALL_DO_LOGCONFIG 32 +#define GENINSTALL_DO_REGSRCPATH 64 +#define GENINSTALL_DO_PERUSER 128 + +#define GEINISTALL_DO_INIREG 14 +#define GENINSTALL_DO_ALL 255 + +/* + * flags for InstallHinfSection() + * 128 can be added, too. This means that the .inf file is provided by you + * instead of being a 32 bit file (i.e. Windows .inf file). + * In this case all files you install must be in the same dir + * as your .inf file on the install disk. + */ +#define HOW_NEVER_REBOOT 0 +#define HOW_ALWAYS_SILENT_REBOOT 1 +#define HOW_ALWAYS_PROMPT_REBOOT 2 +#define HOW_SILENT_REBOOT 3 +#define HOW_PROMPT_REBOOT 4 + +/****** device installation stuff ******/ + +#define MAX_CLASS_NAME_LEN 32 +#define MAX_DEVNODE_ID_LEN 256 +#define MAX_GUID_STR 50 + +typedef struct _DEVICE_INFO +{ + UINT16 cbSize; + struct _DEVICE_INFO *lpNextDi; + char szDescription[LINE_LEN]; + DWORD dnDevnode; + HKEY hRegKey; + char szRegSubkey[MAX_DEVNODE_ID_LEN]; + char szClassName[MAX_CLASS_NAME_LEN]; + DWORD Flags; + HWND16 hwndParent; + /*LPDRIVER_NODE*/ LPVOID lpCompatDrvList; + /*LPDRIVER_NODE*/ LPVOID lpClassDrvList; + /*LPDRIVER_NODE*/ LPVOID lpSelectedDriver; + ATOM atDriverPath; + ATOM atTempInfFile; + HINSTANCE16 hinstClassInstaller; + HINSTANCE16 hinstClassPropProvidor; + HINSTANCE16 hinstDevicePropProvidor; + HINSTANCE16 hinstBasicPropProvidor; + FARPROC16 fpClassInstaller; + FARPROC16 fpClassEnumPropPages; + FARPROC16 fpDeviceEnumPropPages; + FARPROC16 fpEnumBasicProperties; + DWORD dwSetupReserved; + DWORD dwClassInstallReserved; + /*GENCALLBACKPROC*/ LPVOID gicpGenInstallCallBack; + + LPARAM gicplParam; + UINT16 InfType; + + HINSTANCE16 hinstPrivateProblemHandler; + FARPROC16 fpPrivateProblemHandler; + LPARAM lpClassInstallParams; + struct _DEVICE_INFO *lpdiChildList; + DWORD dwFlagsEx; + /*LPDRIVER_INFO*/ LPVOID lpCompatDrvInfoList; + /*LPDRIVER_INFO*/ LPVOID lpClassDrvInfoList; + char szClassGUID[MAX_GUID_STR]; +} DEVICE_INFO16, *LPDEVICE_INFO16, **LPLPDEVICE_INFO16; + + +extern void WINAPI GenFormStrWithoutPlaceHolders16(LPSTR,LPCSTR,HINF16); +extern RETERR16 WINAPI IpOpen16(LPCSTR,HINF16 *); +extern RETERR16 WINAPI IpClose16(HINF16); +extern RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC); +extern RETERR16 WINAPI CtlDelLdd16(LOGDISKID16); +extern RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath); +extern RETERR16 WINAPI GenInstall16(HINF16,LPCSTR,WORD); + +typedef struct tagLDD_LIST { + LPLOGDISKDESC pldd; + struct tagLDD_LIST *next; +} LDD_LIST; + +#define INIT_LDD(ldd, LDID) \ + do { \ + memset(&(ldd), 0, sizeof(LOGDISKDESC_S)); \ + (ldd).cbSize = sizeof(LOGDISKDESC_S); \ + ldd.ldid = LDID; \ + } while(0) + +#endif /* __SETUPX16_H */ diff --git a/reactos/lib/setupapi/setupx_main.c b/reactos/lib/setupapi/setupx_main.c new file mode 100644 index 00000000000..4f5b9525c3b --- /dev/null +++ b/reactos/lib/setupapi/setupx_main.c @@ -0,0 +1,736 @@ +/* + * SETUPX library + * + * Copyright 1998,2000 Andreas Mohr + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FIXME: Rather non-functional functions for now. + * + * See: + * http://www.geocities.com/SiliconValley/Network/5317/drivers.html + * http://willemer.de/informatik/windows/inf_info.htm (German) + * http://www.microsoft.com/ddk/ddkdocs/win98ddk/devinst_12uw.htm + * DDK: setupx.h + * http://mmatrix.tripod.com/customsystemfolder/infsysntaxfull.html + * http://www.rdrop.com/~cary/html/inf_faq.html + * http://support.microsoft.com/support/kb/articles/q194/6/40.asp + * + * Stuff tested with: + * - rs405deu.exe (German Acroread 4.05 setup) + * - ie5setup.exe + * - Netmeeting + * + * FIXME: + * - string handling is... weird ;) (buflen etc.) + * - memory leaks ? + * - separate that mess (but probably only when it's done completely) + * + * SETUPX consists of several parts with the following acronyms/prefixes: + * Di device installer (devinst.c ?) + * Gen generic installer (geninst.c ?) + * Ip .INF parsing (infparse.c) + * LDD logical device descriptor (ldd.c ?) + * LDID logical device ID + * SU setup (setup.c ?) + * Tp text processing (textproc.c ?) + * Vcp virtual copy module (vcp.c ?) + * ... + * + * The SETUPX DLL is NOT thread-safe. That's why many installers urge you to + * "close all open applications". + * All in all the design of it seems to be a bit weak. + * Not sure whether my implementation of it is better, though ;-) + */ + +#include +#include +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winerror.h" +#include "wine/winuser16.h" +#include "wownt32.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupx16.h" +#include "setupapi_private.h" +#include "winerror.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * SURegOpenKey (SETUPX.47) + */ +DWORD WINAPI SURegOpenKey( HKEY hkey, LPCSTR lpszSubKey, PHKEY retkey ) +{ + FIXME("(%p,%s,%p), semi-stub.\n",hkey,debugstr_a(lpszSubKey),retkey); + return RegOpenKeyA( hkey, lpszSubKey, retkey ); +} + +/*********************************************************************** + * SURegQueryValueEx (SETUPX.50) + */ +DWORD WINAPI SURegQueryValueEx( HKEY hkey, LPSTR lpszValueName, + LPDWORD lpdwReserved, LPDWORD lpdwType, + LPBYTE lpbData, LPDWORD lpcbData ) +{ + FIXME("(%p,%s,%p,%p,%p,%ld), semi-stub.\n",hkey,debugstr_a(lpszValueName), + lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0); + return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType, + lpbData, lpcbData ); +} + +/* + * Returns pointer to a string list with the first entry being number + * of strings. + * + * Hmm. Should this be InitSubstrData(), GetFirstSubstr() and GetNextSubstr() + * instead? + */ +static LPSTR *SETUPX_GetSubStrings(LPSTR start, char delimiter) +{ + LPSTR p, q; + LPSTR *res = NULL; + DWORD count = 0; + int len; + + p = start; + + while (1) + { + /* find beginning of real substring */ + while ( (*p == ' ') || (*p == '\t') || (*p == '"') ) p++; + + /* find end of real substring */ + q = p; + while ( (*q) + && (*q != ' ') && (*q != '\t') && (*q != '"') + && (*q != ';') && (*q != delimiter) ) q++; + if (q == p) + break; + len = (int)q - (int)p; + + /* alloc entry for new substring in steps of 32 units and copy over */ + if (count % 32 == 0) + { /* 1 for count field + current count + 32 */ + if (res) + res = HeapReAlloc(GetProcessHeap(), 0, res, (1+count+32)*sizeof(LPSTR)); + else + res = HeapAlloc(GetProcessHeap(), 0, (1+count+32)*sizeof(LPSTR)); + } + *(res+1+count) = HeapAlloc(GetProcessHeap(), 0, len+1); + strncpy(*(res+1+count), p, len); + (*(res+1+count))[len] = '\0'; + count++; + + /* we are still within last substring (before delimiter), + * so get out of it */ + while ((*q) && (*q != ';') && (*q != delimiter)) q++; + if ((!*q) || (*q == ';')) + break; + p = q+1; + } + + /* put number of entries at beginning of list */ + *(DWORD *)res = count; + return res; +} + +static void SETUPX_FreeSubStrings(LPSTR *substr) +{ + DWORD count = *(DWORD *)substr; + LPSTR *pStrings = substr+1; + DWORD n; + + for (n=0; n < count; n++) + HeapFree(GetProcessHeap(), 0, *pStrings++); + + HeapFree(GetProcessHeap(), 0, substr); +} + + +/*********************************************************************** + * InstallHinfSection (SETUPX.527) + * + * hwnd = parent window + * hinst = instance of SETUPX.DLL + * lpszCmdLine = e.g. "DefaultInstall 132 C:\MYINSTALL\MYDEV.INF" + * Here "DefaultInstall" is the .inf file section to be installed (optional). + * The 132 value is made of the HOW_xxx flags and sometimes 128 (-> setupx16.h). + * + * nCmdShow = nCmdShow of CreateProcess + */ +RETERR16 WINAPI InstallHinfSection16( HWND16 hwnd, HINSTANCE16 hinst, LPCSTR lpszCmdLine, INT16 nCmdShow) +{ + LPSTR *pSub; + DWORD count; + HINF16 hInf = 0; + RETERR16 res = OK, tmp; + WORD wFlags; + BOOL reboot = FALSE; + + TRACE("(%04x, %04x, %s, %d);\n", hwnd, hinst, lpszCmdLine, nCmdShow); + + pSub = SETUPX_GetSubStrings((LPSTR)lpszCmdLine, ' '); + + count = *(DWORD *)pSub; + if (count < 2) /* invalid number of arguments ? */ + goto end; + if (IpOpen16(*(pSub+count), &hInf) != OK) + { + res = ERROR_FILE_NOT_FOUND; /* yes, correct */ + goto end; + } + if (VcpOpen16(NULL, 0)) + goto end; + if (GenInstall16(hInf, *(pSub+count-2), GENINSTALL_DO_ALL) != OK) + goto end; + wFlags = atoi(*(pSub+count-1)) & ~128; + switch (wFlags) + { + case HOW_ALWAYS_SILENT_REBOOT: + case HOW_SILENT_REBOOT: + reboot = TRUE; + break; + case HOW_ALWAYS_PROMPT_REBOOT: + case HOW_PROMPT_REBOOT: + if (MessageBoxA(HWND_32(hwnd), "You must restart Wine before the new settings will take effect.\n\nDo you want to exit Wine now ?", "Systems Settings Change", MB_YESNO|MB_ICONQUESTION) == IDYES) + reboot = TRUE; + break; + default: + ERR("invalid flags %d !\n", wFlags); + goto end; + } + + res = OK; +end: + tmp = VcpClose16(VCPFL_ALL, NULL); + if (tmp != OK) + res = tmp; + tmp = IpClose16(hInf); + if (tmp != OK) + res = tmp; + SETUPX_FreeSubStrings(pSub); + if (reboot) + { + /* FIXME: we should have a means of terminating all wine + wineserver */ + MESSAGE("Program or user told me to restart. Exiting Wine...\n"); + ExitProcess(1); + } + + return res; +} + +typedef struct +{ + LPCSTR RegValName; + LPCSTR StdString; /* fallback string; sub dir of windows directory */ +} LDID_DATA; + +static const LDID_DATA LDID_Data[34] = +{ + { /* 0 (LDID_NULL) -- not defined */ + NULL, + NULL + }, + { /* 1 (LDID_SRCPATH) = source of installation. hmm, what to do here ? */ + "SourcePath", /* hmm, does SETUPX have to care about updating it ?? */ + NULL + }, + { /* 2 (LDID_SETUPTEMP) = setup temp dir */ + "SetupTempDir", + NULL + }, + { /* 3 (LDID_UNINSTALL) = uninstall backup dir */ + "UninstallDir", + NULL + }, + { /* 4 (LDID_BACKUP) = backup dir */ + "BackupDir", + NULL + }, + { /* 5 (LDID_SETUPSCRATCH) = setup scratch dir */ + "SetupScratchDir", + NULL + }, + { /* 6 -- not defined */ + NULL, + NULL + }, + { /* 7 -- not defined */ + NULL, + NULL + }, + { /* 8 -- not defined */ + NULL, + NULL + }, + { /* 9 -- not defined */ + NULL, + NULL + }, + { /* 10 (LDID_WIN) = windows dir */ + "WinDir", + "" + }, + { /* 11 (LDID_SYS) = system dir */ + "SysDir", + NULL /* call GetSystemDirectory() instead */ + }, + { /* 12 (LDID_IOS) = IOSubSys dir */ + NULL, /* FIXME: registry string ? */ + "SYSTEM\\IOSUBSYS" + }, + { /* 13 (LDID_CMD) = COMMAND dir */ + NULL, /* FIXME: registry string ? */ + "COMMAND" + }, + { /* 14 (LDID_CPL) = control panel dir */ + NULL, + "" + }, + { /* 15 (LDID_PRINT) = windows printer dir */ + NULL, + "SYSTEM" /* correct ?? */ + }, + { /* 16 (LDID_MAIL) = destination mail dir */ + NULL, + "" + }, + { /* 17 (LDID_INF) = INF dir */ + "SetupScratchDir", /* correct ? */ + "INF" + }, + { /* 18 (LDID_HELP) = HELP dir */ + NULL, /* ??? */ + "HELP" + }, + { /* 19 (LDID_WINADMIN) = Admin dir */ + "WinAdminDir", + "" + }, + { /* 20 (LDID_FONTS) = Fonts dir */ + NULL, /* ??? */ + "FONTS" + }, + { /* 21 (LDID_VIEWERS) = Viewers */ + NULL, /* ??? */ + "SYSTEM\\VIEWERS" + }, + { /* 22 (LDID_VMM32) = VMM32 dir */ + NULL, /* ??? */ + "SYSTEM\\VMM32" + }, + { /* 23 (LDID_COLOR) = ICM dir */ + "ICMPath", + "SYSTEM\\COLOR" + }, + { /* 24 (LDID_APPS) = root of boot drive ? */ + "AppsDir", + "C:\\" + }, + { /* 25 (LDID_SHARED) = shared dir */ + "SharedDir", + "" + }, + { /* 26 (LDID_WINBOOT) = Windows boot dir */ + "WinBootDir", + "" + }, + { /* 27 (LDID_MACHINE) = machine specific files */ + "MachineDir", + NULL + }, + { /* 28 (LDID_HOST_WINBOOT) = Host Windows boot dir */ + "HostWinBootDir", + NULL + }, + { /* 29 -- not defined */ + NULL, + NULL + }, + { /* 30 (LDID_BOOT) = Root of boot drive */ + "BootDir", + NULL + }, + { /* 31 (LDID_BOOT_HOST) = Root of boot drive host */ + "BootHost", + NULL + }, + { /* 32 (LDID_OLD_WINBOOT) = subdir of root */ + "OldWinBootDir", + NULL + }, + { /* 33 (LDID_OLD_WIN) = old win dir */ + "OldWinDir", + NULL + } + /* the rest (34-38) isn't too interesting, so I'll forget about it */ +}; + +/* + * LDD == Logical Device Descriptor + * LDID == Logical Device ID + * + * The whole LDD/LDID business might go into a separate file named + * ldd.c. + * At the moment I don't know what the hell these functions are really doing. + * That's why I added reporting stubs. + * The only thing I do know is that I need them for the LDD/LDID infrastructure. + * That's why I implemented them in a way that's suitable for my purpose. + */ +static LDD_LIST *pFirstLDD = NULL; + +static BOOL std_LDDs_done = FALSE; + +void SETUPX_CreateStandardLDDs(void) +{ + HKEY hKey = 0; + WORD n; + DWORD type, len; + LOGDISKDESC_S ldd; + char buffer[MAX_PATH]; + + /* has to be here, otherwise loop */ + std_LDDs_done = TRUE; + + RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", &hKey); + + for (n=0; n < sizeof(LDID_Data)/sizeof(LDID_DATA); n++) + { + buffer[0] = '\0'; + + len = MAX_PATH; + if ( (hKey) && (LDID_Data[n].RegValName) + && (RegQueryValueExA(hKey, LDID_Data[n].RegValName, + NULL, &type, buffer, &len) == ERROR_SUCCESS) + && (type == REG_SZ) ) + { + TRACE("found value '%s' for LDID %d\n", buffer, n); + } + else + switch(n) + { + case LDID_SRCPATH: + FIXME("LDID_SRCPATH: what exactly do we have to do here ?\n"); + strcpy(buffer, "X:\\FIXME"); + break; + case LDID_SYS: + GetSystemDirectoryA(buffer, MAX_PATH); + break; + case LDID_APPS: + case LDID_MACHINE: + case LDID_HOST_WINBOOT: + case LDID_BOOT: + case LDID_BOOT_HOST: + strcpy(buffer, "C:\\"); + break; + default: + if (LDID_Data[n].StdString) + { + DWORD len = GetWindowsDirectoryA(buffer, MAX_PATH); + LPSTR p; + p = buffer + len; + *p++ = '\\'; + strcpy(p, LDID_Data[n].StdString); + } + break; + } + if (buffer[0]) + { + INIT_LDD(ldd, n); + ldd.pszPath = buffer; + TRACE("LDID %d -> '%s'\n", ldd.ldid, ldd.pszPath); + CtlSetLdd16(&ldd); + } + } + if (hKey) RegCloseKey(hKey); +} + +/*********************************************************************** + * CtlDelLdd (SETUPX.37) + * + * RETURN + * ERR_VCP_LDDINVALID if ldid < LDID_ASSIGN_START. + */ +RETERR16 SETUPX_DelLdd(LOGDISKID16 ldid) +{ + LDD_LIST *pCurr, *pPrev = NULL; + + TRACE("(%d)\n", ldid); + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (ldid < LDID_ASSIGN_START) + return ERR_VCP_LDDINVALID; + + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if ( (pCurr == NULL) /* hit end of list */ + || (ldid != pCurr->pldd->ldid) ) + return ERR_VCP_LDDFIND; /* correct ? */ + + /* ok, found our victim: eliminate it */ + + if (pPrev) + pPrev->next = pCurr->next; + + if (pCurr == pFirstLDD) + pFirstLDD = NULL; + HeapFree(GetProcessHeap(), 0, pCurr); + + return OK; +} + +/*********************************************************************** + * CtlDelLdd (SETUPX.37) + */ +RETERR16 WINAPI CtlDelLdd16(LOGDISKID16 ldid) +{ + FIXME("(%d); - please report this!\n", ldid); + return SETUPX_DelLdd(ldid); +} + +/*********************************************************************** + * CtlFindLdd (SETUPX.35) + * + * doesn't check pldd ptr validity: crash (W98SE) + * + * RETURN + * ERR_VCP_LDDINVALID if pldd->cbSize != structsize + * 1 in all other cases ?? + * + */ +RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC pldd) +{ + LDD_LIST *pCurr, *pPrev = NULL; + + TRACE("(%p)\n", pldd); + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (pldd->cbSize != sizeof(LOGDISKDESC_S)) + return ERR_VCP_LDDINVALID; + + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if ( (pCurr == NULL) /* hit end of list */ + || (pldd->ldid != pCurr->pldd->ldid) ) + return ERR_VCP_LDDFIND; /* correct ? */ + + memcpy(pldd, pCurr->pldd, pldd->cbSize); + /* hmm, we probably ought to strcpy() the string ptrs here */ + + return 1; /* what is this ?? */ +} + +/*********************************************************************** + * CtlSetLdd (SETUPX.33) + * + * Set an LDD entry. + * + * RETURN + * ERR_VCP_LDDINVALID if pldd.cbSize != sizeof(LOGDISKDESC_S) + * + */ +RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC pldd) +{ + LDD_LIST *pCurr, *pPrev = NULL; + LPLOGDISKDESC pCurrLDD; + HANDLE heap; + BOOL is_new = FALSE; + + TRACE("(%p)\n", pldd); + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (pldd->cbSize != sizeof(LOGDISKDESC_S)) + return ERR_VCP_LDDINVALID; + + heap = GetProcessHeap(); + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if (!pCurr || pldd->ldid != pCurr->pldd->ldid) + { + is_new = TRUE; + pCurr = HeapAlloc(heap, 0, sizeof(LDD_LIST)); + pCurr->pldd = HeapAlloc(heap, 0, sizeof(LOGDISKDESC_S)); + pCurr->next = NULL; + pCurrLDD = pCurr->pldd; + } + else + { + pCurrLDD = pCurr->pldd; + if (pCurrLDD->pszPath) HeapFree(heap, 0, pCurrLDD->pszPath); + if (pCurrLDD->pszVolLabel) HeapFree(heap, 0, pCurrLDD->pszVolLabel); + if (pCurrLDD->pszDiskName) HeapFree(heap, 0, pCurrLDD->pszDiskName); + } + + memcpy(pCurrLDD, pldd, sizeof(LOGDISKDESC_S)); + + if (pldd->pszPath) + { + pCurrLDD->pszPath = HeapAlloc( heap, 0, strlen(pldd->pszPath)+1 ); + strcpy( pCurrLDD->pszPath, pldd->pszPath ); + } + if (pldd->pszVolLabel) + { + pCurrLDD->pszVolLabel = HeapAlloc( heap, 0, strlen(pldd->pszVolLabel)+1 ); + strcpy( pCurrLDD->pszVolLabel, pldd->pszVolLabel ); + } + if (pldd->pszDiskName) + { + pCurrLDD->pszDiskName = HeapAlloc( heap, 0, strlen(pldd->pszDiskName)+1 ); + strcpy( pCurrLDD->pszDiskName, pldd->pszDiskName ); + } + + if (is_new) /* link into list */ + { + if (pPrev) + { + pCurr->next = pPrev->next; + pPrev->next = pCurr; + } + if (!pFirstLDD) + pFirstLDD = pCurr; + } + + return OK; +} + + +/*********************************************************************** + * CtlAddLdd (SETUPX.36) + * + * doesn't check pldd ptr validity: crash (W98SE) + * + */ +static LOGDISKID16 ldid_to_add = LDID_ASSIGN_START; +RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC pldd) +{ + pldd->ldid = ldid_to_add++; + return CtlSetLdd16(pldd); +} + +/*********************************************************************** + * CtlGetLdd (SETUPX.34) + * + * doesn't check pldd ptr validity: crash (W98SE) + * What the !@#$%&*( is the difference between CtlFindLdd() and CtlGetLdd() ?? + * + * RETURN + * ERR_VCP_LDDINVALID if pldd->cbSize != structsize + * + */ +static RETERR16 SETUPX_GetLdd(LPLOGDISKDESC pldd) +{ + LDD_LIST *pCurr, *pPrev = NULL; + + if (!std_LDDs_done) + SETUPX_CreateStandardLDDs(); + + if (pldd->cbSize != sizeof(LOGDISKDESC_S)) + return ERR_VCP_LDDINVALID; + + pCurr = pFirstLDD; + /* search until we find the appropriate LDD or hit the end */ + while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid)) + { + pPrev = pCurr; + pCurr = pCurr->next; + } + if (pCurr == NULL) /* hit end of list */ + return ERR_VCP_LDDFIND; /* correct ? */ + + memcpy(pldd, pCurr->pldd, pldd->cbSize); + /* hmm, we probably ought to strcpy() the string ptrs here */ + + return OK; +} + +/**********************************************************************/ + +RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC pldd) +{ + FIXME("(%p); - please report this!\n", pldd); + return SETUPX_GetLdd(pldd); +} + +/*********************************************************************** + * CtlGetLddPath (SETUPX.38) + * + * Gets the path of an LDD. + * No crash if szPath == NULL. + * szPath has to be at least MAX_PATH_LEN bytes long. + * RETURN + * ERR_VCP_LDDUNINIT if LDD for LDID not found. + */ +RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath) +{ + TRACE("(%d, %p);\n", ldid, szPath); + + if (szPath) + { + LOGDISKDESC_S ldd; + INIT_LDD(ldd, ldid); + if (CtlFindLdd16(&ldd) == ERR_VCP_LDDFIND) + return ERR_VCP_LDDUNINIT; + SETUPX_GetLdd(&ldd); + strcpy(szPath, ldd.pszPath); + TRACE("ret '%s' for LDID %d\n", szPath, ldid); + } + return OK; +} + +/*********************************************************************** + * CtlSetLddPath (SETUPX.508) + * + * Sets the path of an LDD. + * Creates LDD for LDID if not existing yet. + */ +RETERR16 WINAPI CtlSetLddPath16(LOGDISKID16 ldid, LPSTR szPath) +{ + LOGDISKDESC_S ldd; + TRACE("(%d, '%s');\n", ldid, szPath); + + SetupSetDirectoryIdA( 0, ldid, szPath ); + INIT_LDD(ldd, ldid); + ldd.pszPath = szPath; + return CtlSetLdd16(&ldd); +} diff --git a/reactos/lib/setupapi/stubs.c b/reactos/lib/setupapi/stubs.c new file mode 100644 index 00000000000..252c1acfdf1 --- /dev/null +++ b/reactos/lib/setupapi/stubs.c @@ -0,0 +1,197 @@ +/* + * SetupAPI stubs + * + * Copyright 2000 James Hatheway + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "wine/debug.h" +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "setupapi.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +/*********************************************************************** + * TPWriteProfileString (SETUPX.62) + */ +BOOL WINAPI TPWriteProfileString16( LPCSTR section, LPCSTR entry, LPCSTR string ) +{ + FIXME( "%s %s %s: stub\n", debugstr_a(section), debugstr_a(entry), debugstr_a(string) ); + return TRUE; +} + + +/*********************************************************************** + * suErrorToIds (SETUPX.61) + */ +DWORD WINAPI suErrorToIds16( WORD w1, WORD w2 ) +{ + FIXME( "%x %x: stub\n", w1, w2 ); + return 0; +} + +/*********************************************************************** + * SetupDiOpenClassRegKeyExW (SETUPAPI.@) + * + * WINAPI in description not given + */ +HKEY WINAPI SetupDiOpenClassRegKeyExW(const GUID* class, REGSAM access, DWORD flags, PCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return INVALID_HANDLE_VALUE; +} + +/*********************************************************************** + * SetupDiGetClassDescriptionExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetClassDescriptionExW (const GUID* class, PWSTR desc, DWORD size, PDWORD required, PCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiClassNameFromGuidExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassNameFromGuidExW (const GUID* class, PWSTR desc, DWORD size, PDWORD required, PCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiBuildClassInfoListExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiBuildClassInfoListExW(DWORD flags, LPGUID list, DWORD size, PDWORD required, LPCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceInfoListDetailA (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInfoListDetailA(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_A devinfo_data ) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiGetDeviceInfoListDetailW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiGetDeviceInfoListDetailW(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_W devinfo_data ) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiCreateDeviceInfoListA (SETUPAPI.@) + */ +HDEVINFO WINAPI SetupDiCreateDeviceInfoList(const GUID *class, HWND parend) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiCreateDeviceInfoListExW (SETUPAPI.@) + */ +HDEVINFO WINAPI SetupDiCreateDeviceInfoListExW(const GUID *class, HWND parend , PCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * (SETUPAPI.@) + * + * NO WINAPI in description given + */ +HDEVINFO WINAPI SetupDiGetClassDevsExA(const GUID *class, PCSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCSTR machine, PVOID reserved) +{ + FIXME("filter %s machine %s\n",debugstr_a(filter),debugstr_a(machine)); + return FALSE; +} + +/*********************************************************************** + * (SETUPAPI.@) + * + * NO WINAPI in description given + */ +HDEVINFO WINAPI SetupDiGetClassDevsExW(const GUID *class, PCWSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * SetupDiClassGuidsFromNameExW (SETUPAPI.@) + */ +BOOL WINAPI SetupDiClassGuidsFromNameExW(LPCWSTR class, LPGUID list, DWORD size, PDWORD required, LPCWSTR machine, PVOID reserved) +{ + FIXME("\n"); + return FALSE; +} + +/*********************************************************************** + * CM_Connect_MachineW (SETUPAPI.@) + */ +DWORD WINAPI CM_Connect_MachineW(LPCWSTR name, void * machine) +{ +#define CR_SUCCESS 0x00000000 +#define CR_ACCESS_DENIED 0x00000033 + FIXME("\n"); + return CR_ACCESS_DENIED; +} + +/*********************************************************************** + * CM_Disconnect_Machine (SETUPAPI.@) + */ +DWORD WINAPI CM_Disconnect_Machine(DWORD handle) +{ + FIXME("\n"); + return CR_SUCCESS; + +} + +/*********************************************************************** + * SetupCopyOEMInfA (SETUPAPI.@) + */ +BOOL WINAPI SetupCopyOEMInfA(PCSTR sourceinffile, PCSTR sourcemedialoc, + DWORD mediatype, DWORD copystyle, PSTR destinfname, + DWORD destnamesize, PDWORD required, + PSTR *destinfnamecomponent) +{ + FIXME("stub: source %s location %s ...\n",sourceinffile, sourcemedialoc); + return FALSE; +} + +/*********************************************************************** + * InstallHinfSection (SETUPAPI.@) + */ +void WINAPI InstallHinfSection(HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show) +{ + FIXME("stub, hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_a(cmdline)); +} diff --git a/reactos/lib/setupapi/virtcopy.c b/reactos/lib/setupapi/virtcopy.c new file mode 100644 index 00000000000..47c79dcf54a --- /dev/null +++ b/reactos/lib/setupapi/virtcopy.c @@ -0,0 +1,789 @@ +/* + * SetupAPI virtual copy operations + * + * Copyright 2001 Andreas Mohr + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FIXME: we now rely on builtin setupapi.dll for dialog resources. + * This is bad ! We ought to have 16bit resource handling working. + */ + +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "wownt32.h" +#include "wingdi.h" +#include "winnls.h" +#include "setupapi.h" +#include "setupx16.h" +#include "setupapi_private.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(setupapi); + +static FARPROC16 VCP_Proc = NULL; +static LPARAM VCP_MsgRef = 0; + +static BOOL VCP_opened = FALSE; + +static VCPSTATUS vcp_status; + +static HINSTANCE SETUPAPI_hInstance; + +static WORD VCP_Callback( LPVOID obj, UINT16 msg, WPARAM16 wParam, LPARAM lParam, LPARAM lParamRef ) +{ + WORD args[8]; + DWORD ret = OK; + if (VCP_Proc) + { + args[7] = HIWORD(obj); + args[6] = LOWORD(obj); + args[5] = msg; + args[4] = wParam; + args[3] = HIWORD(lParam); + args[2] = LOWORD(lParam); + args[1] = HIWORD(lParamRef); + args[0] = LOWORD(lParamRef); + WOWCallback16Ex( (DWORD)VCP_Proc, WCB16_PASCAL, sizeof(args), args, &ret ); + } + return (WORD)ret; +} + +/****************************** VHSTR management ******************************/ + +/* + * This is a totally braindead implementation for now; + * I don't care about speed at all ! Size and implementation time + * is much more important IMHO. I could have created some sophisticated + * tree structure, but... what the hell ! :-) + */ +typedef struct { + DWORD refcount; + LPCSTR pStr; +} VHSTR_STRUCT; + +static VHSTR_STRUCT **vhstrlist = NULL; +static VHSTR vhstr_alloc = 0; + +#define VALID_VHSTR(x) ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount)) + +/*********************************************************************** + * vsmStringAdd (SETUPX.207) + */ +VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName) +{ + VHSTR n; + VHSTR index = 0xffff; + HANDLE heap; + + TRACE("add string '%s'\n", lpszName); + /* search whether string already inserted */ + TRACE("searching for existing string...\n"); + for (n = 0; n < vhstr_alloc; n++) + { + if ((vhstrlist[n]) && (vhstrlist[n]->refcount)) + { + TRACE("checking item: %d\n", n); + if (!strcmp(vhstrlist[n]->pStr, lpszName)) + { + TRACE("found\n"); + vhstrlist[n]->refcount++; + return n; + } + } + } + + /* hmm, not found yet, let's insert it */ + TRACE("inserting item\n"); + for (n = 0; n < vhstr_alloc; n++) + { + if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount))) + { + index = n; + break; + } + } + heap = GetProcessHeap(); + if (n == vhstr_alloc) /* hmm, no free index found yet */ + { + index = vhstr_alloc; + vhstr_alloc += 20; + + if (vhstrlist) + vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist, + sizeof(VHSTR_STRUCT *) * vhstr_alloc); + else + vhstrlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, + sizeof(VHSTR_STRUCT *) * vhstr_alloc); + } + if (index == 0xffff) + return 0xffff; /* failure */ + if (!vhstrlist[index]) + vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT)); + vhstrlist[index]->refcount = 1; + vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1); + strcpy((LPSTR)vhstrlist[index]->pStr, lpszName); + return index; +} + +/*********************************************************************** + * vsmStringDelete (SETUPX.206) + */ +INT16 WINAPI vsmStringDelete16(VHSTR vhstr) +{ + if (VALID_VHSTR(vhstr)) + { + vhstrlist[vhstr]->refcount--; + if (!vhstrlist[vhstr]->refcount) + { + HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr); + vhstrlist[vhstr]->pStr = NULL; + } + return VCPN_OK; + } + + /* string not found */ + return VCPN_FAIL; +} + +/* + * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems + */ +VHSTR WINAPI vsmStringFind16(LPCSTR lpszName) +{ + WORD n; + for (n = 0; n < vhstr_alloc; n++) + if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName))) + return n; + return 0xffff; +} + +/*********************************************************************** + * vsmGetStringName (SETUPX.205) + * + * Pretty correct, I guess + */ +INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer) +{ + if (VALID_VHSTR(vhstr)) + { + int len = strlen(vhstrlist[vhstr]->pStr)+1; + if (cbBuffer >= len) + { + if (lpszBuffer) + strcpy(lpszBuffer, vhstrlist[vhstr]->pStr); + return len; + } + } + return VCPN_FAIL; +} + +/*********************************************************************** + * vsmStringCompare (not exported from a standard SETUPX.DLL, it seems) + */ +INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB) +{ + if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB))) + return VCPN_FAIL; /* correct ? */ + return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr); +} + +/*********************************************************************** + * vsmGetStringRawName (SETUPX.208) + */ +LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr) +{ + return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL; +} + + +/***************************** VIRTNODE management ****************************/ +static LPVIRTNODE *pvnlist = NULL; +static DWORD vn_num = 0; +static DWORD vn_last = 0; + +RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl) +{ + HANDLE heap; + LPVIRTNODE lpvn; + RETERR16 cbres; + + while (vn_last < vn_num) + { + if (pvnlist[vn_last] == NULL) + break; + vn_last++; + } + heap = GetProcessHeap(); + if (vn_last == vn_num) + { + vn_num += 20; + if (pvnlist) + pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist, + sizeof(LPVIRTNODE *) * vn_num); + else + pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, + sizeof(LPVIRTNODE *) * vn_num); + } + pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE)); + lpvn = pvnlist[vn_last]; + vn_last++; + + lpvn->cbSize = sizeof(VIRTNODE); + + if (vfsSrc) + memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC)); + + if (vfsDst) + memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC)); + + lpvn->fl = fl; + lpvn->lParam = lParam; + lpvn->lpExpandVtbl = lpExpandVtbl; + + lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */ + + cbres = VCP_Callback(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef); + lpvn->fl |= VFNL_CREATED; + cbres = VCP_Callback(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef); + + return OK; +} + +BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel) +{ + DWORD n; + RETERR16 cbres; + + for (n = 0; n < vn_last; n++) + { + if (pvnlist[n] == lpvnDel) + { + cbres = VCP_Callback(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef); + HeapFree(GetProcessHeap(), 0, lpvnDel); + pvnlist[n] = NULL; + return TRUE; + } + } + return FALSE; +} + +/*********************************************************************** + * VcpOpen (SETUPX.200) + * + * Sets up a virtual copy operation. + * This means that functions such as GenInstall() + * create a VIRTNODE struct for every file to be touched in a .INF file + * instead of actually touching the file. + * The actual copy/move/rename gets started when VcpClose or + * VcpFlush is called; several different callbacks are made + * (copy, rename, open, close, version conflicts, ...) on every file copied. + */ +RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef) +{ + TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef); + if (VCP_opened) + return ERR_VCP_BUSY; + + VCP_Proc = (FARPROC16)vifproc; + VCP_MsgRef = lparamMsgRef; + + /* load SETUPAPI needed for dialog resources etc. */ + SETUPAPI_hInstance = LoadLibraryA("setupapi.dll"); + if (!SETUPAPI_hInstance) + { + ERR("Could not load sibling setupapi.dll\n"); + return ERR_VCP_NOMEM; + } + VCP_opened = TRUE; + return OK; +} + +/*********************************************************************** + * VcpQueueCopy [SETUPX.13] + * + * lpExpandVtbl seems to be deprecated. + * fl are the CNFL_xxx and VNFL_xxx flags. + * lParam are the VNLP_xxx flags. + */ +RETERR16 WINAPI VcpQueueCopy16( + LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, + LPCSTR lpszSrcDir, LPCSTR lpszDstDir, + LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, + LPEXPANDVTBL lpExpandVtbl, + WORD fl, LPARAM lParam +) +{ + VCPFILESPEC vfsSrc, vfsDst; + + if (!VCP_opened) + return ERR_VCP_NOTOPEN; + + TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n", + lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName); + + TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst); + + vfsSrc.ldid = ldidSrc; + vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir); + vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName); + + vfsDst.ldid = ldidDst; + vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); + vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); + + return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam, + lpExpandVtbl); +} + +/*********************************************************************** + * VcpQueueDelete [SETUPX.17] + * + * Is lParamRef the same as lParam in VcpQueueCopy ? + * Damn docu !! Err... which docu ? + */ +RETERR16 WINAPI VcpQueueDelete16( + LPCSTR lpszDstFileName, + LPCSTR lpszDstDir, + LOGDISKID16 ldidDst, + LPARAM lParamRef +) +{ + VCPFILESPEC vfsDst; + + if (!VCP_opened) + return ERR_VCP_NOTOPEN; + + vfsDst.ldid = ldidDst; + vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); + vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); + + return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0); +} + +/*********************************************************************** + * VcpQueueRename [SETUPX.204] + * + */ +RETERR16 WINAPI VcpQueueRename16( + LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName, + LPCSTR lpszSrcDir, LPCSTR lpszDstDir, + LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst, + LPARAM lParam +) +{ + VCPFILESPEC vfsSrc, vfsDst; + + if (!VCP_opened) + return ERR_VCP_NOTOPEN; + + vfsSrc.ldid = ldidSrc; + vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir); + vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName); + + vfsDst.ldid = ldidDst; + vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir); + vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName); + + return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam, + 0); +} + +/*********************************************************************** + * VcpEnumFiles (SETUPX.@) + */ +INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef) +{ + WORD n; + + for (n = 0; n < vn_last; n++) + vep(pvnlist[n], lParamRef); + + return 0; /* FIXME: return value ? */ +} + +/*********************************************************************** + * VcpExplain (SETUPX.411) + */ +LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat) +{ + static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */ + buffer[0] = '\0'; + switch (dwWhat) + { + case VCPEX_SRC_FULL: + case VCPEX_DST_FULL: + { + LPVCPFILESPEC lpvfs = + (dwWhat == VCPEX_SRC_FULL) ? &lpVn->vfsSrc : &lpVn->vfsDst; + + /* if we have an ldid, use it, otherwise use the string */ + /* from the vhstrlist array */ + if (lpvfs->ldid != 0xffff) + CtlGetLddPath16(lpvfs->ldid, buffer); + else + strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir)); + + strcat(buffer, "\\"); + strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName)); + } + break; + default: + FIXME("%ld unimplemented !\n", dwWhat); + strcpy(buffer, "Unknown error"); + break; + } + return buffer; +} + +RETERR16 VCP_CheckPaths(void) +{ + DWORD n; + LPVIRTNODE lpvn; + RETERR16 cbres; + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef); + for (n = 0; n < vn_num; n++) + { + lpvn = pvnlist[n]; + if (!lpvn) continue; + /* FIXME: check paths of all VIRTNODEs here ! */ + cbres = VCP_Callback(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef); + } + cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef); + return OK; +} + +RETERR16 VCP_CopyFiles(void) +{ + char fn_src[MAX_PATH], fn_dst[MAX_PATH]; + RETERR16 res = OK, cbres; + DWORD n; + LPVIRTNODE lpvn; + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef); + for (n = 0; n < vn_num; n++) + { + lpvn = pvnlist[n]; + if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue; + /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */ + strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL)); + strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL)); + /* FIXME: what is this VCPM_VSTATWRITE here for ? + * I guess it's to signal successful destination file creation */ + cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef); + + /* FIXME: need to do the file copy in small chunks for notifications */ + TRACE("copying '%s' to '%s'\n", fn_src, fn_dst); + /* perform the file copy */ + if (!(CopyFileA(fn_src, fn_dst, + (lpvn->fl & VNLP_COPYIFEXISTS) ? FALSE : TRUE ))) + { + ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst); + res = ERR_VCP_IOFAIL; + } + + vcp_status.prgFileRead.dwSoFar++; + cbres = VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef); + vcp_status.prgFileWrite.dwSoFar++; + cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef); + } + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef); + return res; +} + +/*********************************************************************** + * VcpFlush - internal (not exported), but documented + * + * VNFL_NOW is used for VcpFlush. + */ +RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest) +{ + return OK; +} + +/*********************************************************************** + * VcpClose (SETUPX.201) + * + * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART, + * VCPM_VSTATCLOSEEND. + * + * fl gets VCPFL_xxx flags to indicate what to do with the + * VIRTNODEs (files to mess with) created by e.g. GenInstall() + */ +RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest) +{ + RETERR16 res = OK; + WORD cbres = VCPN_PROCEED; + + TRACE("(%04x, '%s')\n", fl, lpszBackupDest); + + /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER + * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */ + + TRACE("#1\n"); + memset(&vcp_status, 0, sizeof(VCPSTATUS)); + /* yes, vcp_status.cbSize is 0 ! */ + TRACE("#2\n"); + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef); + TRACE("#3\n"); + + res = VCP_CheckPaths(); + TRACE("#4\n"); + if (res != OK) + return res; /* is this ok ? */ + VCP_CopyFiles(); + + TRACE("#5\n"); + cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef); + TRACE("#6\n"); + VCP_Proc = NULL; + FreeLibrary(SETUPAPI_hInstance); + VCP_opened = FALSE; + return OK; +} + +RETERR16 VCP_RenameFiles(void) +{ + char fn_src[MAX_PATH], fn_dst[MAX_PATH]; + RETERR16 res = OK, cbres; + DWORD n; + LPVIRTNODE lpvn; + + cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef); + for (n = 0; n < vn_num; n++) + { + lpvn = pvnlist[n]; + if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue; + strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL)); + strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL)); + cbres = VCP_Callback(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef); + if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING))) + res = ERR_VCP_IOFAIL; + else + VCP_VirtnodeDelete(lpvn); + } + cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef); + return res; +} + +/*********************************************************************** + * vcpDefCallbackProc (SETUPX.202) + */ +RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, + LPARAM lParam, LPARAM lParamRef) +{ + static int count = 0; + if (count < 10) + FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n", + lpvObj, uMsg, wParam, lParam, lParamRef); + count++; + return OK; +} + +/********************* point-and-click stuff from here ***********************/ + +static HWND hDlgCopy = 0; +static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0; +static char BackupDir[12]; + +static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + INT_PTR retval = FALSE; + + if (iMsg == WM_INITDIALOG) + { + ShowWindow(hWndDlg, SW_SHOWNORMAL); + UpdateWindow(hWndDlg); + retval = TRUE; + } + return retval; +} + +BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32) +{ + HRSRC hResInfo; + HGLOBAL hDlgTmpl32; + + if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG))) + return FALSE; + if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) || + !(*template32 = LockResource( hDlgTmpl32 ))) + return FALSE; + return TRUE; +} + +static LRESULT WINAPI +VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg != WM_CREATE) + return DefWindowProcA (hwnd, uMsg, wParam, lParam); + + switch (uMsg) + { + case WM_CREATE: + return 0; + default: + FIXME("%04x: unhandled.\n", uMsg); + } + + return 0; +} + +void VCP_UI_RegisterProgressClass(void) +{ + static BOOL registered = FALSE; + WNDCLASSA wndClass; + + if (registered) + return; + + registered = TRUE; + ZeroMemory (&wndClass, sizeof(WNDCLASSA)); + wndClass.style = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = (WNDPROC)VCP_UI_FileCopyWndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hCursor = LoadCursorA (0, (LPSTR)IDC_ARROW); + wndClass.hbrBackground = NULL; + wndClass.lpszClassName = "setupx_progress"; + + RegisterClassA (&wndClass); +} + +RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2) +{ + LPCSTR file1, file2; + file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName); + file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName); + return (RETERR16)strcmp(file1, file2); +} + +RETERR16 VCP_UI_CopyStart(void) +{ + LPCVOID template32; + char buf[256]; /* plenty */ + BOOL dirty; + DWORD len; + + /* FIXME: should be registered at DLL startup instead */ + VCP_UI_RegisterProgressClass(); + if (!(VCP_UI_GetDialogTemplate(&template32))) + return VCPN_FAIL; + + if (vn_num > 10) /* hack */ + { + hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0, + VCP_UI_FileCopyDlgProc, 0); + if (!hDlgCopy) + return VCPN_FAIL; + SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ..."); + SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET"); + } + strcpy(buf, REG_INSTALLEDFILES); + if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles)) + return VCPN_FAIL; + strcat(buf, REGPART_RENAME); + if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename)) + return VCPN_FAIL; + if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict)) + return VCPN_FAIL; + len = 1; + if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len))) + { + /* FIXME: what does SETUPX.DLL do in this case ? */ + MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n"); + return VCPN_FAIL; + } + dirty = TRUE; + if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1)) + return VCPN_FAIL; + len = 12; + if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len))) + strcpy(BackupDir, "VCM"); + + /* create C:\WINDOWS\[BackupDir] and set registry key to it */ + GetWindowsDirectoryA(buf, 256); + strcat(buf, "\\"); + strcat(buf, BackupDir); + if (!(CreateDirectoryA(buf, NULL))) + return VCPN_FAIL; + if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1)) + return VCPN_FAIL; + RegCloseKey(hKeyConflict); + + return VCPN_OK; +} + +/*********************************************************************** + * vcpUICallbackProc (SETUPX.213) + */ +RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, + LPARAM lParam, LPARAM lParamRef) +{ + static int count = 0; + RETERR16 res = VCPN_OK, cbres; + + if (count < 5) + FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n", + lpvObj, uMsg, wParam, lParam, lParamRef); + count++; + switch (uMsg) + { + /* unused messages, it seems */ + case VCPM_DISKPREPINFO: + + case VCPM_FILENEEDED: + + case VCPM_NODECREATE: + case VCPM_NODEACCEPT: + + case VCPM_VSTATCLOSESTART: + case VCPM_VSTATPATHCHECKSTART: + case VCPM_VSTATPATHCHECKEND: + + case VCPM_CHECKPATH: + break; + + /* the real stuff */ + case VCPM_NODECOMPARE: + res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam); + break; + case VCPM_VSTATREAD: + break; + case VCPM_VSTATWRITE: + cbres = VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef); + break; + case VCPM_VSTATCLOSEEND: + RegCloseKey(hKeyFiles); + RegCloseKey(hKeyRename); + RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT); + break; + case VCPM_VSTATCOPYSTART: + res = VCP_UI_CopyStart(); + break; + case VCPM_VSTATCOPYEND: + if (hDlgCopy) DestroyWindow(hDlgCopy); + break; + default: + FIXME("unhandled msg 0x%04x\n", uMsg); + } + return res; +}