Partial implementation of the Microsoft Installer (msi.dll).
This commit is contained in:
parent
f6fab8c730
commit
6386edc5d8
|
@ -1452,6 +1452,7 @@ dlls/msacm/msadp32/Makefile
|
||||||
dlls/msacm/msg711/Makefile
|
dlls/msacm/msg711/Makefile
|
||||||
dlls/msacm/winemp3/Makefile
|
dlls/msacm/winemp3/Makefile
|
||||||
dlls/msdmo/Makefile
|
dlls/msdmo/Makefile
|
||||||
|
dlls/msi/Makefile
|
||||||
dlls/msimg32/Makefile
|
dlls/msimg32/Makefile
|
||||||
dlls/msisys/Makefile
|
dlls/msisys/Makefile
|
||||||
dlls/msnet32/Makefile
|
dlls/msnet32/Makefile
|
||||||
|
|
|
@ -58,6 +58,7 @@ BASEDIRS = \
|
||||||
msacm/msg711 \
|
msacm/msg711 \
|
||||||
msacm/winemp3 \
|
msacm/winemp3 \
|
||||||
msdmo \
|
msdmo \
|
||||||
|
msi \
|
||||||
msimg32 \
|
msimg32 \
|
||||||
msisys \
|
msisys \
|
||||||
msnet32 \
|
msnet32 \
|
||||||
|
@ -247,6 +248,7 @@ SYMLINKS = \
|
||||||
msadp32.acm$(DLLEXT) \
|
msadp32.acm$(DLLEXT) \
|
||||||
msdmo.dll$(DLLEXT) \
|
msdmo.dll$(DLLEXT) \
|
||||||
msg711.acm$(DLLEXT) \
|
msg711.acm$(DLLEXT) \
|
||||||
|
msi.dll$(DLLEXT) \
|
||||||
msimg32.dll$(DLLEXT) \
|
msimg32.dll$(DLLEXT) \
|
||||||
msisys.ocx$(DLLEXT) \
|
msisys.ocx$(DLLEXT) \
|
||||||
msnet32.dll$(DLLEXT) \
|
msnet32.dll$(DLLEXT) \
|
||||||
|
@ -482,6 +484,9 @@ msdmo.dll$(DLLEXT): msdmo/msdmo.dll$(DLLEXT)
|
||||||
msg711.acm$(DLLEXT): msacm/msg711/msg711.acm$(DLLEXT)
|
msg711.acm$(DLLEXT): msacm/msg711/msg711.acm$(DLLEXT)
|
||||||
$(RM) $@ && $(LN_S) 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)
|
msimg32.dll$(DLLEXT): msimg32/msimg32.dll$(DLLEXT)
|
||||||
$(RM) $@ && $(LN_S) msimg32/msimg32.dll$(DLLEXT) $@
|
$(RM) $@ && $(LN_S) msimg32/msimg32.dll$(DLLEXT) $@
|
||||||
|
|
||||||
|
@ -719,6 +724,7 @@ IMPORT_LIBS = \
|
||||||
libmpr \
|
libmpr \
|
||||||
libmsacm32 \
|
libmsacm32 \
|
||||||
libmsdmo \
|
libmsdmo \
|
||||||
|
libmsi \
|
||||||
libmsimg32 \
|
libmsimg32 \
|
||||||
libmsnet32 \
|
libmsnet32 \
|
||||||
libmsrle32 \
|
libmsrle32 \
|
||||||
|
@ -994,6 +1000,11 @@ libmsdmo.def: msdmo/msdmo.spec.def
|
||||||
libmsdmo.a: msdmo/msdmo.spec.def
|
libmsdmo.a: msdmo/msdmo.spec.def
|
||||||
$(DLLTOOL) -k -l $@ -d 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
|
libmsimg32.def: msimg32/msimg32.spec.def
|
||||||
$(RM) $@ && $(LN_S) msimg32/msimg32.spec.def $@
|
$(RM) $@ && $(LN_S) msimg32/msimg32.spec.def $@
|
||||||
libmsimg32.a: msimg32/msimg32.spec.def
|
libmsimg32.a: msimg32/msimg32.spec.def
|
||||||
|
@ -1308,6 +1319,7 @@ mapi32/mapi32.spec.def: $(WINEBUILD)
|
||||||
mpr/mpr.spec.def: $(WINEBUILD)
|
mpr/mpr.spec.def: $(WINEBUILD)
|
||||||
msacm/msacm32.spec.def: $(WINEBUILD)
|
msacm/msacm32.spec.def: $(WINEBUILD)
|
||||||
msdmo/msdmo.spec.def: $(WINEBUILD)
|
msdmo/msdmo.spec.def: $(WINEBUILD)
|
||||||
|
msi/msi.spec.def: $(WINEBUILD)
|
||||||
msimg32/msimg32.spec.def: $(WINEBUILD)
|
msimg32/msimg32.spec.def: $(WINEBUILD)
|
||||||
msnet32/msnet32.spec.def: $(WINEBUILD)
|
msnet32/msnet32.spec.def: $(WINEBUILD)
|
||||||
msvideo/msrle32/msrle32.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
|
msacm/msadp32/msadp32.acm$(DLLEXT): msacm/msadp32
|
||||||
msdmo/msdmo.dll$(DLLEXT): msdmo
|
msdmo/msdmo.dll$(DLLEXT): msdmo
|
||||||
msacm/msg711/msg711.acm$(DLLEXT): msacm/msg711
|
msacm/msg711/msg711.acm$(DLLEXT): msacm/msg711
|
||||||
|
msi/msi.dll$(DLLEXT): msi
|
||||||
msimg32/msimg32.dll$(DLLEXT): msimg32
|
msimg32/msimg32.dll$(DLLEXT): msimg32
|
||||||
msisys/msisys.ocx$(DLLEXT): msisys
|
msisys/msisys.ocx$(DLLEXT): msisys
|
||||||
msnet32/msnet32.dll$(DLLEXT): msnet32
|
msnet32/msnet32.dll$(DLLEXT): msnet32
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
Makefile
|
||||||
|
msi.dll.dbg.c
|
||||||
|
msi.spec.c
|
||||||
|
msi.spec.def
|
||||||
|
y.tab.c
|
||||||
|
y.tab.h
|
|
@ -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:
|
|
@ -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; i<r_count; i++ )
|
||||||
|
{
|
||||||
|
DISTINCTSET **x = &rowset;
|
||||||
|
|
||||||
|
for( j=1; j<=c_count; j++ )
|
||||||
|
{
|
||||||
|
UINT val = 0;
|
||||||
|
r = dv->table->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;
|
||||||
|
}
|
|
@ -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; i++)
|
||||||
|
if( !msihandletable[i] )
|
||||||
|
break;
|
||||||
|
if( (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; i<MSIMAXHANDLES; i++)
|
||||||
|
MsiCloseHandle( i+1 );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,640 @@
|
||||||
|
/*
|
||||||
|
* 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 szInstaller[] = {
|
||||||
|
'S','o','f','t','w','a','r','e','\\',
|
||||||
|
'M','i','c','r','o','s','o','f','t','\\',
|
||||||
|
'W','i','n','d','o','w','s','\\',
|
||||||
|
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
|
||||||
|
'I','n','s','t','a','l','l','e','r',0 };
|
||||||
|
|
||||||
|
const WCHAR szFeatures[] = {
|
||||||
|
'F','e','a','t','u','r','e','s',0 };
|
||||||
|
const WCHAR szComponents[] = {
|
||||||
|
'C','o','m','p','o','n','e','n','t','s',0 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* .MSI file format
|
||||||
|
*
|
||||||
|
* A .msi file is a structured storage file.
|
||||||
|
* It should contain a number of streams.
|
||||||
|
*/
|
||||||
|
|
||||||
|
BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
|
||||||
|
{
|
||||||
|
DWORD i,n=0;
|
||||||
|
|
||||||
|
out[n++]='{';
|
||||||
|
for(i=0; i<8; i++)
|
||||||
|
out[n++] = in[7-i];
|
||||||
|
out[n++]='-';
|
||||||
|
for(i=0; i<4; i++)
|
||||||
|
out[n++] = in[11-i];
|
||||||
|
out[n++]='-';
|
||||||
|
for(i=0; i<4; i++)
|
||||||
|
out[n++] = in[15-i];
|
||||||
|
out[n++]='-';
|
||||||
|
for(i=0; i<2; i++)
|
||||||
|
{
|
||||||
|
out[n++] = in[17+i*2];
|
||||||
|
out[n++] = in[16+i*2];
|
||||||
|
}
|
||||||
|
out[n++]='-';
|
||||||
|
for( ; i<8; i++)
|
||||||
|
{
|
||||||
|
out[n++] = in[17+i*2];
|
||||||
|
out[n++] = in[16+i*2];
|
||||||
|
}
|
||||||
|
out[n++]='}';
|
||||||
|
out[n]=0;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL squash_guid(LPCWSTR in, LPWSTR out)
|
||||||
|
{
|
||||||
|
DWORD i,n=0;
|
||||||
|
|
||||||
|
if(in[n++] != '{')
|
||||||
|
return FALSE;
|
||||||
|
for(i=0; i<8; i++)
|
||||||
|
out[7-i] = in[n++];
|
||||||
|
if(in[n++] != '-')
|
||||||
|
return FALSE;
|
||||||
|
for(i=0; i<4; i++)
|
||||||
|
out[11-i] = in[n++];
|
||||||
|
if(in[n++] != '-')
|
||||||
|
return FALSE;
|
||||||
|
for(i=0; i<4; i++)
|
||||||
|
out[15-i] = in[n++];
|
||||||
|
if(in[n++] != '-')
|
||||||
|
return FALSE;
|
||||||
|
for(i=0; i<2; i++)
|
||||||
|
{
|
||||||
|
out[17+i*2] = in[n++];
|
||||||
|
out[16+i*2] = in[n++];
|
||||||
|
}
|
||||||
|
if(in[n++] != '-')
|
||||||
|
return FALSE;
|
||||||
|
for( ; i<8; i++)
|
||||||
|
{
|
||||||
|
out[17+i*2] = in[n++];
|
||||||
|
out[16+i*2] = in[n++];
|
||||||
|
}
|
||||||
|
out[32]=0;
|
||||||
|
if(in[n++] != '}')
|
||||||
|
return FALSE;
|
||||||
|
if(in[n])
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID MSI_CloseDatabase( VOID *arg )
|
||||||
|
{
|
||||||
|
MSIDATABASE *db = (MSIDATABASE *) arg;
|
||||||
|
|
||||||
|
free_cached_tables( db );
|
||||||
|
IStorage_Release( db->storage );
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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__ */
|
|
@ -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; i<count; i++ )
|
||||||
|
{
|
||||||
|
name = NULL;
|
||||||
|
ret = view->ops->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;
|
||||||
|
}
|
|
@ -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; i<ov->num_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; i<num_rows; i++ )
|
||||||
|
{
|
||||||
|
r = ORDER_compare( ov, ov->reorder[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; i<num_rows; i++ )
|
||||||
|
ov->reorder[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;
|
||||||
|
}
|
|
@ -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 */
|
|
@ -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; i<rec->count; 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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 <string> 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 <string> 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 <query> oneselect
|
||||||
|
%type <string> column table string_or_id
|
||||||
|
%type <column_list> selcollist
|
||||||
|
%type <table> from unorderedsel
|
||||||
|
%type <expr> 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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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; i<count; i++)
|
||||||
|
{
|
||||||
|
len = st->pool.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; i<string_no; i++)
|
||||||
|
{
|
||||||
|
len = st->pool.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; i<count; i++)
|
||||||
|
{
|
||||||
|
len = st->pool.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<STANDARD_TABLE_COUNT; i++)
|
||||||
|
{
|
||||||
|
if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
|
||||||
|
continue;
|
||||||
|
if(colinfo && (n < *sz) )
|
||||||
|
{
|
||||||
|
colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
|
||||||
|
colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
|
||||||
|
colinfo[n].number = MSI_standard_tables[i].number;
|
||||||
|
colinfo[n].type = MSI_standard_tables[i].type;
|
||||||
|
/* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
|
||||||
|
debugstr_w(colinfo[n].colname)); */
|
||||||
|
if( n )
|
||||||
|
colinfo[n].offset = colinfo[n-1].offset
|
||||||
|
+ bytes_per_column( &colinfo[n-1] );
|
||||||
|
else
|
||||||
|
colinfo[n].offset = 0;
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
if( colinfo && (n >= *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; i<count; i++ )
|
||||||
|
{
|
||||||
|
if( table->data[ 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; i<count; i++ )
|
||||||
|
if( table->data[ 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; i<tv->num_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;
|
||||||
|
}
|
|
@ -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 <ctype.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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<KEYWORD_COUNT; i++)
|
||||||
|
{
|
||||||
|
if(memcmp(buffer, aKeywordTable[i].zName, len))
|
||||||
|
continue;
|
||||||
|
if(strlen(aKeywordTable[i].zName) == len )
|
||||||
|
return aKeywordTable[i].tokenType;
|
||||||
|
}
|
||||||
|
return TK_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** If X is a character that can be used in an identifier then
|
||||||
|
** isIdChar[X] will be 1. Otherwise isIdChar[X] will be 0.
|
||||||
|
**
|
||||||
|
** In this implementation, an identifier can be a string of
|
||||||
|
** alphabetic characters, digits, and "_" plus any character
|
||||||
|
** with the high-order bit set. The latter rule means that
|
||||||
|
** any sequence of UTF-8 characters or characters taken from
|
||||||
|
** an extended ISO8859 character set can form an identifier.
|
||||||
|
*/
|
||||||
|
static const char isIdChar[] = {
|
||||||
|
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */
|
||||||
|
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8x */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9x */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ax */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Bx */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Cx */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Dx */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Ex */
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* Fx */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Return the length of the token that begins at z[0]. Return
|
||||||
|
** -1 if the token is (or might be) incomplete. Store the token
|
||||||
|
** type in *tokenType before returning.
|
||||||
|
*/
|
||||||
|
int sqliteGetToken(const WCHAR *z, int *tokenType){
|
||||||
|
int i;
|
||||||
|
switch( *z ){
|
||||||
|
case ' ': case '\t': case '\n': case '\f': case '\r': {
|
||||||
|
for(i=1; isspace(z[i]); i++){}
|
||||||
|
*tokenType = TK_SPACE;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case '-': {
|
||||||
|
if( z[1]==0 ) return -1;
|
||||||
|
if( z[1]=='-' ){
|
||||||
|
for(i=2; z[i] && z[i]!='\n'; i++){}
|
||||||
|
*tokenType = TK_COMMENT;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
*tokenType = TK_MINUS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '(': {
|
||||||
|
if( z[1]=='+' && z[2]==')' ){
|
||||||
|
*tokenType = TK_ORACLE_OUTER_JOIN;
|
||||||
|
return 3;
|
||||||
|
}else{
|
||||||
|
*tokenType = TK_LP;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ')': {
|
||||||
|
*tokenType = TK_RP;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case ';': {
|
||||||
|
*tokenType = TK_SEMI;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '+': {
|
||||||
|
*tokenType = TK_PLUS;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '*': {
|
||||||
|
*tokenType = TK_STAR;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '/': {
|
||||||
|
if( z[1]!='*' || z[2]==0 ){
|
||||||
|
*tokenType = TK_SLASH;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
for(i=3; z[i] && (z[i]!='/' || z[i-1]!='*'); i++){}
|
||||||
|
if( z[i] ) i++;
|
||||||
|
*tokenType = TK_COMMENT;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
case '%': {
|
||||||
|
*tokenType = TK_REM;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '=': {
|
||||||
|
*tokenType = TK_EQ;
|
||||||
|
return 1 + (z[1]=='=');
|
||||||
|
}
|
||||||
|
case '<': {
|
||||||
|
if( z[1]=='=' ){
|
||||||
|
*tokenType = TK_LE;
|
||||||
|
return 2;
|
||||||
|
}else if( z[1]=='>' ){
|
||||||
|
*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;
|
||||||
|
}
|
|
@ -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; i<count; i++ )
|
||||||
|
{
|
||||||
|
val = 0;
|
||||||
|
r = WHERE_evaluate( table, i, wv->cond, &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;
|
||||||
|
}
|
|
@ -107,6 +107,8 @@ WINDOWS_INCLUDES = \
|
||||||
mmsystem.h \
|
mmsystem.h \
|
||||||
msacm.h \
|
msacm.h \
|
||||||
msacmdlg.h \
|
msacmdlg.h \
|
||||||
|
msi.h \
|
||||||
|
msiquery.h \
|
||||||
mssip.h \
|
mssip.h \
|
||||||
mswsock.h \
|
mswsock.h \
|
||||||
nb30.h \
|
nb30.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 */
|
|
@ -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 */
|
Loading…
Reference in New Issue