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/winemp3/Makefile
|
||||
dlls/msdmo/Makefile
|
||||
dlls/msi/Makefile
|
||||
dlls/msimg32/Makefile
|
||||
dlls/msisys/Makefile
|
||||
dlls/msnet32/Makefile
|
||||
|
|
|
@ -58,6 +58,7 @@ BASEDIRS = \
|
|||
msacm/msg711 \
|
||||
msacm/winemp3 \
|
||||
msdmo \
|
||||
msi \
|
||||
msimg32 \
|
||||
msisys \
|
||||
msnet32 \
|
||||
|
@ -247,6 +248,7 @@ SYMLINKS = \
|
|||
msadp32.acm$(DLLEXT) \
|
||||
msdmo.dll$(DLLEXT) \
|
||||
msg711.acm$(DLLEXT) \
|
||||
msi.dll$(DLLEXT) \
|
||||
msimg32.dll$(DLLEXT) \
|
||||
msisys.ocx$(DLLEXT) \
|
||||
msnet32.dll$(DLLEXT) \
|
||||
|
@ -482,6 +484,9 @@ msdmo.dll$(DLLEXT): msdmo/msdmo.dll$(DLLEXT)
|
|||
msg711.acm$(DLLEXT): msacm/msg711/msg711.acm$(DLLEXT)
|
||||
$(RM) $@ && $(LN_S) msacm/msg711/msg711.acm$(DLLEXT) $@
|
||||
|
||||
msi.dll$(DLLEXT): msi/msi.dll$(DLLEXT)
|
||||
$(RM) $@ && $(LN_S) msi/msi.dll$(DLLEXT) $@
|
||||
|
||||
msimg32.dll$(DLLEXT): msimg32/msimg32.dll$(DLLEXT)
|
||||
$(RM) $@ && $(LN_S) msimg32/msimg32.dll$(DLLEXT) $@
|
||||
|
||||
|
@ -719,6 +724,7 @@ IMPORT_LIBS = \
|
|||
libmpr \
|
||||
libmsacm32 \
|
||||
libmsdmo \
|
||||
libmsi \
|
||||
libmsimg32 \
|
||||
libmsnet32 \
|
||||
libmsrle32 \
|
||||
|
@ -994,6 +1000,11 @@ libmsdmo.def: msdmo/msdmo.spec.def
|
|||
libmsdmo.a: msdmo/msdmo.spec.def
|
||||
$(DLLTOOL) -k -l $@ -d msdmo/msdmo.spec.def
|
||||
|
||||
libmsi.def: msi/msi.spec.def
|
||||
$(RM) $@ && $(LN_S) msi/msi.spec.def $@
|
||||
libmsi.a: msi/msi.spec.def
|
||||
$(DLLTOOL) -k -l $@ -d msi/msi.spec.def
|
||||
|
||||
libmsimg32.def: msimg32/msimg32.spec.def
|
||||
$(RM) $@ && $(LN_S) msimg32/msimg32.spec.def $@
|
||||
libmsimg32.a: msimg32/msimg32.spec.def
|
||||
|
@ -1308,6 +1319,7 @@ mapi32/mapi32.spec.def: $(WINEBUILD)
|
|||
mpr/mpr.spec.def: $(WINEBUILD)
|
||||
msacm/msacm32.spec.def: $(WINEBUILD)
|
||||
msdmo/msdmo.spec.def: $(WINEBUILD)
|
||||
msi/msi.spec.def: $(WINEBUILD)
|
||||
msimg32/msimg32.spec.def: $(WINEBUILD)
|
||||
msnet32/msnet32.spec.def: $(WINEBUILD)
|
||||
msvideo/msrle32/msrle32.spec.def: $(WINEBUILD)
|
||||
|
@ -1424,6 +1436,7 @@ msacm/msacm32.dll$(DLLEXT): msacm
|
|||
msacm/msadp32/msadp32.acm$(DLLEXT): msacm/msadp32
|
||||
msdmo/msdmo.dll$(DLLEXT): msdmo
|
||||
msacm/msg711/msg711.acm$(DLLEXT): msacm/msg711
|
||||
msi/msi.dll$(DLLEXT): msi
|
||||
msimg32/msimg32.dll$(DLLEXT): msimg32
|
||||
msisys/msisys.ocx$(DLLEXT): msisys
|
||||
msnet32/msnet32.dll$(DLLEXT): msnet32
|
||||
|
|
|
@ -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 \
|
||||
msacm.h \
|
||||
msacmdlg.h \
|
||||
msi.h \
|
||||
msiquery.h \
|
||||
mssip.h \
|
||||
mswsock.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