From 6386edc5d8616e3aff9bd4f552cf9c30020bf522 Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Wed, 13 Aug 2003 01:27:48 +0000 Subject: [PATCH] Partial implementation of the Microsoft Installer (msi.dll). --- configure | 3 +- configure.ac | 1 + dlls/Makefile.in | 13 + dlls/msi/.cvsignore | 6 + dlls/msi/Makefile.in | 39 ++ dlls/msi/distinct.c | 286 ++++++++++++++ dlls/msi/handle.c | 115 ++++++ dlls/msi/msi.c | 640 +++++++++++++++++++++++++++++++ dlls/msi/msi.spec | 218 +++++++++++ dlls/msi/msipriv.h | 186 +++++++++ dlls/msi/msiquery.c | 376 ++++++++++++++++++ dlls/msi/order.c | 328 ++++++++++++++++ dlls/msi/query.h | 88 +++++ dlls/msi/record.c | 442 +++++++++++++++++++++ dlls/msi/select.c | 224 +++++++++++ dlls/msi/sql.y | 513 +++++++++++++++++++++++++ dlls/msi/suminfo.c | 223 +++++++++++ dlls/msi/table.c | 887 +++++++++++++++++++++++++++++++++++++++++++ dlls/msi/tokenize.c | 379 ++++++++++++++++++ dlls/msi/where.c | 384 +++++++++++++++++++ include/Makefile.in | 2 + include/msi.h | 85 +++++ include/msiquery.h | 98 +++++ 23 files changed, 5535 insertions(+), 1 deletion(-) create mode 100644 dlls/msi/.cvsignore create mode 100644 dlls/msi/Makefile.in create mode 100644 dlls/msi/distinct.c create mode 100644 dlls/msi/handle.c create mode 100644 dlls/msi/msi.c create mode 100644 dlls/msi/msi.spec create mode 100644 dlls/msi/msipriv.h create mode 100644 dlls/msi/msiquery.c create mode 100644 dlls/msi/order.c create mode 100644 dlls/msi/query.h create mode 100644 dlls/msi/record.c create mode 100644 dlls/msi/select.c create mode 100644 dlls/msi/sql.y create mode 100644 dlls/msi/suminfo.c create mode 100644 dlls/msi/table.c create mode 100644 dlls/msi/tokenize.c create mode 100644 dlls/msi/where.c create mode 100644 include/msi.h create mode 100644 include/msiquery.h diff --git a/configure b/configure index b3fc46d2b7f..587b4ae336d 100755 --- a/configure +++ b/configure @@ -16478,7 +16478,7 @@ MAKE_LIB_RULES=libs/Makelib.rules MAKE_PROG_RULES=programs/Makeprog.rules - ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3dx8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/icmp/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrtd/Makefile dlls/msvideo/Makefile dlls/msvideo/msrle32/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/version/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winenas/Makefile dlls/winmm/winejack/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/uuid/Makefile libs/wine/Makefile libs/wpp/Makefile miscemu/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/osversioncheck/Makefile programs/progman/Makefile programs/regapi/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/regtest/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/wmc/Makefile tools/wrc/Makefile" + ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3dx8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/icmp/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrtd/Makefile dlls/msvideo/Makefile dlls/msvideo/msrle32/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/version/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winenas/Makefile dlls/winmm/winejack/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/uuid/Makefile libs/wine/Makefile libs/wpp/Makefile miscemu/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/osversioncheck/Makefile programs/progman/Makefile programs/regapi/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/regtest/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/wmc/Makefile tools/wrc/Makefile" cat >confcache <<\_ACEOF @@ -17068,6 +17068,7 @@ do "dlls/msacm/msg711/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msacm/msg711/Makefile" ;; "dlls/msacm/winemp3/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msacm/winemp3/Makefile" ;; "dlls/msdmo/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msdmo/Makefile" ;; + "dlls/msi/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msi/Makefile" ;; "dlls/msimg32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msimg32/Makefile" ;; "dlls/msisys/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msisys/Makefile" ;; "dlls/msnet32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msnet32/Makefile" ;; diff --git a/configure.ac b/configure.ac index e9e378b8008..161cca04efa 100644 --- a/configure.ac +++ b/configure.ac @@ -1452,6 +1452,7 @@ dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile +dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile diff --git a/dlls/Makefile.in b/dlls/Makefile.in index 23a4711c76f..a2298753c38 100644 --- a/dlls/Makefile.in +++ b/dlls/Makefile.in @@ -58,6 +58,7 @@ BASEDIRS = \ msacm/msg711 \ msacm/winemp3 \ msdmo \ + msi \ msimg32 \ msisys \ msnet32 \ @@ -247,6 +248,7 @@ SYMLINKS = \ msadp32.acm$(DLLEXT) \ msdmo.dll$(DLLEXT) \ msg711.acm$(DLLEXT) \ + msi.dll$(DLLEXT) \ msimg32.dll$(DLLEXT) \ msisys.ocx$(DLLEXT) \ msnet32.dll$(DLLEXT) \ @@ -482,6 +484,9 @@ msdmo.dll$(DLLEXT): msdmo/msdmo.dll$(DLLEXT) msg711.acm$(DLLEXT): msacm/msg711/msg711.acm$(DLLEXT) $(RM) $@ && $(LN_S) msacm/msg711/msg711.acm$(DLLEXT) $@ +msi.dll$(DLLEXT): msi/msi.dll$(DLLEXT) + $(RM) $@ && $(LN_S) msi/msi.dll$(DLLEXT) $@ + msimg32.dll$(DLLEXT): msimg32/msimg32.dll$(DLLEXT) $(RM) $@ && $(LN_S) msimg32/msimg32.dll$(DLLEXT) $@ @@ -719,6 +724,7 @@ IMPORT_LIBS = \ libmpr \ libmsacm32 \ libmsdmo \ + libmsi \ libmsimg32 \ libmsnet32 \ libmsrle32 \ @@ -994,6 +1000,11 @@ libmsdmo.def: msdmo/msdmo.spec.def libmsdmo.a: msdmo/msdmo.spec.def $(DLLTOOL) -k -l $@ -d msdmo/msdmo.spec.def +libmsi.def: msi/msi.spec.def + $(RM) $@ && $(LN_S) msi/msi.spec.def $@ +libmsi.a: msi/msi.spec.def + $(DLLTOOL) -k -l $@ -d msi/msi.spec.def + libmsimg32.def: msimg32/msimg32.spec.def $(RM) $@ && $(LN_S) msimg32/msimg32.spec.def $@ libmsimg32.a: msimg32/msimg32.spec.def @@ -1308,6 +1319,7 @@ mapi32/mapi32.spec.def: $(WINEBUILD) mpr/mpr.spec.def: $(WINEBUILD) msacm/msacm32.spec.def: $(WINEBUILD) msdmo/msdmo.spec.def: $(WINEBUILD) +msi/msi.spec.def: $(WINEBUILD) msimg32/msimg32.spec.def: $(WINEBUILD) msnet32/msnet32.spec.def: $(WINEBUILD) msvideo/msrle32/msrle32.spec.def: $(WINEBUILD) @@ -1424,6 +1436,7 @@ msacm/msacm32.dll$(DLLEXT): msacm msacm/msadp32/msadp32.acm$(DLLEXT): msacm/msadp32 msdmo/msdmo.dll$(DLLEXT): msdmo msacm/msg711/msg711.acm$(DLLEXT): msacm/msg711 +msi/msi.dll$(DLLEXT): msi msimg32/msimg32.dll$(DLLEXT): msimg32 msisys/msisys.ocx$(DLLEXT): msisys msnet32/msnet32.dll$(DLLEXT): msnet32 diff --git a/dlls/msi/.cvsignore b/dlls/msi/.cvsignore new file mode 100644 index 00000000000..d96b95b4550 --- /dev/null +++ b/dlls/msi/.cvsignore @@ -0,0 +1,6 @@ +Makefile +msi.dll.dbg.c +msi.spec.c +msi.spec.def +y.tab.c +y.tab.h diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in new file mode 100644 index 00000000000..eca9c4b01a4 --- /dev/null +++ b/dlls/msi/Makefile.in @@ -0,0 +1,39 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = msi.dll +IMPORTS = ole32 user32 advapi32 kernel32 +EXTRALIBS = $(LIBUUID) $(LIBUNICODE) + +LDDLLFLAGS = @LDDLLFLAGS@ +SYMBOLFILE = $(MODULE).tmp.o + +C_SRCS = \ + distinct.c \ + handle.c \ + msi.c \ + msiquery.c \ + order.c \ + record.c \ + select.c \ + suminfo.c \ + table.c \ + tokenize.c \ + where.c + +#RC_SRCS= msi_rc.rc + +EXTRA_SRCS = sql.y +EXTRA_OBJS = y.tab.o + +@MAKE_DLL_RULES@ + +y.tab.c y.tab.h: sql.y + $(YACC) -d -t $(SRCDIR)/sql.y + +# hack to allow parallel make +y.tab.h: y.tab.c +y.tab.o: y.tab.h + +### Dependencies: diff --git a/dlls/msi/distinct.c b/dlls/msi/distinct.c new file mode 100644 index 00000000000..aaa140958bd --- /dev/null +++ b/dlls/msi/distinct.c @@ -0,0 +1,286 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tagDISTINCTSET +{ + UINT val; + UINT count; + UINT row; + struct tagDISTINCTSET *nextrow; + struct tagDISTINCTSET *nextcol; +} DISTINCTSET; + +typedef struct tagMSIDISTINCTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT row_count; + UINT *translation; +} MSIDISTINCTVIEW; + +static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row ) +{ + /* horrible O(n) find */ + while( *x ) + { + if( (*x)->val == val ) + { + (*x)->count++; + return x; + } + x = &(*x)->nextrow; + } + + /* nothing found, so add one */ + *x = HeapAlloc( GetProcessHeap(), 0, sizeof (DISTINCTSET) ); + if( *x ) + { + (*x)->val = val; + (*x)->count = 1; + (*x)->row = row; + (*x)->nextrow = NULL; + (*x)->nextcol = NULL; + } + return x; +} + +static void distinct_free( DISTINCTSET *x ) +{ + while( x ) + { + DISTINCTSET *next = x->nextrow; + distinct_free( x->nextcol ); + HeapFree( GetProcessHeap(), 0, x ); + x = next; + } +} + +static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, val ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( row >= dv->row_count ) + return ERROR_INVALID_PARAMETER; + + row = dv->translation[ row ]; + + return dv->table->ops->fetch_int( dv->table, row, col, val ); +} + +static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIHANDLE record ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + UINT r, i, j, r_count, c_count; + DISTINCTSET *rowset = NULL; + + TRACE("%p %ld\n", dv, record); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->execute( dv->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count ); + if( r != ERROR_SUCCESS ) + return r; + + dv->translation = HeapAlloc( GetProcessHeap(), 0, r_count*sizeof(UINT) ); + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + + /* build it */ + for( i=0; itable->ops->fetch_int( dv->table, i, j, &val ); + if( r != ERROR_SUCCESS ) + { + ERR("Failed to fetch int at %d %d\n", i, j ); + distinct_free( rowset ); + return r; + } + x = distinct_insert( x, val, i ); + if( !*x ) + { + ERR("Failed to insert at %d %d\n", i, j ); + distinct_free( rowset ); + return ERROR_FUNCTION_FAILED; + } + if( j != c_count ) + x = &(*x)->nextcol; + } + + /* check if it was distinct and if so, include it */ + if( (*x)->row == i ) + { + TRACE("Row %d -> %d\n", dv->row_count, i); + dv->translation[dv->row_count++] = i; + } + } + + distinct_free( rowset ); + + return ERROR_SUCCESS; +} + +static UINT DISTINCT_close( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( dv->translation ) + HeapFree( GetProcessHeap(), 0, dv->translation ); + dv->translation = NULL; + dv->row_count = 0; + + return dv->table->ops->close( dv->table ); +} + +static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( rows ) + { + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + *rows = dv->row_count; + } + + return dv->table->ops->get_dimensions( dv->table, NULL, cols ); +} + +static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %p %p\n", dv, n, name, type ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->get_column_info( dv->table, n, name, type ); +} + +static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %ld\n", dv, eModifyMode, hrec ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->modify( dv->table, eModifyMode, hrec ); +} + +static UINT DISTINCT_delete( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + if( dv->translation ) + HeapFree( GetProcessHeap(), 0, dv->translation ); + HeapFree( GetProcessHeap(), 0, dv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS distinct_ops = +{ + DISTINCT_fetch_int, + DISTINCT_execute, + DISTINCT_close, + DISTINCT_get_dimensions, + DISTINCT_get_column_info, + DISTINCT_modify, + DISTINCT_delete +}; + +UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIDISTINCTVIEW *dv = NULL; + UINT count = 0, r; + + TRACE("%p\n", dv ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + dv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *dv ); + if( !dv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + dv->view.ops = &distinct_ops; + dv->db = db; + dv->table = table; + dv->translation = NULL; + dv->row_count = 0; + *view = (MSIVIEW*) dv; + + return ERROR_SUCCESS; +} diff --git a/dlls/msi/handle.c b/dlls/msi/handle.c new file mode 100644 index 00000000000..ee73b11f851 --- /dev/null +++ b/dlls/msi/handle.c @@ -0,0 +1,115 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +MSIHANDLEINFO *msihandletable[MSIMAXHANDLES]; + +MSIHANDLE alloc_msihandle(UINT type, UINT size, msihandledestructor destroy) +{ + MSIHANDLEINFO *info; + UINT i; + + /* find a slot */ + for(i=0; i=MSIMAXHANDLES) || msihandletable[i] ) + return 0; + + size += sizeof (MSIHANDLEINFO); + info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); + if( !info ) + return 0; + + info->magic = MSIHANDLE_MAGIC; + info->type = type; + info->destructor = destroy; + + msihandletable[i] = info; + + return (MSIHANDLE) (i+1); +} + +void *msihandle2msiinfo(MSIHANDLE handle, UINT type) +{ + handle--; + if( handle<0 ) + return NULL; + if( handle>=MSIMAXHANDLES ) + return NULL; + if( !msihandletable[handle] ) + return NULL; + if( msihandletable[handle]->magic != MSIHANDLE_MAGIC ) + return NULL; + if( type && (msihandletable[handle]->type != type) ) + return NULL; + + return &msihandletable[handle][1]; +} + +UINT WINAPI MsiCloseHandle(MSIHANDLE handle) +{ + MSIHANDLEINFO *info = msihandle2msiinfo(handle, 0); + + TRACE("%lx\n",handle); + + if( !info ) + return ERROR_INVALID_HANDLE; + + info--; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + return ERROR_INVALID_HANDLE; + } + + if( info->destructor ) + info->destructor( &info[1] ); + + HeapFree( GetProcessHeap(), 0, info ); + msihandletable[handle-1] = NULL; + + TRACE("Destroyed\n"); + + return 0; +} + +UINT WINAPI MsiCloseAllHandles(void) +{ + UINT i; + + TRACE("\n"); + + for(i=0; istorage ); +} + +UINT WINAPI MsiOpenDatabaseA( + LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB) +{ + HRESULT r = ERROR_FUNCTION_FAILED; + LPWSTR szwDBPath = NULL, szwPersist = NULL; + + TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB); + + if( szDBPath ) + { + szwDBPath = HEAP_strdupAtoW( GetProcessHeap(), 0, szDBPath ); + if( !szwDBPath ) + goto end; + } + + if( szPersist ) + { + szwPersist = HEAP_strdupAtoW( GetProcessHeap(), 0, szPersist ); + if( !szwPersist ) + goto end; + } + + r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB ); + +end: + if( szwPersist ) + HeapFree( GetProcessHeap(), 0, szwPersist ); + if( szwDBPath ) + HeapFree( GetProcessHeap(), 0, szwDBPath ); + + return r; +} + +UINT WINAPI MsiOpenDatabaseW( + LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB) +{ + IStorage *stg = NULL; + HRESULT r; + MSIHANDLE handle; + MSIDATABASE *db; + UINT ret; + + TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB); + + if( !phDB ) + return ERROR_INVALID_PARAMETER; + + r = StgOpenStorage( szDBPath, NULL, STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + if( FAILED( r ) ) + { + FIXME("open failed r = %08lx!\n",r); + return ERROR_FUNCTION_FAILED; + } + + handle = alloc_msihandle(MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE), MSI_CloseDatabase ); + if( !handle ) + { + FIXME("Failed to allocate a handle\n"); + ret = ERROR_FUNCTION_FAILED; + goto end; + } + + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + { + FIXME("Failed to get handle pointer \n"); + ret = ERROR_FUNCTION_FAILED; + goto end; + } + db->storage = stg; + ret = load_string_table( db, &db->strings); + if( ret != ERROR_SUCCESS ) + goto end; + + *phDB = handle; + + IStorage_AddRef( stg ); +end: + if( stg ) + IStorage_Release( stg ); + + return ret; +} + +UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct) +{ + FIXME("%s %p\n",debugstr_a(szProduct), phProduct); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiOpenProductW(LPCWSTR szProduct, MSIHANDLE *phProduct) +{ + FIXME("%s %p\n",debugstr_w(szProduct), phProduct); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage) +{ + FIXME("%s %p\n",debugstr_a(szPackage), phPackage); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage) +{ + FIXME("%s %p\n",debugstr_w(szPackage), phPackage); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiInstallProductA(LPCSTR szPackagePath, LPCSTR szCommandLine) +{ + LPWSTR szwPath = NULL, szwCommand = NULL; + UINT r = ERROR_FUNCTION_FAILED; /* FIXME: check return code */ + + TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine)); + + if( szPackagePath ) + { + szwPath = HEAP_strdupAtoW(GetProcessHeap(),0,szPackagePath); + if( szwPath == NULL ) + goto end; + } + + if( szCommandLine ) + { + szwCommand = HEAP_strdupAtoW(GetProcessHeap(),0,szCommandLine); + if( szwCommand == NULL ) + goto end; + } + + r = MsiInstallProductW( szwPath, szwCommand ); + +end: + if( szwPath ) + HeapFree( GetProcessHeap(), 0, szwPath ); + + if( szwCommand ) + HeapFree( GetProcessHeap(), 0, szwCommand ); + + return r; +} + +UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine) +{ + FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine)); + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiConfigureProductA( + LPCSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState) +{ + FIXME("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiConfigureProductW( + LPCWSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState) +{ + FIXME("%s %d %d\n",debugstr_w(szProduct), iInstallLevel, eInstallState); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer) +{ + FIXME("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer) +{ + FIXME("%s %s\n",debugstr_w(szComponent), debugstr_w(szBuffer)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, LPSTR szBuffer, DWORD *pcchValueBuf) +{ + FIXME("%s %s %p %p\n",debugstr_a(szProduct), debugstr_a(szAttribute), szBuffer, pcchValueBuf); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szBuffer, DWORD *pcchValueBuf) +{ + FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szAttribute), szBuffer, pcchValueBuf); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseImportA(LPCSTR szFolderPath, LPCSTR szFilename) +{ + FIXME("%s %s\n",debugstr_a(szFolderPath), debugstr_a(szFilename)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseImportW(LPCWSTR szFolderPath, LPCWSTR szFilename) +{ + FIXME("%s %s\n",debugstr_w(szFolderPath), debugstr_w(szFilename)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, BOOL fAppend) +{ + FIXME("%08lx %s %d\n", dwLogMode, debugstr_a(szLogFile), fAppend); + return ERROR_SUCCESS; + /* return ERROR_CALL_NOT_IMPLEMENTED; */ +} + +UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, BOOL fAppend) +{ + FIXME("%08lx %s %d\n", dwLogMode, debugstr_w(szLogFile), fAppend); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct) +{ + FIXME("%s\n", debugstr_a(szProduct)); + return 0; +} + +INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct) +{ + FIXME("%s\n", debugstr_w(szProduct)); + return 0; +} + +INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd) +{ + FIXME("%08x %p\n", dwUILevel, phWnd); + return dwUILevel; +} + +UINT WINAPI MsiLoadStringA(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f) +{ + FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiLoadStringW(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f) +{ + FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiMessageBoxA(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f) +{ + FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiMessageBoxW(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f) +{ + FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid) +{ + DWORD r; + WCHAR szwGuid[GUID_SIZE]; + + TRACE("%ld %p\n",index,lpguid); + + r = MsiEnumProductsW(index, szwGuid); + if( r == ERROR_SUCCESS ) + WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL); + + return r; +} + +UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid) +{ + HKEY hkey = 0, hkeyFeatures = 0; + DWORD r; + WCHAR szKeyName[33]; + + TRACE("%ld %p\n",index,lpguid); + + r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegOpenKeyW(hkey, szFeatures, &hkeyFeatures); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegEnumKeyW(hkeyFeatures, index, szKeyName, GUID_SIZE); + + unsquash_guid(szKeyName, lpguid); + +end: + + if( hkeyFeatures ) + RegCloseKey(hkeyFeatures); + if( hkey ) + RegCloseKey(hkey); + + return r; +} + +UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, + LPSTR szFeature, LPSTR szParent) +{ + DWORD r; + WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE]; + LPWSTR szwProduct = NULL; + + TRACE("%s %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent); + + if( szProduct ) + { + szwProduct = HEAP_strdupAtoW(GetProcessHeap(),0,szProduct); + if( !szwProduct ) + return ERROR_FUNCTION_FAILED; + } + + r = MsiEnumFeaturesW(szProduct?szwProduct:NULL, + index, szwFeature, szwParent); + if( r == ERROR_SUCCESS ) + { + WideCharToMultiByte(CP_ACP, 0, szwFeature, -1, + szFeature, GUID_SIZE, NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, szwParent, -1, + szParent, GUID_SIZE, NULL, NULL); + } + + if( szwProduct ) + HeapFree( GetProcessHeap(), 0, szwProduct); + + return r; +} + +UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, + LPWSTR szFeature, LPWSTR szParent) +{ + HKEY hkey = 0, hkeyFeatures = 0, hkeyProduct = 0; + DWORD r, sz; + WCHAR szRegName[GUID_SIZE]; + + TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent); + + if( !squash_guid(szProduct, szRegName) ) + return ERROR_INVALID_PARAMETER; + + r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegOpenKeyW(hkey, szFeatures, &hkeyFeatures); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegOpenKeyW(hkeyFeatures, szRegName, &hkeyProduct); + if( r != ERROR_SUCCESS ) + goto end; + + sz = GUID_SIZE; + r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL); + +end: + if( hkeyProduct ) + RegCloseKey(hkeyProduct); + if( hkeyFeatures ) + RegCloseKey(hkeyFeatures); + if( hkey ) + RegCloseKey(hkey); + + return r; +} + +UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid) +{ + DWORD r; + WCHAR szwGuid[GUID_SIZE]; + + TRACE("%ld %p\n",index,lpguid); + + r = MsiEnumComponentsW(index, szwGuid); + if( r == ERROR_SUCCESS ) + WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL); + + return r; +} + +UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid) +{ + HKEY hkey = 0, hkeyComponents = 0; + DWORD r; + WCHAR szKeyName[33]; + + TRACE("%ld %p\n",index,lpguid); + + r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegOpenKeyW(hkey, szComponents, &hkeyComponents); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegEnumKeyW(hkeyComponents, index, szKeyName, GUID_SIZE); + + unsquash_guid(szKeyName, lpguid); + +end: + + if( hkeyComponents ) + RegCloseKey(hkeyComponents); + if( hkey ) + RegCloseKey(hkey); + + return r; +} + +UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct) +{ + DWORD r; + WCHAR szwProduct[GUID_SIZE]; + LPWSTR szwComponent = NULL; + + TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct); + + if( szComponent ) + { + szwComponent = HEAP_strdupAtoW(GetProcessHeap(),0,szComponent); + if( !szwComponent ) + return ERROR_FUNCTION_FAILED; + } + + r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct); + if( r == ERROR_SUCCESS ) + { + WideCharToMultiByte(CP_ACP, 0, szwProduct, -1, + szProduct, GUID_SIZE, NULL, NULL); + } + + if( szwComponent ) + HeapFree( GetProcessHeap(), 0, szwComponent); + + return r; +} + +UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct) +{ + HKEY hkey = 0, hkeyComponents = 0, hkeyComp = 0; + DWORD r, sz; + WCHAR szRegName[GUID_SIZE], szValName[GUID_SIZE]; + + TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct); + + if( !squash_guid(szComponent, szRegName) ) + return ERROR_INVALID_PARAMETER; + + r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegOpenKeyW(hkey, szComponents, &hkeyComponents); + if( r != ERROR_SUCCESS ) + goto end; + + r = RegOpenKeyW(hkeyComponents, szRegName, &hkeyComp); + if( r != ERROR_SUCCESS ) + goto end; + + sz = GUID_SIZE; + r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL); + if( r != ERROR_SUCCESS ) + goto end; + + unsquash_guid(szValName, szProduct); + +end: + if( hkeyComp ) + RegCloseKey(hkeyComp); + if( hkeyComponents ) + RegCloseKey(hkeyComponents); + if( hkey ) + RegCloseKey(hkey); + + return r; +} + +UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor, LPSTR szPath, DWORD *pcchPath, DWORD *pcchArgs ) +{ + FIXME("%s %p %p %p\n", debugstr_a(szDescriptor), szPath, pcchPath, pcchArgs ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideComponentFromDescriptorW( LPCWSTR szDescriptor, LPWSTR szPath, DWORD *pcchPath, DWORD *pcchArgs ) +{ + FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +HRESULT WINAPI MSI_DllGetVersion(DLLVERSIONINFO *pdvi) +{ + TRACE("%p\n",pdvi); + + if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) + return E_INVALIDARG; + + pdvi->dwMajorVersion = MSI_MAJORVERSION; + pdvi->dwMinorVersion = MSI_MINORVERSION; + pdvi->dwBuildNumber = MSI_BUILDNUMBER; + pdvi->dwPlatformID = 1; + + return S_OK; +} + +BOOL WINAPI MSI_DllCanUnloadNow(void) +{ + return FALSE; +} diff --git a/dlls/msi/msi.spec b/dlls/msi/msi.spec new file mode 100644 index 00000000000..56c4607328a --- /dev/null +++ b/dlls/msi/msi.spec @@ -0,0 +1,218 @@ +1 stdcall DllCanUnloadNow() MSI_DllCanUnloadNow +2 stub DllGetClassObject +3 stub DllRegisterServer +4 stub DllUnregisterServer +5 stub MsiAdvertiseProductA +6 stub MsiAdvertiseProductW +7 stdcall MsiCloseAllHandles() MsiCloseAllHandles +8 stdcall MsiCloseHandle(long) MsiCloseHandle +9 stub MsiCollectUserInfoA +10 stub MsiCollectUserInfoW +11 stub MsiConfigureFeatureA +12 stub MsiConfigureFeatureFromDescriptorA +13 stub MsiConfigureFeatureFromDescriptorW +14 stub MsiConfigureFeatureW +15 stdcall MsiConfigureProductA(str long long) MsiConfigureProductA +16 stdcall MsiConfigureProductW(wstr long long) MsiConfigureProductW +17 stdcall MsiCreateRecord(long) MsiCreateRecord +18 stdcall MsiDatabaseApplyTransformA(long str long) MsiDatabaseApplyTransformA +19 stdcall MsiDatabaseApplyTransformW(long wstr long) MsiDatabaseApplyTransformW +20 stdcall MsiDatabaseCommit(long) MsiDatabaseCommit +21 stub MsiDatabaseExportA +22 stub MsiDatabaseExportW +23 stdcall MsiDatabaseGenerateTransformA(long long str long long) MsiDatabaseGenerateTransformA +24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long) MsiDatabaseGenerateTransformW +25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr) MsiDatabaseGetPrimaryKeysA +26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr) MsiDatabaseGetPrimaryKeysW +27 stdcall MsiDatabaseImportA(str str) MsiDatabaseImportA +28 stdcall MsiDatabaseImportW(wstr wstr) MsiDatabaseImportW +29 stub MsiDatabaseMergeA +30 stub MsiDatabaseMergeW +31 stdcall MsiDatabaseOpenViewA(str ptr) MsiDatabaseOpenViewA +32 stdcall MsiDatabaseOpenViewW(wstr ptr) MsiDatabaseOpenViewW +33 stdcall MsiDoActionA(long str) MsiDoActionA +34 stdcall MsiDoActionW(long wstr) MsiDoActionW +35 stub MsiEnableUIPreview +36 stdcall MsiEnumClientsA(long ptr) MsiEnumClientsA +37 stdcall MsiEnumClientsW(long ptr) MsiEnumClientsW +38 stub MsiEnumComponentQualifiersA +39 stub MsiEnumComponentQualifiersW +40 stdcall MsiEnumComponentsA(long ptr) MsiEnumComponentsA +41 stdcall MsiEnumComponentsW(long ptr) MsiEnumComponentsW +42 stdcall MsiEnumFeaturesA(str long ptr ptr) MsiEnumFeaturesA +43 stdcall MsiEnumFeaturesW(wstr long ptr ptr) MsiEnumFeaturesW +44 stdcall MsiEnumProductsA(long ptr) MsiEnumProductsA +45 stdcall MsiEnumProductsW(long ptr) MsiEnumProductsW +46 stub MsiEvaluateConditionA +47 stub MsiEvaluateConditionW +48 stub MsiGetLastErrorRecord +49 stub MsiGetActiveDatabase +50 stub MsiGetComponentStateA +51 stub MsiGetComponentStateW +52 stub MsiGetDatabaseState +53 stub MsiGetFeatureCostA +54 stub MsiGetFeatureCostW +55 stub MsiGetFeatureInfoA +56 stub MsiGetFeatureInfoW +57 stub MsiGetFeatureStateA +58 stub MsiGetFeatureStateW +59 stub MsiGetFeatureUsageA +60 stub MsiGetFeatureUsageW +61 stub MsiGetFeatureValidStatesA +62 stub MsiGetFeatureValidStatesW +63 stub MsiGetLanguage +64 stub MsiGetMode +65 stdcall MsiGetProductCodeA(str str) MsiGetProductCodeA +66 stdcall MsiGetProductCodeW(wstr wstr) MsiGetProductCodeW +67 stdcall MsiGetProductInfoA(str str str long) MsiGetProductInfoA +68 stub MsiGetProductInfoFromScriptA +69 stub MsiGetProductInfoFromScriptW +70 stdcall MsiGetProductInfoW(wstr wstr wstr long) MsiGetProductInfoW +71 stub MsiGetProductPropertyA +72 stub MsiGetProductPropertyW +73 stub MsiGetPropertyA +74 stub MsiGetPropertyW +75 stub MsiGetSourcePathA +76 stub MsiGetSourcePathW +77 stdcall MsiGetSummaryInformationA(str long ptr) MsiGetSummaryInformationA +78 stdcall MsiGetSummaryInformationW(wstr long ptr) MsiGetSummaryInformationW +79 stub MsiGetTargetPathA +80 stub MsiGetTargetPathW +81 stub MsiGetUserInfoA +82 stub MsiGetUserInfoW +83 stub MsiInstallMissingComponentA +84 stub MsiInstallMissingComponentW +85 stub MsiInstallMissingFileA +86 stub MsiInstallMissingFileW +87 stdcall MsiInstallProductA(str str) MsiInstallProductA +88 stdcall MsiInstallProductW(wstr wstr) MsiInstallProductW +89 stub MsiLocateComponentA +90 stub MsiLocateComponentW +91 stdcall MsiOpenDatabaseA(str str ptr) MsiOpenDatabaseA +92 stdcall MsiOpenDatabaseW(wstr wstr ptr) MsiOpenDatabaseW +93 stdcall MsiOpenPackageA(str ptr) MsiOpenPackageA +94 stdcall MsiOpenPackageW(wstr ptr) MsiOpenPackageW +95 stdcall MsiOpenProductA(str ptr) MsiOpenProductA +96 stdcall MsiOpenProductW(wstr ptr) MsiOpenProductW +97 stub MsiPreviewBillboardA +98 stub MsiPreviewBillboardW +99 stub MsiPreviewDialogA +100 stub MsiPreviewDialogW +101 stub MsiProcessAdvertiseScriptA +102 stub MsiProcessAdvertiseScriptW +103 stub MsiProcessMessage +104 stub MsiProvideComponentA +105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr) MsiProvideComponentFromDescriptorA +106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr) MsiProvideComponentFromDescriptorW +107 stub MsiProvideComponentW +108 stub MsiProvideQualifiedComponentA +109 stub MsiProvideQualifiedComponentW +110 stub MsiQueryFeatureStateA +111 stub MsiQueryFeatureStateW +112 stdcall MsiQueryProductStateA(str) MsiQueryProductStateA +113 stdcall MsiQueryProductStateW(wstr) MsiQueryProductStateW +114 stdcall MsiRecordDataSize(long long) MsiRecordDataSize +115 stdcall MsiRecordGetFieldCount(long) MsiRecordGetFieldCount +116 stdcall MsiRecordGetInteger(long long) MsiRecordGetInteger +117 stdcall MsiRecordGetStringA(long long ptr ptr) MsiRecordGetStringA +118 stdcall MsiRecordGetStringW(long long ptr ptr) MsiRecordGetStringW +119 stdcall MsiRecordIsNull(long long) MsiRecordIsNull +120 stdcall MsiRecordReadStream(long long ptr ptr) MsiRecordReadStream +121 stdcall MsiRecordSetInteger(long long long) MsiRecordSetInteger +122 stdcall MsiRecordSetStreamA(long long str) MsiRecordSetStreamA +123 stdcall MsiRecordSetStreamW(long long wstr) MsiRecordSetStreamW +124 stdcall MsiRecordSetStringA(long long str) MsiRecordSetStringA +125 stdcall MsiRecordSetStringW(long long wstr) MsiRecordSetStringW +126 stub MsiReinstallFeatureA +127 stub MsiReinstallFeatureFromDescriptorA +128 stub MsiReinstallFeatureFromDescriptorW +129 stub MsiReinstallFeatureW +130 stub MsiReinstallProductA +131 stub MsiReinstallProductW +132 stub MsiSequenceA +133 stub MsiSequenceW +134 stub MsiSetComponentStateA +135 stub MsiSetComponentStateW +136 stub MsiSetExternalUIA +137 stub MsiSetExternalUIW +138 stub MsiSetFeatureStateA +139 stub MsiSetFeatureStateW +140 stub MsiSetInstallLevel +141 stdcall MsiSetInternalUI(long ptr) MsiSetInternalUI +142 stub MsiVerifyDiskSpace +143 stub MsiSetMode +144 stub MsiSetPropertyA +145 stub MsiSetPropertyW +146 stub MsiSetTargetPathA +147 stub MsiSetTargetPathW +148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr) MsiSummaryInfoGetPropertyA +149 stdcall MsiSummaryInfoGetPropertyCount(long ptr) MsiSummaryInfoGetPropertyCount +150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr) MsiSummaryInfoGetPropertyW +151 stub MsiSummaryInfoPersist +152 stub MsiSummaryInfoSetPropertyA +153 stub MsiSummaryInfoSetPropertyW +154 stub MsiUseFeatureA +155 stub MsiUseFeatureW +156 stub MsiVerifyPackageA +157 stub MsiVerifyPackageW +158 stdcall MsiViewClose(long) MsiViewClose +159 stdcall MsiViewExecute(long long) MsiViewExecute +160 stdcall MsiViewFetch(long ptr) MsiViewFetch +161 stub MsiViewGetErrorA +162 stub MsiViewGetErrorW +163 stub MsiViewModify +164 stdcall MsiDatabaseIsTablePersistentA(long str) MsiDatabaseIsTablePersistentA +165 stdcall MsiDatabaseIsTablePersistentW(long wstr) MsiDatabaseIsTablePersistentW +166 stdcall MsiViewGetColumnInfo(long long ptr) MsiViewGetColumnInfo +167 stdcall MsiRecordClearData(long) MsiRecordClearData +168 stdcall MsiEnableLogA(long str long) MsiEnableLogA +169 stdcall MsiEnableLogW(long wstr long) MsiEnableLogW +170 stdcall MsiFormatRecordA(long long ptr ptr) MsiFormatRecordA +171 stdcall MsiFormatRecordW(long long ptr ptr) MsiFormatRecordW +172 stub MsiGetComponentPathA +173 stub MsiGetComponentPathW +174 stub MsiApplyPatchA +175 stub MsiApplyPatchW +176 stub MsiAdvertiseScriptA +177 stub MsiAdvertiseScriptW +178 stub MsiGetPatchInfoA +179 stub MsiGetPatchInfoW +180 stub MsiEnumPatchesA +181 stub MsiEnumPatchesW +182 stdcall DllGetVersion(ptr) MSI_DllGetVersion +183 stub MsiGetProductCodeFromPackageCodeA +184 stub MsiGetProductCodeFromPackageCodeW +185 stub MsiCreateTransformSummaryInfoA +186 stub MsiCreateTransformSummaryInfoW +187 stub MsiQueryFeatureStateFromDescriptorA +188 stub MsiQueryFeatureStateFromDescriptorW +189 stub MsiConfigureProductExA +190 stub MsiConfigureProductExW +191 stub MsiInvalidateFeatureCache +192 stub MsiUseFeatureExA +193 stub MsiUseFeatureExW +194 stub MsiGetFileVersionA +195 stub MsiGetFileVersionW +196 stdcall MsiLoadStringA(long long long long long long) MsiLoadStringA +197 stdcall MsiLoadStringW(long long long long long long) MsiLoadStringW +198 stdcall MsiMessageBoxA(long long long long long long) MsiMessageBoxA +199 stdcall MsiMessageBoxW(long long long long long long) MsiMessageBoxW +200 stub MsiDecomposeDescriptorA +201 stub MsiDecomposeDescriptorW +202 stub MsiProvideQualifiedComponentExA +203 stub MsiProvideQualifiedComponentExW +204 stub MsiEnumRelatedProductsA +205 stub MsiEnumRelatedProductsW +206 stub MsiSetFeatureAttributesA +207 stub MsiSetFeatureAttributesW +208 stub MsiSourceListClearAllA +209 stub MsiSourceListClearAllW +210 stub MsiSourceListAddSourceA +211 stub MsiSourceListAddSourceW +212 stub MsiSourceListForceResolutionA +213 stub MsiSourceListForceResolutionW +214 stub MsiIsProductElevatedA +215 stub MsiIsProductElevatedW +216 stub MsiGetShortcutTargetA +217 stub MsiGetShortcutTargetW + diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h new file mode 100644 index 00000000000..e1e2eb3aeee --- /dev/null +++ b/dlls/msi/msipriv.h @@ -0,0 +1,186 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 __WINE_MSI_PRIVATE__ +#define __WINE_MSI_PRIVATE__ + +#include "msi.h" +#include "msiquery.h" +#include "objidl.h" + +#define MSI_DATASIZEMASK 0x00ff +#define MSITYPE_VALID 0x0100 +#define MSITYPE_STRING 0x0800 +#define MSITYPE_NULLABLE 0x1000 +#define MSITYPE_KEY 0x2000 + +struct tagMSITABLE; +typedef struct tagMSITABLE MSITABLE; + +typedef struct pool_data_tag +{ + USHORT *data; + UINT size; +} pool_data; + +typedef struct string_data_tag +{ + CHAR *data; + UINT size; +} string_data; + +typedef struct string_table_tag +{ + pool_data pool; + string_data info; +} string_table; + +typedef struct tagMSIDATABASE +{ + IStorage *storage; + string_table strings; + MSITABLE *first_table, *last_table; +} MSIDATABASE; + +struct tagMSIVIEW; + +typedef struct tagMSIVIEWOPS +{ + /* + * fetch_int - reads one integer from {row,col} in the table + * + * This function should be called after the execute method. + * Data returned by the function should not change until + * close or delete is called. + * To get a string value, query the database's string table with + * the integer value returned from this function. + */ + UINT (*fetch_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT *val ); + + /* + * execute - loads the underlying data into memory so it can be read + */ + UINT (*execute)( struct tagMSIVIEW *, MSIHANDLE ); + + /* + * close - clears the data read by execute from memory + */ + UINT (*close)( struct tagMSIVIEW * ); + + /* + * get_dimensions - returns the number of rows or columns in a table. + * + * The number of rows can only be queried after the execute method + * is called. The number of columns can be queried at any time. + */ + UINT (*get_dimensions)( struct tagMSIVIEW *, UINT *rows, UINT *cols ); + + /* + * get_column_info - returns the name and type of a specific column + * + * The name is HeapAlloc'ed by this function and should be freed by + * the caller. + * The column information can be queried at any time. + */ + UINT (*get_column_info)( struct tagMSIVIEW *, UINT n, LPWSTR *name, UINT *type ); + + /* + * modify - not yet implemented properly + */ + UINT (*modify)( struct tagMSIVIEW *, MSIMODIFY, MSIHANDLE ); + + /* + * delete - destroys the structure completely + */ + UINT (*delete)( struct tagMSIVIEW * ); + +} MSIVIEWOPS; + +typedef struct tagMSIVIEW +{ + MSIVIEWOPS *ops; +} MSIVIEW; + +typedef struct tagMSISUMMARYINFO +{ + IPropertyStorage *propstg; +} MSISUMMARYINFO; + +typedef VOID (*msihandledestructor)( VOID * ); + +typedef struct tagMSIHANDLEINFO +{ + UINT magic; + UINT type; + msihandledestructor destructor; + struct tagMSIHANDLEINFO *next; + struct tagMSIHANDLEINFO *prev; +} MSIHANDLEINFO; + +#define MSIHANDLETYPE_ANY 0 +#define MSIHANDLETYPE_DATABASE 1 +#define MSIHANDLETYPE_SUMMARYINFO 2 +#define MSIHANDLETYPE_VIEW 3 +#define MSIHANDLETYPE_RECORD 4 + +#define MSI_MAJORVERSION 1 +#define MSI_MINORVERSION 10 +#define MSI_BUILDNUMBER 1029 + +#define GUID_SIZE 39 + +#define MSIHANDLE_MAGIC 0x4d434923 +#define MSIMAXHANDLES 0x80 + +#define MSISUMINFO_OFFSET 0x30LL + +extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type); + +MSIHANDLE alloc_msihandle(UINT type, UINT extra, msihandledestructor destroy); + +/* add this table to the list of cached tables in the database */ +extern void add_table(MSIDATABASE *db, MSITABLE *table); +extern void remove_table( MSIDATABASE *db, MSITABLE *table ); +extern void free_table( MSIDATABASE *db, MSITABLE *table ); +extern void free_cached_tables( MSIDATABASE *db ); +extern UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table); +extern UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table); +extern UINT dump_string_table(MSIDATABASE *db); +extern UINT load_string_table( MSIDATABASE *db, string_table *pst); +extern UINT msi_id2string( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz ); +extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid); + +UINT VIEW_find_column( MSIVIEW *view, LPWSTR name, UINT *n ); + +/* FIXME! should get rid of that */ +#include "winnls.h" +inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str ) +{ + LPWSTR ret; + INT len; + + if (!str) return NULL; + len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + ret = HeapAlloc( heap, flags, len * sizeof(WCHAR) ); + if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); + return ret; +} + +#endif /* __WINE_MSI_PRIVATE__ */ diff --git a/dlls/msi/msiquery.c b/dlls/msi/msiquery.c new file mode 100644 index 00000000000..ac4b3a92515 --- /dev/null +++ b/dlls/msi/msiquery.c @@ -0,0 +1,376 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tagMSIQUERY +{ + MSIVIEW *view; + UINT row; + MSIDATABASE *db; +} MSIQUERY; + +UINT WINAPI MsiDatabaseIsTablePersistentA( + MSIHANDLE hDatabase, LPSTR szTableName) +{ + FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseIsTablePersistentW( + MSIHANDLE hDatabase, LPWSTR szTableName) +{ + FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +void MSI_CloseView( VOID *arg ) +{ + MSIQUERY *query = arg; + + if( query->view && query->view->ops->delete ) + query->view->ops->delete( query->view ); +} + +UINT VIEW_find_column( MSIVIEW *table, LPWSTR name, UINT *n ) +{ + LPWSTR col_name; + UINT i, count, r; + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + return r; + + for( i=1; i<=count; i++ ) + { + INT x; + + col_name = NULL; + r = table->ops->get_column_info( table, i, &col_name, NULL ); + if( r != ERROR_SUCCESS ) + return r; + x = lstrcmpW( name, col_name ); + HeapFree( GetProcessHeap(), 0, col_name ); + if( !x ) + { + *n = i; + return ERROR_SUCCESS; + } + } + + return ERROR_INVALID_PARAMETER; +} + +UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb, + LPCSTR szQuery, MSIHANDLE *phView) +{ + UINT r; + LPCWSTR szwQuery; + + TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView); + + if( szQuery ) + { + szwQuery = HEAP_strdupAtoW( GetProcessHeap(), 0, szQuery ); + if( !szwQuery ) + return ERROR_FUNCTION_FAILED; + } + else + szwQuery = NULL; + + r = MsiDatabaseOpenViewW( hdb, szwQuery, phView); + + return r; +} + +UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb, + LPCWSTR szQuery, MSIHANDLE *phView) +{ + MSIDATABASE *db; + MSIHANDLE handle; + MSIQUERY *query; + UINT r; + + TRACE("%s %p\n", debugstr_w(szQuery), phView); + + if( !szQuery) + return ERROR_INVALID_PARAMETER; + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + + /* pre allocate a handle to hold a pointer to the view */ + handle = alloc_msihandle( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY), MSI_CloseView ); + if( !handle ) + return ERROR_FUNCTION_FAILED; + query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_FUNCTION_FAILED; + + query->row = 0; + query->db = db; + query->view = NULL; + + r = MSI_ParseSQL( db, szQuery, &query->view ); + if( r != ERROR_SUCCESS ) + { + MsiCloseHandle( handle ); + return r; + } + + *phView = handle; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record) +{ + MSIQUERY *query; + MSIVIEW *view; + MSIHANDLE handle; + UINT row_count = 0, col_count = 0, i, ival, ret, type; + + TRACE("%ld %p\n", hView, record); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + + ret = view->ops->get_dimensions( view, &row_count, &col_count ); + if( ret ) + return ret; + if( !col_count ) + return ERROR_INVALID_PARAMETER; + + if( query->row >= row_count ) + return ERROR_NO_MORE_ITEMS; + + handle = MsiCreateRecord( col_count ); + if( !handle ) + return ERROR_FUNCTION_FAILED; + + for( i=1; i<=col_count; i++ ) + { + ret = view->ops->get_column_info( view, i, NULL, &type ); + if( ret ) + { + ERR("Error getting column type for %d\n", i ); + continue; + } + ret = view->ops->fetch_int( view, query->row, i, &ival ); + if( ret ) + { + ERR("Error fetching data for %d\n", i ); + continue; + } + if( ! (type & MSITYPE_VALID ) ) + ERR("Invalid type!\n"); + + /* check if it's nul (0) - if so, don't set anything */ + if( !ival ) + continue; + + if( type & MSITYPE_STRING ) + { + LPWSTR sval = MSI_makestring( query->db, ival ); + MsiRecordSetStringW( handle, i, sval ); + HeapFree( GetProcessHeap(), 0, sval ); + } + else + { + if( (type & MSI_DATASIZEMASK) == 2 ) + MsiRecordSetInteger( handle, i, ival - (1<<15) ); + else + MsiRecordSetInteger( handle, i, ival - (1<<31) ); + } + } + query->row ++; + + *record = handle; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiViewClose(MSIHANDLE hView) +{ + MSIQUERY *query; + MSIVIEW *view; + + TRACE("%ld\n", hView ); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + if( !view->ops->close ) + return ERROR_FUNCTION_FAILED; + + return view->ops->close( view ); +} + +UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec) +{ + MSIQUERY *query; + MSIVIEW *view; + + TRACE("%ld %ld\n", hView, hRec); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + if( !view->ops->execute ) + return ERROR_FUNCTION_FAILED; + query->row = 0; + + return view->ops->execute( view, hRec ); +} + +UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec) +{ + MSIVIEW *view; + MSIQUERY *query; + MSIHANDLE handle; + UINT ret, i, count = 0, type; + LPWSTR name; + + TRACE("%ld %d %p\n", hView, info, hRec); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + + if( !view->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + + ret = view->ops->get_dimensions( view, NULL, &count ); + if( ret ) + return ret; + if( !count ) + return ERROR_INVALID_PARAMETER; + + handle = MsiCreateRecord( count ); + if( !handle ) + return ERROR_FUNCTION_FAILED; + + for( i=0; iops->get_column_info( view, i+1, &name, &type ); + if( ret != ERROR_SUCCESS ) + continue; + MsiRecordSetStringW( handle, i+1, name ); + HeapFree( GetProcessHeap(), 0, name ); + } + + *hRec = handle; + + return ERROR_SUCCESS; +} + + +UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction ) +{ + FIXME("%ld %s\n", hInstall, debugstr_a(szAction) ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction ) +{ + FIXME("%ld %s\n", hInstall, debugstr_w(szAction) ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, + LPCSTR szTransformFile, int iErrorCond) +{ + FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, + LPCWSTR szTransformFile, int iErrorCond) +{ + FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref, + LPCSTR szTransformFile, int iReserved1, int iReserved2 ) +{ + FIXME("%ld %ld %s %d %d\n", hdb, hdbref, + debugstr_a(szTransformFile), iReserved1, iReserved2); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref, + LPCWSTR szTransformFile, int iReserved1, int iReserved2 ) +{ + FIXME("%ld %ld %s %d %d\n", hdb, hdbref, + debugstr_w(szTransformFile), iReserved1, iReserved2); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb ) +{ + FIXME("%ld\n", hdb); + return ERROR_SUCCESS; +} + +UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, + LPCSTR table, MSIHANDLE* rec) +{ + FIXME("%ld %s %p\n", hdb, debugstr_a(table), rec); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE hdb, + LPCWSTR table, MSIHANDLE* rec) +{ + FIXME("%ld %s %p\n", hdb, debugstr_w(table), rec); + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/dlls/msi/order.c b/dlls/msi/order.c new file mode 100644 index 00000000000..ba900472293 --- /dev/null +++ b/dlls/msi/order.c @@ -0,0 +1,328 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +/* below is the query interface to a table */ + +typedef struct tagMSIORDERVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT *reorder; + UINT num_cols; + UINT cols[1]; +} MSIORDERVIEW; + +static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap ) +{ + UINT r, i, a_val = 0, b_val = 0; + + *swap = 0; + for( i=0; inum_cols; i++ ) + { + r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val ); + if( r != ERROR_SUCCESS ) + return r; + + r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val ); + if( r != ERROR_SUCCESS ) + return r; + + if( a_val != b_val ) + { + if( a_val > b_val ) + *swap = 1; + break; + } + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right ) +{ + UINT r, centre = (left + right)/2, temp, swap = 0, i, j; + UINT *array = ov->reorder; + + if( left == right ) + return ERROR_SUCCESS; + + /* sort the left half */ + r = ORDER_mergesort( ov, left, centre ); + if( r != ERROR_SUCCESS ) + return r; + + /* sort the right half */ + r = ORDER_mergesort( ov, centre+1, right ); + if( r != ERROR_SUCCESS ) + return r; + + for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ ) + { + r = ORDER_compare( ov, array[i], array[j], &swap ); + if( r != ERROR_SUCCESS ) + return r; + if( swap ) + { + temp = array[j]; + memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) ); + array[i] = temp; + j++; + centre++; + } + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows ) +{ + UINT i, swap, r; + + for( i=1; ireorder[i-1], ov->reorder[i], &swap ); + if( r != ERROR_SUCCESS ) + return r; + if( !swap ) + continue; + ERR("Bad order! %d\n", i); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %d %p\n", ov, row, col, val ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + row = ov->reorder[ row ]; + + return ov->table->ops->fetch_int( ov->table, row, col, val ); +} + +static UINT ORDER_execute( struct tagMSIVIEW *view, MSIHANDLE record ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + UINT r, num_rows = 0, i; + + TRACE("%p %ld\n", ov, record); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + r = ov->table->ops->execute( ov->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL ); + if( r != ERROR_SUCCESS ) + return r; + + ov->reorder = HeapAlloc( GetProcessHeap(), 0, num_rows*sizeof(UINT) ); + if( !ov->reorder ) + return ERROR_FUNCTION_FAILED; + + for( i=0; ireorder[i] = i; + + r = ORDER_mergesort( ov, 0, num_rows - 1 ); + if( r != ERROR_SUCCESS ) + return r; + + r = ORDER_verify( ov, num_rows ); + if( r != ERROR_SUCCESS ) + return r; + + return ERROR_SUCCESS; +} + +static UINT ORDER_close( struct tagMSIVIEW *view ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p\n", ov ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + if( ov->reorder ) + HeapFree( GetProcessHeap(), 0, ov->reorder ); + ov->reorder = NULL; + + return ov->table->ops->close( ov->table ); +} + +static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %p %p\n", ov, rows, cols ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->get_dimensions( ov->table, rows, cols ); +} + +static UINT ORDER_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %p %p\n", ov, n, name, type ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->get_column_info( ov->table, n, name, type ); +} + +static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %ld\n", ov, eModifyMode, hrec ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->modify( ov->table, eModifyMode, hrec ); +} + +static UINT ORDER_delete( struct tagMSIVIEW *view ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p\n", ov ); + + if( ov->table ) + ov->table->ops->delete( ov->table ); + + if( ov->reorder ) + HeapFree( GetProcessHeap(), 0, ov->reorder ); + ov->reorder = NULL; + + HeapFree( GetProcessHeap(), 0, ov ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS order_ops = +{ + ORDER_fetch_int, + ORDER_execute, + ORDER_close, + ORDER_get_dimensions, + ORDER_get_column_info, + ORDER_modify, + ORDER_delete +}; + +UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIORDERVIEW *ov = NULL; + UINT count = 0, r; + + TRACE("%p\n", ov ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + ov = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof *ov + sizeof (UINT) * count ); + if( !ov ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + ov->view.ops = &order_ops; + ov->db = db; + ov->table = table; + ov->reorder = NULL; + ov->num_cols = 0; + *view = (MSIVIEW*) ov; + + return ERROR_SUCCESS; +} + +UINT ORDER_AddColumn( MSIVIEW *view, LPWSTR name ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + UINT n, count, r; + MSIVIEW *table; + + TRACE("%p adding %s\n", ov, debugstr_w( name ) ); + + if( ov->view.ops != &order_ops ) + return ERROR_FUNCTION_FAILED; + + table = ov->table; + if( !table ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_column_info ) + return ERROR_FUNCTION_FAILED; + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + return r; + + if( ov->num_cols >= count ) + return ERROR_FUNCTION_FAILED; + + r = VIEW_find_column( table, name, &n ); + if( r != ERROR_SUCCESS ) + return r; + + ov->cols[ov->num_cols] = n; + TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n); + + ov->num_cols++; + + return ERROR_SUCCESS; +} diff --git a/dlls/msi/query.h b/dlls/msi/query.h new file mode 100644 index 00000000000..663a6737737 --- /dev/null +++ b/dlls/msi/query.h @@ -0,0 +1,88 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 __WINE_MSI_QUERY_H +#define __WINE_MSI_QUERY_H + +#include "winbase.h" +#include "objidl.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" + + +#define OP_EQ 1 +#define OP_AND 2 +#define OP_OR 3 +#define OP_GT 4 +#define OP_LT 5 +#define OP_LE 6 +#define OP_GE 7 +#define OP_NE 8 +#define OP_ISNULL 9 +#define OP_NOTNULL 10 + +#define EXPR_COMPLEX 1 +#define EXPR_COLUMN 2 +#define EXPR_COL_NUMBER 3 +#define EXPR_IVAL 4 +#define EXPR_SVAL 5 +#define EXPR_UVAL 6 + +struct complex_expr +{ + UINT op; + struct expr *left; + struct expr *right; +}; + +struct expr +{ + int type; + union + { + struct complex_expr expr; + INT ival; + UINT uval; + LPWSTR sval; + LPWSTR column; + UINT col_number; + } u; +}; + + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phView); + +UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view ); + +UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); +UINT SELECT_AddColumn( MSIVIEW *select, LPWSTR name ); + +UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); + +UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); +UINT ORDER_AddColumn( MSIVIEW *group, LPWSTR name ); + +UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); +UINT WHERE_AddCondition( MSIVIEW *view, struct expr *condition ); + +int sqliteGetToken(const WCHAR *z, int *tokenType); + +#endif /* __WINE_MSI_QUERY_H */ diff --git a/dlls/msi/record.c b/dlls/msi/record.c new file mode 100644 index 00000000000..4bce8d3b470 --- /dev/null +++ b/dlls/msi/record.c @@ -0,0 +1,442 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "objidl.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#define MSIFIELD_NULL 0 +#define MSIFIELD_INT 1 +#define MSIFIELD_STR 2 +#define MSIFIELD_WSTR 3 +#define MSIFIELD_STREAM 4 + +/* maybe we can use a Variant instead of doing it ourselves? */ +typedef struct tagMSIFIELD +{ + UINT type; + union + { + INT iVal; + LPSTR szVal; + LPWSTR szwVal; + IStream *stream; + } u; +} MSIFIELD; + +typedef struct tagMSIRECORD +{ + UINT count; /* as passed to MsiCreateRecord */ + MSIFIELD fields[1]; /* nb. array size is count+1 */ +} MSIRECORD; + +void MSI_FreeField( MSIFIELD *field ) +{ + switch( field->type ) + { + case MSIFIELD_NULL: + case MSIFIELD_INT: + break; + case MSIFIELD_STR: + HeapFree( GetProcessHeap(), 0, field->u.szVal); + break; + case MSIFIELD_WSTR: + HeapFree( GetProcessHeap(), 0, field->u.szwVal); + break; + case MSIFIELD_STREAM: + IStream_Release( field->u.stream ); + break; + default: + ERR("Invalid field type %d\n", field->type); + } +} + +void MSI_CloseRecord( VOID *arg ) +{ + MSIRECORD *rec = (MSIRECORD *) arg; + UINT i; + + for( i=0; icount; i++ ) + MSI_FreeField( &rec->fields[i] ); +} + +MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams ) +{ + MSIHANDLE handle = 0; + UINT sz; + MSIRECORD *rec; + + TRACE("%d\n", cParams); + + sz = sizeof (MSIRECORD) + sizeof(MSIFIELD)*(cParams+1) ; + handle = alloc_msihandle( MSIHANDLETYPE_RECORD, sz, MSI_CloseRecord ); + if( !handle ) + return 0; + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return 0; + + rec->count = cParams; + + return handle; +} + +unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle ) +{ + MSIRECORD *rec; + + TRACE("%ld\n", handle ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + { + ERR("Record not found!\n"); + return 0; + } + + return rec->count; +} + +static BOOL string2intA( LPCSTR str, int *out ) +{ + int x = 0; + LPCSTR p = str; + + if( *p == '-' ) /* skip the minus sign */ + p++; + while ( *p ) + { + if( (*p < '0') || (*p > '9') ) + return FALSE; + x *= 10; + x += (*p - '0'); + p++; + } + + if( str[0] == '-' ) /* check if it's negative */ + x = -x; + *out = x; + + return TRUE; +} + +static BOOL string2intW( LPCWSTR str, int *out ) +{ + int x = 0; + LPCWSTR p = str; + + if( *p == '-' ) /* skip the minus sign */ + p++; + while ( *p ) + { + if( (*p < '0') || (*p > '9') ) + return FALSE; + x *= 10; + x += (*p - '0'); + p++; + } + + if( str[0] == '-' ) /* check if it's negative */ + x = -x; + *out = x; + + return TRUE; +} + +int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField) +{ + MSIRECORD *rec; + int ret = 0; + + TRACE("%ld %d\n", handle, iField ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return MSI_NULL_INTEGER; + + if( iField > rec->count ) + return MSI_NULL_INTEGER; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return rec->fields[iField].u.iVal; + case MSIFIELD_STR: + if( string2intA( rec->fields[iField].u.szVal, &ret ) ) + return ret; + return MSI_NULL_INTEGER; + case MSIFIELD_WSTR: + if( string2intW( rec->fields[iField].u.szwVal, &ret ) ) + return ret; + return MSI_NULL_INTEGER; + default: + break; + } + + return MSI_NULL_INTEGER; +} + +UINT WINAPI MsiRecordClearData( MSIHANDLE handle ) +{ + MSIRECORD *rec; + UINT i; + + TRACE("%ld\n", handle ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + for( i=0; i<=rec->count; i++) + { + MSI_FreeField( &rec->fields[i] ); + rec->fields[i].type = MSIFIELD_NULL; + rec->fields[i].u.iVal = 0; + } + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal ) +{ + MSIRECORD *rec; + + TRACE("%ld %u %d\n", handle,iField, iVal); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_INT; + rec->fields[iField].u.iVal = iVal; + + return ERROR_SUCCESS; +} + +BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField ) +{ + MSIRECORD *rec; + + TRACE("%ld %d\n", handle,iField ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + if( iField > rec->count ) + return TRUE; + + if( rec->fields[iField].type == MSIFIELD_NULL ) + return TRUE; + + return FALSE; +} + +UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, + LPSTR szValue, DWORD *pcchValue) +{ + MSIRECORD *rec; + UINT len=0, ret; + CHAR buffer[16]; + + TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + ret = ERROR_SUCCESS; + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + wsprintfA(buffer, "%d", rec->fields[iField].u.iVal); + len = lstrlenA( buffer ); + lstrcpynA(szValue, buffer, *pcchValue); + break; + case MSIFIELD_STR: + len = lstrlenA( rec->fields[iField].u.szVal ); + lstrcpynA(szValue, rec->fields[iField].u.szVal, *pcchValue); + break; + case MSIFIELD_WSTR: + len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1, + NULL, 0 , NULL, NULL); + WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1, + szValue, *pcchValue, NULL, NULL); + break; + default: + ret = ERROR_INVALID_PARAMETER; + break; + } + + if( *pcchValue < len ) + ret = ERROR_MORE_DATA; + *pcchValue = len; + + return ret; +} + +UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField, + LPWSTR szValue, DWORD *pcchValue) +{ + MSIRECORD *rec; + UINT len=0, ret; + WCHAR buffer[16]; + const WCHAR szFormat[] = { '%','d',0 }; + + TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + ret = ERROR_SUCCESS; + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal); + len = lstrlenW( buffer ); + lstrcpynW(szValue, buffer, *pcchValue); + break; + case MSIFIELD_WSTR: + len = lstrlenW( rec->fields[iField].u.szwVal ); + lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue); + break; + case MSIFIELD_STR: + len = MultiByteToWideChar( CP_ACP, 0, rec->fields[iField].u.szVal, -1, + NULL, 0 ); + MultiByteToWideChar( CP_ACP, 0, rec->fields[iField].u.szVal, -1, + szValue, *pcchValue); + break; + default: + break; + } + + if( *pcchValue < len ) + ret = ERROR_MORE_DATA; + *pcchValue = len; + + return ret; +} + +UINT WINAPI MsiRecordDataSize(MSIHANDLE hRecord, unsigned int iField) +{ + FIXME("%ld %d\n", hRecord, iField); + return 0; +} + +UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue ) +{ + MSIRECORD *rec; + LPSTR str; + + TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + str = HeapAlloc( GetProcessHeap(), 0, (lstrlenA(szValue) + 1)*sizeof str[0]); + lstrcpyA( str, szValue ); + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_STR; + rec->fields[iField].u.szVal = str; + + return 0; +} + +UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue ) +{ + MSIRECORD *rec; + LPWSTR str; + + TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + str = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(szValue) + 1)*sizeof str[0]); + lstrcpyW( str, szValue ); + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_WSTR; + rec->fields[iField].u.szwVal = str; + + return 0; +} + +UINT WINAPI MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, LPSTR szResult, DWORD *sz) +{ + FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, LPWSTR szResult, DWORD *sz) +{ + FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename) +{ + FIXME("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiRecordSetStreamW(MSIHANDLE hRecord, unsigned int iField, LPCWSTR szFilename) +{ + FIXME("%ld %d %s\n", hRecord, iField, debugstr_w(szFilename)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz) +{ + FIXME("%ld %d %p %p\n",handle,iField,buf,sz); + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/dlls/msi/select.c b/dlls/msi/select.c new file mode 100644 index 00000000000..587707574c4 --- /dev/null +++ b/dlls/msi/select.c @@ -0,0 +1,224 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +/* below is the query interface to a table */ + +typedef struct tagMSISELECTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT num_cols; + UINT max_cols; + UINT cols[1]; +} MSISELECTVIEW; + +static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %d %p\n", sv, row, col, val ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (col==0) || (col>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + + return sv->table->ops->fetch_int( sv->table, row, col, val ); +} + +static UINT SELECT_execute( struct tagMSIVIEW *view, MSIHANDLE record ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %ld\n", sv, record); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->execute( sv->table, record ); +} + +static UINT SELECT_close( struct tagMSIVIEW *view ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p\n", sv ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->close( sv->table ); +} + +static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %p %p\n", sv, rows, cols ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( cols ) + *cols = sv->num_cols; + + return sv->table->ops->get_dimensions( sv->table, rows, NULL ); +} + +static UINT SELECT_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %p %p\n", sv, n, name, type ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (n==0) || (n>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + n = sv->cols[ n - 1 ]; + + return sv->table->ops->get_column_info( sv->table, n, name, type ); +} + +static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %ld\n", sv, eModifyMode, hrec ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->modify( sv->table, eModifyMode, hrec ); +} + +static UINT SELECT_delete( struct tagMSIVIEW *view ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p\n", sv ); + + if( sv->table ) + sv->table->ops->delete( sv->table ); + + HeapFree( GetProcessHeap(), 0, sv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS select_ops = +{ + SELECT_fetch_int, + SELECT_execute, + SELECT_close, + SELECT_get_dimensions, + SELECT_get_column_info, + SELECT_modify, + SELECT_delete +}; + +UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSISELECTVIEW *sv = NULL; + UINT count = 0, r; + + TRACE("%p\n", sv ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + sv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof *sv + count*sizeof (UINT) ); + if( !sv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + sv->view.ops = &select_ops; + sv->db = db; + sv->table = table; + sv->num_cols = 0; + sv->max_cols = count; + *view = (MSIVIEW*) sv; + + return ERROR_SUCCESS; +} + +UINT SELECT_AddColumn( MSIVIEW *view, LPWSTR name ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + UINT r, n=0; + MSIVIEW *table; + + TRACE("%p adding %s\n", sv, debugstr_w( name ) ); + + if( sv->view.ops != &select_ops ) + return ERROR_FUNCTION_FAILED; + + table = sv->table; + if( !table ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_column_info ) + return ERROR_FUNCTION_FAILED; + + if( sv->num_cols >= sv->max_cols ) + return ERROR_FUNCTION_FAILED; + + r = VIEW_find_column( table, name, &n ); + if( r != ERROR_SUCCESS ) + return r; + + sv->cols[sv->num_cols] = n; + TRACE("Translating column %s from %d -> %d\n", + debugstr_w( name ), sv->num_cols, n); + + sv->num_cols++; + + return ERROR_SUCCESS; +} diff --git a/dlls/msi/sql.y b/dlls/msi/sql.y new file mode 100644 index 00000000000..94409a3b0a7 --- /dev/null +++ b/dlls/msi/sql.y @@ -0,0 +1,513 @@ +%{ + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "winbase.h" +#include +#include +#include "query.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +extern int yyerror(char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_yyinput +{ + MSIDATABASE *db; + LPCWSTR command; + DWORD n, len; + MSIVIEW **view; /* view structure for the resulting query */ +} yyinput; + +struct string_list +{ + LPWSTR string; + struct string_list *next; +}; + +static LPWSTR yygetstring( yyinput *info ); +static INT yygetint( yyinput *sql ); +static int yylex( void *yylval, yyinput *info); + +static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, + struct string_list *columns ); +static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, + struct string_list *columns ); + +static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r ); +static struct expr * EXPR_column( LPWSTR column ); +static struct expr * EXPR_ival( INT ival ); +static struct expr * EXPR_sval( LPWSTR string ); + +%} + +%pure-parser + +%union +{ + LPWSTR string; + struct string_list *column_list; + MSIVIEW *table; + struct expr *expr; +} + +%token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC +%token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY +%token TK_CASCADE TK_CASE TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN TK_COMMA +%token TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT +%token TK_CONSTRAINT TK_COPY TK_CREATE +%token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC +%token TK_DISTINCT TK_DOT TK_DROP TK_EACH +%token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN +%token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION +%token TK_GE TK_GLOB TK_GROUP TK_GT +%token TK_HAVING +%token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY +%token TK_ID +%token TK_INSERT TK_INSTEAD TK_INTEGER TK_INTERSECT TK_INTO TK_IS TK_ISNULL +%token TK_JOIN TK_JOIN_KW +%token TK_KEY +%token TK_LE TK_LIKE TK_LIMIT TK_LP TK_LSHIFT TK_LT +%token TK_MATCH TK_MINUS +%token TK_NE TK_NOT TK_NOTNULL TK_NULL +%token TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER +%token TK_PLUS TK_PRAGMA TK_PRIMARY +%token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK +%token TK_ROW TK_RP TK_RSHIFT +%token TK_SELECT TK_SEMI TK_SET TK_SLASH TK_SPACE TK_STAR TK_STATEMENT +%token TK_STRING +%token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER +%token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE +%token TK_UPDATE TK_UPLUS TK_USING +%token TK_VACUUM TK_VALUES TK_VIEW +%token TK_WHEN TK_WHERE + +// These are extra tokens used by the lexer but never seen by the +// parser. We put them in a rule so that the parser generator will +// add them to the parse.h output file. +// +%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION + COLUMN AGG_FUNCTION. + +%type oneselect +%type column table string_or_id +%type selcollist +%type from unorderedsel +%type expr val column_val + +%% + +oneselect: + unorderedsel TK_ORDER TK_BY selcollist + { + yyinput* sql = (yyinput*) info; + + if( !$1 ) + YYABORT; + if( $4 ) + *sql->view = do_order_by( sql->db, $1, $4 ); + else + *sql->view = $1; + } + | unorderedsel + { + yyinput* sql = (yyinput*) info; + + *sql->view = $1; + } + ; + +unorderedsel: + TK_SELECT selcollist from + { + yyinput* sql = (yyinput*) info; + if( !$3 ) + YYABORT; + if( $2 ) + $$ = do_one_select( sql->db, $3, $2 ); + else + $$ = $3; + } + | TK_SELECT TK_DISTINCT selcollist from + { + yyinput* sql = (yyinput*) info; + MSIVIEW *view = $4; + + if( !view ) + YYABORT; + if( $3 ) + view = do_one_select( sql->db, view, $3 ); + DISTINCT_CreateView( sql->db, & $$, view ); + } + ; + +selcollist: + column + { + struct string_list *list; + + list = HeapAlloc( GetProcessHeap(), 0, sizeof *list ); + if( !list ) + YYABORT; + list->string = $1; + list->next = NULL; + + $$ = list; + TRACE("Collist %s\n",debugstr_w($$->string)); + } + | column TK_COMMA selcollist + { + struct string_list *list; + + list = HeapAlloc( GetProcessHeap(), 0, sizeof *list ); + if( !list ) + YYABORT; + list->string = $1; + list->next = $3; + + $$ = list; + TRACE("From table: %s\n",debugstr_w($$->string)); + } + | TK_STAR + { + $$ = NULL; + } + ; + +from: + TK_FROM table + { + yyinput* sql = (yyinput*) info; + + $$ = NULL; + TRACE("From table: %s\n",debugstr_w($2)); + TABLE_CreateView( sql->db, $2, & $$ ); + } + | TK_FROM table TK_WHERE expr + { + yyinput* sql = (yyinput*) info; + MSIVIEW *view = NULL; + UINT r; + + $$ = NULL; + TRACE("From table: %s\n",debugstr_w($2)); + r = TABLE_CreateView( sql->db, $2, &view ); + if( r != ERROR_SUCCESS ) + YYABORT; + r = WHERE_CreateView( sql->db, &view, view ); + if( r != ERROR_SUCCESS ) + YYABORT; + r = WHERE_AddCondition( view, $4 ); + if( r != ERROR_SUCCESS ) + YYABORT; + $$ = view; + } + ; + +expr: + TK_LP expr TK_RP + { + $$ = $2; + } + | column_val TK_EQ column_val + { + $$ = EXPR_complex( $1, OP_EQ, $3 ); + } + | expr TK_AND expr + { + $$ = EXPR_complex( $1, OP_AND, $3 ); + } + | expr TK_OR expr + { + $$ = EXPR_complex( $1, OP_OR, $3 ); + } + | column_val TK_EQ val + { + $$ = EXPR_complex( $1, OP_EQ, $3 ); + } + | column_val TK_GT val + { + $$ = EXPR_complex( $1, OP_GT, $3 ); + } + | column_val TK_LT val + { + $$ = EXPR_complex( $1, OP_LT, $3 ); + } + | column_val TK_LE val + { + $$ = EXPR_complex( $1, OP_LE, $3 ); + } + | column_val TK_GE val + { + $$ = EXPR_complex( $1, OP_GE, $3 ); + } + | column_val TK_NE val + { + $$ = EXPR_complex( $1, OP_NE, $3 ); + } + | column_val TK_IS TK_NULL + { + $$ = EXPR_complex( $1, OP_ISNULL, NULL ); + } + | column_val TK_IS TK_NOT TK_NULL + { + $$ = EXPR_complex( $1, OP_NOTNULL, NULL ); + } + ; + +val: + column_val + { + $$ = $1; + } + | TK_INTEGER + { + yyinput* sql = (yyinput*) info; + $$ = EXPR_ival( yygetint(sql) ); + } + | TK_STRING + { + $$ = EXPR_sval( $1 ); + } + ; + +column_val: + column + { + $$ = EXPR_column( $1 ); + } + ; + +column: + table TK_DOT string_or_id + { + $$ = $3; /* FIXME */ + } + | string_or_id + { + $$ = $1; + } + ; + +table: + string_or_id + { + $$ = $1; + } + ; + +string_or_id: + TK_ID + { + yyinput* sql = (yyinput*) info; + $$ = yygetstring(sql); + } + | TK_STRING + { + yyinput* sql = (yyinput*) info; + $$ = yygetstring(sql); + } + ; + +%% + +int yylex( void *yylval, yyinput *sql) +{ + int token; + + do + { + sql->n += sql->len; + if( ! sql->command[sql->n] ) + return 0; /* end of input */ + + TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); + sql->len = sqliteGetToken( &sql->command[sql->n], &token ); + if( sql->len==0 ) + break; + } + while( token == TK_SPACE ); + + TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); + + return token; +} + +LPWSTR yygetstring( yyinput *sql ) +{ + LPCWSTR p = &sql->command[sql->n]; + LPWSTR str; + UINT len = sql->len; + + /* if there's quotes, remove them */ + if( (p[0]=='`') && (p[len-1]=='`') ) + { + p++; + len -= 2; + } + str = HeapAlloc( GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR)); + if(!str ) + return str; + memcpy(str, p, len*sizeof(WCHAR) ); + str[len]=0; + + return str; +} + +INT yygetint( yyinput *sql ) +{ + LPCWSTR p = &sql->command[sql->n]; + + return atoiW( p ); +} + +int yyerror(char *str) +{ + return 0; +} + +static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, + struct string_list *columns ) +{ + MSIVIEW *view = NULL; + + SELECT_CreateView( db, &view, in ); + if( view ) + { + struct string_list *x = columns; + + while( x ) + { + struct string_list *t = x->next; + SELECT_AddColumn( view, x->string ); + HeapFree( GetProcessHeap(), 0, x->string ); + HeapFree( GetProcessHeap(), 0, x ); + x = t; + } + } + else + ERR("Error creating select query\n"); + return view; +} + +static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, + struct string_list *columns ) +{ + MSIVIEW *view = NULL; + + ORDER_CreateView( db, &view, in ); + if( view ) + { + struct string_list *x = columns; + + while( x ) + { + struct string_list *t = x->next; + ORDER_AddColumn( view, x->string ); + HeapFree( GetProcessHeap(), 0, x->string ); + HeapFree( GetProcessHeap(), 0, x ); + x = t; + } + } + else + ERR("Error creating select query\n"); + return view; +} + +static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r ) +{ + struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e ); + if( e ) + { + e->type = EXPR_COMPLEX; + e->u.expr.left = l; + e->u.expr.op = op; + e->u.expr.right = r; + } + return e; +} + +static struct expr * EXPR_column( LPWSTR column ) +{ + struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e ); + if( e ) + { + e->type = EXPR_COLUMN; + e->u.column = column; + } + return e; +} + +static struct expr * EXPR_ival( INT ival ) +{ + struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e ); + if( e ) + { + e->type = EXPR_IVAL; + e->u.ival = ival; + } + return e; +} + +static struct expr * EXPR_sval( LPWSTR string ) +{ + struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e ); + if( e ) + { + e->type = EXPR_SVAL; + e->u.sval = string; + } + return e; +} + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview ) +{ + yyinput sql; + int r; + + *phview = NULL; + + sql.db = db; + sql.command = command; + sql.n = 0; + sql.len = 0; + sql.view = phview; + + r = yyparse(&sql); + + TRACE("Parse returned %d\n", r); + if( r ) + { + if( *sql.view ) + (*sql.view)->ops->delete( *sql.view ); + return ERROR_BAD_QUERY_SYNTAX; + } + + return ERROR_SUCCESS; +} + diff --git a/dlls/msi/suminfo.c b/dlls/msi/suminfo.c new file mode 100644 index 00000000000..3e54818f6c3 --- /dev/null +++ b/dlls/msi/suminfo.c @@ -0,0 +1,223 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 + */ + +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "objidl.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n',0 }; + +static void MSI_CloseSummaryInfo( VOID *arg ) +{ + MSISUMMARYINFO *suminfo = (MSISUMMARYINFO *) arg; + IPropertyStorage_Release( suminfo->propstg ); +} + +UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, + LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *phSummaryInfo) +{ + LPWSTR szwDatabase = NULL; + UINT ret; + + TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), + uiUpdateCount, phSummaryInfo); + + if( szDatabase ) + { + szwDatabase = HEAP_strdupAtoW( GetProcessHeap(), 0, szDatabase ); + if( !szwDatabase ) + return ERROR_FUNCTION_FAILED; + } + + ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, phSummaryInfo); + + if( szwDatabase ) + HeapFree( GetProcessHeap(), 0, szwDatabase ); + + return ret; +} + +UINT WINAPI MsiGetSummaryInformationW(MSIHANDLE hDatabase, + LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *phSummaryInfo) +{ + HRESULT r; + MSIHANDLE handle, hdb = hDatabase; + MSISUMMARYINFO *suminfo; + MSIDATABASE *db; + UINT ret = ERROR_SUCCESS; + IPropertySetStorage *psstg = NULL; + IPropertyStorage *ps = NULL; + DWORD grfMode; + + TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase), + uiUpdateCount, phSummaryInfo); + + if( !phSummaryInfo ) + return ERROR_INVALID_PARAMETER; + + if( szDatabase ) + { + UINT res; + + res = MsiOpenDatabaseW(szDatabase, NULL, &hdb); + if( res != ERROR_SUCCESS ) + return res; + } + + db = msihandle2msiinfo(hdb, MSIHANDLETYPE_DATABASE); + if( !db ) + return ERROR_INVALID_PARAMETER; + + r = IStorage_QueryInterface( db->storage, + &IID_IPropertySetStorage, (LPVOID)&psstg); + if( FAILED( r ) ) + { + ERR("IStorage -> IPropertySetStorage failed\n"); + return ERROR_FUNCTION_FAILED; + } + ERR("storage = %p propertysetstorage = %p\n", db->storage, psstg); + + grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE; + r = IPropertySetStorage_Open( psstg, &FMTID_SummaryInformation, grfMode, &ps ); + if( FAILED( r ) ) + { + ERR("failed to get IPropertyStorage r=%08lx\n",r); + ret = ERROR_FUNCTION_FAILED; + goto end; + } + + handle = alloc_msihandle( MSIHANDLETYPE_SUMMARYINFO, + sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo ); + if( !handle ) + { + ret = ERROR_FUNCTION_FAILED; + goto end; + } + + suminfo = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !suminfo ) + { + ret = ERROR_FUNCTION_FAILED; + goto end; + } + + IPropertyStorage_AddRef(ps); + suminfo->propstg = ps; + *phSummaryInfo = handle; + +end: + if( ps ) + IPropertyStorage_Release(ps); + if( psstg ) + IPropertySetStorage_Release(psstg); + if( !hDatabase ) + MsiCloseHandle( hdb ); + + return ret; +} + +UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount) +{ + MSISUMMARYINFO *suminfo; + + FIXME("%ld %p\n",hSummaryInfo, pCount); + + suminfo = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO ); + if( !suminfo ) + return ERROR_INVALID_HANDLE; + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiSummaryInfoGetPropertyA( + MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue, + FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf) +{ + MSISUMMARYINFO *suminfo; + HRESULT r; + PROPSPEC spec; + PROPVARIANT var; + + TRACE("%ld %d %p %p %p %p %p\n", + hSummaryInfo, uiProperty, puiDataType, piValue, + pftValue, szValueBuf, pcchValueBuf); + + suminfo = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO ); + if( !suminfo ) + return ERROR_INVALID_HANDLE; + + spec.ulKind = PRSPEC_PROPID; + spec.DUMMYUNIONNAME.propid = uiProperty; + + r = IPropertyStorage_ReadMultiple( suminfo->propstg, 1, &spec, &var); + if( FAILED(r) ) + return ERROR_FUNCTION_FAILED; + + if( puiDataType ) + *puiDataType = var.vt; + + switch( var.vt ) + { + case VT_I4: + if( piValue ) + *piValue = var.DUMMYUNIONNAME.lVal; + break; + case VT_LPSTR: + if( pcchValueBuf && szValueBuf ) + { + lstrcpynA(szValueBuf, var.DUMMYUNIONNAME.pszVal, *pcchValueBuf ); + *pcchValueBuf = lstrlenA( var.DUMMYUNIONNAME.pszVal ); + } + break; + case VT_FILETIME: + if( pftValue ) + memcpy(pftValue, &var.DUMMYUNIONNAME.filetime, sizeof (FILETIME) ); + break; + case VT_EMPTY: + break; + default: + FIXME("Unknown property variant type\n"); + break; + } + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiSummaryInfoGetPropertyW( + MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue, + FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf) +{ + FIXME("%ld %d %p %p %p %p %p\n", + hSummaryInfo, uiProperty, puiDataType, piValue, + pftValue, szValueBuf, pcchValueBuf); + + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/dlls/msi/table.c b/dlls/msi/table.c new file mode 100644 index 00000000000..f0f7cbbbc46 --- /dev/null +++ b/dlls/msi/table.c @@ -0,0 +1,887 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tagMSICOLUMNINFO +{ + LPWSTR tablename; + UINT number; + LPWSTR colname; + UINT type; + UINT offset; +} MSICOLUMNINFO; + +struct tagMSITABLE +{ + USHORT *data; + UINT size; + UINT ref_count; + /* MSICOLUMNINFO *columns; */ + /* UINT num_cols; */ + struct tagMSITABLE *next; + struct tagMSITABLE *prev; + WCHAR name[1]; +} ; + +#define MAX_STREAM_NAME 0x1f + +static int utf2mime(int x) +{ + if( (x>='0') && (x<='9') ) + return x-'0'; + if( (x>='A') && (x<='Z') ) + return x-'A'+10; + if( (x>='a') && (x<='z') ) + return x-'a'+10+26; + if( x=='.' ) + return 10+26+26; + if( x=='_' ) + return 10+26+26+1; + return -1; +} + +static BOOL encode_streamname(BOOL bTable, LPCWSTR in, LPWSTR out) +{ + DWORD count = MAX_STREAM_NAME; + DWORD ch, next; + + if( bTable ) + { + *out++ = 0x4840; + count --; + } + while( count -- ) + { + ch = *in++; + if( !ch ) + { + *out = ch; + return TRUE; + } + if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) ) + { + ch = utf2mime(ch) + 0x4800; + next = *in; + if( next && (next<0x80) ) + { + next = utf2mime(next); + if( next >= 0 ) + { + next += 0x3ffffc0; + ch += (next<<6); + in++; + } + } + } + *out++ = ch; + } + return FALSE; +} + +#if 0 +static int mime2utf(int x) +{ + if( x<10 ) + return x + '0'; + if( x<(10+26)) + return x - 10 + 'A'; + if( x<(10+26+26)) + return x - 10 - 26 + 'a'; + if( x == (10+26+26) ) + return '.'; + return '_'; +} + +static BOOL decode_streamname(LPWSTR in, LPWSTR out) +{ + WCHAR ch; + DWORD count = 0; + + while ( (ch = *in++) ) + { + if( (ch >= 0x3800 ) && (ch < 0x4840 ) ) + { + if( ch >= 0x4800 ) + ch = mime2utf(ch-0x4800); + else + { + ch -= 0x3800; + *out++ = mime2utf(ch&0x3f); + count++; + ch = mime2utf((ch>>6)&0x3f); + } + } + *out++ = ch; + count++; + } + *out = 0; + return count; +} +#endif + +UINT read_table_from_storage(IStorage *stg, LPCWSTR name, MSITABLE **ptable) +{ + WCHAR buffer[0x20]; + HRESULT r; + IStream *stm = NULL; + STATSTG stat; + UINT ret = ERROR_FUNCTION_FAILED; + VOID *data; + ULONG sz, count; + MSITABLE *t; + + encode_streamname(TRUE, name, buffer); + + TRACE("%s -> %s\n",debugstr_w(name),debugstr_w(buffer)); + + r = IStorage_OpenStream(stg, buffer, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + if( FAILED( r ) ) + { + ERR("open stream failed r = %08lx!\n",r); + return r; + } + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME ); + if( FAILED( r ) ) + { + ERR("open stream failed r = %08lx!\n",r); + goto end; + } + + if( stat.cbSize.QuadPart >> 32 ) + { + ERR("Too big!\n"); + goto end; + } + + sz = stat.cbSize.QuadPart; + data = HeapAlloc( GetProcessHeap(), 0, sz ); + if( !data ) + { + ERR("couldn't allocate memory r=%08lx!\n",r); + goto end; + } + + r = IStream_Read(stm, data, sz, &count ); + if( FAILED( r ) ) + { + HeapFree( GetProcessHeap(), 0, data ); + ERR("read stream failed r = %08lx!\n",r); + goto end; + } + + t = HeapAlloc( GetProcessHeap(), 0, sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) ); + if( !t ) + { + HeapFree( GetProcessHeap(), 0, data ); + ERR("malloc failed!\n"); + goto end; + } + + if( count == sz ) + { + ret = ERROR_SUCCESS; + t->size = sz; + t->data = data; + lstrcpyW( t->name, name ); + t->ref_count = 1; + *ptable = t; + } + else + { + HeapFree( GetProcessHeap(), 0, data ); + ERR("Count != sz\n"); + } + +end: + if( stm ) + IStream_Release( stm ); + + return ret; +} + +/* add this table to the list of cached tables in the database */ +void add_table(MSIDATABASE *db, MSITABLE *table) +{ + table->next = db->first_table; + table->prev = NULL; + if( db->first_table ) + db->first_table->prev = table; + else + db->last_table = table; + db->first_table = table; +} + +/* remove from the list of cached tables */ +void remove_table( MSIDATABASE *db, MSITABLE *table ) +{ + if( table->next ) + table->next->prev = table->prev; + else + db->last_table = table->prev; + if( table->prev ) + table->prev->next = table->next; + else + db->first_table = table->next; + table->next = NULL; + table->prev = NULL; +} + +void release_table( MSIDATABASE *db, MSITABLE *table ) +{ + if( !table->ref_count ) + ERR("Trying to destroy table with refcount 0\n"); + table->ref_count --; + if( !table->ref_count ) + { + remove_table( db, table ); + HeapFree( GetProcessHeap(), 0, table->data ); + HeapFree( GetProcessHeap(), 0, table ); + TRACE("Destroyed table %s\n", debugstr_w(table->name)); + } +} + +void free_cached_tables( MSIDATABASE *db ) +{ + while( db->first_table ) + { + MSITABLE *t = db->first_table; + + if ( --t->ref_count ) + ERR("table ref count not zero for %s\n", debugstr_w(t->name)); + remove_table( db, t ); + HeapFree( GetProcessHeap(), 0, t->data ); + HeapFree( GetProcessHeap(), 0, t ); + } +} + +UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable) +{ + MSITABLE *t; + + for( t = db->first_table; t; t=t->next ) + { + if( !lstrcmpW( name, t->name ) ) + { + *ptable = t; + return ERROR_SUCCESS; + } + } + + return ERROR_FUNCTION_FAILED; +} + +UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable) +{ + UINT r; + + *ptable = NULL; + + /* first, see if the table is cached */ + r = find_cached_table( db, name, ptable ); + if( r == ERROR_SUCCESS ) + { + (*ptable)->ref_count++; + return r; + } + + r = read_table_from_storage( db->storage, name, ptable ); + if( r != ERROR_SUCCESS ) + return r; + + /* add the table to the list */ + add_table( db, *ptable ); + (*ptable)->ref_count++; + + return ERROR_SUCCESS; +} + +UINT dump_string_table(MSIDATABASE *db) +{ + DWORD i, count, offset, len; + string_table *st = &db->strings; + + MESSAGE("%d,%d bytes\n",st->pool.size,st->info.size); + + count = st->pool.size/4; + + offset = 0; + for(i=0; ipool.data[i*2]; + MESSAGE("[%2ld] = %s\n",i, debugstr_an(st->info.data+offset,len)); + offset += len; + } + + return ERROR_SUCCESS; +} + +UINT load_string_table( MSIDATABASE *db, string_table *pst) +{ + MSITABLE *pool = NULL, *info = NULL; + UINT r, ret = ERROR_FUNCTION_FAILED; + const WCHAR szStringData[] = { + '_','S','t','r','i','n','g','D','a','t','a',0 }; + const WCHAR szStringPool[] = { + '_','S','t','r','i','n','g','P','o','o','l',0 }; + + r = get_table( db, szStringPool, &pool ); + if( r != ERROR_SUCCESS) + goto end; + r = get_table( db, szStringData, &info ); + if( r != ERROR_SUCCESS) + goto end; + + pst->pool.size = pool->size; + pst->pool.data = pool->data; + pst->info.size = info->size; + pst->info.data = (CHAR *)info->data; + + TRACE("Loaded %d,%d bytes\n",pst->pool.size,pst->info.size); + + ret = ERROR_SUCCESS; + +end: + if( info ) + release_table( db, info ); + if( pool ) + release_table( db, pool ); + + return ret; +} + +UINT msi_id2string( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz ) +{ + DWORD i, count, offset, len; + + count = st->pool.size/4; + TRACE("Finding string %d of %ld\n", string_no, count); + if(string_no >= count) + return ERROR_FUNCTION_FAILED; + + offset = 0; + for(i=0; ipool.data[i*2]; + offset += len; + } + + len = st->pool.data[i*2]; + + if( !buffer ) + { + *sz = len; + return ERROR_SUCCESS; + } + + if( (offset+len) > st->info.size ) + return ERROR_FUNCTION_FAILED; + + len = MultiByteToWideChar(CP_ACP,0,&st->info.data[offset],len,buffer,*sz-1); + buffer[len] = 0; + *sz = len+1; + + return ERROR_SUCCESS; +} + +static UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id ) +{ + DWORD i, count, offset, len, sz; + UINT r = ERROR_INVALID_PARAMETER; + LPSTR str; + + count = st->pool.size/4; + TRACE("Finding string %s in %ld strings\n", debugstr_w(buffer), count); + + sz = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL ); + str = HeapAlloc( GetProcessHeap(), 0, sz ); + WideCharToMultiByte( CP_ACP, 0, buffer, -1, str, sz, NULL, NULL ); + + offset = 0; + for(i=0; ipool.data[i*2]; + if ( ( sz == len ) && !memcmp( str, st->info.data+offset, sz ) ) + { + *id = i; + r = ERROR_SUCCESS; + break; + } + offset += len; + } + + if( str ) + HeapFree( GetProcessHeap(), 0, str ); + + return r; +} + +static LPWSTR strdupW( LPCWSTR str ) +{ + UINT len = lstrlenW( str ); + LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) ); + if( ret ) + lstrcpyW( ret, str ); + return ret; +} + +static inline UINT bytes_per_column( MSICOLUMNINFO *col ) +{ + if( col->type & MSITYPE_STRING ) + return 2; + if( (col->type & 0xff) > 4 ) + ERR("Invalid column size!\n"); + return col->type & 0xff; +} + +/* information for default tables */ +const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; +const WCHAR szTable[] = { 'T','a','b','l','e',0 }; +const WCHAR szName[] = { 'N','a','m','e',0 }; +const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 }; +const WCHAR szColumn[] = { 'C','o','l','u','m','n',0 }; +const WCHAR szNumber[] = { 'N','u','m','b','e','r',0 }; +const WCHAR szType[] = { 'T','y','p','e',0 }; + +struct standard_table { + LPCWSTR tablename; + LPCWSTR columnname; + UINT number; + UINT type; +} MSI_standard_tables[] = +{ + { szTables, szName, 1, MSITYPE_VALID | MSITYPE_STRING | 32}, + { szColumns, szTable, 1, MSITYPE_VALID | MSITYPE_STRING | 32}, + { szColumns, szNumber, 2, MSITYPE_VALID | 2}, + { szColumns, szName, 3, MSITYPE_VALID | MSITYPE_STRING | 32}, + { szColumns, szType, 4, MSITYPE_VALID | 2}, +}; + +#define STANDARD_TABLE_COUNT \ + (sizeof(MSI_standard_tables)/sizeof(struct standard_table)) + +UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz) +{ + DWORD i, n=0; + + for(i=0; i= *sz) ) + break; + } + *sz = n; + return ERROR_SUCCESS; +} + +LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid) +{ + UINT sz=0, r; + LPWSTR str; + + r = msi_id2string( &db->strings, stringid, NULL, &sz ); + if( r != ERROR_SUCCESS ) + return NULL; + sz ++; /* space for NUL char */ + str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR)); + if( !str ) + return str; + r = msi_id2string( &db->strings, stringid, str, &sz ); + if( r == ERROR_SUCCESS ) + return str; + HeapFree( GetProcessHeap(), 0, str ); + return NULL; +} + +UINT get_tablecolumns( MSIDATABASE *db, + LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz) +{ + UINT r, i, n=0, table_id, count, maxcount = *sz; + MSITABLE *table = NULL; + const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 }; + + /* first check if there is a default table with that name */ + r = get_defaulttablecolumns( szTableName, colinfo, sz ); + if( ( r == ERROR_SUCCESS ) && *sz ) + return r; + + r = get_table( db, szColumns, &table); + if( r != ERROR_SUCCESS ) + { + ERR("table %s not available\n", debugstr_w(szColumns)); + return r; + } + + /* convert table and column names to IDs from the string table */ + r = msi_string2id( &db->strings, szTableName, &table_id ); + if( r != ERROR_SUCCESS ) + { + release_table( db, table ); + ERR("Couldn't find id for %s\n", debugstr_w(szTableName)); + return r; + } + + TRACE("Table id is %d\n", table_id); + + count = table->size/8; + for( i=0; idata[ i ] != table_id ) + continue; + if( colinfo ) + { + UINT id = table->data[ i + count*2 ]; + colinfo[n].tablename = MSI_makestring( db, table_id ); + colinfo[n].number = table->data[ i + count ] - (1<<15); + colinfo[n].colname = MSI_makestring( db, id ); + colinfo[n].type = table->data[ i + count*3 ]; + /* this assumes that columns are in order in the table */ + if( n ) + colinfo[n].offset = colinfo[n-1].offset + + bytes_per_column( &colinfo[n-1] ); + else + colinfo[n].offset = 0; + TRACE("table %s column %d is [%s] (%d) with type %08x " + "offset %d at row %d\n", debugstr_w(szTableName), + colinfo[n].number, debugstr_w(colinfo[n].colname), + id, colinfo[n].type, colinfo[n].offset, i); + if( n != (colinfo[n].number-1) ) + { + ERR("oops. data in the _Columns table isn't in the right " + "order for table %s\n", debugstr_w(szTableName)); + return ERROR_FUNCTION_FAILED; + } + } + n++; + if( colinfo && ( n >= maxcount ) ) + break; + } + *sz = n; + + release_table( db, table ); + + return ERROR_SUCCESS; +} + +/* try to find the table name in the _Tables table */ +BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name ) +{ + const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; + const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 }; + UINT r, table_id = 0, i, count; + MSITABLE *table = NULL; + + if( !lstrcmpW( name, szTables ) ) + return TRUE; + if( !lstrcmpW( name, szColumns ) ) + return TRUE; + + r = msi_string2id( &db->strings, name, &table_id ); + if( r != ERROR_SUCCESS ) + { + ERR("Couldn't find id for %s\n", debugstr_w(name)); + return FALSE; + } + + r = get_table( db, szTables, &table); + if( r != ERROR_SUCCESS ) + { + ERR("table %s not available\n", debugstr_w(szTables)); + return FALSE; + } + + count = table->size/2; + for( i=0; idata[ i ] == table_id ) + break; + + release_table( db, table ); + + if (i!=count) + return TRUE; + + return FALSE; +} + +/* below is the query interface to a table */ + +typedef struct tagMSITABLEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSITABLE *table; + MSICOLUMNINFO *columns; + UINT num_cols; + UINT row_size; + WCHAR name[1]; +} MSITABLEVIEW; + + +static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT offset, num_rows, n; + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col>tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + /* how many rows are there ? */ + num_rows = tv->table->size / tv->row_size; + if( row >= num_rows ) + return ERROR_NO_MORE_ITEMS; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + offset = row + (tv->columns[col-1].offset/2) * num_rows; + n = bytes_per_column( &tv->columns[col-1] ); + switch( n ) + { + case 4: + offset = row*2 + (tv->columns[col-1].offset/2) * num_rows; + *val = tv->table->data[offset] + (tv->table->data[offset + 1] << 16); + break; + case 2: + offset = row + (tv->columns[col-1].offset/2) * num_rows; + *val = tv->table->data[offset]; + break; + default: + ERR("oops! what is %d bytes per column?\n", n ); + return ERROR_FUNCTION_FAILED; + } + + TRACE("Data [%d][%d] = %d \n", row, col, *val ); + + return ERROR_SUCCESS; +} + +static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT r; + + TRACE("%p %ld\n", tv, record); + + if( tv->table ) + return ERROR_FUNCTION_FAILED; + + r = get_table( tv->db, tv->name, &tv->table ); + if( r != ERROR_SUCCESS ) + return r; + + return ERROR_SUCCESS; +} + +static UINT TABLE_close( struct tagMSIVIEW *view ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p\n", view ); + + if( !tv->table ) + return ERROR_FUNCTION_FAILED; + + release_table( tv->db, tv->table ); + tv->table = NULL; + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %p %p\n", view, rows, cols ); + + if( cols ) + *cols = tv->num_cols; + if( rows ) + { + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + *rows = tv->table->size / tv->row_size; + } + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %d %p %p\n", tv, n, name, type ); + + if( ( n == 0 ) || ( n > tv->num_cols ) ) + return ERROR_INVALID_PARAMETER; + + if( name ) + { + *name = strdupW( tv->columns[n-1].colname ); + if( !*name ) + return ERROR_FUNCTION_FAILED; + } + if( type ) + *type = tv->columns[n-1].type; + + return ERROR_SUCCESS; +} + +static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + FIXME("%p %d %ld\n", view, eModifyMode, hrec ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +static UINT TABLE_delete( struct tagMSIVIEW *view ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p\n", view ); + + if( tv->table ) + release_table( tv->db, tv->table ); + tv->table = NULL; + + if( tv->columns ) + { + UINT i; + for( i=0; inum_cols; i++) + { + HeapFree( GetProcessHeap(), 0, tv->columns[i].colname ); + HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename ); + } + HeapFree( GetProcessHeap(), 0, tv->columns ); + } + tv->columns = NULL; + + HeapFree( GetProcessHeap(), 0, tv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS table_ops = +{ + TABLE_fetch_int, + TABLE_execute, + TABLE_close, + TABLE_get_dimensions, + TABLE_get_column_info, + TABLE_modify, + TABLE_delete +}; + +UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view ) +{ + MSITABLEVIEW *tv ; + UINT r, sz, column_count; + MSICOLUMNINFO *columns, *last_col; + + TRACE("%p %s %p\n", db, debugstr_w(name), view ); + + /* get the number of columns in this table */ + column_count = 0; + r = get_tablecolumns( db, name, NULL, &column_count ); + if( r != ERROR_SUCCESS ) + return r; + + /* if there's no columns, there's no table */ + if( column_count == 0 ) + return ERROR_INVALID_PARAMETER; + + TRACE("Table found\n"); + + sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ; + tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz ); + if( !tv ) + return ERROR_FUNCTION_FAILED; + + columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO)); + if( !columns ) + { + HeapFree( GetProcessHeap(), 0, tv ); + return ERROR_FUNCTION_FAILED; + } + + r = get_tablecolumns( db, name, columns, &column_count ); + if( r != ERROR_SUCCESS ) + { + HeapFree( GetProcessHeap(), 0, columns ); + HeapFree( GetProcessHeap(), 0, tv ); + return ERROR_FUNCTION_FAILED; + } + + TRACE("Table has %d columns\n", column_count); + + last_col = &columns[column_count-1]; + + /* fill the structure */ + tv->view.ops = &table_ops; + tv->db = db; + tv->columns = columns; + tv->num_cols = column_count; + tv->table = NULL; + tv->row_size = last_col->offset + bytes_per_column( last_col ); + + TRACE("one row is %d bytes\n", tv->row_size ); + + *view = (MSIVIEW*) tv; + lstrcpyW( tv->name, name ); + + return ERROR_SUCCESS; +} diff --git a/dlls/msi/tokenize.c b/dlls/msi/tokenize.c new file mode 100644 index 00000000000..3816e510f35 --- /dev/null +++ b/dlls/msi/tokenize.c @@ -0,0 +1,379 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** An tokenizer for SQL +** +** This file contains C code that splits an SQL input string up into +** individual tokens and sends those tokens one-by-one over to the +** parser for analysis. +*/ + +#include "winbase.h" +#include +#include +#include "wine/debug.h" +#include "winnls.h" +#include "query.h" +#include "y.tab.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* +** All the keywords of the SQL language are stored as in a hash +** table composed of instances of the following structure. +*/ +typedef struct Keyword Keyword; +struct Keyword { + const char *zName; /* The keyword name */ + int tokenType; /* The token value for this keyword */ +}; + +/* +** These are the keywords +*/ +static const Keyword aKeywordTable[] = { + { "ABORT", TK_ABORT }, + { "AFTER", TK_AFTER }, + { "ALL", TK_ALL }, + { "AND", TK_AND }, + { "AS", TK_AS }, + { "ASC", TK_ASC }, + { "BEFORE", TK_BEFORE }, + { "BEGIN", TK_BEGIN }, + { "BETWEEN", TK_BETWEEN }, + { "BY", TK_BY }, + { "CASCADE", TK_CASCADE }, + { "CASE", TK_CASE }, + { "CHECK", TK_CHECK }, + { "CLUSTER", TK_CLUSTER }, + { "COLLATE", TK_COLLATE }, + { "COMMIT", TK_COMMIT }, + { "CONFLICT", TK_CONFLICT }, + { "CONSTRAINT", TK_CONSTRAINT }, + { "COPY", TK_COPY }, + { "CREATE", TK_CREATE }, + { "CROSS", TK_JOIN_KW }, + { "DEFAULT", TK_DEFAULT }, + { "DEFERRED", TK_DEFERRED }, + { "DEFERRABLE", TK_DEFERRABLE }, + { "DELETE", TK_DELETE }, + { "DELIMITERS", TK_DELIMITERS }, + { "DESC", TK_DESC }, + { "DISTINCT", TK_DISTINCT }, + { "DROP", TK_DROP }, + { "END", TK_END }, + { "EACH", TK_EACH }, + { "ELSE", TK_ELSE }, + { "EXCEPT", TK_EXCEPT }, + { "EXPLAIN", TK_EXPLAIN }, + { "FAIL", TK_FAIL }, + { "FOR", TK_FOR }, + { "FOREIGN", TK_FOREIGN }, + { "FROM", TK_FROM }, + { "FULL", TK_JOIN_KW }, + { "GLOB", TK_GLOB }, + { "GROUP", TK_GROUP }, + { "HAVING", TK_HAVING }, + { "IGNORE", TK_IGNORE }, + { "IMMEDIATE", TK_IMMEDIATE }, + { "IN", TK_IN }, + { "INDEX", TK_INDEX }, + { "INITIALLY", TK_INITIALLY }, + { "INNER", TK_JOIN_KW }, + { "INSERT", TK_INSERT }, + { "INSTEAD", TK_INSTEAD }, + { "INTERSECT", TK_INTERSECT }, + { "INTO", TK_INTO }, + { "IS", TK_IS }, + { "ISNULL", TK_ISNULL }, + { "JOIN", TK_JOIN }, + { "KEY", TK_KEY }, + { "LEFT", TK_JOIN_KW }, + { "LIKE", TK_LIKE }, + { "LIMIT", TK_LIMIT }, + { "MATCH", TK_MATCH }, + { "NATURAL", TK_JOIN_KW }, + { "NOT", TK_NOT }, + { "NOTNULL", TK_NOTNULL }, + { "NULL", TK_NULL }, + { "OF", TK_OF }, + { "OFFSET", TK_OFFSET }, + { "ON", TK_ON }, + { "OR", TK_OR }, + { "ORDER", TK_ORDER }, + { "OUTER", TK_JOIN_KW }, + { "PRAGMA", TK_PRAGMA }, + { "PRIMARY", TK_PRIMARY }, + { "RAISE", TK_RAISE }, + { "REFERENCES", TK_REFERENCES }, + { "REPLACE", TK_REPLACE }, + { "RESTRICT", TK_RESTRICT }, + { "RIGHT", TK_JOIN_KW }, + { "ROLLBACK", TK_ROLLBACK }, + { "ROW", TK_ROW }, + { "SELECT", TK_SELECT }, + { "SET", TK_SET }, + { "STATEMENT", TK_STATEMENT }, + { "TABLE", TK_TABLE }, + { "TEMP", TK_TEMP }, + { "TEMPORARY", TK_TEMP }, + { "THEN", TK_THEN }, + { "TRANSACTION", TK_TRANSACTION }, + { "TRIGGER", TK_TRIGGER }, + { "UNION", TK_UNION }, + { "UNIQUE", TK_UNIQUE }, + { "UPDATE", TK_UPDATE }, + { "USING", TK_USING }, + { "VACUUM", TK_VACUUM }, + { "VALUES", TK_VALUES }, + { "VIEW", TK_VIEW }, + { "WHEN", TK_WHEN }, + { "WHERE", TK_WHERE }, +}; + +#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) ) + +/* +** This function looks up an identifier to determine if it is a +** keyword. If it is a keyword, the token code of that keyword is +** returned. If the input is not a keyword, TK_ID is returned. +*/ +int sqliteKeywordCode(const WCHAR *z, int n){ + int i, len; + char buffer[0x10]; + + len = WideCharToMultiByte( CP_ACP, 0, z, n, buffer, sizeof buffer, NULL, NULL ); + for(i=0; i' ){ + *tokenType = TK_NE; + return 2; + }else if( z[1]=='<' ){ + *tokenType = TK_LSHIFT; + return 2; + }else{ + *tokenType = TK_LT; + return 1; + } + } + case '>': { + if( z[1]=='=' ){ + *tokenType = TK_GE; + return 2; + }else if( z[1]=='>' ){ + *tokenType = TK_RSHIFT; + return 2; + }else{ + *tokenType = TK_GT; + return 1; + } + } + case '!': { + if( z[1]!='=' ){ + *tokenType = TK_ILLEGAL; + return 2; + }else{ + *tokenType = TK_NE; + return 2; + } + } + case '|': { + if( z[1]!='|' ){ + *tokenType = TK_BITOR; + return 1; + }else{ + *tokenType = TK_CONCAT; + return 2; + } + } + case ',': { + *tokenType = TK_COMMA; + return 1; + } + case '&': { + *tokenType = TK_BITAND; + return 1; + } + case '~': { + *tokenType = TK_BITNOT; + return 1; + } + case '`': case '\'': case '"': { + int delim = z[0]; + for(i=1; z[i]; i++){ + if( z[i]==delim ){ + if( z[i+1]==delim ){ + i++; + }else{ + break; + } + } + } + if( z[i] ) i++; + *tokenType = TK_STRING; + return i; + } + case '.': { + if( !isdigit(z[1]) ){ + *tokenType = TK_DOT; + return 1; + } + /* Fall thru into the next case */ + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + *tokenType = TK_INTEGER; + for(i=1; isdigit(z[i]); i++){} + if( z[i]=='.' ){ + i++; + while( isdigit(z[i]) ){ i++; } + *tokenType = TK_FLOAT; + } + if( (z[i]=='e' || z[i]=='E') && + ( isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2])) + ) + ){ + i += 2; + while( isdigit(z[i]) ){ i++; } + *tokenType = TK_FLOAT; + }else if( z[0]=='.' ){ + *tokenType = TK_FLOAT; + } + return i; + } + case '[': { + for(i=1; z[i] && z[i-1]!=']'; i++){} + *tokenType = TK_ID; + return i; + } + default: { + if( !isIdChar[*z] ){ + break; + } + for(i=1; isIdChar[z[i]]; i++){} + *tokenType = sqliteKeywordCode(z, i); + return i; + } + } + *tokenType = TK_ILLEGAL; + return 1; +} diff --git a/dlls/msi/where.c b/dlls/msi/where.c new file mode 100644 index 00000000000..520293f09e9 --- /dev/null +++ b/dlls/msi/where.c @@ -0,0 +1,384 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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 "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +/* below is the query interface to a table */ + +typedef struct tagMSIWHEREVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT row_count; + UINT *reorder; + struct expr *cond; +} MSIWHEREVIEW; + +static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %d %p\n", wv, row, col, val ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( row > wv->row_count ) + return ERROR_NO_MORE_ITEMS; + + row = wv->reorder[ row ]; + + return wv->table->ops->fetch_int( wv->table, row, col, val ); +} + +static UINT INT_evaluate( UINT lval, UINT op, UINT rval ) +{ + switch( op ) + { + case OP_EQ: + return ( lval == rval ); + case OP_AND: + return ( lval && rval ); + case OP_OR: + return ( lval || rval ); + case OP_GT: + return ( lval > rval ); + case OP_LT: + return ( lval < rval ); + case OP_LE: + return ( lval <= rval ); + case OP_GE: + return ( lval >= rval ); + case OP_NE: + return ( lval != rval ); + case OP_ISNULL: + return ( !lval ); + case OP_NOTNULL: + return ( lval ); + default: + ERR("Unknown operator %d\n", op ); + } + return 0; +} + +static UINT WHERE_evaluate( MSIVIEW *table, UINT row, + struct expr *cond, UINT *val ) +{ + UINT r, lval, rval; + + if( !cond ) + return ERROR_SUCCESS; + + switch( cond->type ) + { + case EXPR_COL_NUMBER: + return table->ops->fetch_int( table, row, cond->u.col_number, val ); + + /* case EXPR_IVAL: + *val = cond->u.ival; + return ERROR_SUCCESS; */ + + case EXPR_UVAL: + *val = cond->u.uval; + return ERROR_SUCCESS; + + case EXPR_COMPLEX: + r = WHERE_evaluate( table, row, cond->u.expr.left, &lval ); + if( r != ERROR_SUCCESS ) + return r; + r = WHERE_evaluate( table, row, cond->u.expr.right, &rval ); + if( r != ERROR_SUCCESS ) + return r; + *val = INT_evaluate( lval, cond->u.expr.op, rval ); + return ERROR_SUCCESS; + + default: + ERR("Invalid expression type\n"); + break; + } + + return ERROR_SUCCESS; + +} + +static UINT WHERE_execute( struct tagMSIVIEW *view, MSIHANDLE record ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + UINT count = 0, r, val, i; + MSIVIEW *table = wv->table; + + TRACE("%p %ld\n", wv, record); + + if( !table ) + return ERROR_FUNCTION_FAILED; + + r = table->ops->execute( table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = table->ops->get_dimensions( table, &count, NULL ); + if( r != ERROR_SUCCESS ) + return r; + + wv->reorder = HeapAlloc( GetProcessHeap(), 0, count*sizeof(UINT) ); + if( !wv->reorder ) + return ERROR_FUNCTION_FAILED; + + for( i=0; icond, &val ); + if( r != ERROR_SUCCESS ) + return r; + if( val ) + wv->reorder[ wv->row_count ++ ] = i; + } + + return ERROR_SUCCESS; +} + +static UINT WHERE_close( struct tagMSIVIEW *view ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p\n", wv ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( wv->reorder ) + HeapFree( GetProcessHeap(), 0, wv->reorder ); + wv->reorder = NULL; + + return wv->table->ops->close( wv->table ); +} + +static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %p %p\n", wv, rows, cols ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( rows ) + { + if( !wv->reorder ) + return ERROR_FUNCTION_FAILED; + *rows = wv->row_count; + } + + return wv->table->ops->get_dimensions( wv->table, NULL, cols ); +} + +static UINT WHERE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %p %p\n", wv, n, name, type ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + return wv->table->ops->get_column_info( wv->table, n, name, type ); +} + +static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %ld\n", wv, eModifyMode, hrec ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + return wv->table->ops->modify( wv->table, eModifyMode, hrec ); +} + +static void WHERE_delete_expr( struct expr *e ) +{ + if( !e ) + return; + if( e->type == EXPR_COMPLEX ) + { + WHERE_delete_expr( e->u.expr.left ); + WHERE_delete_expr( e->u.expr.right ); + } + HeapFree( GetProcessHeap(), 0, e ); +} + +static UINT WHERE_delete( struct tagMSIVIEW *view ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p\n", wv ); + + if( wv->table ) + wv->table->ops->delete( wv->table ); + + if( wv->reorder ) + HeapFree( GetProcessHeap(), 0, wv->reorder ); + wv->reorder = NULL; + wv->row_count = 0; + + WHERE_delete_expr( wv->cond ); + + HeapFree( GetProcessHeap(), 0, wv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS where_ops = +{ + WHERE_fetch_int, + WHERE_execute, + WHERE_close, + WHERE_get_dimensions, + WHERE_get_column_info, + WHERE_modify, + WHERE_delete +}; + +UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIWHEREVIEW *wv = NULL; + UINT count = 0, r; + + TRACE("%p\n", wv ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + wv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *wv ); + if( !wv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + wv->view.ops = &where_ops; + wv->db = db; + wv->table = table; + wv->row_count = 0; + wv->reorder = NULL; + *view = (MSIVIEW*) wv; + + return ERROR_SUCCESS; +} + +static UINT WHERE_VerifyCondition( MSIVIEW *table, struct expr *cond, + UINT *valid ) +{ + UINT r, col = 0; + + switch( cond->type ) + { + case EXPR_COLUMN: + r = VIEW_find_column( table, cond->u.column, &col ); + if( r == ERROR_SUCCESS ) + { + *valid = 1; + cond->type = EXPR_COL_NUMBER; + cond->u.col_number = col; + } + else + { + *valid = 0; + ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) ); + } + break; + case EXPR_COMPLEX: + r = WHERE_VerifyCondition( table, cond->u.expr.left, valid ); + if( r != ERROR_SUCCESS ) + return r; + if( !*valid ) + return ERROR_SUCCESS; + r = WHERE_VerifyCondition( table, cond->u.expr.right, valid ); + if( r != ERROR_SUCCESS ) + return r; + break; + case EXPR_IVAL: + *valid = 1; + cond->type = EXPR_UVAL; + cond->u.uval = cond->u.ival + (1<<15); + break; + case EXPR_SVAL: + *valid = 0; + FIXME("can't deal with string values yet\n"); + break; + default: + ERR("Invalid expression type\n"); + *valid = 0; + break; + } + + return ERROR_SUCCESS; +} + +UINT WHERE_AddCondition( MSIVIEW *view, struct expr *cond ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW *) view; + UINT r, valid = 0; + + if( wv->view.ops != &where_ops ) + return ERROR_FUNCTION_FAILED; + if( !wv->table ) + return ERROR_INVALID_PARAMETER; + + if( !cond ) + return ERROR_SUCCESS; + + TRACE("Adding condition\n"); + + r = WHERE_VerifyCondition( wv->table, cond, &valid ); + if( r != ERROR_SUCCESS ) + ERR("condition evaluation failed\n"); + + TRACE("condition is %s\n", valid ? "valid" : "invalid" ); + if( !valid ) + { + WHERE_delete_expr( cond ); + return ERROR_FUNCTION_FAILED; + } + + wv->cond = cond; + + return ERROR_SUCCESS; +} diff --git a/include/Makefile.in b/include/Makefile.in index ade9121e89b..274ccf37181 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -107,6 +107,8 @@ WINDOWS_INCLUDES = \ mmsystem.h \ msacm.h \ msacmdlg.h \ + msi.h \ + msiquery.h \ mssip.h \ mswsock.h \ nb30.h \ diff --git a/include/msi.h b/include/msi.h new file mode 100644 index 00000000000..b7500cbb90c --- /dev/null +++ b/include/msi.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2002 Mike McCormack + * + * 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 __WINE_MSI_H +#define __WINE_MSI_H + +typedef unsigned long MSIHANDLE; +typedef enum tagINSTALLSTATE +{ + INSTALLSTATE_BADCONFIG = -6, + INSTALLSTATE_INCOMPLETE = -5, + INSTALLSTATE_SOURCEABSENT = -4, + INSTALLSTATE_MOREDATA = -3, + INSTALLSTATE_INVALIDARG = -2, + INSTALLSTATE_UNKNOWN = -1, + INSTALLSTATE_BROKEN = 0, + INSTALLSTATE_ADVERTISED = 1, + INSTALLSTATE_ABSENT = 2, + INSTALLSTATE_LOCAL = 3, + INSTALLSTATE_SOURCE = 4, + INSTALLSTATE_DEFAULT = 5 +} INSTALLSTATE; + +typedef enum tagINSTALLUILEVEL +{ + INSTALLUILEVEL_NOCHANGE = 0, + INSTALLUILEVEL_DEFAULT = 1, + INSTALLUILEVEL_NONE = 2, + INSTALLUILEVEL_BASIC = 3, + INSTALLUILEVEL_REDUCED = 4, + INSTALLUILEVEL_FULL = 5 +} INSTALLUILEVEL; + +#define MSIDBOPEN_READONLY 0 +#define MSIDBOPEN_TRANSACT 1 +#define MSIDBOPEN_DIRECT 2 +#define MSIDBOPEN_CREATE 3 + + +UINT WINAPI MsiInstallProductA(LPCSTR, LPCSTR); +UINT WINAPI MsiInstallProductW(LPCWSTR, LPCWSTR); + +UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid); +UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid); + +UINT WINAPI MsiEnumFeaturesA(LPCSTR, DWORD, LPSTR, LPSTR); +UINT WINAPI MsiEnumFeaturesW(LPCWSTR, DWORD, LPWSTR, LPWSTR); + +UINT WINAPI MsiEnumComponentsA(DWORD, LPSTR); +UINT WINAPI MsiEnumComponentsW(DWORD, LPWSTR); + +UINT WINAPI MsiEnumClientsA(LPCSTR, DWORD, LPSTR); +UINT WINAPI MsiEnumClientsW(LPCWSTR, DWORD, LPWSTR); + +UINT WINAPI MsiOpenDatabaseA(LPCSTR, LPCSTR, MSIHANDLE *); +UINT WINAPI MsiOpenDatabaseW(LPCWSTR, LPCWSTR, MSIHANDLE *); + +UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE, LPCSTR, UINT, MSIHANDLE *); +UINT WINAPI MsiGetSummaryInformationW(MSIHANDLE, LPCWSTR, UINT, MSIHANDLE *); + +UINT WINAPI MsiSummaryInfoGetPropertyA(MSIHANDLE,UINT,UINT*,INT*,FILETIME*,LPSTR,DWORD*); +UINT WINAPI MsiSummaryInfoGetPropertyW(MSIHANDLE,UINT,UINT*,INT*,FILETIME*,LPWSTR,DWORD*); + +UINT WINAPI MsiCloseHandle(MSIHANDLE); +UINT WINAPI MsiCloseAllHandles(); + +UINT WINAPI MsiProvideComponentFromDescriptorA(LPCSTR,LPSTR,DWORD*,DWORD*); +UINT WINAPI MsiProvideComponentFromDescriptorW(LPCWSTR,LPWSTR,DWORD*,DWORD*); + +#endif /* __WINE_MSI_H */ diff --git a/include/msiquery.h b/include/msiquery.h new file mode 100644 index 00000000000..994f5b7f059 --- /dev/null +++ b/include/msiquery.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2002 Mike McCormack + * + * 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 __WINE_MSIQUERY_H +#define __WINE_MSIQUERY_H + +#include "msi.h" + +typedef enum tagMSICONDITION +{ + MSICONDITION_FALSE = 0, + MSICONDITION_TRUE = 1, + MSICONDITION_NONE = 2, + MSICONDITION_ERROR = 3, +} MSICONDITION; + +#define MSI_NULL_INTEGER 0x80000000 + +typedef enum tagMSICOLINFO +{ + MSICOLINFO_NAMES = 0, + MSICOLINFO_TYPES = 1 +} MSICOLINFO; + +typedef enum tagMSIMODIFY +{ + MSIMODIFY_REFRESH = 0, + MSIMODIFY_INSERT = 1, + MSIMODIFY_UPDATE = 2, + MSIMODIFY_ASSIGN = 3, + MSIMODIFY_REPLACE = 4, + MSIMODIFY_MERGE = 5, + MSIMODIFY_DELETE = 6, + MSIMODIFY_INSERT_TEMPORARY = 7, + MSIMODIFY_VALIDATE = 8, + MSIMODIFY_VALIDATE_NEW = 9, + MSIMODIFY_VALIDATE_FIELD = 10, + MSIMODIFY_VALIDATE_DELETE = 11 +} MSIMODIFY; + +#define MSI_NULL_INTEGER 0x80000000 + +/* view manipulation */ +UINT WINAPI MsiViewFetch(MSIHANDLE,MSIHANDLE*); +UINT WINAPI MsiViewExecute(MSIHANDLE,MSIHANDLE); +UINT WINAPI MsiViewClose(MSIHANDLE); +UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE,LPCSTR,MSIHANDLE*); +UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE,LPCWSTR,MSIHANDLE*); + +/* record manipulation */ +MSIHANDLE WINAPI MsiCreateRecord(unsigned int); +UINT WINAPI MsiRecordClearData(MSIHANDLE); +UINT WINAPI MsiRecordSetInteger(MSIHANDLE,unsigned int,int); +UINT WINAPI MsiRecordSetStringA(MSIHANDLE,unsigned int,LPCSTR); +UINT WINAPI MsiRecordSetStringW(MSIHANDLE,unsigned int,LPCWSTR); +UINT WINAPI MsiRecordGetStringA(MSIHANDLE,unsigned int,LPSTR,DWORD*); +UINT WINAPI MsiRecordGetStringW(MSIHANDLE,unsigned int,LPWSTR,DWORD*); +UINT WINAPI MsiRecordGetFieldCount(MSIHANDLE); +int WINAPI MsiRecordGetInteger(MSIHANDLE,unsigned int); +UINT WINAPI MsiRecordDataSize(MSIHANDLE,unsigned int); +BOOL WINAPI MsiRecordIsNull(MSIHANDLE,unsigned int); +UINT WINAPI MsiFormatRecordA(MSIHANDLE,MSIHANDLE,LPSTR,DWORD*); +UINT WINAPI MsiFormatRecordW(MSIHANDLE,MSIHANDLE,LPWSTR,DWORD*); +UINT WINAPI MsiRecordSetStreamA(MSIHANDLE,unsigned int,LPCSTR); +UINT WINAPI MsiRecordSetStreamW(MSIHANDLE,unsigned int,LPCWSTR); +UINT WINAPI MsiRecordReadStream(MSIHANDLE,unsigned int,char*,DWORD *); + +UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE,LPCSTR,MSIHANDLE*); +UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE,LPCWSTR,MSIHANDLE*); + +/* installing */ +UINT WINAPI MsiDoActionA(MSIHANDLE,LPCSTR ); +UINT WINAPI MsiDoActionW(MSIHANDLE,LPCWSTR ); + +/* database transforms */ +UINT WINAPI MsiDatabaseApplyTransformA(MSIHANDLE,LPCSTR,int); +UINT WINAPI MsiDatabaseApplyTransformW(MSIHANDLE,LPCWSTR,int); +UINT WINAPI MsiDatabaseGenerateTransformA(MSIHANDLE,MSIHANDLE,LPCSTR,int,int); +UINT WINAPI MsiDatabaseGenerateTransformW(MSIHANDLE,MSIHANDLE,LPCWSTR,int,int); + +UINT WINAPI MsiDatabaseCommit(MSIHANDLE); + +#endif /* __WINE_MSIQUERY_H */