First port of Wine projects msi.dll
authorJames Tabor <james.tabor@reactos.org>
Tue, 30 Nov 2004 19:10:50 +0000 (19:10 +0000)
committerJames Tabor <james.tabor@reactos.org>
Tue, 30 Nov 2004 19:10:50 +0000 (19:10 +0000)
svn path=/trunk/; revision=11877

29 files changed:
reactos/lib/msi/.cvsignore [new file with mode: 0644]
reactos/lib/msi/Makefile [new file with mode: 0644]
reactos/lib/msi/Makefile.in [new file with mode: 0644]
reactos/lib/msi/Makefile.ros-template [new file with mode: 0644]
reactos/lib/msi/action.c [new file with mode: 0644]
reactos/lib/msi/cond.y [new file with mode: 0644]
reactos/lib/msi/create.c [new file with mode: 0644]
reactos/lib/msi/distinct.c [new file with mode: 0644]
reactos/lib/msi/handle.c [new file with mode: 0644]
reactos/lib/msi/insert.c [new file with mode: 0644]
reactos/lib/msi/msi.c [new file with mode: 0644]
reactos/lib/msi/msi.spec [new file with mode: 0644]
reactos/lib/msi/msipriv.h [new file with mode: 0644]
reactos/lib/msi/msiquery.c [new file with mode: 0644]
reactos/lib/msi/order.c [new file with mode: 0644]
reactos/lib/msi/package.c [new file with mode: 0644]
reactos/lib/msi/query.h [new file with mode: 0644]
reactos/lib/msi/record.c [new file with mode: 0644]
reactos/lib/msi/regsvr.c [new file with mode: 0644]
reactos/lib/msi/select.c [new file with mode: 0644]
reactos/lib/msi/sql.y [new file with mode: 0644]
reactos/lib/msi/string.c [new file with mode: 0644]
reactos/lib/msi/suminfo.c [new file with mode: 0644]
reactos/lib/msi/table.c [new file with mode: 0644]
reactos/lib/msi/tokenize.c [new file with mode: 0644]
reactos/lib/msi/update.c [new file with mode: 0644]
reactos/lib/msi/version.rc [new file with mode: 0644]
reactos/lib/msi/where.c [new file with mode: 0644]
reactos/lib/msi/winehq2ros.patch [new file with mode: 0644]

diff --git a/reactos/lib/msi/.cvsignore b/reactos/lib/msi/.cvsignore
new file mode 100644 (file)
index 0000000..9103a52
--- /dev/null
@@ -0,0 +1,8 @@
+*.coff
+*.dll
+*.d
+*.a
+*.o
+*.sym
+*.map
+*.tmp
diff --git a/reactos/lib/msi/Makefile b/reactos/lib/msi/Makefile
new file mode 100644 (file)
index 0000000..bca96cd
--- /dev/null
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.1 2004/11/30 19:10:50 jimtabor Exp $
+
+PATH_TO_TOP = ../..
+
+TARGET_TYPE = winedll
+       
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
diff --git a/reactos/lib/msi/Makefile.in b/reactos/lib/msi/Makefile.in
new file mode 100644 (file)
index 0000000..a2a4063
--- /dev/null
@@ -0,0 +1,50 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = msi.dll
+IMPORTS   = shell32 cabinet oleaut32 ole32 version user32 advapi32 kernel32
+EXTRALIBS = -luuid $(LIBUNICODE)
+
+C_SRCS = \
+       action.c \
+       create.c \
+       distinct.c \
+       handle.c \
+       insert.c \
+       msi.c \
+       msiquery.c \
+       order.c \
+       package.c \
+       record.c \
+       regsvr.c \
+       select.c \
+       string.c \
+       suminfo.c \
+       table.c \
+       tokenize.c \
+       update.c \
+       where.c
+
+RC_SRCS = version.rc
+
+EXTRA_SRCS = sql.y cond.y
+EXTRA_OBJS = sql.tab.o cond.tab.o
+
+@MAKE_DLL_RULES@
+
+sql.tab.c sql.tab.h: sql.y
+       $(BISON) -p SQL_ -d $(SRCDIR)/sql.y -o sql.tab.c
+
+cond.tab.c cond.tab.h: cond.y
+       $(BISON) -p COND_ -d $(SRCDIR)/cond.y -o cond.tab.c
+
+# hack to allow parallel make
+sql.tab.h: sql.tab.c
+sql.tab.o: sql.tab.h
+cond.tab.h: cond.tab.c
+cond.tab.o: cond.tab.h
+
+tokenize.o: sql.tab.h
+
+### Dependencies:
diff --git a/reactos/lib/msi/Makefile.ros-template b/reactos/lib/msi/Makefile.ros-template
new file mode 100644 (file)
index 0000000..0ad0d31
--- /dev/null
@@ -0,0 +1,35 @@
+# $Id: Makefile.ros-template
+
+TARGET_NAME = msi
+
+TARGET_OBJECTS = @C_SRCS@ @EXTRA_OBJS@
+
+TARGET_CFLAGS = -D__REACTOS__ @EXTRADEFS@
+
+TARGET_SDKLIBS = @IMPORTS@ libwine.a wine_uuid.a ntdll.a
+
+TARGET_BASE = $(TARGET_BASE_LIB_WINMM)
+
+TARGET_RC_SRCS = @RC_SRCS@
+TARGET_RC_BINSRC = @RC_BINSRC@
+TARGET_RC_BINARIES = @RC_BINARIES@
+
+TARGET_CLEAN = *.tab.c *.tab.h
+
+default: all
+
+DEP_OBJECTS = $(TARGET_OBJECTS)
+
+include $(TOOLS_PATH)/depend.mk
+
+#
+#      Bison is requiered for building msi.dll. If MingW32 for windows,
+#      download bison from http://gnuwin32.sourceforge.net/
+#      Make sure bison.exe is placed in your command path for execution.
+#
+#
+sql.tab.c sql.tab.h: sql.y
+       bison -p SQL_ -d ./sql.y -o sql.tab.c
+
+cond.tab.c cond.tab.h: cond.y
+       bison -p COND_ -d ./cond.y -o cond.tab.c
diff --git a/reactos/lib/msi/action.c b/reactos/lib/msi/action.c
new file mode 100644 (file)
index 0000000..27ff78f
--- /dev/null
@@ -0,0 +1,4733 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 Aric Stewart 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
+ */
+
+/*
+ * Pages I need
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
+
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "fdi.h"
+#include "msi.h"
+#include "msiquery.h"
+//#include "msvcrt/fcntl.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "ver.h"
+
+#define CUSTOM_ACTION_TYPE_MASK 0x3F
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagMSIFEATURE
+{
+    WCHAR Feature[96];
+    WCHAR Feature_Parent[96];
+    WCHAR Title[0x100];
+    WCHAR Description[0x100];
+    INT Display;
+    INT Level;
+    WCHAR Directory[96];
+    INT Attributes;
+    
+    INSTALLSTATE State;
+    BOOL Enabled;
+    INT ComponentCount;
+    INT Components[1024]; /* yes hardcoded limit.... I am bad */
+    INT Cost;
+} MSIFEATURE;
+
+typedef struct tagMSICOMPONENT
+{
+    WCHAR Component[96];
+    WCHAR ComponentId[96];
+    WCHAR Directory[96];
+    INT Attributes;
+    WCHAR Condition[0x100];
+    WCHAR KeyPath[96];
+
+    INSTALLSTATE State;
+    BOOL FeatureState;
+    BOOL Enabled;
+    INT  Cost;
+} MSICOMPONENT;
+
+typedef struct tagMSIFOLDER
+{
+    WCHAR Directory[96];
+    WCHAR TargetDefault[96];
+    WCHAR SourceDefault[96];
+
+    WCHAR ResolvedTarget[MAX_PATH];
+    WCHAR ResolvedSource[MAX_PATH];
+    WCHAR Property[MAX_PATH];   /* initially set property */
+    INT   ParentIndex;
+    INT   State;
+        /* 0 = uninitialized */
+        /* 1 = existing */
+        /* 2 = created remove if empty */
+        /* 3 = created persist if empty */
+    INT   Cost;
+    INT   Space;
+}MSIFOLDER;
+
+typedef struct tagMSIFILE
+{
+    WCHAR File[72];
+    INT ComponentIndex;
+    WCHAR FileName[MAX_PATH];
+    INT FileSize;
+    WCHAR Version[72];
+    WCHAR Language[20];
+    INT Attributes;
+    INT Sequence;   
+
+    INT State;
+       /* 0 = uninitialize */
+       /* 1 = not present */
+       /* 2 = present but replace */
+       /* 3 = present do not replace */
+       /* 4 = Installed */
+    WCHAR   SourcePath[MAX_PATH];
+    WCHAR   TargetPath[MAX_PATH];
+    BOOL    Temporary; 
+}MSIFILE;
+
+/*
+ * Prototypes
+ */
+static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
+static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
+
+UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action);
+
+static UINT ACTION_LaunchConditions(MSIPACKAGE *package);
+static UINT ACTION_CostInitialize(MSIPACKAGE *package);
+static UINT ACTION_CreateFolders(MSIPACKAGE *package);
+static UINT ACTION_CostFinalize(MSIPACKAGE *package);
+static UINT ACTION_FileCost(MSIPACKAGE *package);
+static UINT ACTION_InstallFiles(MSIPACKAGE *package);
+static UINT ACTION_DuplicateFiles(MSIPACKAGE *package);
+static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package);
+static UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action);
+static UINT ACTION_InstallInitialize(MSIPACKAGE *package);
+static UINT ACTION_InstallValidate(MSIPACKAGE *package);
+static UINT ACTION_ProcessComponents(MSIPACKAGE *package);
+static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package);
+static UINT ACTION_RegisterClassInfo(MSIPACKAGE *package);
+static UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package);
+static UINT ACTION_CreateShortcuts(MSIPACKAGE *package);
+static UINT ACTION_PublishProduct(MSIPACKAGE *package);
+
+static UINT HANDLE_CustomType1(MSIPACKAGE *package, const LPWSTR source, 
+                                const LPWSTR target, const INT type);
+static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source, 
+                                const LPWSTR target, const INT type);
+
+static DWORD deformat_string(MSIPACKAGE *package, WCHAR* ptr,WCHAR** data);
+static UINT resolve_folder(MSIPACKAGE *package, LPCWSTR name, LPWSTR path, 
+                           BOOL source, BOOL set_prop, MSIFOLDER **folder);
+
+static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path);
+/*
+ * consts and values used
+ */
+static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
+static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
+static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
+static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
+static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
+static const WCHAR c_collen[] = {'C',':','\\',0};
+static const WCHAR cszlsb[]={'[',0};
+static const WCHAR cszrsb[]={']',0};
+static const WCHAR cszbs[]={'\\',0};
+
+const static WCHAR szCreateFolders[] =
+    {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
+const static WCHAR szCostFinalize[] =
+    {'C','o','s','t','F','i','n','a','l','i','z','e',0};
+const static WCHAR szInstallFiles[] =
+    {'I','n','s','t','a','l','l','F','i','l','e','s',0};
+const static WCHAR szDuplicateFiles[] =
+    {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
+const static WCHAR szWriteRegistryValues[] =
+{'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
+const static WCHAR szCostInitialize[] =
+    {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
+const static WCHAR szFileCost[] = {'F','i','l','e','C','o','s','t',0};
+const static WCHAR szInstallInitialize[] = 
+    {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
+const static WCHAR szInstallValidate[] = 
+    {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
+const static WCHAR szLaunchConditions[] = 
+    {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
+const static WCHAR szProcessComponents[] = 
+    {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
+const static WCHAR szRegisterTypeLibraries[] = 
+{'R','e','g','i','s','t','e','r','T','y','p','e','L','i','b','r','a','r',
+'i','e','s',0};
+const static WCHAR szRegisterClassInfo[] = 
+{'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
+const static WCHAR szRegisterProgIdInfo[] = 
+{'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
+const static WCHAR szCreateShortcuts[] = 
+{'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
+const static WCHAR szPublishProduct[] = 
+{'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
+
+/******************************************************** 
+ * helper functions to get around current HACKS and such
+ ********************************************************/
+inline static void reduce_to_longfilename(WCHAR* filename)
+{
+    if (strchrW(filename,'|'))
+    {
+        WCHAR newname[MAX_PATH];
+        strcpyW(newname,strchrW(filename,'|')+1);
+        strcpyW(filename,newname);
+    }
+}
+
+inline static char *strdupWtoA( const WCHAR *str )
+{
+    char *ret = NULL;
+    if (str)
+    {
+        DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
+);
+        if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
+            WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
+    }
+    return ret;
+}
+
+inline static WCHAR *strdupAtoW( const char *str )
+{
+    WCHAR *ret = NULL;
+    if (str)
+    {
+        DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+        if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+            MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
+    }
+    return ret;
+}
+
+inline static WCHAR *load_dynamic_stringW(MSIRECORD *row, INT index)
+{
+    UINT rc;
+    DWORD sz;
+    LPWSTR ret;
+   
+    sz = 0; 
+    rc = MSI_RecordGetStringW(row,index,NULL,&sz);
+    if (sz <= 0)
+        return NULL;
+
+    sz ++;
+    ret = HeapAlloc(GetProcessHeap(),0,sz * sizeof (WCHAR));
+    rc = MSI_RecordGetStringW(row,index,ret,&sz);
+    return ret;
+}
+
+inline static int get_loaded_component(MSIPACKAGE* package, LPCWSTR Component )
+{
+    int rc = -1;
+    DWORD i;
+
+    for (i = 0; i < package->loaded_components; i++)
+    {
+        if (strcmpW(Component,package->components[i].Component)==0)
+        {
+            rc = i;
+            break;
+        }
+    }
+    return rc;
+}
+
+inline static int get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature )
+{
+    int rc = -1;
+    DWORD i;
+
+    for (i = 0; i < package->loaded_features; i++)
+    {
+        if (strcmpW(Feature,package->features[i].Feature)==0)
+        {
+            rc = i;
+            break;
+        }
+    }
+    return rc;
+}
+
+inline static int get_loaded_file(MSIPACKAGE* package, LPCWSTR file)
+{
+    int rc = -1;
+    DWORD i;
+
+    for (i = 0; i < package->loaded_files; i++)
+    {
+        if (strcmpW(file,package->files[i].File)==0)
+        {
+            rc = i;
+            break;
+        }
+    }
+    return rc;
+}
+
+static int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path)
+{
+    DWORD i;
+    DWORD index;
+
+    if (!package)
+        return -2;
+
+    for (i=0; i < package->loaded_files; i++)
+        if (strcmpW(package->files[i].File,name)==0)
+            return -1;
+
+    index = package->loaded_files;
+    package->loaded_files++;
+    if (package->loaded_files== 1)
+        package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
+    else
+        package->files = HeapReAlloc(GetProcessHeap(),0,
+            package->files , package->loaded_files * sizeof(MSIFILE));
+
+    memset(&package->files[index],0,sizeof(MSIFILE));
+
+    strcpyW(package->files[index].File,name);
+    strcpyW(package->files[index].TargetPath,path);
+    package->files[index].Temporary = TRUE;
+
+    TRACE("Tracking tempfile (%s)\n",debugstr_w(package->files[index].File));  
+
+    return 0;
+}
+
+void ACTION_remove_tracked_tempfiles(MSIPACKAGE* package)
+{
+    DWORD i;
+
+    if (!package)
+        return;
+
+    for (i = 0; i < package->loaded_files; i++)
+    {
+        if (package->files[i].Temporary)
+            DeleteFileW(package->files[i].TargetPath);
+
+    }
+}
+
+static void ui_progress(MSIPACKAGE *package, int a, int b, int c, int d )
+{
+    MSIRECORD * row;
+
+    row = MSI_CreateRecord(4);
+    MSI_RecordSetInteger(row,1,a);
+    MSI_RecordSetInteger(row,2,b);
+    MSI_RecordSetInteger(row,3,c);
+    MSI_RecordSetInteger(row,4,d);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_PROGRESS, row);
+    msiobj_release(&row->hdr);
+}
+
+static void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record)
+{
+    static const WCHAR Query_t[] = 
+{'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ','A','c','t','i','o',
+'n','T','e','x','t',' ','w','h','e','r','e',' ','A','c','t','i','o','n',' ','=',
+' ','\'','%','s','\'',0};
+    WCHAR message[1024];
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static WCHAR *ActionFormat=NULL;
+    static WCHAR LastAction[0x100] = {0};
+    WCHAR Query[1024];
+    LPWSTR ptr;
+
+    if (strcmpW(LastAction,action)!=0)
+    {
+        sprintfW(Query,Query_t,action);
+        rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+        if (rc != ERROR_SUCCESS)
+            return;
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            return;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            return;
+        }
+
+        if (MSI_RecordIsNull(row,3))
+        {
+            msiobj_release(&row->hdr);
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            return;
+        }
+
+        if (ActionFormat)
+            HeapFree(GetProcessHeap(),0,ActionFormat);
+
+        ActionFormat = load_dynamic_stringW(row,3);
+        strcpyW(LastAction,action);
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+
+    message[0]=0;
+    ptr = ActionFormat;
+    while (*ptr)
+    {
+        LPWSTR ptr2;
+        LPWSTR data=NULL;
+        WCHAR tmp[1023];
+        INT field;
+
+        ptr2 = strchrW(ptr,'[');
+        if (ptr2)
+        {
+            strncpyW(tmp,ptr,ptr2-ptr);
+            tmp[ptr2-ptr]=0;
+            strcatW(message,tmp);
+            ptr2++;
+            field = atoiW(ptr2);
+            data = load_dynamic_stringW(record,field);
+            if (data)
+            {
+                strcatW(message,data);
+                HeapFree(GetProcessHeap(),0,data);
+            }
+            ptr=strchrW(ptr2,']');
+            ptr++;
+        }
+        else
+        {
+            strcatW(message,ptr);
+            break;
+        }
+    }
+
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,message);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
+    msiobj_release(&row->hdr);
+}
+
+
+static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
+{
+    static const WCHAR template_s[]=
+{'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ','%','s','.',0};
+    static const WCHAR format[] = 
+{'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
+    static const WCHAR Query_t[] = 
+{'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ','A','c','t','i','o',
+'n','T','e','x','t',' ','w','h','e','r','e',' ','A','c','t','i','o','n',' ','=',
+' ','\'','%','s','\'',0};
+    WCHAR message[1024];
+    WCHAR timet[0x100];
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    WCHAR *ActionText=NULL;
+    WCHAR Query[1024];
+
+    GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
+
+    sprintfW(Query,Query_t,action);
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return;
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return;
+    }
+    rc = MSI_ViewFetch(view,&row);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return;
+    }
+
+    ActionText = load_dynamic_stringW(row,2);
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+    sprintfW(message,template_s,timet,action,ActionText);
+
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,message);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
+    msiobj_release(&row->hdr);
+    HeapFree(GetProcessHeap(),0,ActionText);
+}
+
+static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, 
+                          UINT rc)
+{
+    MSIRECORD * row;
+    static const WCHAR template_s[]=
+{'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ','%','s',
+'.',0};
+    static const WCHAR template_e[]=
+{'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ','%','s',
+'.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ','%','i','.',0};
+    static const WCHAR format[] = 
+{'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
+    WCHAR message[1024];
+    WCHAR timet[0x100];
+
+    GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
+    if (start)
+        sprintfW(message,template_s,timet,action);
+    else
+        sprintfW(message,template_e,timet,action,rc);
+    
+    row = MSI_CreateRecord(1);
+    MSI_RecordSetStringW(row,1,message);
+    MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
+    msiobj_release(&row->hdr);
+}
+
+/****************************************************
+ * TOP level entry points 
+ *****************************************************/
+
+UINT ACTION_DoTopLevelINSTALL(MSIPACKAGE *package, LPCWSTR szPackagePath,
+                              LPCWSTR szCommandLine)
+{
+    DWORD sz;
+    WCHAR buffer[10];
+    UINT rc;
+    static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
+
+    if (szPackagePath)   
+    {
+        LPWSTR p;
+        WCHAR check[MAX_PATH];
+        WCHAR pth[MAX_PATH];
+        DWORD size;
+        strcpyW(pth,szPackagePath);
+        p = strrchrW(pth,'\\');    
+        if (p)
+        {
+            p++;
+            *p=0;
+        }
+
+        size = MAX_PATH;
+        if (MSI_GetPropertyW(package,cszSourceDir,check,&size) 
+            != ERROR_SUCCESS )
+            MSI_SetPropertyW(package, cszSourceDir, pth);
+    }
+
+    if (szCommandLine)
+    {
+        LPWSTR ptr,ptr2;
+        ptr = (LPWSTR)szCommandLine;
+       
+        while (*ptr)
+        {
+            WCHAR prop[0x100];
+            WCHAR val[0x100];
+
+            TRACE("Looking at %s\n",debugstr_w(ptr));
+
+            ptr2 = strchrW(ptr,'=');
+            if (ptr2)
+            {
+                BOOL quote=FALSE;
+                DWORD len = 0;
+
+                while (*ptr == ' ') ptr++;
+                strncpyW(prop,ptr,ptr2-ptr);
+                prop[ptr2-ptr]=0;
+                ptr2++;
+            
+                ptr = ptr2; 
+                while (*ptr && (quote || (!quote && *ptr!=' ')))
+                {
+                    if (*ptr == '"')
+                        quote = !quote;
+                    ptr++;
+                    len++;
+                }
+               
+                if (*ptr2=='"')
+                {
+                    ptr2++;
+                    len -= 2;
+                }
+                strncpyW(val,ptr2,len);
+                val[len]=0;
+
+                if (strlenW(prop) > 0)
+                {
+                    TRACE("Found commandline property (%s) = (%s)\n", debugstr_w(prop), debugstr_w(val));
+                    MSI_SetPropertyW(package,prop,val);
+                }
+            }
+            ptr++;
+        }
+    }
+  
+    sz = 10; 
+    if (MSI_GetPropertyW(package,szUILevel,buffer,&sz) == ERROR_SUCCESS)
+    {
+        if (atoiW(buffer) >= INSTALLUILEVEL_REDUCED)
+        {
+            rc = ACTION_ProcessUISequence(package);
+            if (rc == ERROR_SUCCESS)
+                rc = ACTION_ProcessExecSequence(package,TRUE);
+        }
+        else
+            rc = ACTION_ProcessExecSequence(package,FALSE);
+    }
+    else
+        rc = ACTION_ProcessExecSequence(package,FALSE);
+
+    return rc;
+}
+
+
+static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
+{
+    MSIQUERY * view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery[] =  {
+       's','e','l','e','c','t',' ','*',' ',
+       'f','r','o','m',' ',
+           'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
+           'S','e','q','u','e','n','c','e',' ',
+       'w','h','e','r','e',' ','S','e','q','u','e','n','c','e',' ',
+           '>',' ','%','i',' ','o','r','d','e','r',' ',
+       'b','y',' ','S','e','q','u','e','n','c','e',0 };
+    WCHAR Query[1024];
+    MSIRECORD * row = 0;
+    static const WCHAR IVQuery[] = {
+       's','e','l','e','c','t',' ','S','e','q','u','e','n','c','e',' ',
+       'f','r','o','m',' ','I','n','s','t','a','l','l',
+           'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e',' ',
+       'w','h','e','r','e',' ','A','c','t','i','o','n',' ','=',' ',
+           '`','I','n','s','t','a','l','l','V','a','l','i','d','a','t','e','`',
+       0};
+
+    if (UIran)
+    {
+        INT seq = 0;
+        
+        rc = MSI_DatabaseOpenViewW(package->db, IVQuery, &view);
+        if (rc != ERROR_SUCCESS)
+            return rc;
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            return rc;
+        }
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            return rc;
+        }
+        seq = MSI_RecordGetInteger(row,1);
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        sprintfW(Query,ExecSeqQuery,seq);
+    }
+    else
+        sprintfW(Query,ExecSeqQuery,0);
+    
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_ViewExecute(view, 0);
+
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            goto end;
+        }
+       
+        TRACE("Running the actions \n"); 
+
+        while (1)
+        {
+            WCHAR buffer[0x100];
+            DWORD sz = 0x100;
+
+            rc = MSI_ViewFetch(view,&row);
+            if (rc != ERROR_SUCCESS)
+            {
+                rc = ERROR_SUCCESS;
+                break;
+            }
+
+            /* check conditions */
+            if (!MSI_RecordIsNull(row,2))
+            {
+                LPWSTR cond = NULL;
+                cond = load_dynamic_stringW(row,2);
+
+                if (cond)
+                {
+                    /* this is a hack to skip errors in the condition code */
+                    if (MSI_EvaluateConditionW(package, cond) ==
+                            MSICONDITION_FALSE)
+                    {
+                        HeapFree(GetProcessHeap(),0,cond);
+                        msiobj_release(&row->hdr);
+                        continue; 
+                    }
+                    else
+                        HeapFree(GetProcessHeap(),0,cond);
+                }
+            }
+
+            sz=0x100;
+            rc =  MSI_RecordGetStringW(row,1,buffer,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Error is %x\n",rc);
+                msiobj_release(&row->hdr);
+                break;
+            }
+
+            rc = ACTION_PerformAction(package,buffer);
+
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Execution halted due to error (%i)\n",rc);
+                msiobj_release(&row->hdr);
+                break;
+            }
+
+            msiobj_release(&row->hdr);
+        }
+
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+
+end:
+    return rc;
+}
+
+
+static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
+{
+    MSIQUERY * view;
+    UINT rc;
+    static const WCHAR ExecSeqQuery [] = {
+      's','e','l','e','c','t',' ','*',' ',
+      'f','r','o','m',' ','I','n','s','t','a','l','l',
+            'U','I','S','e','q','u','e','n','c','e',' ',
+      'w','h','e','r','e',' ','S','e','q','u','e','n','c','e',' ', '>',' ','0',' ',
+      'o','r','d','e','r',' ','b','y',' ','S','e','q','u','e','n','c','e',0};
+    
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_ViewExecute(view, 0);
+
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            goto end;
+        }
+       
+        TRACE("Running the actions \n"); 
+
+        while (1)
+        {
+            WCHAR buffer[0x100];
+            DWORD sz = 0x100;
+            MSIRECORD * row = 0;
+
+            rc = MSI_ViewFetch(view,&row);
+            if (rc != ERROR_SUCCESS)
+            {
+                rc = ERROR_SUCCESS;
+                break;
+            }
+
+            /* check conditions */
+            if (!MSI_RecordIsNull(row,2))
+            {
+                LPWSTR cond = NULL;
+                cond = load_dynamic_stringW(row,2);
+
+                if (cond)
+                {
+                    /* this is a hack to skip errors in the condition code */
+                    if (MSI_EvaluateConditionW(package, cond) ==
+                            MSICONDITION_FALSE)
+                    {
+                        HeapFree(GetProcessHeap(),0,cond);
+                        msiobj_release(&row->hdr);
+                        continue; 
+                    }
+                    else
+                        HeapFree(GetProcessHeap(),0,cond);
+                }
+            }
+
+            sz=0x100;
+            rc =  MSI_RecordGetStringW(row,1,buffer,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Error is %x\n",rc);
+                msiobj_release(&row->hdr);
+                break;
+            }
+
+            rc = ACTION_PerformAction(package,buffer);
+
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Execution halted due to error (%i)\n",rc);
+                msiobj_release(&row->hdr);
+                break;
+            }
+
+            msiobj_release(&row->hdr);
+        }
+
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+
+end:
+    return rc;
+}
+
+/********************************************************
+ * ACTION helper functions and functions that perform the actions
+ *******************************************************/
+
+/* 
+ * Alot of actions are really important even if they don't do anything
+ * explicit.. Lots of properties are set at the beginning of the installation
+ * CostFinalize does a bunch of work to translated the directories and such
+ * 
+ * But until I get write access to the database that is hard, so I am going to
+ * hack it to see if I can get something to run.
+ */
+UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action)
+{
+    UINT rc = ERROR_SUCCESS; 
+
+    TRACE("Performing action (%s)\n",debugstr_w(action));
+    ui_actioninfo(package, action, TRUE, 0);
+    ui_actionstart(package, action);
+    ui_progress(package,2,1,0,0);
+
+    /* pre install, setup and configuration block */
+    if (strcmpW(action,szLaunchConditions)==0)
+        rc = ACTION_LaunchConditions(package);
+    else if (strcmpW(action,szCostInitialize)==0)
+        rc = ACTION_CostInitialize(package);
+    else if (strcmpW(action,szFileCost)==0)
+        rc = ACTION_FileCost(package);
+    else if (strcmpW(action,szCostFinalize)==0)
+        rc = ACTION_CostFinalize(package);
+    else if (strcmpW(action,szInstallValidate)==0)
+        rc = ACTION_InstallValidate(package);
+
+    /* install block */
+    else if (strcmpW(action,szProcessComponents)==0)
+        rc = ACTION_ProcessComponents(package);
+    else if (strcmpW(action,szInstallInitialize)==0)
+        rc = ACTION_InstallInitialize(package);
+    else if (strcmpW(action,szCreateFolders)==0)
+        rc = ACTION_CreateFolders(package);
+    else if (strcmpW(action,szInstallFiles)==0)
+        rc = ACTION_InstallFiles(package);
+    else if (strcmpW(action,szDuplicateFiles)==0)
+        rc = ACTION_DuplicateFiles(package);
+    else if (strcmpW(action,szWriteRegistryValues)==0)
+        rc = ACTION_WriteRegistryValues(package);
+     else if (strcmpW(action,szRegisterTypeLibraries)==0)
+        rc = ACTION_RegisterTypeLibraries(package);
+     else if (strcmpW(action,szRegisterClassInfo)==0)
+        rc = ACTION_RegisterClassInfo(package);
+     else if (strcmpW(action,szRegisterProgIdInfo)==0)
+        rc = ACTION_RegisterProgIdInfo(package);
+     else if (strcmpW(action,szCreateShortcuts)==0)
+        rc = ACTION_CreateShortcuts(package);
+    else if (strcmpW(action,szPublishProduct)==0)
+        rc = ACTION_PublishProduct(package);
+
+    /*
+     Called during iTunes but unimplemented and seem important
+
+     ResolveSource  (sets SourceDir)
+     RegisterProduct
+     InstallFinalize
+     */
+     else if ((rc = ACTION_CustomAction(package,action)) != ERROR_SUCCESS)
+     {
+        FIXME("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
+        rc = ERROR_SUCCESS;
+     }
+
+    ui_actioninfo(package, action, FALSE, rc);
+    return rc;
+}
+
+
+static UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action)
+{
+    UINT rc = ERROR_SUCCESS;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    WCHAR ExecSeqQuery[1024] = 
+    {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
+,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
+,'o','n','`',' ','=',' ','`',0};
+    static const WCHAR end[]={'`',0};
+    UINT type;
+    LPWSTR source;
+    LPWSTR target;
+    WCHAR *deformated=NULL;
+
+    strcatW(ExecSeqQuery,action);
+    strcatW(ExecSeqQuery,end);
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    rc = MSI_ViewFetch(view,&row);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    type = MSI_RecordGetInteger(row,2);
+
+    source = load_dynamic_stringW(row,3);
+    target = load_dynamic_stringW(row,4);
+
+    TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type,
+          debugstr_w(source), debugstr_w(target));
+
+    /* we are ignoring ALOT of flags and important synchronization stuff */
+    switch (type & CUSTOM_ACTION_TYPE_MASK)
+    {
+        case 1: /* DLL file stored in a Binary table stream */
+            rc = HANDLE_CustomType1(package,source,target,type);
+            break;
+        case 2: /* EXE file stored in a Binary table strem */
+            rc = HANDLE_CustomType2(package,source,target,type);
+            break;
+        case 35: /* Directory set with formatted text. */
+        case 51: /* Property set with formatted text. */
+            deformat_string(package,target,&deformated);
+            rc = MSI_SetPropertyW(package,source,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+            break;
+        default:
+            FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n",
+             type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source),
+             debugstr_w(target));
+    }
+
+    HeapFree(GetProcessHeap(),0,source);
+    HeapFree(GetProcessHeap(),0,target);
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static UINT store_binary_to_temp(MSIPACKAGE *package, const LPWSTR source, 
+                                LPWSTR tmp_file)
+{
+    DWORD sz=MAX_PATH;
+
+    if (MSI_GetPropertyW(package, cszTempFolder, tmp_file, &sz) 
+        != ERROR_SUCCESS)
+        GetTempPathW(MAX_PATH,tmp_file);
+
+    strcatW(tmp_file,source);
+
+    if (GetFileAttributesW(tmp_file) != INVALID_FILE_ATTRIBUTES)
+    {
+        TRACE("File already exists\n");
+        return ERROR_SUCCESS;
+    }
+    else
+    {
+        /* write out the file */
+        UINT rc;
+        MSIQUERY * view;
+        MSIRECORD * row = 0;
+        WCHAR Query[1024] =
+        {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','B','i'
+,'n','a','r','y',' ','w','h','e','r','e',' ','N','a','m','e','=','`',0};
+        static const WCHAR end[]={'`',0};
+        HANDLE the_file;
+        CHAR buffer[1024];
+
+        if (track_tempfile(package, source, tmp_file)!=0)
+            FIXME("File Name in temp tracking collision\n");
+
+        the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+    
+        if (the_file == INVALID_HANDLE_VALUE)
+            return ERROR_FUNCTION_FAILED;
+
+        strcatW(Query,source);
+        strcatW(Query,end);
+
+        rc = MSI_DatabaseOpenViewW( package->db, Query, &view);
+        if (rc != ERROR_SUCCESS)
+            return rc;
+
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            return rc;
+        }
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            return rc;
+        }
+
+        do 
+        {
+            DWORD write;
+            sz = 1024;
+            rc = MSI_RecordReadStream(row,2,buffer,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Failed to get stream\n");
+                CloseHandle(the_file);  
+                DeleteFileW(tmp_file);
+                break;
+            }
+            WriteFile(the_file,buffer,sz,&write,NULL);
+        } while (sz == 1024);
+
+        CloseHandle(the_file);
+
+        msiobj_release(&row->hdr);
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+
+    return ERROR_SUCCESS;
+}
+
+
+typedef UINT __stdcall CustomEntry(MSIHANDLE);
+typedef struct 
+{
+        MSIPACKAGE *package;
+        WCHAR target[MAX_PATH];
+        WCHAR source[MAX_PATH];
+} thread_struct;
+
+#if 0
+static DWORD WINAPI DllThread(LPVOID info)
+{
+    HANDLE DLL;
+    LPSTR proc;
+    thread_struct *stuff;
+    CustomEntry *fn;
+     
+    stuff = (thread_struct*)info;
+
+    TRACE("Asynchronous start (%s, %s) \n", debugstr_w(stuff->source),
+          debugstr_w(stuff->target));
+
+    DLL = LoadLibraryW(stuff->source);
+    if (DLL)
+    {
+        proc = strdupWtoA( stuff->target );
+        fn = (CustomEntry*)GetProcAddress(DLL,proc);
+        if (fn)
+        {
+            MSIHANDLE hPackage;
+            MSIPACKAGE *package = stuff->package;
+
+            TRACE("Calling function\n");
+            hPackage = msiobj_findhandle( &package->hdr );
+            if( !hPackage )
+                ERR("Handle for object %p not found\n", package );
+            fn(hPackage);
+            msiobj_release( &package->hdr );
+        }
+        else
+            ERR("Cannot load functon\n");
+
+        HeapFree(GetProcessHeap(),0,proc);
+        FreeLibrary(DLL);
+    }
+    else
+        ERR("Unable to load library\n");
+    msiobj_release( &stuff->package->hdr );
+    HeapFree( GetProcessHeap(), 0, info );
+    return 0;
+}
+#endif
+
+static UINT HANDLE_CustomType1(MSIPACKAGE *package, const LPWSTR source, 
+                                const LPWSTR target, const INT type)
+{
+    WCHAR tmp_file[MAX_PATH];
+    CustomEntry *fn;
+    HANDLE DLL;
+    LPSTR proc;
+
+    store_binary_to_temp(package, source, tmp_file);
+
+    TRACE("Calling function %s from %s\n",debugstr_w(target),
+          debugstr_w(tmp_file));
+
+    if (!strchrW(tmp_file,'.'))
+    {
+        static const WCHAR dot[]={'.',0};
+        strcatW(tmp_file,dot);
+    } 
+
+    if (type & 0xc0)
+    {
+        /* DWORD ThreadId; */
+        thread_struct *info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
+
+        /* msiobj_addref( &package->hdr ); */
+        info->package = package;
+        strcpyW(info->target,target);
+        strcpyW(info->source,tmp_file);
+        TRACE("Start Asynchronous execution\n");
+        FIXME("DATABASE NOT THREADSAFE... not starting\n");
+        /* CreateThread(NULL,0,DllThread,(LPVOID)&info,0,&ThreadId); */
+        /* FIXME: release the package if the CreateThread fails */
+        HeapFree( GetProcessHeap(), 0, info );
+        return ERROR_SUCCESS;
+    }
+    DLL = LoadLibraryW(tmp_file);
+    if (DLL)
+    {
+        proc = strdupWtoA( target );
+        fn = (CustomEntry*)GetProcAddress(DLL,proc);
+        if (fn)
+        {
+            MSIHANDLE hPackage;
+
+            TRACE("Calling function\n");
+            hPackage = msiobj_findhandle( &package->hdr );
+            if( !hPackage )
+                ERR("Handle for object %p not found\n", package );
+            fn(hPackage);
+            msiobj_release( &package->hdr );
+        }
+        else
+            ERR("Cannot load functon\n");
+
+        HeapFree(GetProcessHeap(),0,proc);
+        FreeLibrary(DLL);
+    }
+    else
+        ERR("Unable to load library\n");
+
+    return ERROR_SUCCESS;
+}
+
+static UINT HANDLE_CustomType2(MSIPACKAGE *package, const LPWSTR source, 
+                                const LPWSTR target, const INT type)
+{
+    WCHAR tmp_file[MAX_PATH*2];
+    STARTUPINFOW si;
+    PROCESS_INFORMATION info;
+    BOOL rc;
+    WCHAR *deformated;
+    static const WCHAR spc[] = {' ',0};
+
+    memset(&si,0,sizeof(STARTUPINFOW));
+    memset(&info,0,sizeof(PROCESS_INFORMATION));
+
+    store_binary_to_temp(package, source, tmp_file);
+
+    strcatW(tmp_file,spc);
+    deformat_string(package,target,&deformated);
+    strcatW(tmp_file,deformated);
+
+    HeapFree(GetProcessHeap(),0,deformated);
+
+    TRACE("executing exe %s \n",debugstr_w(tmp_file));
+
+    rc = CreateProcessW(NULL, tmp_file, NULL, NULL, FALSE, 0, NULL,
+                  c_collen, &si, &info);
+
+    if ( !rc )
+    {
+        ERR("Unable to execute command\n");
+        return ERROR_SUCCESS;
+    }
+
+    if (!(type & 0xc0))
+        WaitForSingleObject(info.hProcess,INFINITE);
+
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *            create_full_pathW
+ *
+ * Recursively create all directories in the path.
+ *
+ * shamelessly stolen from setupapi/queue.c
+ */
+static BOOL create_full_pathW(const WCHAR *path)
+{
+    BOOL ret = TRUE;
+    int len;
+    WCHAR *new_path;
+
+    new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
+sizeof(WCHAR));
+    strcpyW(new_path, path);
+
+    while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
+    new_path[len - 1] = 0;
+
+    while(!CreateDirectoryW(new_path, NULL))
+    {
+    WCHAR *slash;
+    DWORD last_error = GetLastError();
+    if(last_error == ERROR_ALREADY_EXISTS)
+        break;
+
+    if(last_error != ERROR_PATH_NOT_FOUND)
+    {
+        ret = FALSE;
+        break;
+    }
+
+    if(!(slash = strrchrW(new_path, '\\')))
+    {
+        ret = FALSE;
+        break;
+    }
+
+    len = slash - new_path;
+    new_path[len] = 0;
+    if(!create_full_pathW(new_path))
+    {
+        ret = FALSE;
+        break;
+    }
+    new_path[len] = '\\';
+    }
+
+    HeapFree(GetProcessHeap(), 0, new_path);
+    return ret;
+}
+
+/*
+ * Also we cannot enable/disable components either, so for now I am just going 
+ * to do all the directories for all the components.
+ */
+static UINT ACTION_CreateFolders(MSIPACKAGE *package)
+{
+    static const WCHAR ExecSeqQuery[] = {
+        's','e','l','e','c','t',' ','D','i','r','e','c','t','o','r','y','_',' ',
+        'f','r','o','m',' ','C','r','e','a','t','e','F','o','l','d','e','r',0 };
+    UINT rc;
+    MSIQUERY *view;
+    MSIFOLDER *folder;
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+    
+    while (1)
+    {
+        WCHAR dir[0x100];
+        WCHAR full_path[MAX_PATH];
+        DWORD sz;
+        MSIRECORD *row = NULL, *uirow;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        sz=0x100;
+        rc = MSI_RecordGetStringW(row,1,dir,&sz);
+
+        if (rc!= ERROR_SUCCESS)
+        {
+            ERR("Unable to get folder id \n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        sz = MAX_PATH;
+        rc = resolve_folder(package,dir,full_path,FALSE,FALSE,&folder);
+
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        TRACE("Folder is %s\n",debugstr_w(full_path));
+
+        /* UI stuff */
+        uirow = MSI_CreateRecord(1);
+        MSI_RecordSetStringW(uirow,1,full_path);
+        ui_actiondata(package,szCreateFolders,uirow);
+        msiobj_release( &uirow->hdr );
+
+        if (folder->State == 0)
+            create_full_pathW(full_path);
+
+        folder->State = 3;
+
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+   
+    return rc;
+}
+
+static int load_component(MSIPACKAGE* package, MSIRECORD * row)
+{
+    int index = package->loaded_components;
+    DWORD sz;
+
+    /* fill in the data */
+
+    package->loaded_components++;
+    if (package->loaded_components == 1)
+        package->components = HeapAlloc(GetProcessHeap(),0,
+                                        sizeof(MSICOMPONENT));
+    else
+        package->components = HeapReAlloc(GetProcessHeap(),0,
+            package->components, package->loaded_components * 
+            sizeof(MSICOMPONENT));
+
+    memset(&package->components[index],0,sizeof(MSICOMPONENT));
+
+    sz = 96;       
+    MSI_RecordGetStringW(row,1,package->components[index].Component,&sz);
+
+    TRACE("Loading Component %s\n",
+           debugstr_w(package->components[index].Component));
+
+    sz = 0x100;
+    if (!MSI_RecordIsNull(row,2))
+        MSI_RecordGetStringW(row,2,package->components[index].ComponentId,&sz);
+            
+    sz = 96;       
+    MSI_RecordGetStringW(row,3,package->components[index].Directory,&sz);
+
+    package->components[index].Attributes = MSI_RecordGetInteger(row,4);
+
+    sz = 0x100;       
+    MSI_RecordGetStringW(row,5,package->components[index].Condition,&sz);
+
+    sz = 96;       
+    MSI_RecordGetStringW(row,6,package->components[index].KeyPath,&sz);
+
+    package->components[index].State = INSTALLSTATE_UNKNOWN;
+    package->components[index].Enabled = TRUE;
+    package->components[index].FeatureState= FALSE;
+
+    return index;
+}
+
+static void load_feature(MSIPACKAGE* package, MSIRECORD * row)
+{
+    int index = package->loaded_features;
+    DWORD sz;
+    static const WCHAR Query1[] = {'S','E','L','E','C','T',' ','C','o','m','p',
+'o','n','e','n','t','_',' ','F','R','O','M',' ','F','e','a','t','u','r','e',
+'C','o','m','p','o','n','e','n','t','s',' ','W','H','E','R','E',' ','F','e',
+'a','t','u','r','e','_','=','\'','%','s','\'',0};
+    static const WCHAR Query2[] = {'S','E','L','E','C','T',' ','*',' ','F','R',
+'O','M',' ','C','o','m','p','o','n','e','n','t',' ','W','H','E','R','E',' ','C',
+'o','m','p','o','n','e','n','t','=','\'','%','s','\'',0};
+    WCHAR Query[1024];
+    MSIQUERY * view;
+    MSIQUERY * view2;
+    MSIRECORD * row2;
+    MSIRECORD * row3;
+    UINT    rc;
+
+    /* fill in the data */
+
+    package->loaded_features ++;
+    if (package->loaded_features == 1)
+        package->features = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFEATURE));
+    else
+        package->features = HeapReAlloc(GetProcessHeap(),0,package->features,
+                                package->loaded_features * sizeof(MSIFEATURE));
+
+    memset(&package->features[index],0,sizeof(MSIFEATURE));
+    
+    sz = 96;       
+    MSI_RecordGetStringW(row,1,package->features[index].Feature,&sz);
+
+    TRACE("Loading feature %s\n",debugstr_w(package->features[index].Feature));
+
+    sz = 96;
+    if (!MSI_RecordIsNull(row,2))
+        MSI_RecordGetStringW(row,2,package->features[index].Feature_Parent,&sz);
+
+    sz = 0x100;
+     if (!MSI_RecordIsNull(row,3))
+        MSI_RecordGetStringW(row,3,package->features[index].Title,&sz);
+
+     sz = 0x100;
+     if (!MSI_RecordIsNull(row,4))
+        MSI_RecordGetStringW(row,4,package->features[index].Description,&sz);
+
+    if (!MSI_RecordIsNull(row,5))
+        package->features[index].Display = MSI_RecordGetInteger(row,5);
+  
+    package->features[index].Level= MSI_RecordGetInteger(row,6);
+
+     sz = 96;
+     if (!MSI_RecordIsNull(row,7))
+        MSI_RecordGetStringW(row,7,package->features[index].Directory,&sz);
+
+    package->features[index].Attributes= MSI_RecordGetInteger(row,8);
+    package->features[index].State = INSTALLSTATE_UNKNOWN;
+
+    /* load feature components */
+
+    sprintfW(Query,Query1,package->features[index].Feature);
+    rc = MSI_DatabaseOpenViewW(package->db,Query,&view);
+    if (rc != ERROR_SUCCESS)
+        return;
+    rc = MSI_ViewExecute(view,0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return;
+    }
+    while (1)
+    {
+        DWORD sz = 0x100;
+        WCHAR buffer[0x100];
+        DWORD rc;
+        INT c_indx;
+        INT cnt = package->features[index].ComponentCount;
+
+        rc = MSI_ViewFetch(view,&row2);
+        if (rc != ERROR_SUCCESS)
+            break;
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row2,1,buffer,&sz);
+
+        /* check to see if the component is already loaded */
+        c_indx = get_loaded_component(package,buffer);
+        if (c_indx != -1)
+        {
+            TRACE("Component %s already loaded at %i\n", debugstr_w(buffer),
+                  c_indx);
+            package->features[index].Components[cnt] = c_indx;
+            package->features[index].ComponentCount ++;
+        }
+
+        sprintfW(Query,Query2,buffer);
+   
+        rc = MSI_DatabaseOpenViewW(package->db,Query,&view2);
+        if (rc != ERROR_SUCCESS)
+        {
+            msiobj_release( &row2->hdr );
+            continue;
+        }
+        rc = MSI_ViewExecute(view2,0);
+        if (rc != ERROR_SUCCESS)
+        {
+            msiobj_release( &row2->hdr );
+            MSI_ViewClose(view2);
+            msiobj_release( &view2->hdr );  
+            continue;
+        }
+        while (1)
+        {
+            DWORD rc;
+
+            rc = MSI_ViewFetch(view2,&row3);
+            if (rc != ERROR_SUCCESS)
+                break;
+            c_indx = load_component(package,row3);
+            msiobj_release( &row3->hdr );
+
+            package->features[index].Components[cnt] = c_indx;
+            package->features[index].ComponentCount ++;
+        }
+        MSI_ViewClose(view2);
+        msiobj_release( &view2->hdr );
+        msiobj_release( &row2->hdr );
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+}
+
+/*
+ * I am not doing any of the costing functionality yet. 
+ * Mostly looking at doing the Component and Feature loading
+ *
+ * The native MSI does ALOT of modification to tables here. Mostly adding alot
+ * of temporary columns to the Feature and Component tables. 
+ *
+ *    note: native msi also tracks the short filename. but I am only going to
+ *          track the long ones.  Also looking at this directory table
+ *          it appears that the directory table does not get the parents
+ *          resolved base on property only based on their entrys in the 
+ *          directory table.
+ */
+static UINT ACTION_CostInitialize(MSIPACKAGE *package)
+{
+    MSIQUERY * view;
+    MSIRECORD * row;
+    DWORD sz;
+    UINT rc;
+    static const WCHAR Query_all[] = {
+       'S','E','L','E','C','T',' ','*',' ',
+       'F','R','O','M',' ','F','e','a','t','u','r','e',0};
+    static const WCHAR szCosting[] = {
+       'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
+    static const WCHAR szZero[] = { '0', 0 };
+
+    MSI_SetPropertyW(package, szCosting, szZero);
+    MSI_SetPropertyW(package, cszRootDrive , c_collen);
+
+    sz = 0x100;
+    rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+    rc = MSI_ViewExecute(view,0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+    while (1)
+    {
+        DWORD rc;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+            break;
+       
+        load_feature(package,row); 
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT load_file(MSIPACKAGE* package, MSIRECORD * row)
+{
+    DWORD index = package->loaded_files;
+    DWORD i;
+    WCHAR buffer[0x100];
+    DWORD sz;
+
+    /* fill in the data */
+
+    package->loaded_files++;
+    if (package->loaded_files== 1)
+        package->files = HeapAlloc(GetProcessHeap(),0,sizeof(MSIFILE));
+    else
+        package->files = HeapReAlloc(GetProcessHeap(),0,
+            package->files , package->loaded_files * sizeof(MSIFILE));
+
+    memset(&package->files[index],0,sizeof(MSIFILE));
+
+    sz = 72;       
+    MSI_RecordGetStringW(row,1,package->files[index].File,&sz);
+
+    sz = 0x100;       
+    MSI_RecordGetStringW(row,2,buffer,&sz);
+
+    package->files[index].ComponentIndex = -1;
+    for (i = 0; i < package->loaded_components; i++)
+        if (strcmpW(package->components[i].Component,buffer)==0)
+        {
+            package->files[index].ComponentIndex = i;
+            break;
+        }
+    if (package->files[index].ComponentIndex == -1)
+        ERR("Unfound Component %s\n",debugstr_w(buffer));
+
+    sz = MAX_PATH;       
+    MSI_RecordGetStringW(row,3,package->files[index].FileName,&sz);
+
+    reduce_to_longfilename(package->files[index].FileName);
+    
+    package->files[index].FileSize = MSI_RecordGetInteger(row,4);
+
+    sz = 72;       
+    if (!MSI_RecordIsNull(row,5))
+        MSI_RecordGetStringW(row,5,package->files[index].Version,&sz);
+
+    sz = 20;       
+    if (!MSI_RecordIsNull(row,6))
+        MSI_RecordGetStringW(row,6,package->files[index].Language,&sz);
+
+    if (!MSI_RecordIsNull(row,7))
+        package->files[index].Attributes= MSI_RecordGetInteger(row,7);
+
+    package->files[index].Sequence= MSI_RecordGetInteger(row,8);
+
+    package->files[index].Temporary = FALSE;
+    package->files[index].State = 0;
+
+    TRACE("File Loaded (%s)\n",debugstr_w(package->files[index].File));  
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_FileCost(MSIPACKAGE *package)
+{
+    MSIQUERY * view;
+    MSIRECORD * row;
+    UINT rc;
+    static const WCHAR Query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','F','i','l','e',' ',
+        'O','r','d','e','r',' ','b','y',' ','S','e','q','u','e','n','c','e', 0};
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+   
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return ERROR_SUCCESS;
+    }
+
+    while (1)
+    {
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+        load_file(package,row);
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
+static INT load_folder(MSIPACKAGE *package, const WCHAR* dir)
+
+{
+    WCHAR Query[1024] = 
+{'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
+'t','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
+'o','r','y','`',' ','=',' ','`',0};
+    static const WCHAR end[]={'`',0};
+    UINT rc;
+    MSIQUERY * view;
+    WCHAR targetbuffer[0x100];
+    WCHAR *srcdir = NULL;
+    WCHAR *targetdir = NULL;
+    WCHAR parent[0x100];
+    DWORD sz=0x100;
+    MSIRECORD * row = 0;
+    INT index = -1;
+    DWORD i;
+
+    TRACE("Looking for dir %s\n",debugstr_w(dir));
+
+    for (i = 0; i < package->loaded_folders; i++)
+    {
+        if (strcmpW(package->folders[i].Directory,dir)==0)
+        {
+            TRACE(" %s retuning on index %lu\n",debugstr_w(dir),i);
+            return i;
+        }
+    }
+
+    TRACE("Working to load %s\n",debugstr_w(dir));
+
+    index = package->loaded_folders; 
+
+    package->loaded_folders++;
+    if (package->loaded_folders== 1)
+        package->folders = HeapAlloc(GetProcessHeap(),0,
+                                        sizeof(MSIFOLDER));
+    else
+        package->folders= HeapReAlloc(GetProcessHeap(),0,
+            package->folders, package->loaded_folders* 
+            sizeof(MSIFOLDER));
+
+    memset(&package->folders[index],0,sizeof(MSIFOLDER));
+
+    strcpyW(package->folders[index].Directory,dir);
+
+    strcatW(Query,dir);
+    strcatW(Query,end);
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+
+    if (rc != ERROR_SUCCESS)
+        return -1;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return -1;
+    }
+
+    rc = MSI_ViewFetch(view,&row);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return -1;
+    }
+
+    sz=0x100;
+    MSI_RecordGetStringW(row,3,targetbuffer,&sz);
+    targetdir=targetbuffer;
+
+    /* split src and target dir */
+    if (strchrW(targetdir,':'))
+    {
+        srcdir=strchrW(targetdir,':');
+        *srcdir=0;
+        srcdir ++;
+    }
+    else
+        srcdir=NULL;
+
+    /* for now only pick long filename versions */
+    if (strchrW(targetdir,'|'))
+    {
+        targetdir = strchrW(targetdir,'|'); 
+        *targetdir = 0;
+        targetdir ++;
+    }
+    if (srcdir && strchrW(srcdir,'|'))
+    {
+        srcdir= strchrW(srcdir,'|'); 
+        *srcdir= 0;
+        srcdir ++;
+    }
+
+    /* now check for root dirs */
+    if (targetdir[0] == '.' && targetdir[1] == 0)
+        targetdir = NULL;
+        
+    if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
+        srcdir = NULL;
+
+     if (targetdir)
+        strcpyW(package->folders[index].TargetDefault,targetdir);
+
+     if (srcdir)
+        strcpyW(package->folders[index].SourceDefault,srcdir);
+     else if (targetdir)
+        strcpyW(package->folders[index].SourceDefault,targetdir);
+
+    if (MSI_RecordIsNull(row,2))
+        parent[0]=0;
+    else
+    {
+            sz=0x100;
+            MSI_RecordGetStringW(row,2,parent,&sz);
+    }
+
+    if (parent[0]) 
+    {
+        i = load_folder(package,parent);
+        package->folders[index].ParentIndex = i;
+        TRACE("Parent is index %i... %s %s\n",
+                    package->folders[index].ParentIndex,
+    debugstr_w(package->folders[package->folders[index].ParentIndex].Directory),
+                    debugstr_w(parent));
+    }
+    else
+        package->folders[index].ParentIndex = -2;
+
+    sz = MAX_PATH;
+    rc = MSI_GetPropertyW(package, dir, package->folders[index].Property, &sz);
+    if (rc != ERROR_SUCCESS)
+        package->folders[index].Property[0]=0;
+
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    TRACE(" %s retuning on index %i\n",debugstr_w(dir),index);
+    return index;
+}
+
+static UINT resolve_folder(MSIPACKAGE *package, LPCWSTR name, LPWSTR path, 
+                           BOOL source, BOOL set_prop, MSIFOLDER **folder)
+{
+    DWORD i;
+    UINT rc = ERROR_SUCCESS;
+    DWORD sz;
+
+    TRACE("Working to resolve %s\n",debugstr_w(name));
+
+    if (!path)
+        return rc;
+
+    /* special resolving for Target and Source root dir */
+    if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
+    {
+        if (!source)
+        {
+            sz = MAX_PATH;
+            rc = MSI_GetPropertyW(package,cszTargetDir,path,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                sz = MAX_PATH;
+                rc = MSI_GetPropertyW(package,cszRootDrive,path,&sz);
+                if (set_prop)
+                    MSI_SetPropertyW(package,cszTargetDir,path);
+            }
+            if (folder)
+                *folder = &(package->folders[0]);
+            return rc;
+        }
+        else
+        {
+            sz = MAX_PATH;
+            rc = MSI_GetPropertyW(package,cszSourceDir,path,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                sz = MAX_PATH;
+                rc = MSI_GetPropertyW(package,cszDatabase,path,&sz);
+                if (rc == ERROR_SUCCESS)
+                {
+                    LPWSTR ptr = strrchrW(path,'\\');
+                    if (ptr)
+                    {
+                        ptr++;
+                        *ptr = 0;
+                    }
+                }
+            }
+            if (folder)
+                *folder = &(package->folders[0]);
+            return rc;
+        }
+    }
+
+    for (i = 0; i < package->loaded_folders; i++)
+    {
+        if (strcmpW(package->folders[i].Directory,name)==0)
+            break;
+    }
+
+    if (i >= package->loaded_folders)
+        return ERROR_FUNCTION_FAILED;
+
+    if (folder)
+        *folder = &(package->folders[i]);
+
+    if (!source && package->folders[i].ResolvedTarget[0])
+    {
+        strcpyW(path,package->folders[i].ResolvedTarget);
+        TRACE("   already resolved to %s\n",debugstr_w(path));
+        return ERROR_SUCCESS;
+    }
+    else if (source && package->folders[i].ResolvedSource[0])
+    {
+        strcpyW(path,package->folders[i].ResolvedSource);
+        return ERROR_SUCCESS;
+    }
+    else if (!source && package->folders[i].Property[0])
+    {
+        strcpyW(path,package->folders[i].Property);
+        TRACE("   internally set to %s\n",debugstr_w(path));
+        if (set_prop)
+            MSI_SetPropertyW(package,name,path);
+        return ERROR_SUCCESS;
+    }
+
+    if (package->folders[i].ParentIndex >= 0)
+    {
+        int len;
+        TRACE(" ! Parent is %s\n", debugstr_w(package->folders[
+                   package->folders[i].ParentIndex].Directory));
+        resolve_folder(package, package->folders[
+                       package->folders[i].ParentIndex].Directory, path,source,
+                       set_prop, NULL);
+
+        len = strlenW(path);
+        if (len && path[len-1] != '\\')
+            strcatW(path, cszbs);
+
+        if (!source)
+        {
+            if (package->folders[i].TargetDefault[0])
+            {
+                strcatW(path,package->folders[i].TargetDefault);
+                strcatW(path,cszbs);
+            }
+            strcpyW(package->folders[i].ResolvedTarget,path);
+            TRACE("   resolved into %s\n",debugstr_w(path));
+            if (set_prop)
+                MSI_SetPropertyW(package,name,path);
+        }
+        else 
+        {
+            if (package->folders[i].SourceDefault[0])
+            {
+                strcatW(path,package->folders[i].SourceDefault);
+                strcatW(path,cszbs);
+            }
+            strcpyW(package->folders[i].ResolvedSource,path);
+        }
+    }
+    return rc;
+}
+
+/* 
+ * Alot is done in this function aside from just the costing.
+ * The costing needs to be implemented at some point but for now I am going
+ * to focus on the directory building
+ *
+ */
+static UINT ACTION_CostFinalize(MSIPACKAGE *package)
+{
+    static const WCHAR ExecSeqQuery[] = {
+        's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
+        'D','i','r','e','c','t','o','r','y',0};
+    static const WCHAR ConditionQuery[] = {
+        's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
+        'C','o','n','d','i','t','i','o','n',0};
+    static const WCHAR szCosting[] = {
+       'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
+    static const WCHAR szOne[] = { '1', 0 };
+    UINT rc;
+    MSIQUERY * view;
+    DWORD i;
+
+    TRACE("Building Directory properties\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_ViewExecute(view, 0);
+        if (rc != ERROR_SUCCESS)
+        {
+            MSI_ViewClose(view);
+            msiobj_release(&view->hdr);
+            return rc;
+        }
+
+        while (1)
+        {
+            WCHAR name[0x100];
+            WCHAR path[MAX_PATH];
+            MSIRECORD * row = 0;
+            DWORD sz;
+
+            rc = MSI_ViewFetch(view,&row);
+            if (rc != ERROR_SUCCESS)
+            {
+                rc = ERROR_SUCCESS;
+                break;
+            }
+
+            sz=0x100;
+            MSI_RecordGetStringW(row,1,name,&sz);
+
+            /* This helper function now does ALL the work */
+            TRACE("Dir %s ...\n",debugstr_w(name));
+            load_folder(package,name);
+            resolve_folder(package,name,path,FALSE,TRUE,NULL);
+            TRACE("resolves to %s\n",debugstr_w(path));
+
+            msiobj_release(&row->hdr);
+        }
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+
+    TRACE("File calculations %i files\n",package->loaded_files);
+
+    for (i = 0; i < package->loaded_files; i++)
+    {
+        MSICOMPONENT* comp = NULL;
+        MSIFILE* file= NULL;
+
+        file = &package->files[i];
+        if (file->ComponentIndex >= 0)
+            comp = &package->components[file->ComponentIndex];
+
+        if (comp)
+        {
+            int len;
+            /* calculate target */
+            resolve_folder(package, comp->Directory, file->TargetPath, FALSE,
+                       FALSE, NULL);
+            /* make sure that the path ends in a \ */
+            len = strlenW(file->TargetPath);
+            if (len && file->TargetPath[len-1] != '\\')
+                strcatW(file->TargetPath, cszbs);
+            strcatW(file->TargetPath,file->FileName);
+
+            TRACE("file %s resolves to %s\n",
+                   debugstr_w(file->File),debugstr_w(file->TargetPath));       
+            if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
+            {
+                file->State = 1;
+                comp->Cost += file->FileSize;
+            }
+            else
+            {
+                if (file->Version[0])
+                {
+                    DWORD handle;
+                    DWORD versize;
+                    UINT sz;
+                    LPVOID version;
+                    static const WCHAR name[] = 
+                    {'\\',0};
+                    static const WCHAR name_fmt[] = 
+                    {'%','u','.','%','u','.','%','u','.','%','u',0};
+                    WCHAR filever[0x100];
+                    VS_FIXEDFILEINFO *lpVer;
+
+                    FIXME("Version comparison.. \n");
+                    versize = GetFileVersionInfoSizeW(file->TargetPath,&handle);
+                    version = HeapAlloc(GetProcessHeap(),0,versize);
+                    GetFileVersionInfoW(file->TargetPath, 0, versize, version);
+
+                    VerQueryValueW(version, name, (LPVOID*)&lpVer, &sz);
+
+                    sprintfW(filever,name_fmt,
+                        HIWORD(lpVer->dwFileVersionMS),
+                        LOWORD(lpVer->dwFileVersionMS),
+                        HIWORD(lpVer->dwFileVersionLS),
+                        LOWORD(lpVer->dwFileVersionLS));
+
+                    TRACE("new %s old %s\n", debugstr_w(file->Version),
+                          debugstr_w(filever));
+                    if (strcmpiW(filever,file->Version)<0)
+                    {
+                        file->State = 2;
+                        FIXME("cost should be diff in size\n");
+                        comp->Cost += file->FileSize;
+                    }
+                    else
+                        file->State = 3;
+                    HeapFree(GetProcessHeap(),0,version);
+                }
+                else
+                    file->State = 3;
+            }
+        } 
+    }
+
+    TRACE("Evaluating Condition Table\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
+    if (rc == ERROR_SUCCESS)
+    {
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+    
+    while (1)
+    {
+        WCHAR Feature[0x100];
+        MSIRECORD * row = 0;
+        DWORD sz;
+        int feature_index;
+
+        rc = MSI_ViewFetch(view,&row);
+
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row,1,Feature,&sz);
+
+        feature_index = get_loaded_feature(package,Feature);
+        if (feature_index < 0)
+            ERR("FAILED to find loaded feature %s\n",debugstr_w(Feature));
+        else
+        {
+            LPWSTR Condition;
+            Condition = load_dynamic_stringW(row,3);
+
+                if (MSI_EvaluateConditionW(package,Condition) == 
+                    MSICONDITION_TRUE)
+            {
+                int level = MSI_RecordGetInteger(row,2);
+                    TRACE("Reseting feature %s to level %i\n",
+                           debugstr_w(Feature), level);
+                package->features[feature_index].Level = level;
+            }
+            HeapFree(GetProcessHeap(),0,Condition);
+        }
+
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    }
+
+    TRACE("Enabling or Disabling Components\n");
+    for (i = 0; i < package->loaded_components; i++)
+    {
+        if (package->components[i].Condition[0])
+        {
+            if (MSI_EvaluateConditionW(package,
+                package->components[i].Condition) == MSICONDITION_FALSE)
+            {
+                TRACE("Disabling component %s\n",
+                      debugstr_w(package->components[i].Component));
+                package->components[i].Enabled = FALSE;
+            }
+        }
+    }
+
+    MSI_SetPropertyW(package,szCosting,szOne);
+    return ERROR_SUCCESS;
+}
+
+/*
+ * This is a helper function for handling embedded cabinet media
+ */
+static UINT writeout_cabinet_stream(MSIPACKAGE *package, WCHAR* stream_name,
+                                    WCHAR* source)
+{
+    UINT rc;
+    USHORT* data;
+    UINT    size;
+    DWORD   write;
+    HANDLE  the_file;
+    WCHAR tmp[MAX_PATH];
+
+    rc = read_raw_stream_data(package->db,stream_name,&data,&size); 
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    write = MAX_PATH;
+    if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write))
+        GetTempPathW(MAX_PATH,tmp);
+
+    GetTempFileNameW(tmp,stream_name,0,source);
+
+    track_tempfile(package,strrchrW(source,'\\'), source);
+    the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (the_file == INVALID_HANDLE_VALUE)
+    {
+        rc = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    WriteFile(the_file,data,size,&write,NULL);
+    CloseHandle(the_file);
+    TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
+end:
+    HeapFree(GetProcessHeap(),0,data);
+    return rc;
+}
+
+
+/* Support functions for FDI functions */
+
+static void * cabinet_alloc(ULONG cb)
+{
+    return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+static void cabinet_free(void *pv)
+{
+    HeapFree(GetProcessHeap(), 0, pv);
+}
+
+static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode)
+{
+    DWORD dwAccess = 0;
+    DWORD dwShareMode = 0;
+    DWORD dwCreateDisposition = OPEN_EXISTING;
+    switch (oflag & _O_ACCMODE)
+    {
+    case _O_RDONLY:
+        dwAccess = GENERIC_READ;
+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
+        break;
+    case _O_WRONLY:
+        dwAccess = GENERIC_WRITE;
+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+        break;
+    case _O_RDWR:
+        dwAccess = GENERIC_READ | GENERIC_WRITE;
+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
+        break;
+    }
+    if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
+        dwCreateDisposition = CREATE_NEW;
+    else if (oflag & _O_CREAT)
+        dwCreateDisposition = CREATE_ALWAYS;
+    return (INT_PTR)CreateFileA(pszFile, dwAccess, dwShareMode, NULL, dwCreateDisposition, 0, NULL);
+}
+
+static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb)
+{
+    DWORD dwRead;
+    if (ReadFile((HANDLE)hf, pv, cb, &dwRead, NULL))
+        return dwRead;
+    return 0;
+}
+
+static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb)
+{
+    DWORD dwWritten;
+    if (WriteFile((HANDLE)hf, pv, cb, &dwWritten, NULL))
+        return dwWritten;
+    return 0;
+}
+
+static int cabinet_close(INT_PTR hf)
+{
+    return CloseHandle((HANDLE)hf) ? 0 : -1;
+}
+
+static long cabinet_seek(INT_PTR hf, long dist, int seektype)
+{
+    /* flags are compatible and so are passed straight through */
+    return SetFilePointer((HANDLE)hf, dist, NULL, seektype);
+}
+
+static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
+{
+    /* FIXME: try to do more processing in this function */
+    switch (fdint)
+    {
+    case fdintCOPY_FILE:
+    {
+        ULONG len = strlen((char*)pfdin->pv) + strlen(pfdin->psz1);
+        char *file = cabinet_alloc((len+1)*sizeof(char));
+
+        strcpy(file, (char*)pfdin->pv);
+        strcat(file, pfdin->psz1);
+
+        TRACE("file: %s\n", debugstr_a(file));
+
+        return cabinet_open(file, _O_WRONLY | _O_CREAT, 0);
+    }
+    case fdintCLOSE_FILE_INFO:
+    {
+        FILETIME ft;
+           FILETIME ftLocal;
+        if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
+            return -1;
+        if (!LocalFileTimeToFileTime(&ft, &ftLocal))
+            return -1;
+        if (!SetFileTime((HANDLE)pfdin->hf, &ftLocal, 0, &ftLocal))
+            return -1;
+
+        cabinet_close(pfdin->hf);
+        return 1;
+    }
+    default:
+        return 0;
+    }
+}
+
+/***********************************************************************
+ *            extract_cabinet_file
+ *
+ * Extract files from a cab file.
+ */
+static BOOL extract_cabinet_file(const WCHAR* source, const WCHAR* path)
+{
+    HFDI hfdi;
+    ERF erf;
+    BOOL ret;
+    char *cabinet;
+    char *cab_path;
+
+    TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path));
+
+    hfdi = FDICreate(cabinet_alloc,
+                     cabinet_free,
+                     cabinet_open,
+                     cabinet_read,
+                     cabinet_write,
+                     cabinet_close,
+                     cabinet_seek,
+                     0,
+                     &erf);
+    if (!hfdi)
+    {
+        ERR("FDICreate failed\n");
+        return FALSE;
+    }
+
+    if (!(cabinet = strdupWtoA( source )))
+    {
+        FDIDestroy(hfdi);
+        return FALSE;
+    }
+    if (!(cab_path = strdupWtoA( path )))
+    {
+        FDIDestroy(hfdi);
+        HeapFree(GetProcessHeap(), 0, cabinet);
+        return FALSE;
+    }
+
+    ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, cab_path);
+
+    if (!ret)
+        ERR("FDICopy failed\n");
+
+    FDIDestroy(hfdi);
+
+    HeapFree(GetProcessHeap(), 0, cabinet);
+    HeapFree(GetProcessHeap(), 0, cab_path);
+
+    return ret;
+}
+
+static UINT ready_media_for_file(MSIPACKAGE *package, UINT sequence, 
+                                 WCHAR* path)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    WCHAR source[MAX_PATH];
+    static const WCHAR ExecSeqQuery[] = {
+        's','e','l','e','c','t',' ','*',' ',
+        'f','r','o','m',' ','M','e','d','i','a',' ',
+        'w','h','e','r','e',' ','L','a','s','t','S','e','q','u','e','n','c','e',' ','>','=',' ','%','i',' ',
+        'o','r','d','e','r',' ','b','y',' ','L','a','s','t','S','e','q','u','e','n','c','e',0};
+    WCHAR Query[1024];
+    WCHAR cab[0x100];
+    DWORD sz=0x100;
+    INT seq;
+    static UINT last_sequence = 0; 
+
+    if (sequence <= last_sequence)
+    {
+        TRACE("Media already ready (%u, %u)\n",sequence,last_sequence);
+        return ERROR_SUCCESS;
+    }
+
+    sprintfW(Query,ExecSeqQuery,sequence);
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    rc = MSI_ViewFetch(view,&row);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+    seq = MSI_RecordGetInteger(row,2);
+    last_sequence = seq;
+
+    if (!MSI_RecordIsNull(row,4))
+    {
+        sz=0x100;
+        MSI_RecordGetStringW(row,4,cab,&sz);
+        TRACE("Source is CAB %s\n",debugstr_w(cab));
+        /* the stream does not contain the # character */
+        if (cab[0]=='#')
+        {
+            writeout_cabinet_stream(package,&cab[1],source);
+            strcpyW(path,source);
+            *(strrchrW(path,'\\')+1)=0;
+        }
+        else
+        {
+            sz = MAX_PATH;
+            if (MSI_GetPropertyW(package, cszSourceDir, source, &sz))
+            {
+                ERR("No Source dir defined \n");
+                rc = ERROR_FUNCTION_FAILED;
+            }
+            else
+            {
+                strcpyW(path,source);
+                strcatW(source,cab);
+                /* extract the cab file into a folder in the temp folder */
+                sz = MAX_PATH;
+                if (MSI_GetPropertyW(package, cszTempFolder,path, &sz) 
+                                    != ERROR_SUCCESS)
+                    GetTempPathW(MAX_PATH,path);
+            }
+        }
+        rc = !extract_cabinet_file(source,path);
+    }
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+inline static UINT create_component_directory ( MSIPACKAGE* package, INT component)
+{
+    UINT rc;
+    MSIFOLDER *folder;
+    WCHAR install_path[MAX_PATH];
+
+    rc = resolve_folder(package, package->components[component].Directory,
+                        install_path, FALSE, FALSE, &folder);
+
+    if (rc != ERROR_SUCCESS)
+        return rc; 
+
+    /* create the path */
+    if (folder->State == 0)
+    {
+        create_full_pathW(install_path);
+        folder->State = 2;
+    }
+
+    return rc;
+}
+
+static UINT ACTION_InstallFiles(MSIPACKAGE *package)
+{
+    UINT rc = ERROR_SUCCESS;
+    DWORD index;
+    MSIRECORD * uirow;
+    WCHAR uipath[MAX_PATH];
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    /* increment progress bar each time action data is sent */
+    ui_progress(package,1,1,1,0);
+
+    for (index = 0; index < package->loaded_files; index++)
+    {
+        WCHAR path_to_source[MAX_PATH];
+        MSIFILE *file;
+        
+        file = &package->files[index];
+
+        if (file->Temporary)
+            continue;
+
+        if (!package->components[file->ComponentIndex].Enabled ||
+            !package->components[file->ComponentIndex].FeatureState)
+        {
+            TRACE("File %s is not scheduled for install\n",
+                   debugstr_w(file->File));
+            continue;
+        }
+
+        if ((file->State == 1) || (file->State == 2))
+        {
+            TRACE("Installing %s\n",debugstr_w(file->File));
+            rc = ready_media_for_file(package,file->Sequence,path_to_source);
+            /* 
+             * WARNING!
+             * our file table could change here because a new temp file
+             * may have been created
+             */
+            file = &package->files[index];
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Unable to ready media\n");
+                rc = ERROR_FUNCTION_FAILED;
+                break;
+            }
+
+            create_component_directory( package, file->ComponentIndex);
+
+            strcpyW(file->SourcePath, path_to_source);
+            strcatW(file->SourcePath, file->File);
+
+            TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath),
+                  debugstr_w(file->TargetPath));
+
+            /* the UI chunk */
+            uirow=MSI_CreateRecord(9);
+            MSI_RecordSetStringW(uirow,1,file->File);
+            strcpyW(uipath,file->TargetPath);
+            *(strrchrW(uipath,'\\')+1)=0;
+            MSI_RecordSetStringW(uirow,9,uipath);
+            MSI_RecordSetInteger(uirow,6,file->FileSize);
+            ui_actiondata(package,szInstallFiles,uirow);
+            msiobj_release( &uirow->hdr );
+
+            if (!MoveFileW(file->SourcePath,file->TargetPath))
+            {
+                rc = GetLastError();
+                ERR("Unable to move file (%s -> %s) (error %d)\n",
+                     debugstr_w(file->SourcePath), debugstr_w(file->TargetPath),
+                      rc);
+                if (rc == ERROR_ALREADY_EXISTS && file->State == 2)
+                {
+                    CopyFileW(file->SourcePath,file->TargetPath,FALSE);
+                    DeleteFileW(file->SourcePath);
+                    rc = 0;
+                }
+                else
+                    break;
+            }
+            else
+                file->State = 4;
+
+            ui_progress(package,2,0,0,0);
+        }
+    }
+
+    return rc;
+}
+
+inline static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, 
+                                   LPWSTR file_source)
+{
+    DWORD index;
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    for (index = 0; index < package->loaded_files; index ++)
+    {
+        if (strcmpW(file_key,package->files[index].File)==0)
+        {
+            if (package->files[index].State >= 3)
+            {
+                strcpyW(file_source,package->files[index].TargetPath);
+                return ERROR_SUCCESS;
+            }
+            else
+                return ERROR_FILE_NOT_FOUND;
+        }
+    }
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] = {
+        's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ',
+        'D','u','p','l','i','c','a','t','e','F','i','l','e',0};
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    while (1)
+    {
+        WCHAR file_key[0x100];
+        WCHAR file_source[MAX_PATH];
+        WCHAR dest_name[0x100];
+        WCHAR dest_path[MAX_PATH];
+        WCHAR component[0x100];
+        INT component_index;
+
+        DWORD sz=0x100;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        sz=0x100;
+        rc = MSI_RecordGetStringW(row,2,component,&sz);
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Unable to get component\n");
+            msiobj_release(&row->hdr);
+            break;
+        }
+
+        component_index = get_loaded_component(package,component);
+        if (!package->components[component_index].Enabled ||
+            !package->components[component_index].FeatureState)
+        {
+            TRACE("Skipping copy due to disabled component\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        sz=0x100;
+        rc = MSI_RecordGetStringW(row,3,file_key,&sz);
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Unable to get file key\n");
+            msiobj_release(&row->hdr);
+            break;
+        }
+
+        rc = get_file_target(package,file_key,file_source);
+
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Original file unknown %s\n",debugstr_w(file_key));
+            msiobj_release(&row->hdr);
+            break;
+        }
+
+        if (MSI_RecordIsNull(row,4))
+        {
+            strcpyW(dest_name,strrchrW(file_source,'\\')+1);
+        }
+        else
+        {
+            sz=0x100;
+            MSI_RecordGetStringW(row,4,dest_name,&sz);
+            reduce_to_longfilename(dest_name);
+         }
+
+        if (MSI_RecordIsNull(row,5))
+        {
+            strcpyW(dest_path,file_source);
+            *strrchrW(dest_path,'\\')=0;
+        }
+        else
+        {
+            WCHAR destkey[0x100];
+            sz=0x100;
+            MSI_RecordGetStringW(row,5,destkey,&sz);
+            sz = 0x100;
+            rc = resolve_folder(package, destkey, dest_path,FALSE,FALSE,NULL);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Unable to get destination folder\n");
+                msiobj_release(&row->hdr);
+                break;
+            }
+        }
+
+        strcatW(dest_path,dest_name);
+           
+        TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
+              debugstr_w(dest_path)); 
+        
+        if (strcmpW(file_source,dest_path))
+            rc = !CopyFileW(file_source,dest_path,TRUE);
+        else
+            rc = ERROR_SUCCESS;
+        
+        if (rc != ERROR_SUCCESS)
+            ERR("Failed to copy file\n");
+
+        FIXME("We should track these duplicate files as well\n");   
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+
+}
+
+
+/* OK this value is "interpretted" and then formatted based on the 
+   first few characters */
+static LPSTR parse_value(MSIPACKAGE *package, WCHAR *value, DWORD *type, 
+                         DWORD *size)
+{
+    LPSTR data = NULL;
+    if (value[0]=='#' && value[1]!='#' && value[1]!='%')
+    {
+        if (value[1]=='x')
+        {
+            LPWSTR ptr;
+            CHAR byte[5];
+            LPWSTR deformated;
+            int count;
+
+            deformat_string(package, &value[2], &deformated);
+
+            /* binary value type */
+            ptr = deformated; 
+            *type=REG_BINARY;
+            *size = strlenW(ptr)/2;
+            data = HeapAlloc(GetProcessHeap(),0,*size);
+          
+            byte[0] = '0'; 
+            byte[1] = 'x'; 
+            byte[4] = 0; 
+            count = 0;
+            while (*ptr)
+            {
+                byte[2]= *ptr;
+                ptr++;
+                byte[3]= *ptr;
+                ptr++;
+                data[count] = (BYTE)strtol(byte,NULL,0);
+                count ++;
+            }
+            HeapFree(GetProcessHeap(),0,deformated);
+
+            TRACE("Data %li bytes(%i)\n",*size,count);
+        }
+        else
+        {
+            LPWSTR deformated;
+            deformat_string(package, &value[1], &deformated);
+
+            *type=REG_DWORD; 
+            *size = sizeof(DWORD);
+            data = HeapAlloc(GetProcessHeap(),0,*size);
+            *(LPDWORD)data = atoiW(deformated); 
+            TRACE("DWORD %i\n",*data);
+
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+    }
+    else
+    {
+        WCHAR *ptr;
+        *type=REG_SZ;
+
+        if (value[0]=='#')
+        {
+            if (value[1]=='%')
+            {
+                ptr = &value[2];
+                *type=REG_EXPAND_SZ;
+            }
+            else
+                ptr = &value[1];
+         }
+         else
+            ptr=value;
+
+        *size = deformat_string(package, ptr,(LPWSTR*)&data);
+    }
+    return data;
+}
+
+static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] = {
+        's','e','l','e','c','t',' ','*',' ',
+        'f','r','o','m',' ','R','e','g','i','s','t','r','y',0 };
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    /* increment progress bar each time action data is sent */
+    ui_progress(package,1,1,1,0);
+
+    while (1)
+    {
+        static const WCHAR szHCR[] = 
+{'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T','\\',0};
+        static const WCHAR szHCU[] =
+{'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R','\\',0};
+        static const WCHAR szHLM[] =
+{'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',
+'\\',0};
+        static const WCHAR szHU[] =
+{'H','K','E','Y','_','U','S','E','R','S','\\',0};
+
+        WCHAR key[0x100];
+        WCHAR name[0x100];
+        LPWSTR value;
+        LPSTR value_data = NULL;
+        HKEY  root_key, hkey;
+        DWORD type,size;
+        WCHAR component[0x100];
+        INT component_index;
+        MSIRECORD * uirow;
+        WCHAR uikey[0x110];
+
+        INT   root;
+        DWORD sz=0x100;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        sz= 0x100;
+        MSI_RecordGetStringW(row,6,component,&sz);
+        component_index = get_loaded_component(package,component);
+
+        if (!package->components[component_index].Enabled ||
+            !package->components[component_index].FeatureState)
+        {
+            TRACE("Skipping write due to disabled component\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        /* null values have special meanings during uninstalls and such */
+        
+        if(MSI_RecordIsNull(row,5))
+        {
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        root = MSI_RecordGetInteger(row,2);
+        sz = 0x100;
+        MSI_RecordGetStringW(row,3,key,&sz);
+      
+        sz = 0x100; 
+        if (MSI_RecordIsNull(row,4))
+            name[0]=0;
+        else
+            MSI_RecordGetStringW(row,4,name,&sz);
+   
+        /* get the root key */
+        switch (root)
+        {
+            case 0:  root_key = HKEY_CLASSES_ROOT; 
+                     strcpyW(uikey,szHCR); break;
+            case 1:  root_key = HKEY_CURRENT_USER;
+                     strcpyW(uikey,szHCU); break;
+            case 2:  root_key = HKEY_LOCAL_MACHINE;
+                     strcpyW(uikey,szHLM); break;
+            case 3:  root_key = HKEY_USERS; 
+                     strcpyW(uikey,szHU); break;
+            default:
+                 ERR("Unknown root %i\n",root);
+                 root_key=NULL;
+                 break;
+        }
+        if (!root_key)
+        {
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        strcatW(uikey,key);
+        if (RegCreateKeyW( root_key, key, &hkey))
+        {
+            ERR("Could not create key %s\n",debugstr_w(key));
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        value = load_dynamic_stringW(row,5);
+        value_data = parse_value(package, value, &type, &size); 
+
+        if (value_data)
+        {
+            TRACE("Setting value %s\n",debugstr_w(name));
+            RegSetValueExW(hkey, name, 0, type, value_data, size);
+
+            uirow = MSI_CreateRecord(3);
+            MSI_RecordSetStringW(uirow,2,name);
+            MSI_RecordSetStringW(uirow,1,uikey);
+
+            if (type == REG_SZ)
+                MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
+            else
+                MSI_RecordSetStringW(uirow,3,value);
+
+            ui_actiondata(package,szWriteRegistryValues,uirow);
+            ui_progress(package,2,0,0,0);
+            msiobj_release( &uirow->hdr );
+
+            HeapFree(GetProcessHeap(),0,value_data);
+        }
+        HeapFree(GetProcessHeap(),0,value);
+
+        msiobj_release(&row->hdr);
+        RegCloseKey(hkey);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+/*
+ * This helper function should probably go alot of places
+ *
+ * Thinking about this, maybe this should become yet another Bison file
+ */
+static DWORD deformat_string(MSIPACKAGE *package, WCHAR* ptr,WCHAR** data)
+{
+    WCHAR* mark=NULL;
+    DWORD size=0;
+    DWORD chunk=0;
+    WCHAR key[0x100];
+    LPWSTR value;
+    DWORD sz;
+    UINT rc;
+
+    /* scan for special characters */
+    if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
+    {
+        /* not formatted */
+        size = (strlenW(ptr)+1) * sizeof(WCHAR);
+        *data = HeapAlloc(GetProcessHeap(),0,size);
+        strcpyW(*data,ptr);
+        return size;
+    }
+   
+    /* formatted string located */ 
+    mark = strchrW(ptr,'[');
+    if (mark != ptr)
+    {
+        INT cnt = (mark - ptr);
+        TRACE("%i  (%i) characters before marker\n",cnt,(mark-ptr));
+        size = cnt * sizeof(WCHAR);
+        size += sizeof(WCHAR);
+        *data = HeapAlloc(GetProcessHeap(),0,size);
+        strncpyW(*data,ptr,cnt);
+        (*data)[cnt]=0;
+    }
+    else
+    {
+        size = sizeof(WCHAR);
+        *data = HeapAlloc(GetProcessHeap(),0,size);
+        (*data)[0]=0;
+    }
+    mark++;
+    strcpyW(key,mark);
+    *strchrW(key,']')=0;
+    mark = strchrW(mark,']');
+    mark++;
+    TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+    sz = 0;
+    rc = MSI_GetPropertyW(package, key, NULL, &sz);
+    if ((rc == ERROR_SUCCESS) || (rc == ERROR_MORE_DATA))
+    {
+        LPWSTR newdata;
+
+        sz++;
+        value = HeapAlloc(GetProcessHeap(),0,sz * sizeof(WCHAR));
+        MSI_GetPropertyW(package, key, value, &sz);
+
+        chunk = (strlenW(value)+1) * sizeof(WCHAR);
+        size+=chunk;   
+        newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
+        *data = newdata;
+        strcatW(*data,value);
+    }
+    TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+    if (*mark!=0)
+    {
+        LPWSTR newdata;
+        chunk = (strlenW(mark)+1) * sizeof(WCHAR);
+        size+=chunk;
+        newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
+        *data = newdata;
+        strcatW(*data,mark);
+    }
+    (*data)[strlenW(*data)]=0;
+    TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+
+    /* recursively do this to clean up */
+    mark = HeapAlloc(GetProcessHeap(),0,size);
+    strcpyW(mark,*data);
+    TRACE("String at this point %s\n",debugstr_w(mark));
+    size = deformat_string(package,mark,data);
+    HeapFree(GetProcessHeap(),0,mark);
+    return size;
+}
+
+static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
+{
+    WCHAR level[10000];
+    INT install_level;
+    DWORD sz;
+    DWORD i;
+    INT j;
+    DWORD rc;
+    LPWSTR override = NULL;
+    static const WCHAR addlocal[]={'A','D','D','L','O','C','A','L',0};
+    static const WCHAR all[]={'A','L','L',0};
+    static const WCHAR szlevel[] = {
+        'I','N','S','T','A','L','L','L','E','V','E','L',0};
+    static const WCHAR szAddLocal[] = {
+        'A','D','D','L','O','C','A','L',0};
+
+    /* I do not know if this is where it should happen.. but */
+
+    TRACE("Checking Install Level\n");
+
+    sz = 10000;
+    if (MSI_GetPropertyW(package,szlevel,level,&sz)==ERROR_SUCCESS)
+        install_level = atoiW(level);
+    else
+        install_level = 1;
+
+    sz = 0;
+    rc = MSI_GetPropertyW(package,szAddLocal,NULL,&sz);
+    if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
+    {
+        sz++;
+        override = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
+        MSI_GetPropertyW(package, addlocal,override,&sz);
+    }
+   
+    /*
+     * Components FeatureState defaults to FALSE. The idea is we want to 
+     * enable the component is ANY feature that uses it is enabled to install
+     */
+    for(i = 0; i < package->loaded_features; i++)
+    {
+        BOOL feature_state= ((package->features[i].Level > 0) &&
+                             (package->features[i].Level <= install_level));
+
+        if (override && (strcmpiW(override,all)==0 || 
+                         strstrW(override,package->features[i].Feature)))
+        {
+            TRACE("Override of install level found\n");
+            feature_state = TRUE;
+            package->features[i].Enabled = feature_state;
+        }
+
+        TRACE("Feature %s has a state of %i\n",
+               debugstr_w(package->features[i].Feature), feature_state);
+        for( j = 0; j < package->features[i].ComponentCount; j++)
+        {
+            package->components[package->features[i].Components[j]].FeatureState
+            |= feature_state;
+        }
+    } 
+    if (override != NULL)
+        HeapFree(GetProcessHeap(),0,override);
+    /* 
+     * So basically we ONLY want to install a component if its Enabled AND
+     * FeatureState are both TRUE 
+     */
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_InstallValidate(MSIPACKAGE *package)
+{
+    DWORD progress = 0;
+    static const WCHAR q1[]={
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','R','e','g','i','s','t','r','y',0};
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+
+    TRACE(" InstallValidate \n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+    while (1)
+    {
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+        progress +=1;
+
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+    ui_progress(package,0,progress+package->loaded_files,0,0);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view = NULL;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'f','r','o','m',' ','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n',0};
+    static const WCHAR title[]=
+            {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
+
+    TRACE("Checking launch conditions\n");
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    rc = ERROR_SUCCESS;
+    while (rc == ERROR_SUCCESS)
+    {
+        LPWSTR cond = NULL; 
+        LPWSTR message = NULL;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        cond = load_dynamic_stringW(row,1);
+
+        if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE)
+        {
+            message = load_dynamic_stringW(row,2);
+            MessageBoxW(NULL,message,title,MB_OK);
+            HeapFree(GetProcessHeap(),0,message);
+            rc = ERROR_FUNCTION_FAILED;
+        }
+        HeapFree(GetProcessHeap(),0,cond);
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static void resolve_keypath( MSIPACKAGE* package, INT
+                            component_index, WCHAR *keypath)
+{
+    MSICOMPONENT* cmp = &package->components[component_index];
+
+    if (cmp->KeyPath[0]==0)
+    {
+        resolve_folder(package,cmp->Directory,keypath,FALSE,FALSE,NULL);
+        return;
+    }
+    if ((cmp->Attributes & 0x4) || (cmp->Attributes & 0x20))
+    {
+        FIXME("UNIMPLEMENTED keypath as Registry or ODBC Source\n");
+        keypath[0]=0;
+    }
+    else
+    {
+        int j;
+        j = get_loaded_file(package,cmp->KeyPath);
+
+        if (j>=0)
+            strcpyW(keypath,package->files[j].TargetPath);
+    }
+}
+
+/*
+ * Ok further analysis makes me think that this work is
+ * actually done in the PublishComponents and PublishFeatures
+ * step, and not here.  It appears like the keypath and all that is
+ * resolved in this step, however actually written in the Publish steps.
+ * But we will leave it here for now because it is unclear
+ */
+static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
+{
+    WCHAR productcode[0x100];
+    WCHAR squished_pc[0x100];
+    WCHAR squished_cc[0x100];
+    DWORD sz;
+    UINT rc;
+    DWORD i;
+    HKEY hkey=0,hkey2=0,hkey3=0;
+    static const WCHAR szProductCode[]=
+{'P','r','o','d','u','c','t','C','o','d','e',0};
+    static 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 };
+    static const WCHAR szFeatures[] = {
+'F','e','a','t','u','r','e','s',0 };
+    static const WCHAR szComponents[] = {
+'C','o','m','p','o','n','e','n','t','s',0 };
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    /* writes the Component and Features values to the registry */
+    sz = 0x100;
+    rc = MSI_GetPropertyW(package,szProductCode,productcode,&sz);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    squash_guid(productcode,squished_pc);
+    rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller,&hkey);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = RegCreateKeyW(hkey,szFeatures,&hkey2);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    rc = RegCreateKeyW(hkey2,squished_pc,&hkey3);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+
+    /* here the guids are base 85 encoded */
+    for (i = 0; i < package->loaded_features; i++)
+    {
+        LPWSTR data = NULL;
+        GUID clsid;
+        int j;
+        INT size;
+
+        size = package->features[i].ComponentCount*21*sizeof(WCHAR);
+        data = HeapAlloc(GetProcessHeap(), 0, size);
+
+        data[0] = 0;
+        for (j = 0; j < package->features[i].ComponentCount; j++)
+        {
+            WCHAR buf[21];
+            TRACE("From %s\n",debugstr_w(package->components
+                            [package->features[i].Components[j]].ComponentId));
+            CLSIDFromString(package->components
+                            [package->features[i].Components[j]].ComponentId,
+                            &clsid);
+            encode_base85_guid(&clsid,buf);
+            TRACE("to %s\n",debugstr_w(buf));
+            strcatW(data,buf);
+        }
+
+        size = strlenW(data)*sizeof(WCHAR);
+        RegSetValueExW(hkey3,package->features[i].Feature,0,REG_SZ,
+                       (LPSTR)data,size);
+        HeapFree(GetProcessHeap(),0,data);
+    }
+
+    RegCloseKey(hkey3);
+    RegCloseKey(hkey2);
+
+    rc = RegCreateKeyW(hkey,szComponents,&hkey2);
+    if (rc != ERROR_SUCCESS)
+        goto end;
+  
+    for (i = 0; i < package->loaded_components; i++)
+    {
+        if (package->components[i].ComponentId[0]!=0)
+        {
+            WCHAR keypath[0x1000];
+            MSIRECORD * uirow;
+
+            squash_guid(package->components[i].ComponentId,squished_cc);
+            rc = RegCreateKeyW(hkey2,squished_cc,&hkey3);
+            if (rc != ERROR_SUCCESS)
+                continue;
+           
+            resolve_keypath(package,i,keypath);
+
+            RegSetValueExW(hkey3,squished_pc,0,REG_SZ,(LPVOID)keypath,
+                            (strlenW(keypath)+1)*sizeof(WCHAR));
+            RegCloseKey(hkey3);
+        
+            /* UI stuff */
+            uirow = MSI_CreateRecord(3);
+            MSI_RecordSetStringW(uirow,1,productcode);
+            MSI_RecordSetStringW(uirow,2,package->components[i].ComponentId);
+            MSI_RecordSetStringW(uirow,3,keypath);
+            ui_actiondata(package,szProcessComponents,uirow);
+            msiobj_release( &uirow->hdr );
+        }
+    } 
+end:
+    RegCloseKey(hkey2);
+    RegCloseKey(hkey);
+    return rc;
+}
+
+static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
+{
+    /* 
+     * OK this is a bit confusing.. I am given a _Component key and I believe
+     * that the file that is being registered as a type library is the "key file
+     * of that component" which I interpret to mean "The file in the KeyPath of
+     * that component".
+     */
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR Query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'f','r','o','m',' ','T','y','p','e','L','i','b',0};
+    ITypeLib *ptLib;
+    HRESULT res;
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    while (1)
+    {   
+        WCHAR component[0x100];
+        DWORD sz;
+        INT index;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row,3,component,&sz);
+
+        index = get_loaded_component(package,component);
+        if (index < 0)
+        {
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        if (!package->components[index].Enabled ||
+            !package->components[index].FeatureState)
+        {
+            TRACE("Skipping typelib reg due to disabled component\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        index = get_loaded_file(package,package->components[index].KeyPath); 
+   
+        if (index < 0)
+        {
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+//        res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
+        if (SUCCEEDED(res))
+        {
+            WCHAR help[MAX_PATH];
+            WCHAR helpid[0x100];
+
+            sz = 0x100;
+            MSI_RecordGetStringW(row,6,helpid,&sz);
+
+            resolve_folder(package,helpid,help,FALSE,FALSE,NULL);
+
+//            res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
+            if (!SUCCEEDED(res))
+                ERR("Failed to register type library %s\n",
+                     debugstr_w(package->files[index].TargetPath));
+            else
+            {
+                /* Yes the row has more fields than I need, but #1 is 
+                   correct and the only one I need. Why make a new row? */
+
+                ui_actiondata(package,szRegisterTypeLibraries,row);
+                
+                TRACE("Registered %s\n",
+                       debugstr_w(package->files[index].TargetPath));
+            }
+
+            if (ptLib){
+                //ITypeLib_Release(ptLib);
+                }
+        }
+        else
+            ERR("Failed to load type library %s\n",
+                debugstr_w(package->files[index].TargetPath));
+        
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+   
+}
+
+static UINT register_appid(MSIPACKAGE *package, LPCWSTR clsid, LPCWSTR app )
+{
+    static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] = 
+{'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ','A','p','p','I'
+,'d',' ','w','h','e','r','e',' ','A','p','p','I','d','=','`','%','s','`',0};
+    WCHAR Query[0x1000];
+    HKEY hkey2,hkey3;
+    LPWSTR buffer=0;
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    sprintfW(Query,ExecSeqQuery,clsid);
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2);
+    RegCreateKeyW(hkey2,clsid,&hkey3);
+    RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)app,
+                   (strlenW(app)+1)*sizeof(WCHAR));
+
+    rc = MSI_ViewFetch(view,&row);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    if (!MSI_RecordIsNull(row,2)) 
+    {
+        LPWSTR deformated=0;
+        UINT size; 
+        static const WCHAR szRemoteServerName[] =
+{'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0};
+        buffer = load_dynamic_stringW(row,2);
+        size = deformat_string(package,buffer,&deformated);
+        RegSetValueExW(hkey3,szRemoteServerName,0,REG_SZ,(LPVOID)deformated,
+                       size);
+        HeapFree(GetProcessHeap(),0,deformated);
+        HeapFree(GetProcessHeap(),0,buffer);
+    }
+
+    if (!MSI_RecordIsNull(row,3)) 
+    {
+        static const WCHAR szLocalService[] =
+{'L','o','c','a','l','S','e','r','v','i','c','e',0};
+        UINT size;
+        buffer = load_dynamic_stringW(row,3);
+        size = (strlenW(buffer)+1) * sizeof(WCHAR);
+        RegSetValueExW(hkey3,szLocalService,0,REG_SZ,(LPVOID)buffer,size);
+        HeapFree(GetProcessHeap(),0,buffer);
+    }
+
+    if (!MSI_RecordIsNull(row,4)) 
+    {
+        static const WCHAR szService[] =
+{'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0};
+        UINT size;
+        buffer = load_dynamic_stringW(row,4);
+        size = (strlenW(buffer)+1) * sizeof(WCHAR);
+        RegSetValueExW(hkey3,szService,0,REG_SZ,(LPVOID)buffer,size);
+        HeapFree(GetProcessHeap(),0,buffer);
+    }
+
+    if (!MSI_RecordIsNull(row,5)) 
+    {
+        static const WCHAR szDLL[] =
+{'D','l','l','S','u','r','r','o','g','a','t','e',0};
+        UINT size;
+        buffer = load_dynamic_stringW(row,5);
+        size = (strlenW(buffer)+1) * sizeof(WCHAR);
+        RegSetValueExW(hkey3,szDLL,0,REG_SZ,(LPVOID)buffer,size);
+        HeapFree(GetProcessHeap(),0,buffer);
+    }
+
+    if (!MSI_RecordIsNull(row,6)) 
+    {
+        static const WCHAR szActivate[] =
+{'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0};
+        static const WCHAR szY[] = {'Y',0};
+
+        if (MSI_RecordGetInteger(row,6))
+            RegSetValueExW(hkey3,szActivate,0,REG_SZ,(LPVOID)szY,4);
+    }
+
+    if (!MSI_RecordIsNull(row,7)) 
+    {
+        static const WCHAR szRunAs[] = {'R','u','n','A','s',0};
+        static const WCHAR szUser[] = 
+{'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0};
+
+        if (MSI_RecordGetInteger(row,7))
+            RegSetValueExW(hkey3,szRunAs,0,REG_SZ,(LPVOID)szUser,34);
+    }
+
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    RegCloseKey(hkey3);
+    RegCloseKey(hkey2);
+    return rc;
+}
+
+static UINT ACTION_RegisterClassInfo(MSIPACKAGE *package)
+{
+    /* 
+     * Again I am assuming the words, "Whose key file represents" when referring
+     * to a Component as to meaning that Components KeyPath file
+     *
+     * Also there is a very strong connection between ClassInfo and ProgID
+     * that I am mostly glossing over.  
+     * What would be more propper is to load the ClassInfo and the ProgID info
+     * into memory data structures and then be able to enable and disable them
+     * based on component. 
+     */
+    
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'f','r','o','m',' ','C','l','a','s','s',0};
+    static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+    static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 };
+    static const WCHAR szAppID[] = { 'A','p','p','I','D',0 };
+    HKEY hkey,hkey2,hkey3;
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_FUNCTION_FAILED;
+
+    rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+    {
+        rc = ERROR_SUCCESS;
+        goto end;
+    }
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        goto end;
+    }
+
+    while (1)
+    {
+        WCHAR clsid[0x100];
+        WCHAR buffer[0x100];
+        WCHAR desc[0x100];
+        DWORD sz;
+        INT index;
+     
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        sz=0x100;
+        MSI_RecordGetStringW(row,3,buffer,&sz);
+
+        index = get_loaded_component(package,buffer);
+
+        if (index < 0)
+        {
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        if (!package->components[index].Enabled ||
+            !package->components[index].FeatureState)
+        {
+            TRACE("Skipping class reg due to disabled component\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        sz=0x100;
+        MSI_RecordGetStringW(row,1,clsid,&sz);
+        RegCreateKeyW(hkey,clsid,&hkey2);
+
+        if (!MSI_RecordIsNull(row,5))
+        {
+            sz=0x100;
+            MSI_RecordGetStringW(row,5,desc,&sz);
+
+            RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)desc,
+                           (strlenW(desc)+1)*sizeof(WCHAR));
+        }
+        else
+            desc[0]=0;
+
+        sz=0x100;
+        MSI_RecordGetStringW(row,2,buffer,&sz);
+
+        RegCreateKeyW(hkey2,buffer,&hkey3);
+
+        index = get_loaded_file(package,package->components[index].KeyPath);
+        RegSetValueExW(hkey3,NULL,0,REG_SZ,
+                       (LPVOID)package->files[index].TargetPath,
+                       (strlenW(package->files[index].TargetPath)+1)
+                        *sizeof(WCHAR));
+
+        RegCloseKey(hkey3);
+
+        if (!MSI_RecordIsNull(row,4))
+        {
+            sz=0x100;
+            MSI_RecordGetStringW(row,4,buffer,&sz);
+
+            RegCreateKeyW(hkey2,szProgID,&hkey3);
+    
+            RegSetValueExW(hkey3,NULL,0,REG_SZ,(LPVOID)buffer,
+                       (strlenW(buffer)+1)*sizeof(WCHAR));
+
+            RegCloseKey(hkey3);
+        }
+
+        if (!MSI_RecordIsNull(row,6))
+        { 
+            sz=0x100;
+            MSI_RecordGetStringW(row,6,buffer,&sz);
+
+            RegSetValueExW(hkey2,szAppID,0,REG_SZ,(LPVOID)buffer,
+                       (strlenW(buffer)+1)*sizeof(WCHAR));
+
+            register_appid(package,buffer,desc);
+        }
+
+        RegCloseKey(hkey2);
+
+        FIXME("Process the rest of the fields >7\n");
+
+        ui_actiondata(package,szRegisterClassInfo,row);
+
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+end:
+    RegCloseKey(hkey);
+    return rc;
+}
+
+static UINT register_progid_base(MSIRECORD * row, LPWSTR clsid)
+{
+    static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+    HKEY hkey,hkey2;
+    WCHAR buffer[0x100];
+    DWORD sz;
+
+
+    sz = 0x100;
+    MSI_RecordGetStringW(row,1,buffer,&sz);
+    RegCreateKeyW(HKEY_CLASSES_ROOT,buffer,&hkey);
+
+    if (!MSI_RecordIsNull(row,4))
+    {
+        sz = 0x100;
+        MSI_RecordGetStringW(row,4,buffer,&sz);
+        RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)buffer, (strlenW(buffer)+1) *
+                       sizeof(WCHAR));
+    }
+
+    if (!MSI_RecordIsNull(row,3))
+    {   
+        sz = 0x100;
+    
+        MSI_RecordGetStringW(row,3,buffer,&sz);
+        RegCreateKeyW(hkey,szCLSID,&hkey2);
+        RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)buffer, (strlenW(buffer)+1) *
+                       sizeof(WCHAR));
+
+        if (clsid)
+            strcpyW(clsid,buffer);
+
+        RegCloseKey(hkey2);
+    }
+    else
+    {
+        FIXME("UNHANDLED case, Parent progid but classid is NULL\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+    if (!MSI_RecordIsNull(row,5))
+        FIXME ("UNHANDLED icon in Progid\n");
+    return ERROR_SUCCESS;
+}
+
+static UINT register_progid(MSIPACKAGE *package, MSIRECORD * row, LPWSTR clsid);
+
+static UINT register_parent_progid(MSIPACKAGE *package, LPCWSTR parent, 
+                                   LPWSTR clsid)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR Query_t[] = 
+{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','r','o','g'
+,'I','d',' ','w','h','e','r','e',' ','P','r','o','g','I','d',' ','=',' ','`'
+,'%','s','`',0};
+    WCHAR Query[0x1000];
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    sprintfW(Query,Query_t,parent);
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    rc = MSI_ViewFetch(view,&row);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    register_progid(package,row,clsid);
+
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static UINT register_progid(MSIPACKAGE *package, MSIRECORD * row, LPWSTR clsid)
+{
+    UINT rc = ERROR_SUCCESS; 
+
+    if (MSI_RecordIsNull(row,2))
+        rc = register_progid_base(row,clsid);
+    else
+    {
+        WCHAR buffer[0x1000];
+        DWORD sz, disp;
+        HKEY hkey,hkey2;
+        static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 };
+
+        /* check if already registered */
+        sz = 0x100;
+        MSI_RecordGetStringW(row,1,buffer,&sz);
+        RegCreateKeyExW(HKEY_CLASSES_ROOT, buffer, 0, NULL, 0,
+                        KEY_ALL_ACCESS, NULL, &hkey, &disp );
+        if (disp == REG_OPENED_EXISTING_KEY)
+        {
+            TRACE("Key already registered\n");
+            RegCloseKey(hkey);
+            return rc;
+        }
+        /* clsid is same as parent */
+        RegCreateKeyW(hkey,szCLSID,&hkey2);
+        RegSetValueExW(hkey2,NULL,0,REG_SZ,(LPVOID)clsid, (strlenW(clsid)+1) *
+                       sizeof(WCHAR));
+
+        RegCloseKey(hkey2);
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row,2,buffer,&sz);
+        rc = register_parent_progid(package,buffer,clsid);
+
+        if (!MSI_RecordIsNull(row,4))
+        {
+            sz = 0x100;
+            MSI_RecordGetStringW(row,4,buffer,&sz);
+            RegSetValueExW(hkey,NULL,0,REG_SZ,(LPVOID)buffer,
+                           (strlenW(buffer)+1) * sizeof(WCHAR));
+        }
+
+        if (!MSI_RecordIsNull(row,5))
+            FIXME ("UNHANDLED icon in Progid\n");
+
+        RegCloseKey(hkey);
+    }
+    return rc;
+}
+
+static UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package)
+{
+    /* 
+     * Sigh, here I am just brute force registering all progids
+     * this needs to be linked to the Classes that have been registered
+     * but the easiest way to do that is to load all these stuff into
+     * memory for easy checking.
+     *
+     * Gives me something to continue to work toward.
+     */
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR Query[] = {
+        'S','E','L','E','C','T',' ','*',' ',
+        'F','R','O','M',' ','P','r','o','g','I','d',0};
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    while (1)
+    {
+        WCHAR clsid[0x1000];
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+        
+        register_progid(package,row,clsid);
+        ui_actiondata(package,szRegisterProgIdInfo,row);
+
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+
+static UINT build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name, 
+                            LPWSTR FilePath)
+{
+    WCHAR ProductCode[0x100];
+    WCHAR SystemFolder[MAX_PATH];
+    DWORD sz;
+
+    static const WCHAR szInstaller[] = 
+{'I','n','s','t','a','l','l','e','r','\\',0};
+    static const WCHAR szProductCode[] =
+{'P','r','o','d','u','c','t','C','o','d','e',0};
+    static const WCHAR szFolder[] =
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+
+    sz = 0x100;
+    MSI_GetPropertyW(package,szProductCode,ProductCode,&sz);
+    if (strlenW(ProductCode)==0)
+        return ERROR_FUNCTION_FAILED;
+
+    sz = MAX_PATH;
+    MSI_GetPropertyW(package,szFolder,SystemFolder,&sz);
+    strcatW(SystemFolder,szInstaller); 
+    strcatW(SystemFolder,ProductCode);
+    create_full_pathW(SystemFolder);
+
+    strcpyW(FilePath,SystemFolder);
+    strcatW(FilePath,cszbs);
+    strcatW(FilePath,icon_name);
+    return ERROR_SUCCESS;
+}
+
+static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR Query[] = {
+       'S','E','L','E','C','T',' ','*',' ','f','r','o','m',' ',
+       'S','h','o','r','t','c','u','t',0};
+    IShellLinkW *sl;
+    IPersistFile *pf;
+    HRESULT res;
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    res = CoInitialize( NULL );
+    if (FAILED (res))
+    {
+        ERR("CoInitialize failed\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    while (1)
+    {
+        WCHAR target_file[MAX_PATH];
+        WCHAR buffer[0x100];
+        DWORD sz;
+        DWORD index;
+        static const WCHAR szlnk[]={'.','l','n','k',0};
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+        
+        sz = 0x100;
+        MSI_RecordGetStringW(row,4,buffer,&sz);
+
+        index = get_loaded_component(package,buffer);
+
+        if (index < 0)
+        {
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        if (!package->components[index].Enabled ||
+            !package->components[index].FeatureState)
+        {
+            TRACE("Skipping shortcut creation due to disabled component\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        ui_actiondata(package,szCreateShortcuts,row);
+
+        res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+                              &IID_IShellLinkW, (LPVOID *) &sl );
+
+        if (FAILED(res))
+        {
+            ERR("Is IID_IShellLink\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
+        if( FAILED( res ) )
+        {
+            ERR("Is IID_IPersistFile\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row,2,buffer,&sz);
+        resolve_folder(package, buffer,target_file,FALSE,FALSE,NULL);
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row,3,buffer,&sz);
+        reduce_to_longfilename(buffer);
+        strcatW(target_file,buffer);
+        if (!strchrW(target_file,'.'))
+            strcatW(target_file,szlnk);
+
+        sz = 0x100;
+        MSI_RecordGetStringW(row,5,buffer,&sz);
+        if (strchrW(buffer,'['))
+        {
+            LPWSTR deformated;
+            deformat_string(package,buffer,&deformated);
+            IShellLinkW_SetPath(sl,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+        else
+        {
+            FIXME("UNHANDLED shortcut format, advertised shortcut\n");
+            IPersistFile_Release( pf );
+            IShellLinkW_Release( sl );
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        if (!MSI_RecordIsNull(row,6))
+        {
+            LPWSTR deformated;
+            sz = 0x100;
+            MSI_RecordGetStringW(row,6,buffer,&sz);
+            deformat_string(package,buffer,&deformated);
+            IShellLinkW_SetArguments(sl,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+
+        if (!MSI_RecordIsNull(row,7))
+        {
+            LPWSTR deformated;
+            deformated = load_dynamic_stringW(row,7);
+            IShellLinkW_SetDescription(sl,deformated);
+            HeapFree(GetProcessHeap(),0,deformated);
+        }
+
+        if (!MSI_RecordIsNull(row,8))
+            IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
+
+        if (!MSI_RecordIsNull(row,9))
+        {
+            WCHAR Path[MAX_PATH];
+            INT index; 
+
+            sz = 0x100;
+            MSI_RecordGetStringW(row,9,buffer,&sz);
+
+            build_icon_path(package,buffer,Path);
+            index = MSI_RecordGetInteger(row,10);
+
+            IShellLinkW_SetIconLocation(sl,Path,index);
+        }
+
+        if (!MSI_RecordIsNull(row,11))
+            IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
+
+        if (!MSI_RecordIsNull(row,12))
+        {
+            WCHAR Path[MAX_PATH];
+
+            sz = 0x100;
+            MSI_RecordGetStringW(row,12,buffer,&sz);
+            resolve_folder(package, buffer, Path, FALSE, FALSE, NULL);
+            IShellLinkW_SetWorkingDirectory(sl,Path);
+        }
+
+        TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
+        IPersistFile_Save(pf,target_file,FALSE);
+        
+        IPersistFile_Release( pf );
+        IShellLinkW_Release( sl );
+
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+
+    CoUninitialize();
+
+    return rc;
+}
+
+
+/*
+ * 99% of the work done here is only done for 
+ * advertised installs. However this is where the
+ * Icon table is processed and written out
+ * so that is what I am going to do here.
+ */
+static UINT ACTION_PublishProduct(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR Query[]={
+        'S','E','L','E','C','T',' ','*',' ',
+        'f','r','o','m',' ','I','c','o','n',0};
+    DWORD sz;
+
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    while (1)
+    {
+        HANDLE the_file;
+        WCHAR FilePath[MAX_PATH];
+        WCHAR FileName[MAX_PATH];
+        CHAR buffer[1024];
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+    
+        sz = MAX_PATH;
+        MSI_RecordGetStringW(row,1,FileName,&sz);
+        if (sz == 0)
+        {
+            ERR("Unable to get FileName\n");
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        build_icon_path(package,FileName,FilePath);
+
+        TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
+        
+        the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+
+        if (the_file == INVALID_HANDLE_VALUE)
+        {
+            ERR("Unable to create file %s\n",debugstr_w(FilePath));
+            msiobj_release(&row->hdr);
+            continue;
+        }
+
+        do 
+        {
+            DWORD write;
+            sz = 1024;
+            rc = MSI_RecordReadStream(row,2,buffer,&sz);
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Failed to get stream\n");
+                CloseHandle(the_file);  
+                DeleteFileW(FilePath);
+                break;
+            }
+            WriteFile(the_file,buffer,sz,&write,NULL);
+        } while (sz == 1024);
+
+        CloseHandle(the_file);
+        msiobj_release(&row->hdr);
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+
+}
+
+/* Msi functions that seem appropriate here */
+UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
+{
+    LPWSTR szwAction;
+    UINT rc;
+
+    TRACE(" exteral attempt at action %s\n",szAction);
+
+    if (!szAction)
+        return ERROR_FUNCTION_FAILED;
+    if (hInstall == 0)
+        return ERROR_FUNCTION_FAILED;
+
+    szwAction = strdupAtoW(szAction);
+
+    if (!szwAction)
+        return ERROR_FUNCTION_FAILED; 
+
+
+    rc = MsiDoActionW(hInstall, szwAction);
+    HeapFree(GetProcessHeap(),0,szwAction);
+    return rc;
+}
+
+UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
+{
+    MSIPACKAGE *package;
+    UINT ret = ERROR_INVALID_HANDLE;
+
+    TRACE(" external attempt at action %s \n",debugstr_w(szAction));
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if( package )
+    {
+        ret = ACTION_PerformAction(package,szAction);
+        msiobj_release( &package->hdr );
+    }
+    return ret;
+}
+
+UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, 
+                               LPSTR szPathBuf, DWORD* pcchPathBuf) 
+{
+    LPWSTR szwFolder;
+    LPWSTR szwPathBuf;
+    UINT rc;
+
+    TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
+
+    if (!szFolder)
+        return ERROR_FUNCTION_FAILED;
+    if (hInstall == 0)
+        return ERROR_FUNCTION_FAILED;
+
+    szwFolder = strdupAtoW(szFolder);
+
+    if (!szwFolder)
+        return ERROR_FUNCTION_FAILED; 
+
+    szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
+
+    rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
+
+    WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
+                         *pcchPathBuf, NULL, NULL );
+
+    HeapFree(GetProcessHeap(),0,szwFolder);
+    HeapFree(GetProcessHeap(),0,szwPathBuf);
+
+    return rc;
+}
+
+UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
+                                szPathBuf, DWORD* pcchPathBuf) 
+{
+    WCHAR path[MAX_PATH];
+    UINT rc;
+    MSIPACKAGE *package;
+
+    TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package )
+        return ERROR_INVALID_HANDLE;
+    rc = resolve_folder(package, szFolder, path, FALSE, FALSE, NULL);
+    msiobj_release( &package->hdr );
+
+    if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
+    {
+        *pcchPathBuf = strlenW(path)+1;
+        return ERROR_MORE_DATA;
+    }
+    else if (rc == ERROR_SUCCESS)
+    {
+        *pcchPathBuf = strlenW(path)+1;
+        strcpyW(szPathBuf,path);
+        TRACE("Returning Path %s\n",debugstr_w(path));
+    }
+    
+    return rc;
+}
+
+
+UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, 
+                               LPSTR szPathBuf, DWORD* pcchPathBuf) 
+{
+    LPWSTR szwFolder;
+    LPWSTR szwPathBuf;
+    UINT rc;
+
+    TRACE("getting source %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
+
+    if (!szFolder)
+        return ERROR_FUNCTION_FAILED;
+    if (hInstall == 0)
+        return ERROR_FUNCTION_FAILED;
+
+    szwFolder = strdupAtoW(szFolder);
+    if (!szwFolder)
+        return ERROR_FUNCTION_FAILED; 
+
+    szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
+
+    rc = MsiGetSourcePathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
+
+    WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
+                         *pcchPathBuf, NULL, NULL );
+
+    HeapFree(GetProcessHeap(),0,szwFolder);
+    HeapFree(GetProcessHeap(),0,szwPathBuf);
+
+    return rc;
+}
+
+UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
+                                szPathBuf, DWORD* pcchPathBuf) 
+{
+    WCHAR path[MAX_PATH];
+    UINT rc;
+    MSIPACKAGE *package;
+
+    TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package )
+        return ERROR_INVALID_HANDLE;
+    rc = resolve_folder(package, szFolder, path, TRUE, FALSE, NULL);
+    msiobj_release( &package->hdr );
+
+    if (rc == ERROR_SUCCESS && strlenW(path) > *pcchPathBuf)
+    {
+        *pcchPathBuf = strlenW(path)+1;
+        return ERROR_MORE_DATA;
+    }
+    else if (rc == ERROR_SUCCESS)
+    {
+        *pcchPathBuf = strlenW(path)+1;
+        strcpyW(szPathBuf,path);
+        TRACE("Returning Path %s\n",debugstr_w(path));
+    }
+    
+    return rc;
+}
+
+
+UINT WINAPI MsiSetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder, 
+                             LPCSTR szFolderPath)
+{
+    LPWSTR szwFolder;
+    LPWSTR szwFolderPath;
+    UINT rc;
+
+    if (!szFolder)
+        return ERROR_FUNCTION_FAILED;
+    if (hInstall == 0)
+        return ERROR_FUNCTION_FAILED;
+
+    szwFolder = strdupAtoW(szFolder);
+    if (!szwFolder)
+        return ERROR_FUNCTION_FAILED; 
+
+    szwFolderPath = strdupAtoW(szFolderPath);
+    if (!szwFolderPath)
+    {
+        HeapFree(GetProcessHeap(),0,szwFolder);
+        return ERROR_FUNCTION_FAILED; 
+    }
+
+    rc = MsiSetTargetPathW(hInstall, szwFolder, szwFolderPath);
+
+    HeapFree(GetProcessHeap(),0,szwFolder);
+    HeapFree(GetProcessHeap(),0,szwFolderPath);
+
+    return rc;
+}
+
+UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder, 
+                             LPCWSTR szFolderPath)
+{
+    DWORD i;
+    WCHAR path[MAX_PATH];
+    MSIFOLDER *folder;
+
+    TRACE("(%p %s %s)\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
+
+    if (package==NULL)
+        return ERROR_INVALID_HANDLE;
+
+    if (szFolderPath[0]==0)
+        return ERROR_FUNCTION_FAILED;
+
+    if (GetFileAttributesW(szFolderPath) == INVALID_FILE_ATTRIBUTES)
+        return ERROR_FUNCTION_FAILED;
+
+    resolve_folder(package,szFolder,path,FALSE,FALSE,&folder);
+
+    if (!folder)
+        return ERROR_INVALID_PARAMETER;
+
+    strcpyW(folder->Property,szFolderPath);
+
+    for (i = 0; i < package->loaded_folders; i++)
+        package->folders[i].ResolvedTarget[0]=0;
+
+    for (i = 0; i < package->loaded_folders; i++)
+        resolve_folder(package, package->folders[i].Directory, path, FALSE,
+                       TRUE, NULL);
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, 
+                             LPCWSTR szFolderPath)
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    ret = MSI_SetTargetPathW( package, szFolder, szFolderPath );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, DWORD iRunMode)
+{
+    FIXME("STUB (%li)\n",iRunMode);
+    return FALSE;
+}
+
+/*
+ * According to the docs, when this is called it immediately recalculates
+ * all the component states as well
+ */
+UINT WINAPI MsiSetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature,
+                                INSTALLSTATE iState)
+{
+    LPWSTR szwFeature = NULL;
+    UINT rc;
+
+    szwFeature = strdupAtoW(szFeature);
+
+    if (!szwFeature)
+        return ERROR_FUNCTION_FAILED;
+   
+    rc = MsiSetFeatureStateW(hInstall,szwFeature, iState); 
+
+    HeapFree(GetProcessHeap(),0,szwFeature);
+
+    return rc;
+}
+
+UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature,
+                                INSTALLSTATE iState)
+{
+    MSIPACKAGE* package;
+    INT index;
+
+    TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+
+    index = get_loaded_feature(package,szFeature);
+    if (index < 0)
+        return ERROR_UNKNOWN_FEATURE;
+
+    package->features[index].State = iState;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPSTR szFeature,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    LPWSTR szwFeature = NULL;
+    UINT rc;
+    
+    szwFeature = strdupAtoW(szFeature);
+
+    rc = MsiGetFeatureStateW(hInstall,szwFeature,piInstalled, piAction);
+
+    HeapFree( GetProcessHeap(), 0 , szwFeature);
+
+    return rc;
+}
+
+UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPWSTR szFeature,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    INT index;
+
+    index = get_loaded_feature(package,szFeature);
+    if (index < 0)
+        return ERROR_UNKNOWN_FEATURE;
+
+    if (piInstalled)
+        *piInstalled = package->features[index].State;
+
+    if (piAction)
+    {
+        if (package->features[index].Enabled)
+            *piAction = INSTALLSTATE_LOCAL;
+        else
+            *piAction = INSTALLSTATE_UNKNOWN;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPWSTR szFeature,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    MSIPACKAGE* package;
+    UINT ret;
+
+    TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled,
+piAction);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction);
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPSTR szComponent,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    LPWSTR szwComponent= NULL;
+    UINT rc;
+    
+    szwComponent= strdupAtoW(szComponent);
+
+    rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction);
+
+    HeapFree( GetProcessHeap(), 0 , szwComponent);
+
+    return rc;
+}
+
+UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPWSTR szComponent,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    INT index;
+
+    TRACE("%p %s %p %p\n", package, debugstr_w(szComponent), piInstalled,
+piAction);
+
+    index = get_loaded_component(package,szComponent);
+    if (index < 0)
+        return ERROR_UNKNOWN_COMPONENT;
+
+    if (piInstalled)
+        *piInstalled = package->components[index].State;
+
+    if (piAction)
+    {
+        if (package->components[index].Enabled &&
+            package->components[index].FeatureState)
+            *piAction = INSTALLSTATE_LOCAL;
+        else
+            *piAction = INSTALLSTATE_UNKNOWN;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPWSTR szComponent,
+                  INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
+{
+    MSIPACKAGE* package;
+    UINT ret;
+
+    TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szComponent),
+           piInstalled, piAction);
+
+    package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction);
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+#if 0
+static UINT ACTION_Template(MSIPACKAGE *package)
+{
+    UINT rc;
+    MSIQUERY * view;
+    MSIRECORD * row = 0;
+    static const WCHAR ExecSeqQuery[] = {0};
+
+    rc = MsiDatabaseOpenViewW(package->db, ExecSeqQuery, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MsiViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MsiViewClose(view);
+        msiobj_release(&view->hdr);
+        return rc;
+    }
+
+    while (1)
+    {
+        rc = MsiViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+        {
+            rc = ERROR_SUCCESS;
+            break;
+        }
+
+        msiobj_release(&row->hdr);
+    }
+    MsiViewClose(view);
+    msiobj_release(&view->hdr);
+    return rc;
+}
+#endif
diff --git a/reactos/lib/msi/cond.y b/reactos/lib/msi/cond.y
new file mode 100644 (file)
index 0000000..fe7551f
--- /dev/null
@@ -0,0 +1,774 @@
+%{
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2003 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+static int COND_error(char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_yyinput
+{
+    MSIPACKAGE *package;
+    LPCWSTR str;
+    INT    n;
+    MSICONDITION result;
+} COND_input;
+
+struct cond_str {
+    LPCWSTR data;
+    INT len;
+};
+
+static LPWSTR COND_GetString( struct cond_str *str );
+static LPWSTR COND_GetLiteral( struct cond_str *str );
+static int COND_lex( void *COND_lval, COND_input *info);
+
+typedef INT (*comp_int)(INT a, INT b);
+typedef INT (*comp_str)(LPWSTR a, LPWSTR b, BOOL caseless);
+typedef INT (*comp_m1)(LPWSTR a,int b);
+typedef INT (*comp_m2)(int a,LPWSTR b);
+
+static INT comp_lt_i(INT a, INT b);
+static INT comp_gt_i(INT a, INT b);
+static INT comp_le_i(INT a, INT b);
+static INT comp_ge_i(INT a, INT b);
+static INT comp_eq_i(INT a, INT b);
+static INT comp_ne_i(INT a, INT b);
+static INT comp_bitand(INT a, INT b);
+static INT comp_highcomp(INT a, INT b);
+static INT comp_lowcomp(INT a, INT b);
+
+static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless);
+static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless);
+
+static INT comp_eq_m1(LPWSTR a, INT b);
+static INT comp_ne_m1(LPWSTR a, INT b);
+static INT comp_lt_m1(LPWSTR a, INT b);
+static INT comp_gt_m1(LPWSTR a, INT b);
+static INT comp_le_m1(LPWSTR a, INT b);
+static INT comp_ge_m1(LPWSTR a, INT b);
+
+static INT comp_eq_m2(INT a, LPWSTR b);
+static INT comp_ne_m2(INT a, LPWSTR b);
+static INT comp_lt_m2(INT a, LPWSTR b);
+static INT comp_gt_m2(INT a, LPWSTR b);
+static INT comp_le_m2(INT a, LPWSTR b);
+static INT comp_ge_m2(INT a, LPWSTR b);
+
+%}
+
+%pure-parser
+
+%union
+{
+    struct cond_str str;
+    LPWSTR    string;
+    INT       value;
+    comp_int  fn_comp_int;
+    comp_str  fn_comp_str;
+    comp_m1   fn_comp_m1;
+    comp_m2   fn_comp_m2;
+}
+
+%token COND_SPACE COND_EOF COND_SPACE
+%token COND_OR COND_AND COND_NOT
+%token COND_LT COND_GT COND_EQ 
+%token COND_LPAR COND_RPAR COND_TILDA
+%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM
+%token <str> COND_IDENT <str> COND_NUMBER <str> COND_LITER
+
+%nonassoc COND_EOF COND_ERROR
+
+%type <value> expression boolean_term boolean_factor 
+%type <value> term value_i symbol_i integer
+%type <string> identifier value_s symbol_s literal
+%type <fn_comp_int> comp_op_i
+%type <fn_comp_str> comp_op_s 
+%type <fn_comp_m1>  comp_op_m1 
+%type <fn_comp_m2>  comp_op_m2
+
+%%
+
+condition:
+    expression
+        {
+            COND_input* cond = (COND_input*) info;
+            cond->result = $1;
+        }
+    ;
+
+expression:
+    boolean_term 
+        {
+            $$ = $1;
+        }
+  | boolean_term COND_OR expression
+        {
+            $$ = $1 || $3;
+        }
+    ;
+
+boolean_term:
+    boolean_factor
+        {
+            $$ = $1;
+        }
+    | boolean_term COND_AND boolean_factor
+        {
+            $$ = $1 && $3;
+        }
+    ;
+
+boolean_factor:
+    term
+        {
+            $$ = $1;
+        }
+  | COND_NOT term
+        {
+            $$ = ! $2;
+        }
+    ;
+
+
+term:
+    value_i
+        {
+            $$ = $1;
+        }
+  | value_s
+        {
+            $$ = atoiW($1);
+        }
+  | value_i comp_op_i value_i
+        {
+            $$ = $2( $1, $3 );
+        }
+  | value_s comp_op_s value_s
+        {
+            $$ = $2( $1, $3, FALSE );
+        }
+  | value_s COND_TILDA comp_op_s value_s
+        {
+            $$ = $3( $1, $4, TRUE );
+        }
+  | value_s comp_op_m1 value_i
+        {
+            $$ = $2( $1, $3 );
+        }
+  | value_i comp_op_m2 value_s
+        {
+            $$ = $2( $1, $3 );
+        }
+  | COND_LPAR expression COND_RPAR
+        {
+            $$ = $2;
+        }
+    ;
+
+comp_op_i:
+    /* common functions */
+   COND_EQ
+        {
+            $$ = comp_eq_i;
+        }
+  | COND_LT COND_GT
+        {
+            $$ = comp_ne_i;
+        }
+  | COND_LT
+        {
+            $$ = comp_lt_i;
+        }
+  | COND_GT
+        {
+            $$ = comp_gt_i;
+        }
+  | COND_LT COND_EQ
+        {
+            $$ = comp_le_i;
+        }
+  | COND_GT COND_EQ
+        {
+            $$ = comp_ge_i;
+        }
+  /*Int only*/
+  | COND_GT COND_LT
+        {
+            $$ = comp_bitand;
+        }
+  | COND_LT COND_LT
+        {
+            $$ = comp_highcomp;
+        }
+  | COND_GT COND_GT
+        {
+            $$ = comp_lowcomp;
+        }
+    ;
+
+comp_op_s:
+    /* common functions */
+   COND_EQ
+        {
+            $$ = comp_eq_s;
+        }
+  | COND_LT COND_GT
+        {
+            $$ = comp_ne_s;
+        }
+  | COND_LT
+        {
+            $$ = comp_lt_s;
+        }
+  | COND_GT
+        {
+            $$ = comp_gt_s;
+        }
+  | COND_LT COND_EQ
+        {
+            $$ = comp_le_s;
+        }
+  | COND_GT COND_EQ
+        {
+            $$ = comp_ge_s;
+        }
+  /*string only*/
+  | COND_GT COND_LT
+        {
+            $$ = comp_substring;
+        }
+  | COND_LT COND_LT
+        {
+            $$ = comp_start;
+        }
+  | COND_GT COND_GT
+        {
+            $$ = comp_end;
+        }
+    ;
+
+comp_op_m1:
+    /* common functions */
+   COND_EQ
+        {
+            $$ = comp_eq_m1;
+        }
+  | COND_LT COND_GT
+        {
+            $$ = comp_ne_m1;
+        }
+  | COND_LT
+        {
+            $$ = comp_lt_m1;
+        }
+  | COND_GT
+        {
+            $$ = comp_gt_m1;
+        }
+  | COND_LT COND_EQ
+        {
+            $$ = comp_le_m1;
+        }
+  | COND_GT COND_EQ
+        {
+            $$ = comp_ge_m1;
+        }
+  /*Not valid for mixed compares*/
+  | COND_GT COND_LT
+        {
+            $$ = 0;
+        }
+  | COND_LT COND_LT
+        {
+            $$ = 0;
+        }
+  | COND_GT COND_GT
+        {
+            $$ = 0;
+        }
+    ;
+
+comp_op_m2:
+    /* common functions */
+   COND_EQ
+        {
+            $$ = comp_eq_m2;
+        }
+  | COND_LT COND_GT
+        {
+            $$ = comp_ne_m2;
+        }
+  | COND_LT
+        {
+            $$ = comp_lt_m2;
+        }
+  | COND_GT
+        {
+            $$ = comp_gt_m2;
+        }
+  | COND_LT COND_EQ
+        {
+            $$ = comp_le_m2;
+        }
+  | COND_GT COND_EQ
+        {
+            $$ = comp_ge_m2;
+        }
+  /*Not valid for mixed compares*/
+  | COND_GT COND_LT
+        {
+            $$ = 0;
+        }
+  | COND_LT COND_LT
+        {
+            $$ = 0;
+        }
+  | COND_GT COND_GT
+        {
+            $$ = 0;
+        }
+    ;
+
+value_i:
+    symbol_i
+        {
+            $$ = $1;
+        }
+  | integer
+        {
+            $$ = $1;
+        }
+    ;
+
+value_s:
+  symbol_s
+    {
+        $$ = $1;
+    } 
+  | literal
+    {
+        $$ = $1;
+    }
+    ;
+
+literal:
+    COND_LITER
+        {
+            $$ = COND_GetLiteral(&$1);
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+symbol_i:
+    COND_DOLLARS identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetComponentStateW(cond->package, $2, &install, &action );
+            $$ = action;
+            HeapFree( GetProcessHeap(), 0, $2 );
+        }
+  | COND_QUESTION identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetComponentStateW(cond->package, $2, &install, &action );
+            $$ = install;
+            HeapFree( GetProcessHeap(), 0, $2 );
+        }
+  | COND_AMPER identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetFeatureStateW(cond->package, $2, &install, &action );
+            $$ = action;
+            HeapFree( GetProcessHeap(), 0, $2 );
+        }
+  | COND_EXCLAM identifier
+        {
+            COND_input* cond = (COND_input*) info;
+            INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN;
+      
+            MSI_GetFeatureStateW(cond->package, $2, &install, &action );
+            $$ = install;
+            HeapFree( GetProcessHeap(), 0, $2 );
+        }
+    ;
+
+symbol_s:
+    identifier
+        {
+            DWORD sz;
+            COND_input* cond = (COND_input*) info;
+            $$ = HeapAlloc( GetProcessHeap(), 0, 0x100*sizeof (WCHAR) );
+
+            /* Lookup the identifier */
+
+            sz=0x100;
+            if (MSI_GetPropertyW(cond->package,$1,$$,&sz) != ERROR_SUCCESS)
+            {
+                $$[0]=0;
+            }
+            HeapFree( GetProcessHeap(), 0, $1 );
+        }
+    | COND_PERCENT identifier
+        {
+            UINT len = GetEnvironmentVariableW( $2, NULL, 0 );
+            if( len++ )
+            {
+                $$ = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
+                if( $$ )
+                    GetEnvironmentVariableW( $2, $$, len );
+            }
+            HeapFree( GetProcessHeap(), 0, $2 );
+        }
+    ;
+
+identifier:
+    COND_IDENT
+        {
+            $$ = COND_GetString(&$1);
+            if( !$$ )
+                YYABORT;
+        }
+    ;
+
+integer:
+    COND_NUMBER
+        {
+            LPWSTR szNum = COND_GetString(&$1);
+            if( !szNum )
+                YYABORT;
+            $$ = atoiW( szNum );
+            HeapFree( GetProcessHeap(), 0, szNum );
+        }
+    ;
+
+%%
+
+
+static int COND_IsAlpha( WCHAR x )
+{
+    return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) ||
+            ( ( x >= 'a' ) && ( x <= 'z' ) ) ||
+            ( ( x == '_' ) ) );
+}
+
+static int COND_IsNumber( WCHAR x )
+{
+    return( (( x >= '0' ) && ( x <= '9' ))  || (x =='-') || (x =='.') );
+}
+
+
+/* the mess of comparison functions */
+
+static INT comp_lt_i(INT a, INT b)
+{ return (a < b); }
+static INT comp_gt_i(INT a, INT b)
+{ return (a > b); }
+static INT comp_le_i(INT a, INT b)
+{ return (a <= b); }
+static INT comp_ge_i(INT a, INT b)
+{ return (a >= b); }
+static INT comp_eq_i(INT a, INT b)
+{ return (a == b); }
+static INT comp_ne_i(INT a, INT b)
+{ return (a != b); }
+static INT comp_bitand(INT a, INT b)
+{ return a & b;}
+static INT comp_highcomp(INT a, INT b)
+{ return HIWORD(a)==b; }
+static INT comp_lowcomp(INT a, INT b)
+{ return LOWORD(a)==b; }
+
+static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return !strcmpiW(a,b); else return !strcmpW(a,b);}
+static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b); else  return strcmpW(a,b);}
+static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)<0; else return strcmpW(a,b)<0;}
+static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)>0; else return strcmpW(a,b)>0;}
+static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)<=0; else return strcmpW(a,b)<=0;}
+static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strcmpiW(a,b)>=0; else return  strcmpW(a,b)>=0;}
+static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless)
+/* ERROR NOT WORKING REWRITE */
+{ if (casless) return strstrW(a,b)!=NULL; else return strstrW(a,b)!=NULL;}
+static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless)
+{ if (casless) return strncmpiW(a,b,strlenW(b))==0; 
+  else return strncmpW(a,b,strlenW(b))==0;}
+static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless)
+{ 
+    int i = strlenW(a); 
+    int j = strlenW(b); 
+    if (j>i)
+        return 0;
+    if (casless) return (!strcmpiW(&a[i-j-1],b));
+    else  return (!strcmpW(&a[i-j-1],b));
+}
+
+
+static INT comp_eq_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)==b; else return 0;}
+static INT comp_ne_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)!=b; else return 1;}
+static INT comp_lt_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)<b; else return 0;}
+static INT comp_gt_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)>b; else return 0;}
+static INT comp_le_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)<=b; else return 0;}
+static INT comp_ge_m1(LPWSTR a, INT b)
+{ if (COND_IsNumber(a[0])) return atoiW(a)>=b; else return 0;}
+
+static INT comp_eq_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a == atoiW(b); else return 0;}
+static INT comp_ne_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a != atoiW(b); else return 1;}
+static INT comp_lt_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a < atoiW(b); else return 0;}
+static INT comp_gt_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a > atoiW(b); else return 0;}
+static INT comp_le_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a <= atoiW(b); else return 0;}
+static INT comp_ge_m2(INT a, LPWSTR b)
+{ if (COND_IsNumber(b[0])) return a >= atoiW(b); else return 0;}
+
+
+
+static int COND_IsIdent( WCHAR x )
+{
+    return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) 
+            || ( x == '#' ) || (x == '.') );
+}
+
+static int COND_GetOne( struct cond_str *str, COND_input *cond )
+{
+    static const WCHAR szNot[] = {'N','O','T',0};
+    static const WCHAR szAnd[] = {'A','N','D',0};
+    static const WCHAR szOr[] = {'O','R',0};
+    WCHAR ch;
+    int rc, len = 1;
+
+    str->data = &cond->str[cond->n];
+
+    ch = str->data[0];
+    switch( ch )
+    {
+    case 0: return 0;
+    case '(': rc = COND_LPAR; break;
+    case ')': rc = COND_RPAR; break;
+    case '&': rc = COND_AMPER; break;
+    case '!': rc = COND_EXCLAM; break;
+    case '$': rc = COND_DOLLARS; break;
+    case '?': rc = COND_QUESTION; break;
+    case '%': rc = COND_PERCENT; break;
+    case ' ': rc = COND_SPACE; break;
+    case '=': rc = COND_EQ; break;
+    case '~': rc = COND_TILDA; break;
+    case '<': rc = COND_LT; break;
+    case '>': rc = COND_GT; break;
+    case '"':
+       {
+           const WCHAR *ch2 = str->data + 1;
+
+
+           while ( *ch2 && *ch2 != '"' )
+               ++ch2;
+           if (*ch2 == '"')
+           {
+               len = ch2 - str->data + 1;
+               rc = COND_LITER;
+               break;
+           }
+       }
+       ERR("Unterminated string\n");
+       rc = COND_ERROR;
+       break;
+    default: 
+        if( COND_IsAlpha( ch ) )
+        {
+            while( COND_IsIdent( str->data[len] ) )
+                len++;
+            rc = COND_IDENT;
+            break;
+        }
+
+        if( COND_IsNumber( ch ) )
+        {
+            while( COND_IsNumber( str->data[len] ) )
+                len++;
+            rc = COND_NUMBER;
+            break;
+        }
+
+        ERR("Got unknown character %c(%x)\n",ch,ch);
+        rc = COND_ERROR;
+        break;
+    }
+
+    /* keyword identifiers */
+    if( rc == COND_IDENT )
+    {
+        if( (len==3) && (strncmpiW(str->data,szNot,len)==0) )
+            rc = COND_NOT;
+        else if( (len==3) && (strncmpiW(str->data,szAnd,len)==0) )
+            rc = COND_AND;
+        else if( (len==2) && (strncmpiW(str->data,szOr,len)==0) )
+            rc = COND_OR;
+    }
+
+    cond->n += len;
+    str->len = len;
+
+    return rc;
+}
+
+static int COND_lex( void *COND_lval, COND_input *cond )
+{
+    int rc;
+    struct cond_str *str = COND_lval;
+
+    do {
+        rc = COND_GetOne( str, cond );
+    } while (rc == COND_SPACE);
+    
+    return rc;
+}
+
+static LPWSTR COND_GetString( struct cond_str *str )
+{
+    LPWSTR ret;
+
+    ret = HeapAlloc( GetProcessHeap(), 0, (str->len+1) * sizeof (WCHAR) );
+    if( ret )
+    {
+        strncpyW( ret, str->data, str->len );
+        ret[str->len]=0;
+    }
+    TRACE("Got identifier %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static LPWSTR COND_GetLiteral( struct cond_str *str )
+{
+    LPWSTR ret;
+
+    ret = HeapAlloc( GetProcessHeap(), 0, (str->len-1) * sizeof (WCHAR) );
+    if( ret )
+    {
+        memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) );
+        ret[str->len - 2]=0;
+    }
+    TRACE("Got literal %s\n",debugstr_w(ret));
+    return ret;
+}
+
+static int COND_error(char *str)
+{
+    return 0;
+}
+
+MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition )
+{
+    COND_input cond;
+    MSICONDITION r;
+
+    cond.package = package;
+    cond.str   = szCondition;
+    cond.n     = 0;
+    cond.result = -1;
+    
+    TRACE("Evaluating %s\n",debugstr_w(szCondition));    
+
+    if( !COND_parse( &cond ) )
+        r = cond.result;
+    else
+        r = MSICONDITION_ERROR;
+
+    TRACE("Evaluates to %i\n",r);
+    return r;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition )
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_EvaluateConditionW( package, szCondition );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition )
+{
+    LPWSTR szwCond = NULL;
+    MSICONDITION r;
+
+    if( szCondition )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szCondition, -1, NULL, 0 );
+        szwCond = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+        MultiByteToWideChar( CP_ACP, 0, szCondition, -1, szwCond, len );
+    }
+
+    r = MsiEvaluateConditionW( hInstall, szwCond );
+
+    if( szwCond )
+        HeapFree( GetProcessHeap(), 0, szwCond );
+
+    return r;
+}
diff --git a/reactos/lib/msi/create.c b/reactos/lib/msi/create.c
new file mode 100644 (file)
index 0000000..b1ad8c7
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+
+#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 tagMSICREATEVIEW
+{
+    MSIVIEW          view;
+    MSIDATABASE     *db;
+    LPWSTR           name;
+    BOOL             bIsTemp;
+    create_col_info *col_info;
+} MSICREATEVIEW;
+
+static UINT CREATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %d %d %p\n", cv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+    create_col_info *col;
+    UINT r, nField, row, table_val, column_val;
+    static const WCHAR szTables[] =  { '_','T','a','b','l','e','s',0 };
+    static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+    MSIVIEW *tv = NULL;
+
+    TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name), 
+          cv->bIsTemp?"temporary":"permanent");
+
+    /* only add tables that don't exist already */
+    if( TABLE_Exists(cv->db, cv->name ) )
+        return ERROR_BAD_QUERY_SYNTAX;
+
+    /* add the name to the _Tables table */
+    table_val = msi_addstringW( cv->db->strings, 0, cv->name, -1, 1 );
+    TRACE("New string %s -> %d\n", debugstr_w( cv->name ), table_val );
+    if( table_val < 0 )
+        return ERROR_FUNCTION_FAILED;
+
+    r = TABLE_CreateView( cv->db, szTables, &tv );
+    TRACE("CreateView returned %x\n", r);
+    if( r )
+        return r;
+
+    r = tv->ops->execute( tv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        return r;
+
+    row = -1;
+    r = tv->ops->insert_row( tv, &row );
+    TRACE("insert_row returned %x\n", r);
+    if( r )
+        goto err;
+
+    r = tv->ops->set_int( tv, row, 1, table_val );
+    if( r )
+        goto err;
+    tv->ops->delete( tv );
+    tv = NULL;
+
+    /* add each column to the _Columns table */
+    r = TABLE_CreateView( cv->db, szColumns, &tv );
+    if( r )
+        return r;
+
+    r = tv->ops->execute( tv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        return r;
+
+    /*
+     * need to set the table, column number, col name and type
+     * for each column we enter in the table
+     */
+    nField = 1;
+    for( col = cv->col_info; col; col = col->next )
+    {
+        row = -1;
+        r = tv->ops->insert_row( tv, &row );
+        if( r )
+            goto err;
+
+        column_val = msi_addstringW( cv->db->strings, 0, col->colname, -1, 1 );
+        TRACE("New string %s -> %d\n", debugstr_w( col->colname ), column_val );
+        if( column_val < 0 )
+            break;
+
+        /* add the string again here so we increase the reference count */
+        table_val = msi_addstringW( cv->db->strings, 0, cv->name, -1, 1 );
+        if( table_val < 0 )
+            break;
+
+        r = tv->ops->set_int( tv, row, 1, table_val );
+        if( r )
+            break;
+
+        r = tv->ops->set_int( tv, row, 2, 0x8000|nField );
+        if( r )
+            break;
+
+        r = tv->ops->set_int( tv, row, 3, column_val );
+        if( r )
+            break;
+
+        r = tv->ops->set_int( tv, row, 4, 0x8000|col->type );
+        if( r )
+            break;
+
+        nField++;
+    }
+    if( !col )
+        r = ERROR_SUCCESS;
+
+err:
+    /* FIXME: remove values from the string table on error */
+    if( tv )
+        tv->ops->delete( tv );
+    return r;
+}
+
+static UINT CREATE_close( struct tagMSIVIEW *view )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p\n", cv);
+
+    return ERROR_SUCCESS;
+}
+
+static UINT CREATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %p %p\n", cv, rows, cols );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %d %p %p\n", cv, n, name, type );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+
+    TRACE("%p %d %ld\n", cv, eModifyMode, hrec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT CREATE_delete( struct tagMSIVIEW *view )
+{
+    MSICREATEVIEW *cv = (MSICREATEVIEW*)view;
+    create_col_info *col;
+
+    TRACE("%p\n", cv );
+
+    col = cv->col_info; 
+    while( col )
+    {
+        create_col_info *t = col;
+        col = col->next;
+        HeapFree( GetProcessHeap(), 0, t->colname );
+        HeapFree( GetProcessHeap(), 0, t );
+    }
+    msiobj_release( &cv->db->hdr );
+    HeapFree( GetProcessHeap(), 0, cv->name );
+    HeapFree( GetProcessHeap(), 0, cv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS create_ops =
+{
+    CREATE_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    CREATE_execute,
+    CREATE_close,
+    CREATE_get_dimensions,
+    CREATE_get_column_info,
+    CREATE_modify,
+    CREATE_delete
+};
+
+UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        create_col_info *col_info, BOOL temp )
+{
+    MSICREATEVIEW *cv = NULL;
+
+    TRACE("%p\n", cv );
+
+    cv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *cv );
+    if( !cv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    cv->view.ops = &create_ops;
+    msiobj_addref( &db->hdr );
+    cv->db = db;
+    cv->name = table;  /* FIXME: strdupW it? */
+    cv->col_info = col_info;
+    cv->bIsTemp = temp;
+    *view = (MSIVIEW*) cv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/distinct.c b/reactos/lib/msi/distinct.c
new file mode 100644 (file)
index 0000000..536bdc6
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * 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 <stdarg.h>
+
+#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, MSIRECORD *record )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+    UINT r, i, j, r_count, c_count;
+    DISTINCTSET *rowset = NULL;
+
+    TRACE("%p %p\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 );
+    msiobj_release( &dv->db->hdr );
+    HeapFree( GetProcessHeap(), 0, dv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS distinct_ops =
+{
+    DISTINCT_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    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;
+    msiobj_addref( &db->hdr );
+    dv->db = db;
+    dv->table = table;
+    dv->translation = NULL;
+    dv->row_count = 0;
+    *view = (MSIVIEW*) dv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/handle.c b/reactos/lib/msi/handle.c
new file mode 100644 (file)
index 0000000..cd94a8d
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+
+#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);
+
+static CRITICAL_SECTION MSI_handle_cs;
+static CRITICAL_SECTION_DEBUG MSI_handle_cs_debug =
+{
+    0, 0, &MSI_handle_cs,
+    { &MSI_handle_cs_debug.ProcessLocksList, 
+      &MSI_handle_cs_debug.ProcessLocksList },
+      0, 0, { 0, (DWORD)(__FILE__ ": MSI_handle_cs") }
+};
+static CRITICAL_SECTION MSI_handle_cs = { &MSI_handle_cs_debug, -1, 0, 0, 0, 0 };
+
+MSIOBJECTHDR *msihandletable[MSIMAXHANDLES];
+
+MSIHANDLE alloc_msihandle( MSIOBJECTHDR *obj )
+{
+    MSIHANDLE ret = 0;
+    UINT i;
+
+    EnterCriticalSection( &MSI_handle_cs );
+
+    /* find a slot */
+    for(i=0; i<MSIMAXHANDLES; i++)
+        if( !msihandletable[i] )
+            break;
+    if( (i>=MSIMAXHANDLES) || msihandletable[i] )
+        goto out;
+
+    msiobj_addref( obj );
+    msihandletable[i] = obj;
+    ret = (MSIHANDLE) (i+1);
+out:
+    TRACE("%p -> %ld\n", obj, ret );
+
+    LeaveCriticalSection( &MSI_handle_cs );
+    return ret;
+}
+
+void *msihandle2msiinfo(MSIHANDLE handle, UINT type)
+{
+    MSIOBJECTHDR *ret = NULL;
+
+    EnterCriticalSection( &MSI_handle_cs );
+    handle--;
+    if( handle<0 )
+        goto out;
+    if( handle>=MSIMAXHANDLES )
+        goto out;
+    if( !msihandletable[handle] )
+        goto out;
+    if( msihandletable[handle]->magic != MSIHANDLE_MAGIC )
+        goto out;
+    if( type && (msihandletable[handle]->type != type) )
+        goto out;
+    ret = msihandletable[handle];
+    msiobj_addref( ret );
+    
+out:
+    LeaveCriticalSection( &MSI_handle_cs );
+
+    return (void*) ret;
+}
+
+MSIHANDLE msiobj_findhandle( MSIOBJECTHDR *hdr )
+{
+    MSIHANDLE ret = 0;
+    UINT i;
+
+    TRACE("%p\n", hdr);
+
+    EnterCriticalSection( &MSI_handle_cs );
+    for(i=0; (i<MSIMAXHANDLES) && !ret; i++)
+        if( msihandletable[i] == hdr )
+            ret = i+1;
+    LeaveCriticalSection( &MSI_handle_cs );
+
+    TRACE("%p -> %ld\n", hdr, ret);
+
+    msiobj_addref( hdr );
+    return ret;
+}
+
+void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy )
+{
+    MSIOBJECTHDR *info;
+
+    info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
+    if( info )
+    {
+        info->magic = MSIHANDLE_MAGIC;
+        info->type = type;
+        info->refcount = 1;
+        info->destructor = destroy;
+    }
+
+    return info;
+}
+
+void msiobj_addref( MSIOBJECTHDR *info )
+{
+    TRACE("%p\n", info);
+
+    if( !info )
+        return;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        return;
+    }
+
+    info->refcount++;
+}
+
+int msiobj_release( MSIOBJECTHDR *info )
+{
+    int ret;
+
+    TRACE("%p\n",info);
+
+    if( !info )
+        return -1;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        return -1;
+    }
+
+    ret = info->refcount--;
+    if (info->refcount == 0)
+    {
+    if( info->destructor )
+            info->destructor( info );
+    HeapFree( GetProcessHeap(), 0, info );
+        TRACE("object %p destroyed\n", info);
+    }
+
+    return ret;
+}
+
+UINT WINAPI MsiCloseHandle(MSIHANDLE handle)
+{
+    MSIOBJECTHDR *info;
+    UINT ret = ERROR_INVALID_HANDLE;
+
+    TRACE("%lx\n",handle);
+
+    EnterCriticalSection( &MSI_handle_cs );
+
+    info = msihandle2msiinfo(handle, 0);
+    if( !info )
+        goto out;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        goto out;
+    }
+
+    msiobj_release( info );
+    msihandletable[handle-1] = NULL;
+    ret = ERROR_SUCCESS;
+
+    TRACE("handle %lx Destroyed\n", handle);
+out:
+    LeaveCriticalSection( &MSI_handle_cs );
+    if( info )
+        msiobj_release( info );
+
+    return ret;
+}
+
+UINT WINAPI MsiCloseAllHandles(void)
+{
+    UINT i;
+
+    TRACE("\n");
+
+    for(i=0; i<MSIMAXHANDLES; i++)
+        MsiCloseHandle( i+1 );
+
+    return 0;
+}
diff --git a/reactos/lib/msi/insert.c b/reactos/lib/msi/insert.c
new file mode 100644 (file)
index 0000000..d00a4d7
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 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 <stdarg.h>
+
+#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 tagMSIINSERTVIEW
+{
+    MSIVIEW          view;
+    MSIDATABASE     *db;
+    BOOL             bIsTemp;
+    MSIVIEW         *sv;
+    value_list      *vals;   /* looks like these may be ignored... */
+} MSIINSERTVIEW;
+
+static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", iv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    UINT n, type, val, r, row, col_count = 0;
+    MSIVIEW *sv;
+
+    TRACE("%p %p\n", iv, record );
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    r = sv->ops->execute( sv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        return r;
+
+    r = sv->ops->get_dimensions( sv, NULL, &col_count );
+    if( r )
+        goto err;
+
+    n = MSI_RecordGetFieldCount( record );
+    if( n != col_count )
+    {
+        ERR("Number of fields do not match\n");
+        goto err;
+    }
+
+    row = -1;
+    r = sv->ops->insert_row( sv, &row );
+    TRACE("insert_row returned %x\n", r);
+    if( r )
+        goto err;
+
+    for( n = 1; n <= col_count; n++ )
+    {
+        r = sv->ops->get_column_info( sv, n, NULL, &type );
+        if( r )
+            break;
+
+        if( type & MSITYPE_STRING )
+        {
+            const WCHAR *str = MSI_RecordGetString( record, n );
+            val = msi_addstringW( iv->db->strings, 0, str, -1, 1 );
+        }
+        else
+        {
+            val = MSI_RecordGetInteger( record, n );
+            val |= 0x8000;
+        }
+        r = sv->ops->set_int( sv, row, n, val );
+        if( r )
+            break;
+    }
+
+err:
+    return ERROR_SUCCESS;
+}
+
+
+static UINT INSERT_close( struct tagMSIVIEW *view )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p\n", iv);
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    return sv->ops->close( sv );
+}
+
+static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p %p %p\n", iv, rows, cols );
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    return sv->ops->get_dimensions( sv, rows, cols );
+}
+
+static UINT INSERT_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p %d %p %p\n", iv, n, name, type );
+
+    sv = iv->sv;
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+
+    return sv->ops->get_column_info( sv, n, name, type );
+}
+
+static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+
+    TRACE("%p %d %ld\n", iv, eModifyMode, hrec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT INSERT_delete( struct tagMSIVIEW *view )
+{
+    MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view;
+    MSIVIEW *sv;
+
+    TRACE("%p\n", iv );
+
+    sv = iv->sv;
+    if( sv )
+        sv->ops->delete( sv );
+    delete_value_list( iv->vals );
+    msiobj_release( &iv->db->hdr );
+    HeapFree( GetProcessHeap(), 0, iv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS insert_ops =
+{
+    INSERT_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    INSERT_execute,
+    INSERT_close,
+    INSERT_get_dimensions,
+    INSERT_get_column_info,
+    INSERT_modify,
+    INSERT_delete
+};
+
+UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        string_list *columns, value_list *values, BOOL temp )
+{
+    MSIINSERTVIEW *iv = NULL;
+    UINT r;
+    MSIVIEW *tv = NULL, *sv = NULL;
+
+    TRACE("%p\n", iv );
+
+    r = TABLE_CreateView( db, table, &tv );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = SELECT_CreateView( db, &sv, tv, columns );
+    if( r != ERROR_SUCCESS )
+    {
+        if( tv )
+            tv->ops->delete( tv );
+        return r;
+    }
+    
+    iv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *iv );
+    if( !iv )
+        return ERROR_FUNCTION_FAILED;
+
+    /* fill the structure */
+    iv->view.ops = &insert_ops;
+    msiobj_addref( &db->hdr );
+    iv->db = db;
+    iv->vals = values;
+    iv->bIsTemp = temp;
+    iv->sv = sv;
+    *view = (MSIVIEW*) iv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/msi.c b/reactos/lib/msi/msi.c
new file mode 100644 (file)
index 0000000..97b2eca
--- /dev/null
@@ -0,0 +1,1584 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002,2003,2004 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "wincrypt.h"
+#include "wine/unicode.h"
+#include "objbase.h"
+#include "winver.h"
+
+#include "initguid.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * The MSVC headers define the MSIDBOPEN_* macros cast to LPCTSTR,
+ *  which is a problem because LPCTSTR isn't defined when compiling wine.
+ * To work around this problem, we need to define LPCTSTR as LPCWSTR here,
+ *  and make sure to only use it in W functions.
+ */
+#define LPCTSTR LPCWSTR
+
+DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+static 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 };
+
+static const WCHAR szFeatures[] = {
+'F','e','a','t','u','r','e','s',0 };
+static const WCHAR szComponents[] = {
+'C','o','m','p','o','n','e','n','t','s',0 };
+
+/* the UI level */
+INSTALLUILEVEL gUILevel;
+HWND           gUIhwnd;
+INSTALLUI_HANDLERA gUIHandler;
+DWORD gUIFilter;
+LPVOID gUIContext;
+WCHAR gszLogFile[MAX_PATH];
+
+/*
+ *  .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;
+}
+
+/* tables for encoding and decoding base85 */
+static const unsigned char table_dec85[0x80] = {
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
+0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff,
+0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17,
+0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
+0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36,
+0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,
+0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff,
+};
+
+static const char table_enc85[] =
+"!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx"
+"yz{}~";
+
+/*
+ *  Converts a base85 encoded guid into a GUID pointer
+ *  Base85 encoded GUIDs should be 20 characters long.
+ *
+ *  returns TRUE if successful, FALSE if not
+ */
+BOOL decode_base85_guid( LPCWSTR str, GUID *guid )
+{
+    DWORD i, val = 0, base = 1, *p;
+
+    p = (DWORD*) guid;
+    for( i=0; i<20; i++ )
+    {
+        if( (i%5) == 0 )
+        {
+            val = 0;
+            base = 1;
+        }
+        val += table_dec85[str[i]] * base;
+        if( str[i] >= 0x80 )
+            return FALSE;
+        if( table_dec85[str[i]] == 0xff )
+            return FALSE;
+        if( (i%5) == 4 )
+            p[i/5] = val;
+        base *= 85;
+    }
+    return TRUE;
+}
+
+/*
+ *  Encodes a base85 guid given a GUID pointer
+ *  Caller should provide a 21 character buffer for the encoded string.
+ *
+ *  returns TRUE if successful, FALSE if not
+ */
+BOOL encode_base85_guid( GUID *guid, LPWSTR str )
+{
+    unsigned int x, *p, i;
+
+    p = (unsigned int*) guid;
+    for( i=0; i<4; i++ )
+    {
+        x = p[i];
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+        x = x/85;
+        *str++ = table_enc85[x%85];
+    }
+    *str = 0;
+    
+    return TRUE;
+}
+
+
+VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
+{
+    MSIDATABASE *db = (MSIDATABASE *) arg;
+
+    free_cached_tables( db );
+    IStorage_Release( db->storage );
+}
+
+UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
+{
+    IStorage *stg = NULL;
+    HRESULT r;
+    MSIDATABASE *db = NULL;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    LPWSTR szMode;
+    STATSTG stat;
+
+    TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
+
+    if( !pdb )
+        return ERROR_INVALID_PARAMETER;
+
+    szMode = (LPWSTR) szPersist;
+    if( HIWORD( szPersist ) )
+    {
+        /* UINT len = lstrlenW( szPerist ) + 1; */
+        FIXME("don't support persist files yet\b");
+        return ERROR_INVALID_PARAMETER;
+        /* szMode = HeapAlloc( GetProcessHeap(), 0, len * sizeof (DWORD) ); */
+    }
+    else if( szPersist == MSIDBOPEN_READONLY )
+    {
+        r = StgOpenStorage( szDBPath, NULL,
+              STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+    }
+    else if( szPersist == MSIDBOPEN_CREATE )
+    {
+        r = StgCreateDocfile( szDBPath, 
+              STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
+        if( r == ERROR_SUCCESS )
+        {
+            IStorage_SetClass( stg, &CLSID_MsiDatabase );
+            r = init_string_table( stg );
+        }
+    }
+    else if( szPersist == MSIDBOPEN_TRANSACT )
+    {
+        r = StgOpenStorage( szDBPath, NULL,
+              STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
+    }
+    else
+    {
+        ERR("unknown flag %p\n",szPersist);
+        return ERROR_INVALID_PARAMETER;
+    }
+
+    if( FAILED( r ) )
+    {
+        FIXME("open failed r = %08lx!\n",r);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
+    if( FAILED( r ) )
+    {
+        FIXME("Failed to stat storage\n");
+        goto end;
+    }
+
+    if( memcmp( &stat.clsid, &CLSID_MsiDatabase, sizeof (GUID) ) )
+    {
+        ERR("storage GUID is not a MSI database GUID %s\n",
+             debugstr_guid(&stat.clsid) );
+        goto end;
+    }
+
+
+    db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
+                              MSI_CloseDatabase );
+    if( !db )
+    {
+        FIXME("Failed to allocate a handle\n");
+        goto end;
+    }
+
+    if( TRACE_ON( msi ) )
+        enum_stream_names( stg );
+
+    db->storage = stg;
+    db->mode = szMode;
+
+    ret = load_string_table( db );
+    if( ret != ERROR_SUCCESS )
+        goto end;
+
+    msiobj_addref( &db->hdr );
+    IStorage_AddRef( stg );
+    *pdb = db;
+
+end:
+    if( db )
+        msiobj_release( &db->hdr );
+    if( stg )
+        IStorage_Release( stg );
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
+{
+    MSIDATABASE *db;
+    UINT ret;
+
+    TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
+
+    ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
+    if( ret == ERROR_SUCCESS )
+    {
+        *phDB = alloc_msihandle( &db->hdr );
+        msiobj_release( &db->hdr );
+    }
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
+{
+    HRESULT r = ERROR_FUNCTION_FAILED;
+    LPWSTR szwDBPath = NULL, szwPersist = NULL;
+    UINT len;
+
+    TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
+
+    if( szDBPath )
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szDBPath, -1, NULL, 0 );
+        szwDBPath = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwDBPath )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szDBPath, -1, szwDBPath, len );
+    }
+
+    if( HIWORD(szPersist) )
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szPersist, -1, NULL, 0 );
+        szwPersist = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwPersist )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szPersist, -1, szwPersist, len );
+    }
+    else
+        szwPersist = (LPWSTR) szPersist;
+
+    r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
+
+end:
+    if( szwPersist )
+        HeapFree( GetProcessHeap(), 0, szwPersist );
+    if( szwDBPath )
+        HeapFree( GetProcessHeap(), 0, szwDBPath );
+
+    return r;
+}
+
+UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct)
+{
+    UINT len, ret;
+    LPWSTR szwProd = NULL;
+
+    TRACE("%s %p\n",debugstr_a(szProduct), phProduct);
+
+    if( szProduct )
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+        szwProd = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+        if( szwProd )
+            MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProd, len );
+    }
+
+    ret = MsiOpenProductW( szwProd, phProduct );
+
+    if( szwProd )
+        HeapFree( GetProcessHeap(), 0, szwProd );
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenProductW(LPCWSTR szProduct, MSIHANDLE *phProduct)
+{
+    static const WCHAR szKey[] = {
+        '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','\\',
+        'U','n','i','n','s','t','a','l','l',0 };
+    static const WCHAR szLocalPackage[] = {
+        'L','o','c','a','l','P','a','c','k','a','g','e', 0
+    };
+    LPWSTR path = NULL;
+    UINT r;
+    HKEY hKeyProduct = NULL, hKeyUninstall = NULL;
+    DWORD count, type;
+
+    TRACE("%s %p\n",debugstr_w(szProduct), phProduct);
+
+    r = RegOpenKeyW( HKEY_LOCAL_MACHINE, szKey, &hKeyUninstall );
+    if( r != ERROR_SUCCESS )
+        return ERROR_UNKNOWN_PRODUCT;
+
+    r = RegOpenKeyW( hKeyUninstall, szProduct, &hKeyProduct );
+    if( r != ERROR_SUCCESS )
+    {
+        r = ERROR_UNKNOWN_PRODUCT;
+        goto end;
+    }
+
+    /* find the size of the path */
+    type = count = 0;
+    r = RegQueryValueExW( hKeyProduct, szLocalPackage,
+                          NULL, &type, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        r = ERROR_UNKNOWN_PRODUCT;
+        goto end;
+    }
+
+    /* now alloc and fetch the path of the database to open */
+    path = HeapAlloc( GetProcessHeap(), 0, count );
+    if( !path )
+        goto end;
+
+    r = RegQueryValueExW( hKeyProduct, szLocalPackage,
+                          NULL, &type, (LPBYTE) path, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        r = ERROR_UNKNOWN_PRODUCT;
+        goto end;
+    }
+
+    r = MsiOpenPackageW( path, phProduct );
+
+end:
+    if( path )
+        HeapFree( GetProcessHeap(), 0, path );
+    if( hKeyProduct )
+        RegCloseKey( hKeyProduct );
+    RegCloseKey( hKeyUninstall );
+
+    return r;
+}
+
+UINT WINAPI MsiAdvertiseProductA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, LPCSTR szTransforms, LANGID lgidLanguage)
+{
+    FIXME("%s %s %s 0x%08x\n",debugstr_a(szPackagePath), debugstr_a(szScriptfilePath), debugstr_a(szTransforms), lgidLanguage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiAdvertiseProductW(LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, LPCWSTR szTransforms, LANGID lgidLanguage)
+{
+    FIXME("%s %s %s 0x%08x\n",debugstr_w(szPackagePath), debugstr_w(szScriptfilePath), debugstr_w(szTransforms), lgidLanguage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiAdvertiseProductExA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, LPCSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions)
+{
+    FIXME("%s %s %s 0x%08x 0x%08lx 0x%08lx\n",
+       debugstr_a(szPackagePath), debugstr_a(szScriptfilePath), debugstr_a(szTransforms), lgidLanguage, dwPlatform, dwOptions);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiAdvertiseProductExW( LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, LPCWSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions)
+{
+    FIXME("%s %s %s 0x%08x 0x%08lx 0x%08lx\n",
+       debugstr_w(szPackagePath), debugstr_w(szScriptfilePath), debugstr_w(szTransforms), lgidLanguage, dwPlatform, dwOptions);
+    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 )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szPackagePath, -1, NULL, 0 );
+        szwPath = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwPath )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szPackagePath, -1, szwPath, len );
+    }
+
+    if( szCommandLine )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szCommandLine, -1, NULL, 0 );
+        szwCommand = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwCommand )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szCommandLine, -1, szwCommand, len );
+    }
+    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)
+{
+    MSIPACKAGE *package = NULL;
+    UINT rc = ERROR_SUCCESS; 
+    MSIHANDLE handle;
+
+    FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine));
+
+    rc = MsiVerifyPackageW(szPackagePath);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_OpenPackageW(szPackagePath,&package);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    handle = alloc_msihandle( &package->hdr );
+
+    rc = ACTION_DoTopLevelINSTALL(package, szPackagePath, szCommandLine);
+
+    MsiCloseHandle(handle);
+    msiobj_release( &package->hdr );
+    return rc;
+}
+
+UINT WINAPI MsiReinstallProductA(LPCSTR szProduct, DWORD dwReinstallMode)
+{
+       FIXME("%s 0x%08lx\n", debugstr_a(szProduct), dwReinstallMode);
+       return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiReinstallProductW(LPCWSTR szProduct, DWORD dwReinstallMode)
+{
+       FIXME("%s 0x%08lx\n", debugstr_w(szProduct), dwReinstallMode);
+       return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiApplyPatchA(LPCSTR szPatchPackage, LPCSTR szInstallPackage, INSTALLTYPE eInstallType, LPCSTR szCommandLine)
+{
+       FIXME("%s %s %d %s\n", debugstr_a(szPatchPackage), debugstr_a(szInstallPackage), eInstallType, debugstr_a(szCommandLine));
+       return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szInstallPackage, INSTALLTYPE eInstallType, LPCWSTR szCommandLine)
+{
+       FIXME("%s %s %d %s\n", debugstr_w(szPatchPackage), debugstr_w(szInstallPackage), eInstallType, debugstr_w(szCommandLine));
+       return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
+{
+    LPWSTR szwProduct = NULL;
+    UINT hr = ERROR_SUCCESS;
+
+    FIXME("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState);
+
+    if( szProduct )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+        szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwProduct )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+    }
+
+    hr = MsiConfigureProductW( szwProduct, iInstallLevel, eInstallState );
+
+end:
+    if( szwProduct )
+        HeapFree( GetProcessHeap(), 0, szwProduct );
+
+    return hr;
+}
+
+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)
+{
+    LPWSTR szwComponent = NULL, szwBuffer = NULL;
+    UINT hr = ERROR_INSTALL_FAILURE;
+
+    FIXME("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer));
+
+    if( szComponent )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
+        szwComponent = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwComponent )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
+    } else {
+      return ERROR_INVALID_PARAMETER;
+    }
+
+    {
+        szwBuffer = HeapAlloc( GetProcessHeap(), 0, GUID_SIZE * sizeof(WCHAR) );
+        if( !szwBuffer )        
+            goto end;
+    }
+
+    hr = MsiGetProductCodeW( szwComponent, szwBuffer );
+
+    if( ERROR_SUCCESS == hr )
+    {
+        WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, GUID_SIZE, NULL, NULL);
+    }
+
+end:
+    if( szwComponent )
+        HeapFree( GetProcessHeap(), 0, szwComponent );
+    if( szwBuffer )
+        HeapFree( GetProcessHeap(), 0, szwBuffer );
+
+    return hr;
+}
+
+UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer)
+{
+    FIXME("%s %s\n",debugstr_w(szComponent), debugstr_w(szBuffer));
+    if (NULL == szComponent) {
+      return ERROR_INVALID_PARAMETER;
+    }
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+
+
+
+UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, LPSTR szBuffer, DWORD *pcchValueBuf)
+{
+    LPWSTR szwProduct = NULL, szwAttribute = NULL, szwBuffer = NULL;
+    UINT hr = ERROR_INSTALL_FAILURE;
+
+    FIXME("%s %s %p %p\n",debugstr_a(szProduct), debugstr_a(szAttribute), szBuffer, pcchValueBuf);
+
+    if (NULL != szBuffer && NULL == pcchValueBuf) {
+      return ERROR_INVALID_PARAMETER;
+    }
+    if( szProduct )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+        szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwProduct )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+    } else {
+      return ERROR_INVALID_PARAMETER;
+    }
+    
+    if( szAttribute )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szAttribute, -1, NULL, 0 );
+        szwAttribute = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwAttribute )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szAttribute, -1, szwAttribute, len );
+    } else {
+      return ERROR_INVALID_PARAMETER;
+    }
+
+    if( szBuffer )
+    {
+        szwBuffer = HeapAlloc( GetProcessHeap(), 0, (*pcchValueBuf) * sizeof(WCHAR) );
+        if( !szwBuffer )        
+            goto end;
+    }
+
+    hr = MsiGetProductInfoW( szwProduct, szwAttribute, szwBuffer, pcchValueBuf );
+
+    if( ERROR_SUCCESS == hr )
+    {
+        WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, *pcchValueBuf, NULL, NULL);
+    }
+
+end:
+    if( szwProduct )
+        HeapFree( GetProcessHeap(), 0, szwProduct );
+    if( szwAttribute )
+        HeapFree( GetProcessHeap(), 0, szwAttribute );
+    if( szwBuffer )
+        HeapFree( GetProcessHeap(), 0, szwBuffer );
+
+    return hr;    
+}
+
+UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szBuffer, DWORD *pcchValueBuf)
+{
+    MSIHANDLE hProduct;
+    UINT hr;
+    
+    FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szAttribute), szBuffer, pcchValueBuf);
+
+    if (NULL != szBuffer && NULL == pcchValueBuf) {
+      return ERROR_INVALID_PARAMETER;
+    }
+    if (NULL == szProduct || NULL == szAttribute) {
+      return ERROR_INVALID_PARAMETER;
+    }
+
+    hr = MsiOpenProductW(szProduct, &hProduct);
+    if (ERROR_SUCCESS != hr) return hr;
+
+    hr = MsiGetPropertyW(hProduct, szAttribute, szBuffer, pcchValueBuf);
+    MsiCloseHandle(hProduct);
+    return hr;
+}
+
+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, DWORD attributes)
+{
+    LPWSTR szwLogFile = NULL;
+    UINT hr = ERROR_INSTALL_FAILURE;
+
+    FIXME("%08lx %s %08lx\n", dwLogMode, debugstr_a(szLogFile), attributes);
+
+    if( szLogFile )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szLogFile, -1, NULL, 0 );
+        szwLogFile = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        if( !szwLogFile )
+            goto end;
+        MultiByteToWideChar( CP_ACP, 0, szLogFile, -1, szwLogFile, len );
+    } else {
+      return ERROR_INVALID_PARAMETER;
+    }
+
+    hr = MsiEnableLogW( dwLogMode, szwLogFile, attributes );
+
+end:
+    if( szwLogFile )
+        HeapFree( GetProcessHeap(), 0, szwLogFile );
+
+    return hr;
+}
+
+UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, DWORD attributes)
+{
+    HANDLE the_file = INVALID_HANDLE_VALUE;
+    TRACE("%08lx %s %08lx\n", dwLogMode, debugstr_w(szLogFile), attributes);
+    strcpyW(gszLogFile,szLogFile);
+    if (!(attributes & INSTALLLOGATTRIBUTES_APPEND))
+        DeleteFileW(szLogFile);
+    the_file = CreateFileW(szLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS,
+                           FILE_ATTRIBUTE_NORMAL, NULL);
+    if (the_file != INVALID_HANDLE_VALUE)
+        CloseHandle(the_file);
+    else
+        ERR("Unable to enable log %s\n",debugstr_w(szLogFile));
+
+    return ERROR_SUCCESS;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct)
+{
+    FIXME("%s\n", debugstr_a(szProduct));
+    return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct)
+{
+    FIXME("%s\n", debugstr_w(szProduct));
+    return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd)
+{
+    INSTALLUILEVEL old = gUILevel;
+    HWND oldwnd = gUIhwnd;
+    TRACE("%08x %p\n", dwUILevel, phWnd);
+
+    gUILevel = dwUILevel;
+    if (phWnd)
+    {
+        gUIhwnd = *phWnd;
+        *phWnd = oldwnd;
+    }
+    return old;
+}
+
+INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA puiHandler, 
+                                  DWORD dwMessageFilter, LPVOID pvContext)
+{
+    INSTALLUI_HANDLERA prev = gUIHandler;
+
+    TRACE("(%p %lx %p)\n",puiHandler,dwMessageFilter,pvContext);
+    gUIHandler = puiHandler;
+    gUIFilter = dwMessageFilter;
+    gUIContext = pvContext;
+
+    return prev;
+}
+
+UINT WINAPI MsiLoadStringA(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int nBufferMax, DWORD e)
+{
+    /*FIXME("%08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e);*/
+    FIXME("%p %u %p %d %08lx\n",hInstance,uID,lpBuffer,nBufferMax,e);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiLoadStringW(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBufferMax, DWORD e)
+{
+    FIXME("%p %u %p %d %08lx\n",hInstance,uID,lpBuffer,nBufferMax,e);
+    /*
+    int ret =  LoadStringW(hInstance,uID,lpBuffer,nBufferMax);
+    FIXME("%s\n",debugstr_w(lpBuffer));
+    return ret;
+    */
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR szComponent, LPSTR lpPathBuf, DWORD *pcchBuf)
+{
+    FIXME("%s %p %08lx\n", debugstr_a(szComponent), lpPathBuf, *pcchBuf);
+    return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR szComponent, LPSTR lpPathBuf, DWORD *pcchBuf)
+{
+    FIXME("%s %p %08lx\n", debugstr_w(szComponent), lpPathBuf, *pcchBuf);
+    return INSTALLSTATE_UNKNOWN;
+}
+
+#include "winuser.h"
+
+UINT WINAPI MsiMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, WORD wLanguageId, DWORD f)
+{
+    FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_a(lpText),debugstr_a(lpCaption),uType,wLanguageId,f);
+    /*
+    MessageBoxExA(hWnd,lpText,lpCaption,uType|MB_OK,wLanguageId);
+    */
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, WORD wLanguageId, DWORD f)
+{
+    /*FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f);*/
+    FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_w(lpText),debugstr_w(lpCaption),uType,wLanguageId,f);
+    /*
+    MessageBoxExW(hWnd,lpText,lpCaption,uType|MB_OK,wLanguageId);
+    */
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
+{
+    DWORD r;
+    WCHAR szwGuid[GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+    
+    if (NULL == lpguid) {
+      return ERROR_INVALID_PARAMETER;
+    }
+    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);
+
+    if (NULL == lpguid) {
+      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 = 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 )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+        szwProduct = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+        if( szwProduct )
+            MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+        else
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    r = MsiEnumFeaturesW(szwProduct, 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 )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
+        szwComponent = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+        if( szwComponent )
+            MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
+        else
+            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 MsiEnumComponentQualifiersA(
+    LPSTR szComponent, DWORD iIndex, LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf, LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf)
+{
+FIXME("%s 0x%08lx %p %p %p %p\n", debugstr_a(szComponent), iIndex, lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, pcchApplicationDataBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnumComponentQualifiersW(
+    LPWSTR szComponent, DWORD iIndex, LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf, LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf)
+{
+FIXME("%s 0x%08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex, lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, pcchApplicationDataBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideAssemblyA(
+    LPCSTR szAssemblyName, LPCSTR szAppContext, DWORD dwInstallMode, DWORD dwAssemblyInfo, LPSTR lpPathBuf, DWORD* pcchPathBuf) 
+{
+    FIXME("%s %s 0x%08lx 0x%08lx %p %p\n", 
+       debugstr_a(szAssemblyName),  debugstr_a(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, pcchPathBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideAssemblyW(
+    LPCWSTR szAssemblyName, LPCWSTR szAppContext, DWORD dwInstallMode, DWORD dwAssemblyInfo, LPWSTR lpPathBuf, DWORD* pcchPathBuf) 
+{
+    FIXME("%s %s 0x%08lx 0x%08lx %p %p\n", 
+       debugstr_w(szAssemblyName),  debugstr_w(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, pcchPathBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+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 MsiGetFileSignatureInformationA(
+  LPCSTR szSignedObjectPath, DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData, DWORD* pcbHashData)
+{
+    FIXME("%s 0x%08lx %p %p %p\n", debugstr_a(szSignedObjectPath), dwFlags, ppcCertContext, pbHashData, pcbHashData);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+HRESULT WINAPI MsiGetFileSignatureInformationW(
+  LPCWSTR szSignedObjectPath, DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData, DWORD* pcbHashData)
+{
+    FIXME("%s 0x%08lx %p %p %p\n", debugstr_w(szSignedObjectPath), dwFlags, ppcCertContext, pbHashData, pcbHashData);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductPropertyA( MSIHANDLE hProduct, LPCSTR szProperty,
+                                    LPSTR szValue, DWORD *pccbValue )
+{
+    FIXME("%ld %s %p %p\n", hProduct, debugstr_a(szProperty), szValue, pccbValue);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductPropertyW( MSIHANDLE hProduct, LPCWSTR szProperty,
+                                    LPWSTR szValue, DWORD *pccbValue )
+{
+    FIXME("%ld %s %p %p\n", hProduct, debugstr_w(szProperty), szValue, pccbValue);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiVerifyPackageA( LPCSTR szPackage )
+{
+    UINT r, len;
+    LPWSTR szPack = NULL;
+
+    TRACE("%s\n", debugstr_a(szPackage) );
+
+    if( szPackage )
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szPackage, -1, NULL, 0 );
+        szPack = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szPack )
+            return ERROR_OUTOFMEMORY;
+        MultiByteToWideChar( CP_ACP, 0, szPackage, -1, szPack, len );
+    }
+
+    r = MsiVerifyPackageW( szPack );
+
+    HeapFree( GetProcessHeap(), 0, szPack );
+
+    return r;
+}
+
+UINT WINAPI MsiVerifyPackageW( LPCWSTR szPackage )
+{
+    MSIHANDLE handle;
+    UINT r;
+
+    TRACE("%s\n", debugstr_w(szPackage) );
+
+    r = MsiOpenDatabaseW( szPackage, MSIDBOPEN_READONLY, &handle );
+    MsiCloseHandle( handle );
+
+    return r;
+}
+
+INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR szProduct, LPCSTR szComponent,
+                                         LPSTR lpPathBuf, DWORD* pcchBuf)
+{
+    INSTALLSTATE rc;
+    UINT len;
+    LPWSTR szwProduct= NULL;
+    LPWSTR szwComponent= NULL;
+    LPWSTR lpwPathBuf= NULL;
+
+    if( szProduct)
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+        szwProduct= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwProduct)
+            return ERROR_OUTOFMEMORY;
+        MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+    }
+
+    if( szComponent)
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szComponent, -1, NULL, 0 );
+        szwComponent= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwComponent)
+            return ERROR_OUTOFMEMORY;
+        MultiByteToWideChar( CP_ACP, 0, szComponent, -1, szwComponent, len );
+    }
+
+    if (pcchBuf && *pcchBuf > 0)
+        lpwPathBuf = HeapAlloc( GetProcessHeap(), 0, *pcchBuf * sizeof(WCHAR));
+    else
+        lpwPathBuf = NULL;
+
+    rc = MsiGetComponentPathW(szwProduct, szwComponent, lpwPathBuf, pcchBuf);
+
+    HeapFree( GetProcessHeap(), 0, szwProduct);
+    HeapFree( GetProcessHeap(), 0, szwComponent);
+    if (lpwPathBuf)
+    {
+        WideCharToMultiByte(CP_ACP, 0, lpwPathBuf, *pcchBuf,
+                            lpPathBuf, GUID_SIZE, NULL, NULL);
+        HeapFree( GetProcessHeap(), 0, lpwPathBuf);
+    }
+
+    return rc;
+}
+
+INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR szProduct, LPCWSTR szComponent,
+                                         LPWSTR lpPathBuf, DWORD* pcchBuf)
+{
+    FIXME("STUB: (%s %s %p %p)\n", debugstr_w(szProduct),
+           debugstr_w(szComponent), lpPathBuf, pcchBuf);
+
+    return INSTALLSTATE_UNKNOWN;
+}
+
+INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR szProduct, LPCSTR szFeature)
+{
+    INSTALLSTATE rc;
+    UINT len;
+    LPWSTR szwProduct= NULL;
+    LPWSTR szwFeature= NULL;
+
+    if( szProduct)
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szProduct, -1, NULL, 0 );
+        szwProduct= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwProduct)
+            return ERROR_OUTOFMEMORY;
+        MultiByteToWideChar( CP_ACP, 0, szProduct, -1, szwProduct, len );
+    }
+
+    if( szFeature)
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szFeature, -1, NULL, 0 );
+        szwFeature= HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwFeature)
+            return ERROR_OUTOFMEMORY;
+        MultiByteToWideChar( CP_ACP, 0, szFeature, -1, szwFeature, len );
+    }
+
+    rc = MsiQueryFeatureStateW(szwProduct, szwFeature);
+
+    HeapFree( GetProcessHeap(), 0, szwProduct);
+    HeapFree( GetProcessHeap(), 0, szwFeature);
+
+    return rc;
+}
+
+INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR szProduct, LPCWSTR szFeature)
+{
+    FIXME("STUB: (%s %s)\n", debugstr_w(szProduct), debugstr_w(szFeature));
+    return INSTALLSTATE_UNKNOWN;
+}
+
+UINT WINAPI MsiGetFileVersionA(LPCSTR szFilePath, LPSTR lpVersionBuf, DWORD* pcchVersionBuf, LPSTR lpLangBuf, DWORD* pcchLangBuf)
+{
+    UINT len;
+    UINT ret;
+    LPWSTR szwFilePath = NULL;
+    LPWSTR lpwVersionBuff = NULL;
+    LPWSTR lpwLangBuff = NULL;
+    
+    if(szFilePath) {
+        len = MultiByteToWideChar( CP_ACP, 0, szFilePath, -1, NULL, 0 );
+        szwFilePath = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwFilePath)
+            return ERROR_OUTOFMEMORY;
+        MultiByteToWideChar( CP_ACP, 0, szFilePath, -1, szwFilePath, len );
+    }
+    
+    if(lpVersionBuf && pcchVersionBuf && *pcchVersionBuf) {
+        lpwVersionBuff = HeapAlloc(GetProcessHeap(), 0, *pcchVersionBuf * sizeof(WCHAR));
+        if( !lpwVersionBuff)
+            return ERROR_OUTOFMEMORY;
+    }
+
+    if(lpLangBuf && pcchLangBuf && *pcchLangBuf) {
+        lpwLangBuff = HeapAlloc(GetProcessHeap(), 0, *pcchVersionBuf * sizeof(WCHAR));
+        if( !lpwLangBuff)
+            return ERROR_OUTOFMEMORY;
+    }
+        
+    ret = MsiGetFileVersionW(szwFilePath, lpwVersionBuff, pcchVersionBuf, lpwLangBuff, pcchLangBuf);
+    
+    if(lpwVersionBuff)
+        WideCharToMultiByte(CP_ACP, 0, lpwVersionBuff, -1, lpVersionBuf, *pcchVersionBuf, NULL, NULL);
+    if(lpwLangBuff)
+        WideCharToMultiByte(CP_ACP, 0, lpwLangBuff, -1, lpLangBuf, *pcchLangBuf, NULL, NULL);
+    
+    if(szwFilePath) HeapFree(GetProcessHeap(), 0, szwFilePath);
+    if(lpwVersionBuff) HeapFree(GetProcessHeap(), 0, lpwVersionBuff);
+    if(lpwLangBuff) HeapFree(GetProcessHeap(), 0, lpwLangBuff);
+    
+    return ret;
+}
+
+UINT WINAPI MsiGetFileVersionW(LPCWSTR szFilePath, LPWSTR lpVersionBuf, DWORD* pcchVersionBuf, LPWSTR lpLangBuf, DWORD* pcchLangBuf)
+{
+    static const WCHAR szVersionResource[] = {'\\',0};
+    static const WCHAR szVersionFormat[] = {'%','d','.','%','d','.','%','d','.','%','d',0};
+    static const WCHAR szLangFormat[] = {'%','d',0};
+    UINT ret = 0;
+    DWORD dwVerLen;
+    LPVOID lpVer = NULL;
+    VS_FIXEDFILEINFO *ffi;
+    UINT puLen;
+    WCHAR tmp[32];
+
+    TRACE("(%s,%p,%ld,%p,%ld)\n", debugstr_w(szFilePath),
+          lpVersionBuf, pcchVersionBuf?*pcchVersionBuf:0,
+          lpLangBuf, pcchLangBuf?*pcchLangBuf:0);
+
+    dwVerLen = GetFileVersionInfoSizeW(szFilePath, NULL);
+    if(!dwVerLen)
+        return GetLastError();
+
+    lpVer = HeapAlloc(GetProcessHeap(), 0, dwVerLen);
+    if(!lpVer) {
+        ret = ERROR_OUTOFMEMORY;
+        goto end;
+    }
+
+    if(!GetFileVersionInfoW(szFilePath, 0, dwVerLen, lpVer)) {
+        ret = GetLastError();
+        goto end;
+    }
+    if(lpVersionBuf && pcchVersionBuf && *pcchVersionBuf) {
+        if(VerQueryValueW(lpVer, szVersionResource, (LPVOID*)&ffi, &puLen) && puLen > 0) {
+            wsprintfW(tmp, szVersionFormat, HIWORD(ffi->dwFileVersionMS), LOWORD(ffi->dwFileVersionMS), HIWORD(ffi->dwFileVersionLS), LOWORD(ffi->dwFileVersionLS));
+            lstrcpynW(lpVersionBuf, tmp, *pcchVersionBuf);
+            *pcchVersionBuf = strlenW(lpVersionBuf);
+        }
+        else {
+            *lpVersionBuf = 0;
+            *pcchVersionBuf = 0;
+        }
+    }
+
+    if(lpLangBuf && pcchLangBuf && *pcchLangBuf) {
+        DWORD lang = GetUserDefaultLangID();
+        FIXME("Retrieve language from file\n");
+        wsprintfW(tmp, szLangFormat, lang);
+        lstrcpynW(lpLangBuf, tmp, *pcchLangBuf);
+        *pcchLangBuf = strlenW(lpLangBuf);
+    }
+
+end:
+    if(lpVer) HeapFree(GetProcessHeap(), 0, lpVer);
+    return ret;
+}
+
+
+/******************************************************************
+ *             DllMain
+ *
+ * @todo: maybe we can check here if MsiServer service is declared no ?
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
+  if (fdwReason == DLL_PROCESS_ATTACH) {
+    DisableThreadLibraryCalls(hinstDLL);
+    /*
+     * UI Initialization
+     */
+    gUILevel = INSTALLUILEVEL_BASIC;
+    gUIhwnd = 0;
+    gUIHandler = NULL;
+    gUIFilter = 0;
+    gUIContext = NULL;
+    gszLogFile[0]=0;
+    /* FIXME: Initialisation */
+  } else if (fdwReason == DLL_PROCESS_DETACH) {
+    /* FIXME: Cleanup */
+  }
+  /*
+  static const WCHAR szMSIServerSvc[] = { 'M','S','I','S','e','r','v','e','r',0 };
+  static const WCHAR szNull[] = { 0 };
+  if (!strcmpW(lpServiceName, szMSIServerSvc)) {
+    hKey = CreateServiceW(hSCManager, 
+                         szMSIServerSvc, 
+                         szMSIServerSvc, 
+                         SC_MANAGER_ALL_ACCESS, 
+                         SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS, 
+                         SERVICE_AUTO_START, 
+                         SERVICE_ERROR_IGNORE,
+                         szNull, 
+                         NULL, 
+                         NULL,
+                         NULL,
+                         NULL,
+                         szNull);
+  */
+  return TRUE;
+}
+
+typedef struct {
+  /* IUnknown fields */
+  IClassFactoryVtbl          *lpVtbl;
+  DWORD                       ref;
+} IClassFactoryImpl;
+
+static HRESULT WINAPI MsiCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
+  IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+  FIXME("(%p, %s, %p): stub\n",This,debugstr_guid(riid),ppobj);
+  return E_NOINTERFACE;
+}
+
+static ULONG WINAPI MsiCF_AddRef(LPCLASSFACTORY iface) {
+  IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+  return ++(This->ref);
+}
+
+static ULONG WINAPI MsiCF_Release(LPCLASSFACTORY iface) {
+  IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+  /* static class, won't be  freed */
+  return --(This->ref);
+}
+
+static HRESULT WINAPI MsiCF_CreateInstance(LPCLASSFACTORY iface, LPUNKNOWN pOuter, REFIID riid, LPVOID *ppobj) {
+  IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+  FIXME ("(%p, %p, %s, %p): to implement\n", This, pOuter, debugstr_guid(riid), ppobj);
+  return 0;
+}
+
+static HRESULT WINAPI MsiCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
+  IClassFactoryImpl *This = (IClassFactoryImpl *)iface;
+  FIXME("(%p, %d): stub\n", This, dolock);
+  return S_OK;
+}
+
+static IClassFactoryVtbl MsiCF_Vtbl = {
+  MsiCF_QueryInterface,
+  MsiCF_AddRef,
+  MsiCF_Release,
+  MsiCF_CreateInstance,
+  MsiCF_LockServer
+};
+
+static IClassFactoryImpl Msi_CF = {&MsiCF_Vtbl, 1 };
+
+/******************************************************************
+ *             DllGetClassObject (MSI.@)
+ */
+HRESULT WINAPI MSI_DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) {
+  FIXME("(%s, %s, %p): almost a stub.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+  if (IsEqualCLSID (rclsid, &CLSID_IMsiServer)) {
+    *ppv = (LPVOID) &Msi_CF;
+    IClassFactory_AddRef((IClassFactory*)*ppv);
+    return S_OK;
+  } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerMessage)) {
+    *ppv = (LPVOID) &Msi_CF;
+    IClassFactory_AddRef((IClassFactory*)*ppv);
+    return S_OK;
+  } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerX1)) {
+    *ppv = (LPVOID) &Msi_CF;
+    IClassFactory_AddRef((IClassFactory*)*ppv);
+    return S_OK;
+  } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerX2)) {
+    *ppv = (LPVOID) &Msi_CF;
+    IClassFactory_AddRef((IClassFactory*)*ppv);
+    return S_OK;
+  } else if (IsEqualCLSID (rclsid, &CLSID_IMsiServerX3)) {
+    *ppv = (LPVOID) &Msi_CF;
+    IClassFactory_AddRef((IClassFactory*)*ppv);
+    return S_OK;
+  }
+  WARN("(%s, %s, %p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
+  return CLASS_E_CLASSNOTAVAILABLE;
+}
+
+/******************************************************************
+ *             DllGetVersion (MSI.@)
+ */
+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;
+}
+
+/******************************************************************
+ *             DllCanUnloadNow (MSI.@)
+ */
+BOOL WINAPI MSI_DllCanUnloadNow(void)
+{
+  return S_FALSE;
+}
+
+UINT WINAPI MsiEnumRelatedProductsA (LPCSTR lpUpgradeCode, DWORD dwReserved,
+                                    DWORD iProductIndex, LPSTR lpProductBuf)
+{
+    FIXME("STUB: (%s, %li %li %s)\n",lpUpgradeCode, dwReserved, iProductIndex,
+          lpProductBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/reactos/lib/msi/msi.spec b/reactos/lib/msi/msi.spec
new file mode 100644 (file)
index 0000000..0310438
--- /dev/null
@@ -0,0 +1,236 @@
+1 stdcall -private DllCanUnloadNow() MSI_DllCanUnloadNow
+2 stdcall -private DllGetClassObject(ptr ptr ptr) MSI_DllGetClassObject
+3 stdcall -private DllRegisterServer() MSI_DllRegisterServer
+4 stdcall -private DllUnregisterServer() MSI_DllUnregisterServer
+5 stdcall MsiAdvertiseProductA(str str str long)
+6 stdcall MsiAdvertiseProductW(wstr wstr wstr long)
+7 stdcall MsiCloseAllHandles()
+8 stdcall MsiCloseHandle(long)
+9 stub MsiCollectUserInfoA
+10 stub MsiCollectUserInfoW
+11 stub MsiConfigureFeatureA
+12 stub MsiConfigureFeatureFromDescriptorA
+13 stub MsiConfigureFeatureFromDescriptorW
+14 stub MsiConfigureFeatureW
+15 stdcall MsiConfigureProductA(str long long)
+16 stdcall MsiConfigureProductW(wstr long long)
+17 stdcall MsiCreateRecord(long)
+18 stdcall MsiDatabaseApplyTransformA(long str long)
+19 stdcall MsiDatabaseApplyTransformW(long wstr long)
+20 stdcall MsiDatabaseCommit(long)
+21 stub MsiDatabaseExportA
+22 stub MsiDatabaseExportW
+23 stdcall MsiDatabaseGenerateTransformA(long long str long long)
+24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long)
+25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr)
+26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr)
+27 stdcall MsiDatabaseImportA(str str)
+28 stdcall MsiDatabaseImportW(wstr wstr)
+29 stub MsiDatabaseMergeA
+30 stub MsiDatabaseMergeW
+31 stdcall MsiDatabaseOpenViewA(long str ptr)
+32 stdcall MsiDatabaseOpenViewW(long wstr ptr)
+33 stdcall MsiDoActionA(long str)
+34 stdcall MsiDoActionW(long wstr)
+35 stub MsiEnableUIPreview
+36 stdcall MsiEnumClientsA(str long ptr)
+37 stdcall MsiEnumClientsW(wstr long ptr)
+38 stdcall MsiEnumComponentQualifiersA(str long str ptr str ptr)
+39 stdcall MsiEnumComponentQualifiersW(wstr long wstr ptr wstr ptr)
+40 stdcall MsiEnumComponentsA(long ptr)
+41 stdcall MsiEnumComponentsW(long ptr)
+42 stdcall MsiEnumFeaturesA(str long ptr ptr)
+43 stdcall MsiEnumFeaturesW(wstr long ptr ptr)
+44 stdcall MsiEnumProductsA(long ptr)
+45 stdcall MsiEnumProductsW(long ptr)
+46 stdcall MsiEvaluateConditionA(long str)
+47 stdcall MsiEvaluateConditionW(long wstr)
+48 stub MsiGetLastErrorRecord
+49 stdcall MsiGetActiveDatabase(long)
+50 stdcall MsiGetComponentStateA(long str ptr ptr)
+51 stdcall MsiGetComponentStateW(long wstr ptr ptr)
+52 stub MsiGetDatabaseState
+53 stub MsiGetFeatureCostA
+54 stub MsiGetFeatureCostW
+55 stub MsiGetFeatureInfoA
+56 stub MsiGetFeatureInfoW
+57 stdcall MsiGetFeatureStateA(long str ptr ptr)
+58 stdcall MsiGetFeatureStateW(long wstr ptr ptr)
+59 stub MsiGetFeatureUsageA
+60 stub MsiGetFeatureUsageW
+61 stub MsiGetFeatureValidStatesA
+62 stub MsiGetFeatureValidStatesW
+63 stub MsiGetLanguage
+64 stdcall MsiGetMode(long long)
+65 stdcall MsiGetProductCodeA(str str)
+66 stdcall MsiGetProductCodeW(wstr wstr)
+67 stdcall MsiGetProductInfoA(str str str long)
+68 stub MsiGetProductInfoFromScriptA
+69 stub MsiGetProductInfoFromScriptW
+70 stdcall MsiGetProductInfoW(wstr wstr wstr long)
+71 stdcall MsiGetProductPropertyA(long str ptr ptr)
+72 stdcall MsiGetProductPropertyW(long wstr ptr ptr)
+73 stdcall MsiGetPropertyA(ptr str str ptr)
+74 stdcall MsiGetPropertyW(ptr wstr wstr ptr)
+75 stdcall MsiGetSourcePathA(long str ptr ptr)
+76 stdcall MsiGetSourcePathW(long wstr ptr ptr)
+77 stdcall MsiGetSummaryInformationA(long str long ptr)
+78 stdcall MsiGetSummaryInformationW(long wstr long ptr)
+79 stdcall MsiGetTargetPathA(long str ptr ptr)
+80 stdcall MsiGetTargetPathW(long wstr ptr ptr)
+81 stub MsiGetUserInfoA
+82 stub MsiGetUserInfoW
+83 stub MsiInstallMissingComponentA
+84 stub MsiInstallMissingComponentW
+85 stub MsiInstallMissingFileA
+86 stub MsiInstallMissingFileW
+87 stdcall MsiInstallProductA(str str)
+88 stdcall MsiInstallProductW(wstr wstr)
+89 stdcall MsiLocateComponentA(str ptr long)
+90 stdcall MsiLocateComponentW(wstr ptr long)
+91 stdcall MsiOpenDatabaseA(str str ptr)
+92 stdcall MsiOpenDatabaseW(wstr wstr ptr)
+93 stdcall MsiOpenPackageA(str ptr)
+94 stdcall MsiOpenPackageW(wstr ptr)
+95 stdcall MsiOpenProductA(str ptr)
+96 stdcall MsiOpenProductW(wstr ptr)
+97 stub MsiPreviewBillboardA
+98 stub MsiPreviewBillboardW
+99 stub MsiPreviewDialogA
+100 stub MsiPreviewDialogW
+101 stub MsiProcessAdvertiseScriptA
+102 stub MsiProcessAdvertiseScriptW
+103 stdcall MsiProcessMessage(long long long)
+104 stub MsiProvideComponentA
+105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr)
+106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr)
+107 stub MsiProvideComponentW
+108 stub MsiProvideQualifiedComponentA
+109 stub MsiProvideQualifiedComponentW
+110 stdcall MsiQueryFeatureStateA(str str)
+111 stdcall MsiQueryFeatureStateW(wstr wstr)
+112 stdcall MsiQueryProductStateA(str)
+113 stdcall MsiQueryProductStateW(wstr)
+114 stdcall MsiRecordDataSize(long long)
+115 stdcall MsiRecordGetFieldCount(long)
+116 stdcall MsiRecordGetInteger(long long)
+117 stdcall MsiRecordGetStringA(long long ptr ptr)
+118 stdcall MsiRecordGetStringW(long long ptr ptr)
+119 stdcall MsiRecordIsNull(long long)
+120 stdcall MsiRecordReadStream(long long ptr ptr)
+121 stdcall MsiRecordSetInteger(long long long)
+122 stdcall MsiRecordSetStreamA(long long str)
+123 stdcall MsiRecordSetStreamW(long long wstr)
+124 stdcall MsiRecordSetStringA(long long str)
+125 stdcall MsiRecordSetStringW(long long wstr)
+126 stub MsiReinstallFeatureA
+127 stub MsiReinstallFeatureFromDescriptorA
+128 stub MsiReinstallFeatureFromDescriptorW
+129 stub MsiReinstallFeatureW
+130 stdcall MsiReinstallProductA(str long)
+131 stdcall MsiReinstallProductW(wstr long)
+132 stub MsiSequenceA
+133 stub MsiSequenceW
+134 stub MsiSetComponentStateA
+135 stub MsiSetComponentStateW
+136 stdcall MsiSetExternalUIA(ptr long ptr)
+137 stub MsiSetExternalUIW
+138 stdcall MsiSetFeatureStateA(long str long)
+139 stdcall MsiSetFeatureStateW(long wstr long)
+140 stub MsiSetInstallLevel
+141 stdcall MsiSetInternalUI(long ptr)
+142 stub MsiVerifyDiskSpace
+143 stub MsiSetMode
+144 stdcall MsiSetPropertyA(long str str)
+145 stdcall MsiSetPropertyW(long wstr wstr)
+146 stdcall MsiSetTargetPathA(long str str)
+147 stdcall MsiSetTargetPathW(long wstr wstr)
+148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr)
+149 stdcall MsiSummaryInfoGetPropertyCount(long ptr)
+150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr)
+151 stub MsiSummaryInfoPersist
+152 stub MsiSummaryInfoSetPropertyA
+153 stub MsiSummaryInfoSetPropertyW
+154 stub MsiUseFeatureA
+155 stub MsiUseFeatureW
+156 stdcall MsiVerifyPackageA(str)
+157 stdcall MsiVerifyPackageW(wstr)
+158 stdcall MsiViewClose(long)
+159 stdcall MsiViewExecute(long long)
+160 stdcall MsiViewFetch(long ptr)
+161 stub MsiViewGetErrorA
+162 stub MsiViewGetErrorW
+163 stdcall MsiViewModify(long long long)
+164 stdcall MsiDatabaseIsTablePersistentA(long str)
+165 stdcall MsiDatabaseIsTablePersistentW(long wstr)
+166 stdcall MsiViewGetColumnInfo(long long ptr)
+167 stdcall MsiRecordClearData(long)
+168 stdcall MsiEnableLogA(long str long)
+169 stdcall MsiEnableLogW(long wstr long)
+170 stdcall MsiFormatRecordA(long long ptr ptr)
+171 stdcall MsiFormatRecordW(long long ptr ptr)
+172 stdcall MsiGetComponentPathA(str str ptr ptr)
+173 stdcall MsiGetComponentPathW(wstr wstr ptr ptr)
+174 stdcall MsiApplyPatchA(str str long str)
+175 stdcall MsiApplyPatchW(wstr wstr long wstr)
+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 stdcall MsiGetFileVersionA(str str ptr str ptr)
+195 stdcall MsiGetFileVersionW(wstr wstr ptr wstr ptr)
+196 stdcall MsiLoadStringA(long long long long long)
+197 stdcall MsiLoadStringW(long long long long long)
+198 stdcall MsiMessageBoxA(long long long long long long)
+199 stdcall MsiMessageBoxW(long long long long long long)
+200 stub MsiDecomposeDescriptorA
+201 stub MsiDecomposeDescriptorW
+202 stub MsiProvideQualifiedComponentExA
+203 stub MsiProvideQualifiedComponentExW
+204 stdcall MsiEnumRelatedProductsA(str long long str)
+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
+218 stub MsiGetFileHashA
+219 stub MsiGetFileHashW
+220 stub MsiEnumComponentCostsA
+221 stub MsiEnumComponentCostsW
+222 stub MsiCreateAndVerifyInstallerDirectory
+223 stdcall MsiGetFileSignatureInformationA(str long ptr ptr ptr)
+224 stdcall MsiGetFileSignatureInformationW(wstr long ptr ptr ptr)
+225 stdcall MsiProvideAssemblyA(str str long long str ptr)
+226 stdcall MsiProvideAssemblyW(wstr wstr long long wstr ptr)
+227 stdcall MsiAdvertiseProductExA(str str str long long long)
+228 stdcall MsiAdvertiseProductExW(wstr wstr wstr long long long)
+229 stub MsiNotifySidChangeA
+230 stub MsiNotifySidChangeW
+231 stdcall MsiOpenPackageExA(str long ptr)
+232 stdcall MsiOpenPackageExW(wstr long ptr)
+233 stub MsiDeleteUserDataA
+234 stub MsiDeleteUserDataW
+235 stub Migrate10CachedPackagesA
+236 stub Migrate10CachedPackagesW
diff --git a/reactos/lib/msi/msipriv.h b/reactos/lib/msi/msipriv.h
new file mode 100644 (file)
index 0000000..e8a606c
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+
+#define MSI_DATASIZEMASK 0x00ff
+#define MSITYPE_VALID    0x0100
+#define MSITYPE_STRING   0x0800
+#define MSITYPE_NULLABLE 0x1000
+#define MSITYPE_KEY      0x2000
+
+#define MSITYPE_BINARY 0x8900
+
+struct tagMSITABLE;
+typedef struct tagMSITABLE MSITABLE;
+
+struct string_table;
+typedef struct string_table string_table;
+
+struct tagMSIOBJECTHDR;
+typedef struct tagMSIOBJECTHDR MSIOBJECTHDR;
+
+typedef VOID (*msihandledestructor)( MSIOBJECTHDR * );
+
+struct tagMSIOBJECTHDR
+{
+    UINT magic;
+    UINT type;
+    UINT refcount;
+    msihandledestructor destructor;
+    struct tagMSIOBJECTHDR *next;
+    struct tagMSIOBJECTHDR *prev;
+};
+
+typedef struct tagMSIDATABASE
+{
+    MSIOBJECTHDR hdr;
+    IStorage *storage;
+    string_table *strings;
+    LPWSTR mode;
+    MSITABLE *first_table, *last_table;
+} MSIDATABASE;
+
+typedef struct tagMSIVIEW MSIVIEW;
+
+typedef struct tagMSIQUERY
+{
+    MSIOBJECTHDR hdr;
+    MSIVIEW *view;
+    UINT row;
+    MSIDATABASE *db;
+} MSIQUERY;
+
+/* maybe we can use a Variant instead of doing it ourselves? */
+typedef struct tagMSIFIELD
+{
+    UINT type;
+    union
+    {
+        INT iVal;
+        LPWSTR szwVal;
+        IStream *stream;
+    } u;
+} MSIFIELD;
+
+typedef struct tagMSIRECORD
+{
+    MSIOBJECTHDR hdr;
+    UINT count;       /* as passed to MsiCreateRecord */
+    MSIFIELD fields[1]; /* nb. array size is count+1 */
+} MSIRECORD;
+
+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 );
+
+    /*
+     * fetch_int - reads one integer from {row,col} in the table
+     *
+     *  This function is similar to fetch_int, except fetches a
+     *    stream instead of an integer.
+     */
+    UINT (*fetch_stream)( struct tagMSIVIEW *, UINT row, UINT col, IStream **stm );
+
+    /*
+     * get_int - sets one integer at {row,col} in the table
+     *
+     *  Similar semantics to fetch_int
+     */
+    UINT (*set_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT val );
+
+    /*
+     * Inserts a new, blank row into the database
+     *  *row receives the number of the new row
+     */
+    UINT (*insert_row)( struct tagMSIVIEW *, UINT *row );
+
+    /*
+     * execute - loads the underlying data into memory so it can be read
+     */
+    UINT (*execute)( struct tagMSIVIEW *, MSIRECORD * );
+
+    /*
+     * 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 tagMSISUMMARYINFO
+{
+    MSIOBJECTHDR hdr;
+    IPropertyStorage *propstg;
+} MSISUMMARYINFO;
+
+struct tagMSIVIEW
+{
+    MSIOBJECTHDR hdr;
+    MSIVIEWOPS   *ops;
+};
+
+typedef struct tagMSIPACKAGE
+{
+    MSIOBJECTHDR hdr;
+    MSIDATABASE *db;
+    struct tagMSIFEATURE *features;
+    UINT loaded_features;
+    struct tagMSIFOLDER  *folders;
+    UINT loaded_folders;
+    struct tagMSICOMPONENT *components;
+    UINT loaded_components;
+    struct tagMSIFILE *files;
+    UINT loaded_files;
+} MSIPACKAGE;
+
+#define MSIHANDLETYPE_ANY 0
+#define MSIHANDLETYPE_DATABASE 1
+#define MSIHANDLETYPE_SUMMARYINFO 2
+#define MSIHANDLETYPE_VIEW 3
+#define MSIHANDLETYPE_RECORD 4
+#define MSIHANDLETYPE_PACKAGE 5
+
+#define MSI_MAJORVERSION 2
+#define MSI_MINORVERSION 0
+#define MSI_BUILDNUMBER 2600
+
+#define GUID_SIZE 39
+
+#define MSIHANDLE_MAGIC 0x4d434923
+#define MSIMAXHANDLES 0x80
+
+#define MSISUMINFO_OFFSET 0x30LL
+
+DEFINE_GUID(CLSID_IMsiServer,   0x000C101C,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX1, 0x000C103E,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX2, 0x000C1090,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+DEFINE_GUID(CLSID_IMsiServerX3, 0x000C1094,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+DEFINE_GUID(CLSID_IMsiServerMessage, 0x000C101D,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
+
+
+/* handle functions */
+extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type);
+extern MSIHANDLE alloc_msihandle( MSIOBJECTHDR * );
+extern void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy );
+extern void msiobj_addref(MSIOBJECTHDR *);
+extern int msiobj_release(MSIOBJECTHDR *);
+extern MSIHANDLE msiobj_findhandle( MSIOBJECTHDR *hdr );
+
+/* 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 load_string_table( MSIDATABASE *db );
+extern UINT MSI_CommitTables( MSIDATABASE *db );
+extern HRESULT init_string_table( IStorage *stg );
+
+
+/* string table functions */
+extern BOOL msi_addstring( string_table *st, int string_no, const CHAR *data, int len, UINT refcount );
+extern BOOL msi_addstringW( string_table *st, int string_no, const WCHAR *data, int len, UINT refcount );
+extern UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz );
+extern UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz );
+
+extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid);
+extern UINT msi_string2idW( string_table *st, LPCWSTR buffer, UINT *id );
+extern UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id );
+extern string_table *msi_init_stringtable( int entries, UINT codepage );
+extern VOID msi_destroy_stringtable( string_table *st );
+extern UINT msi_string_count( string_table *st );
+extern UINT msi_id_refcount( string_table *st, UINT i );
+extern UINT msi_string_totalsize( string_table *st, UINT *last );
+extern UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res );
+extern const WCHAR *msi_string_lookup_id( string_table *st, UINT id );
+extern UINT msi_string_get_codepage( string_table *st );
+
+
+extern UINT VIEW_find_column( MSIVIEW *view, LPWSTR name, UINT *n );
+
+extern BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name );
+
+extern UINT read_raw_stream_data( MSIDATABASE*, LPCWSTR stname,
+                              USHORT **pdata, UINT *psz );
+extern UINT ACTION_DoTopLevelINSTALL( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern void ACTION_remove_tracked_tempfiles( MSIPACKAGE* );
+
+/* record internals */
+extern UINT MSI_RecordSetIStream( MSIRECORD *, unsigned int, IStream *);
+extern const WCHAR *MSI_RecordGetString( MSIRECORD *, unsigned int );
+extern MSIRECORD *MSI_CreateRecord( unsigned int );
+extern UINT MSI_RecordSetInteger( MSIRECORD *, unsigned int, int );
+extern UINT MSI_RecordSetStringW( MSIRECORD *, unsigned int, LPCWSTR );
+extern BOOL MSI_RecordIsNull( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordGetStringW( MSIRECORD * , unsigned int, LPWSTR, DWORD *);
+extern UINT MSI_RecordGetStringA( MSIRECORD *, unsigned int, LPSTR, DWORD *);
+extern int MSI_RecordGetInteger( MSIRECORD *, unsigned int );
+extern UINT MSI_RecordReadStream( MSIRECORD *, unsigned int, char *, DWORD *);
+extern unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec );
+
+/* stream internals */
+extern UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm );
+extern UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm );
+extern void enum_stream_names( IStorage *stg );
+
+/* database internals */
+extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** );
+extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** );
+
+/* view internals */
+extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * );
+extern UINT MSI_ViewFetch( MSIQUERY*, MSIRECORD ** );
+extern UINT MSI_ViewClose( MSIQUERY* );
+
+/* package internals */
+extern UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE ** );
+extern UINT MSI_SetTargetPathW( MSIPACKAGE *, LPCWSTR, LPCWSTR);
+extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern INT MSI_ProcessMessage( MSIPACKAGE *, INSTALLMESSAGE, MSIRECORD* );
+extern UINT MSI_GetPropertyW( MSIPACKAGE *, LPCWSTR, LPWSTR, DWORD*);
+extern MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *, LPCWSTR );
+extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR );
+extern UINT MSI_GetComponentStateW(MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE *);
+extern UINT MSI_GetFeatureStateW(MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE *);
+
+/* registry data encoding/decoding functions */
+BOOL unsquash_guid(LPCWSTR in, LPWSTR out);
+BOOL squash_guid(LPCWSTR in, LPWSTR out);
+BOOL encode_base85_guid(GUID *,LPWSTR);
+BOOL decode_base85_guid(LPCWSTR,GUID*);
+
+/* UI globals */
+extern INSTALLUILEVEL gUILevel;
+extern HWND gUIhwnd;
+extern INSTALLUI_HANDLERA gUIHandler;
+extern DWORD gUIFilter;
+extern LPVOID gUIContext;
+extern WCHAR gszLogFile[MAX_PATH];
+
+#endif /* __WINE_MSI_PRIVATE__ */
diff --git a/reactos/lib/msi/msiquery.c b/reactos/lib/msi/msiquery.c
new file mode 100644 (file)
index 0000000..c30cd26
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "wine/unicode.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);
+
+#if 0
+typedef struct tagMSIQUERY
+{
+    MSIOBJECTHDR hdr;
+    MSIVIEW *view;
+    UINT row;
+    MSIDATABASE *db;
+} MSIQUERY;
+#endif
+
+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( MSIOBJECTHDR *arg )
+{
+    MSIQUERY *query = (MSIQUERY*) arg;
+
+    if( query->view && query->view->ops->delete )
+        query->view->ops->delete( query->view );
+    msiobj_release( &query->db->hdr );
+}
+
+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;
+    LPWSTR szwQuery;
+
+    TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
+
+    if( szQuery )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szQuery, -1, NULL, 0 );
+        szwQuery = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwQuery )
+            return ERROR_FUNCTION_FAILED;
+        MultiByteToWideChar( CP_ACP, 0, szQuery, -1, szwQuery, len );
+    }
+    else
+        szwQuery = NULL;
+
+    r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
+
+    return r;
+}
+
+UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
+              LPCWSTR szQuery, MSIQUERY **pView)
+{
+    MSIQUERY *query;
+    UINT r;
+
+    TRACE("%s %p\n", debugstr_w(szQuery), pView);
+
+    if( !szQuery)
+        return ERROR_INVALID_PARAMETER;
+
+    /* pre allocate a handle to hold a pointer to the view */
+    query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
+                              MSI_CloseView );
+    if( !query )
+        return ERROR_FUNCTION_FAILED;
+
+    msiobj_addref( &db->hdr );
+    query->row = 0;
+    query->db = db;
+    query->view = NULL;
+
+    r = MSI_ParseSQL( db, szQuery, &query->view );
+    if( r == ERROR_SUCCESS )
+    {
+        msiobj_addref( &query->hdr );
+        *pView = query;
+    }
+
+    msiobj_release( &query->hdr );
+    return r;
+}
+
+UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
+              LPCWSTR szQuery, MSIHANDLE *phView)
+{
+    MSIDATABASE *db;
+    MSIQUERY *query = NULL;
+    UINT ret;
+
+    TRACE("%s %p\n", debugstr_w(szQuery), phView);
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
+    if( ret == ERROR_SUCCESS )
+    {
+        *phView = alloc_msihandle( &query->hdr );
+        msiobj_release( &query->hdr );
+    }
+    msiobj_release( &db->hdr );
+
+    return ret;
+}
+
+UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
+{
+    MSIVIEW *view;
+    MSIRECORD *rec;
+    UINT row_count = 0, col_count = 0, i, ival, ret, type;
+
+    TRACE("%p %p\n", query, prec );
+
+    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;
+
+    rec = MSI_CreateRecord( col_count );
+    if( !rec )
+        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;
+        }
+        if (( type != MSITYPE_BINARY) && (type != (MSITYPE_BINARY |
+                                                   MSITYPE_NULLABLE)))
+        {
+            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;
+
+                sval = MSI_makestring( query->db, ival );
+                MSI_RecordSetStringW( rec, i, sval );
+                HeapFree( GetProcessHeap(), 0, sval );
+            }
+            else
+            {
+                if( (type & MSI_DATASIZEMASK) == 2 )
+                    MSI_RecordSetInteger( rec, i, ival - (1<<15) );
+                else
+                    MSI_RecordSetInteger( rec, i, ival - (1<<31) );
+            }
+        }
+        else
+        {
+            IStream *stm = NULL;
+
+            ret = view->ops->fetch_stream( view, query->row, i, &stm );
+            if( ( ret == ERROR_SUCCESS ) && stm )
+            {
+                MSI_RecordSetIStream( rec, i, stm );
+                IStream_Release( stm );
+            }
+            else
+                ERR("failed to get stream\n");
+        }
+    }
+    query->row ++;
+
+    *prec = rec;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
+{
+    MSIQUERY *query;
+    MSIRECORD *rec = NULL;
+    UINT ret;
+
+    TRACE("%ld %p\n", hView, record);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_ViewFetch( query, &rec );
+    if( ret == ERROR_SUCCESS )
+    {
+        *record = alloc_msihandle( &rec->hdr );
+        msiobj_release( &rec->hdr );
+    }
+    msiobj_release( &query->hdr );
+    return ret;
+}
+
+UINT MSI_ViewClose(MSIQUERY *query)
+{
+    MSIVIEW *view;
+
+    TRACE("%p\n", query );
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+    if( !view->ops->close )
+        return ERROR_FUNCTION_FAILED;
+
+    return view->ops->close( view );
+}
+
+UINT WINAPI MsiViewClose(MSIHANDLE hView)
+{
+    MSIQUERY *query;
+    UINT ret;
+
+    TRACE("%ld\n", hView );
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_ViewClose( query );
+    msiobj_release( &query->hdr );
+    return ret;
+}
+
+UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
+{
+    MSIVIEW *view;
+
+    TRACE("%p %p\n", query, rec);
+
+    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, rec );
+}
+
+UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
+{
+    MSIQUERY *query;
+    MSIRECORD *rec = NULL;
+    UINT ret;
+    
+    TRACE("%ld %ld\n", hView, hRec);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    if( hRec )
+    {
+        rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
+        if( !rec )
+        {
+            ret = ERROR_INVALID_HANDLE;
+            goto out;
+        }
+    }
+
+    ret = MSI_ViewExecute( query, rec );
+out:
+    if( query )
+        msiobj_release( &query->hdr );
+    if( rec )
+        msiobj_release( &rec->hdr );
+
+    return ret;
+}
+
+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 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 )
+{
+    MSIDATABASE *db;
+    UINT r;
+
+    TRACE("%ld\n", hdb);
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    /* FIXME: lock the database */
+
+    r = MSI_CommitTables( db );
+
+    /* FIXME: unlock the database */
+
+    return r;
+}
+
+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;
+}
+
+UINT WINAPI MsiViewModify(MSIHANDLE hView, MSIMODIFY eModifyMode, MSIHANDLE
+hRecord)
+{
+    FIXME("%ld %x %ld\n",hView, eModifyMode, hRecord);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/reactos/lib/msi/order.c b/reactos/lib/msi/order.c
new file mode 100644 (file)
index 0000000..559a794
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * 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 <stdarg.h>
+
+#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, MSIRECORD *record )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+    UINT r, num_rows = 0, i;
+
+    TRACE("%p %p\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;
+
+    msiobj_release( &ov->db->hdr );
+    HeapFree( GetProcessHeap(), 0, ov );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS order_ops =
+{
+    ORDER_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    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;
+    msiobj_addref( &db->hdr );
+    ov->db = db;
+    ov->table = table;
+    ov->reorder = NULL;
+    ov->num_cols = 0;
+    *view = (MSIVIEW*) ov;
+
+    return ERROR_SUCCESS;
+}
+
+UINT ORDER_AddColumn( MSIVIEW *view, LPWSTR name )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+    UINT n, count, r;
+    MSIVIEW *table;
+
+    TRACE("%p adding %s\n", ov, debugstr_w( name ) );
+
+    if( ov->view.ops != &order_ops )
+        return ERROR_FUNCTION_FAILED;
+
+    table = ov->table;
+    if( !table )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_dimensions )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_column_info )
+        return ERROR_FUNCTION_FAILED;
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    if( ov->num_cols >= count )
+        return ERROR_FUNCTION_FAILED;
+
+    r = VIEW_find_column( table, name, &n );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    ov->cols[ov->num_cols] = n;
+    TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
+
+    ov->num_cols++;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/package.c b/reactos/lib/msi/package.c
new file mode 100644 (file)
index 0000000..2cd5f7b
--- /dev/null
@@ -0,0 +1,843 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 Aric Stewart 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 <stdarg.h>
+#include <stdio.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "wincrypt.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+#include "objbase.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * The MSVC headers define the MSIDBOPEN_* macros cast to LPCTSTR,
+ *  which is a problem because LPCTSTR isn't defined when compiling wine.
+ * To work around this problem, we need to define LPCTSTR as LPCWSTR here,
+ *  and make sure to only use it in W functions.
+ */
+#define LPCTSTR LPCWSTR
+
+void MSI_FreePackage( MSIOBJECTHDR *arg)
+{
+    MSIPACKAGE *package= (MSIPACKAGE*) arg;
+
+    ACTION_remove_tracked_tempfiles(package);
+
+    if (package->features && package->loaded_features > 0)
+        HeapFree(GetProcessHeap(),0,package->features);
+
+    if (package->folders && package->loaded_folders > 0)
+        HeapFree(GetProcessHeap(),0,package->folders);
+    
+    if (package->components && package->loaded_components > 0)
+        HeapFree(GetProcessHeap(),0,package->components);
+
+    if (package->files && package->loaded_files > 0)
+        HeapFree(GetProcessHeap(),0,package->files);
+    msiobj_release( &package->db->hdr );
+}
+
+UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage)
+{
+    LPWSTR szwPack = NULL;
+    UINT len, ret;
+
+    TRACE("%s %p\n",debugstr_a(szPackage), phPackage);
+
+    if( szPackage )
+    {
+        len = MultiByteToWideChar( CP_ACP, 0, szPackage, -1, NULL, 0 );
+        szwPack = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
+        if( szwPack )
+            MultiByteToWideChar( CP_ACP, 0, szPackage, -1, szwPack, len );
+    }
+
+    ret = MsiOpenPackageW( szwPack, phPackage );
+
+    if( szwPack )
+        HeapFree( GetProcessHeap(), 0, szwPack );
+
+    return ret;
+}
+
+
+static const UINT clone_properties(MSIDATABASE *db)
+{
+    MSIQUERY * view = NULL;
+    UINT rc;
+    static const WCHAR CreateSql[] = {
+       'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','_','P','r','o',
+       'p','e','r','t','y','`',' ','(',' ','`','_','P','r','o','p','e','r','t',
+       'y','`',' ','C','H','A','R','(','5','6',')',' ','N','O','T',' ','N','U',
+       'L','L',',',' ','`','V','a','l','u','e','`',' ','C','H','A','R','(','9',
+       '8',')',' ','N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R',
+       'Y',' ','K','E','Y',' ','`','_','P','r','o','p','e','r','t','y','`',')',0};
+    static const WCHAR Query[] = {
+       'S','E','L','E','C','T',' ','*',' ',
+       'f','r','o','m',' ','P','r','o','p','e','r','t','y',0};
+    static const WCHAR Insert[] = {
+       'I','N','S','E','R','T',' ','i','n','t','o',' ',
+       '`','_','P','r','o','p','e','r','t','y','`',' ',
+       '(','`','_','P','r','o','p','e','r','t','y','`',',',
+       '`','V','a','l','u','e','`',')',' ',
+       'V','A','L','U','E','S',' ','(','?',')',0};
+
+    /* create the temporary properties table */
+    rc = MSI_DatabaseOpenViewW(db, CreateSql, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+    rc = MSI_ViewExecute(view,0);   
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr); 
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    /* clone the existing properties */
+    rc = MSI_DatabaseOpenViewW(db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return rc;
+
+    rc = MSI_ViewExecute(view, 0);
+    if (rc != ERROR_SUCCESS)
+    {
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr); 
+        return rc;
+    }
+    while (1)
+    {
+        MSIRECORD * row;
+        MSIQUERY * view2;
+
+        rc = MSI_ViewFetch(view,&row);
+        if (rc != ERROR_SUCCESS)
+            break;
+
+        rc = MSI_DatabaseOpenViewW(db,Insert,&view2);  
+        if (rc!= ERROR_SUCCESS)
+            continue;
+        rc = MSI_ViewExecute(view2,row);
+        MSI_ViewClose(view2);
+        msiobj_release(&view2->hdr);
+        if (rc == ERROR_SUCCESS) 
+            msiobj_release(&row->hdr); 
+    }
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+    
+    return rc;
+}
+
+/*
+ * There are a whole slew of these we need to set
+ *
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
+ */
+static VOID set_installer_properties(MSIPACKAGE *package)
+{
+    WCHAR pth[MAX_PATH];
+    OSVERSIONINFOA OSVersion;
+    DWORD verval;
+    WCHAR verstr[10], msiver[10];
+
+    static const WCHAR cszbs[]={'\\',0};
+    static const WCHAR CFF[] = 
+{'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
+    static const WCHAR PFF[] = 
+{'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
+    static const WCHAR CADF[] = 
+{'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+    static const WCHAR FaF[] = 
+{'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
+    static const WCHAR FoF[] = 
+{'F','o','n','t','s','F','o','l','d','e','r',0};
+    static const WCHAR SendTF[] = 
+{'S','e','n','d','T','o','F','o','l','d','e','r',0};
+    static const WCHAR SMF[] = 
+{'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0};
+    static const WCHAR StF[] = 
+{'S','t','a','r','t','u','p','F','o','l','d','e','r',0};
+    static const WCHAR TemplF[] = 
+{'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0};
+    static const WCHAR DF[] = 
+{'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
+    static const WCHAR PMF[] = 
+{'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0};
+    static const WCHAR ATF[] = 
+{'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
+    static const WCHAR ADF[] = 
+{'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+    static const WCHAR SF[] = 
+{'S','y','s','t','e','m','F','o','l','d','e','r',0};
+    static const WCHAR LADF[] = 
+{'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+    static const WCHAR MPF[] = 
+{'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
+    static const WCHAR PF[] = 
+{'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
+    static const WCHAR WF[] = 
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+    static const WCHAR TF[]=
+{'T','e','m','p','F','o','l','d','e','r',0};
+    static const WCHAR szAdminUser[] =
+{'A','d','m','i','n','U','s','e','r',0};
+    static const WCHAR szPriv[] =
+{'P','r','i','v','i','l','e','g','e','d',0};
+    static const WCHAR szOne[] =
+{'1',0};
+    static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 };
+    static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 };
+    static const WCHAR szFormat[] = {'%','l','i',0};
+    static const WCHAR szWinBuild[] =
+{'W','i','n','d','o','w','s','B','u','i','l','d', 0 };
+    static const WCHAR szSPL[] = 
+{'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 };
+    static const WCHAR szSix[] = {'6',0 };
+
+    static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 };
+    static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0};
+
+/*
+ * Other things I notice set
+ *
+ScreenY
+ScreenX
+SystemLanguageID
+ComputerName
+UserLanguageID
+LogonUser
+VirtualMemory
+PhysicalMemory
+Intel
+ShellAdvSupport
+DefaultUIFont
+VersionDatabase
+PackagecodeChanging
+ProductState
+CaptionHeight
+BorderTop
+BorderSide
+TextHeight
+ColorBits
+RedirectedDllSupport
+Time
+Date
+Privileged
+*/
+
+    SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, CFF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, PFF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, CADF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, FaF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, FoF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, SendTF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, SMF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, StF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, TemplF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, DF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, PMF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, ATF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, ADF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, SF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, LADF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, MPF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, PF, pth);
+
+    SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
+    strcatW(pth,cszbs);
+    MSI_SetPropertyW(package, WF, pth);
+
+    GetTempPathW(MAX_PATH,pth);
+    MSI_SetPropertyW(package, TF, pth);
+
+
+    /* in a wine environment the user is always admin and privileged */
+    MSI_SetPropertyW(package,szAdminUser,szOne);
+    MSI_SetPropertyW(package,szPriv,szOne);
+
+    /* set the os things */
+    OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+    GetVersionExA(&OSVersion);
+    verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100;
+    sprintfW(verstr,szFormat,verval);
+    switch (OSVersion.dwPlatformId)
+    {
+        case VER_PLATFORM_WIN32_WINDOWS:    
+            MSI_SetPropertyW(package,v9x,verstr);
+            break;
+        case VER_PLATFORM_WIN32_NT:
+            MSI_SetPropertyW(package,vNT,verstr);
+            break;
+    }
+    sprintfW(verstr,szFormat,OSVersion.dwBuildNumber);
+    MSI_SetPropertyW(package,szWinBuild,verstr);
+    /* just fudge this */
+    MSI_SetPropertyW(package,szSPL,szSix);
+
+    sprintfW( msiver, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION);
+    MSI_SetPropertyW( package, szVersionMsi, msiver );
+}
+
+UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage)
+{
+    UINT rc;
+    MSIDATABASE *db = NULL;
+    MSIPACKAGE *package = NULL;
+    WCHAR uilevel[10];
+    UINT ret = ERROR_FUNCTION_FAILED;
+
+    static const WCHAR OriginalDatabase[] =
+{'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
+    static const WCHAR Database[] =
+{'D','A','T','A','B','A','S','E',0};
+    static const WCHAR szpi[] = {'%','i',0};
+    static const WCHAR szLevel[] = { 'U','I','L','e','v','e','l',0 };
+
+    TRACE("%s %p\n",debugstr_w(szPackage), pPackage);
+
+    if (szPackage[0] == '#')
+    {
+        INT handle = atoiW(&szPackage[1]);
+        db = msihandle2msiinfo( handle , MSIHANDLETYPE_DATABASE);
+    }
+    else
+    {
+        rc = MSI_OpenDatabaseW(szPackage, MSIDBOPEN_READONLY, &db);
+        if (rc != ERROR_SUCCESS)
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE),
+                               MSI_FreePackage );
+
+    if (package)
+    {
+        msiobj_addref( &db->hdr );
+
+        package->db = db;
+        package->features = NULL;
+        package->folders = NULL;
+        package->components = NULL;
+        package->files = NULL;
+        package->loaded_features = 0;
+        package->loaded_folders = 0;
+        package->loaded_components= 0;
+        package->loaded_files = 0;
+
+        /* OK, here is where we do a slew of things to the database to 
+         * prep for all that is to come as a package */
+
+        clone_properties(db);
+        set_installer_properties(package);
+        MSI_SetPropertyW(package, OriginalDatabase, szPackage);
+        MSI_SetPropertyW(package, Database, szPackage);
+        sprintfW(uilevel,szpi,gUILevel);
+        MSI_SetPropertyW(package, szLevel, uilevel);
+
+        msiobj_addref( &package->hdr );
+        *pPackage = package;
+        ret = ERROR_SUCCESS;
+    }
+
+    if( package )
+        msiobj_release( &package->hdr );
+    if( db )
+        msiobj_release( &db->hdr );
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
+{
+    MSIPACKAGE *package = NULL;
+    UINT ret;
+
+    ret = MSI_OpenPackageW( szPackage, &package);
+    if( ret == ERROR_SUCCESS )
+    {
+        *phPackage = alloc_msihandle( &package->hdr );
+        msiobj_release( &package->hdr );
+    }
+    return ret;
+}
+
+UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
+{
+    FIXME("%s 0x%08lx %p\n",debugstr_a(szPackage), dwOptions, phPackage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
+{
+    FIXME("%s 0x%08lx %p\n",debugstr_w(szPackage), dwOptions, phPackage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
+{
+    MSIPACKAGE *package;
+    MSIHANDLE handle = 0;
+
+    TRACE("(%ld)\n",hInstall);
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( package)
+    {
+        handle = alloc_msihandle( &package->db->hdr );
+        msiobj_release( &package->hdr );
+    }
+
+    return handle;
+}
+
+INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType,
+                               MSIRECORD *record)
+{
+    DWORD log_type = 0;
+    LPWSTR message;
+    DWORD sz;
+    DWORD total_size = 0;
+    INT msg_field=1;
+    INT i;
+    INT rc;
+    char *msg;
+    int len;
+
+    TRACE("%x \n",eMessageType);
+    rc = 0;
+
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ERROR)
+        log_type |= INSTALLLOGMODE_ERROR;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_WARNING)
+        log_type |= INSTALLLOGMODE_WARNING;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_USER)
+        log_type |= INSTALLLOGMODE_USER;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INFO)
+        log_type |= INSTALLLOGMODE_INFO;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_COMMONDATA)
+        log_type |= INSTALLLOGMODE_COMMONDATA;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART)
+        log_type |= INSTALLLOGMODE_ACTIONSTART;
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA)
+        log_type |= INSTALLLOGMODE_ACTIONDATA;
+    /* just a guess */
+    if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS)
+        log_type |= 0x800;
+
+    message = HeapAlloc(GetProcessHeap(),0,1*sizeof (WCHAR));
+    message[0]=0;
+    msg_field = MSI_RecordGetFieldCount(record);
+    for (i = 1; i <= msg_field; i++)
+    {
+        LPWSTR tmp;
+        WCHAR number[3];
+        const static WCHAR format[] = { '%','i',':',' ',0};
+        const static WCHAR space[] = { ' ',0};
+        sz = 0;
+        MSI_RecordGetStringW(record,i,NULL,&sz);
+        sz+=4;
+        total_size+=sz*sizeof(WCHAR);
+        tmp = HeapAlloc(GetProcessHeap(),0,sz*sizeof(WCHAR));
+        message = HeapReAlloc(GetProcessHeap(),0,message,total_size*sizeof (WCHAR));
+
+        MSI_RecordGetStringW(record,i,tmp,&sz);
+
+        if (msg_field > 1)
+        {
+            sprintfW(number,format,i);
+            strcatW(message,number);
+        }
+        strcatW(message,tmp);
+        if (msg_field > 1)
+            strcatW(message,space);
+
+        HeapFree(GetProcessHeap(),0,tmp);
+    }
+
+    TRACE("(%p %lx %lx %s)\n",gUIHandler, gUIFilter, log_type,
+                             debugstr_w(message));
+
+    /* convert it to ASCII */
+    len = WideCharToMultiByte( CP_ACP, 0, message, -1,
+                               NULL, 0, NULL, NULL );
+    msg = HeapAlloc( GetProcessHeap(), 0, len );
+    WideCharToMultiByte( CP_ACP, 0, message, -1,
+                         msg, len, NULL, NULL );
+
+    if (gUIHandler && (gUIFilter & log_type))
+    {
+        rc = gUIHandler(gUIContext,eMessageType,msg);
+    }
+
+    if ((!rc) && (gszLogFile[0]) && !((eMessageType & 0xff000000) ==
+                                      INSTALLMESSAGE_PROGRESS))
+    {
+        DWORD write;
+        HANDLE log_file = CreateFileW(gszLogFile,GENERIC_WRITE, 0, NULL,
+                                  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+        if (log_file != INVALID_HANDLE_VALUE)
+        {
+            SetFilePointer(log_file,0, NULL, FILE_END);
+            WriteFile(log_file,msg,strlen(msg),&write,NULL);
+            WriteFile(log_file,"\n",1,&write,NULL);
+            CloseHandle(log_file);
+        }
+    }
+    HeapFree( GetProcessHeap(), 0, msg );
+    
+    HeapFree(GetProcessHeap(),0,message);
+    return ERROR_SUCCESS;
+}
+
+INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType,
+                              MSIHANDLE hRecord)
+{
+    UINT ret = ERROR_INVALID_HANDLE;
+    MSIPACKAGE *package = NULL;
+    MSIRECORD *record = NULL;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
+    if( !package )
+        goto out;
+
+    record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
+    if( !record )
+        goto out;
+
+    ret = MSI_ProcessMessage( package, eMessageType, record );
+
+out:
+    if( package )
+        msiobj_release( &package->hdr );
+    if( record )
+        msiobj_release( &record->hdr );
+
+    return ret;
+}
+
+/* property code */
+UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue)
+{
+    LPWSTR szwName = NULL, szwValue = NULL;
+    UINT hr = ERROR_INSTALL_FAILURE;
+    UINT len;
+
+    if (0 == hInstall) {
+      return ERROR_INVALID_HANDLE;
+    }
+    if (NULL == szName) {
+      return ERROR_INVALID_PARAMETER;
+    }
+    if (NULL == szValue) {
+      return ERROR_INVALID_PARAMETER;
+    }
+
+    len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
+    szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+    if( !szwName )
+        goto end;
+    MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
+
+    len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 );
+    szwValue = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+    if( !szwValue)
+        goto end;
+    MultiByteToWideChar( CP_ACP, 0, szValue , -1, szwValue, len );
+
+    hr = MsiSetPropertyW( hInstall, szwName, szwValue);
+
+end:
+    if( szwName )
+        HeapFree( GetProcessHeap(), 0, szwName );
+    if( szwValue )
+        HeapFree( GetProcessHeap(), 0, szwValue );
+
+    return hr;
+}
+
+UINT MSI_SetPropertyW( MSIPACKAGE *package, LPCWSTR szName, LPCWSTR szValue)
+{
+    MSIQUERY *view;
+    MSIRECORD *row;
+    UINT rc;
+    DWORD sz = 0;
+    static const WCHAR Insert[]=
+     {'I','N','S','E','R','T',' ','i','n','t','o',' ','`','_','P','r','o','p'
+,'e','r','t','y','`',' ','(','`','_','P','r','o','p','e','r','t','y','`'
+,',','`','V','a','l','u','e','`',')',' ','V','A','L','U','E','S'
+,' ','(','?',')',0};
+    static const WCHAR Update[]=
+     {'U','P','D','A','T','E',' ','_','P','r','o','p','e'
+,'r','t','y',' ','s','e','t',' ','`','V','a','l','u','e','`',' ','='
+,' ','?',' ','w','h','e','r','e',' ','`','_','P','r','o','p'
+,'e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
+    WCHAR Query[1024];
+
+    TRACE("Setting property (%s %s)\n",debugstr_w(szName),
+          debugstr_w(szValue));
+
+    rc = MSI_GetPropertyW(package,szName,0,&sz);
+    if (rc==ERROR_MORE_DATA || rc == ERROR_SUCCESS)
+    {
+        sprintfW(Query,Update,szName);
+
+        row = MSI_CreateRecord(1);
+        MSI_RecordSetStringW(row,1,szValue);
+
+    }
+    else
+    {
+       strcpyW(Query,Insert);
+
+        row = MSI_CreateRecord(2);
+        MSI_RecordSetStringW(row,1,szName);
+        MSI_RecordSetStringW(row,2,szValue);
+    }
+
+
+    rc = MSI_DatabaseOpenViewW(package->db,Query,&view);
+    if (rc!= ERROR_SUCCESS)
+    {
+        msiobj_release(&row->hdr);
+        return rc;
+    }
+
+    rc = MSI_ViewExecute(view,row);
+
+    msiobj_release(&row->hdr);
+    MSI_ViewClose(view);
+    msiobj_release(&view->hdr);
+
+    return rc;
+}
+
+UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue)
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if( !package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_SetPropertyW( package, szName, szValue);
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+static UINT MSI_GetPropertyRow(MSIPACKAGE *package, LPCWSTR szName, MSIRECORD **row)
+{
+    MSIQUERY *view;
+    UINT rc, sz;
+    static const WCHAR select[]=
+    {'s','e','l','e','c','t',' ','V','a','l','u','e',' ','f','r','o','m',' '
+     ,'_','P','r','o','p','e','r','t','y',' ','w','h','e','r','e',' '
+     ,'_','P','r','o','p','e','r','t','y','=','`','%','s','`',0};
+    LPWSTR query;
+
+    if (!szName)
+        return ERROR_INVALID_PARAMETER;
+
+    sz = sizeof select + strlenW(szName)*sizeof(WCHAR);
+    query = HeapAlloc(GetProcessHeap(), 0, sz);
+    sprintfW(query,select,szName);
+
+    rc = MSI_DatabaseOpenViewW(package->db, query, &view);
+    HeapFree(GetProcessHeap(), 0, query);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_ViewExecute(view, 0);
+        if (rc == ERROR_SUCCESS)
+            rc = MSI_ViewFetch(view,row);
+
+        MSI_ViewClose(view);
+        msiobj_release(&view->hdr);
+    }
+
+    return rc;
+}
+
+UINT MSI_GetPropertyW(MSIPACKAGE *package, LPCWSTR szName, 
+                           LPWSTR szValueBuf, DWORD* pchValueBuf)
+{
+    MSIRECORD *row;
+    UINT rc;
+
+    rc = MSI_GetPropertyRow(package, szName, &row);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_RecordGetStringW(row,1,szValueBuf,pchValueBuf);
+        msiobj_release(&row->hdr);
+    }
+
+    if (rc == ERROR_SUCCESS)
+        TRACE("returning %s for property %s\n", debugstr_w(szValueBuf),
+            debugstr_w(szName));
+    else
+    {
+        *pchValueBuf = 0;
+        TRACE("property not found\n");
+    }
+
+    return rc;
+}
+
+UINT MSI_GetPropertyA(MSIPACKAGE *package, LPCSTR szName, 
+                           LPSTR szValueBuf, DWORD* pchValueBuf)
+{
+    MSIRECORD *row;
+    UINT rc, len;
+    LPWSTR szwName;
+
+    len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
+    szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+    if (!szwName)
+        return ERROR_NOT_ENOUGH_MEMORY;
+    MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
+
+    rc = MSI_GetPropertyRow(package, szwName, &row);
+    if (rc == ERROR_SUCCESS)
+    {
+        rc = MSI_RecordGetStringA(row,1,szValueBuf,pchValueBuf);
+        msiobj_release(&row->hdr);
+    }
+
+    if (rc == ERROR_SUCCESS)
+        TRACE("returning %s for property %s\n", debugstr_a(szValueBuf),
+            debugstr_a(szName));
+    else
+    {
+        *pchValueBuf = 0;
+        TRACE("property not found\n");
+    }
+    HeapFree( GetProcessHeap(), 0, szwName );
+
+    return rc;
+}
+
+UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPSTR szValueBuf, DWORD* pchValueBuf) 
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    TRACE("%lu %s %lu\n", hInstall, debugstr_a(szName), *pchValueBuf);
+
+    if (0 == hInstall)
+        return ERROR_INVALID_HANDLE;
+    if (NULL == szName)
+        return ERROR_INVALID_PARAMETER;
+    if (NULL != szValueBuf && NULL == pchValueBuf)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_GetPropertyA(package, szName, szValueBuf, pchValueBuf );
+    msiobj_release( &package->hdr );
+    return ret;
+}
+
+  
+UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName, 
+                           LPWSTR szValueBuf, DWORD* pchValueBuf)
+{
+    MSIPACKAGE *package;
+    UINT ret;
+
+    if (0 == hInstall)
+        return ERROR_INVALID_HANDLE;
+    if (NULL == szName)
+        return ERROR_INVALID_PARAMETER;
+    if (NULL != szValueBuf && NULL == pchValueBuf)
+        return ERROR_INVALID_PARAMETER;
+
+    package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
+    if (!package)
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_GetPropertyW(package, szName, szValueBuf, pchValueBuf );
+    msiobj_release( &package->hdr );
+    return ret;
+}
diff --git a/reactos/lib/msi/query.h b/reactos/lib/msi/query.h
new file mode 100644 (file)
index 0000000..6d949c6
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "objbase.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
+#define EXPR_STRCMP   7
+#define EXPR_UTF8     8
+#define EXPR_WILDCARD 9
+
+struct sql_str {
+    LPCWSTR data;
+    INT len;
+};
+
+typedef struct _string_list
+{
+    LPWSTR string;
+    struct _string_list *next;
+} string_list;
+
+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;
+        char *utf8;
+    } u;
+};
+
+typedef struct _create_col_info
+{
+    LPWSTR colname;
+    UINT   type;
+    struct _create_col_info *next;
+} create_col_info;
+
+typedef struct _value_list
+{
+    struct expr *val;
+    struct _value_list *next;
+} value_list;
+
+typedef struct _column_assignment
+{
+    string_list *col_list;
+    value_list *val_list;
+} column_assignment;
+
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phView);
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view );
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                        string_list *columns );
+
+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,
+                       struct expr *cond );
+
+UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        create_col_info *col_info, BOOL temp );
+
+UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        string_list *columns, value_list *values, BOOL temp );
+
+UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **, LPWSTR table,
+                        column_assignment *list, struct expr *expr );
+
+void delete_expr( struct expr *e );
+void delete_string_list( string_list *sl );
+void delete_value_list( value_list *vl );
+
+int sqliteGetToken(const WCHAR *z, int *tokenType);
+
+#endif /* __WINE_MSI_QUERY_H */
diff --git a/reactos/lib/msi/record.c b/reactos/lib/msi/record.c
new file mode 100644 (file)
index 0000000..84a363e
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+
+#define COBJMACROS
+
+#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
+
+void MSI_FreeField( MSIFIELD *field )
+{
+    switch( field->type )
+    {
+    case MSIFIELD_NULL:
+    case MSIFIELD_INT:
+        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( MSIOBJECTHDR *arg )
+{
+    MSIRECORD *rec = (MSIRECORD *) arg;
+    UINT i;
+
+    for( i=0; i<=rec->count; i++ )
+        MSI_FreeField( &rec->fields[i] );
+}
+
+MSIRECORD *MSI_CreateRecord( unsigned int cParams )
+{
+    MSIRECORD *rec;
+    UINT len;
+
+    TRACE("%d\n", cParams);
+
+    len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
+    rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
+    if( rec )
+    rec->count = cParams;
+    return rec;
+}
+
+MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams )
+{
+    MSIRECORD *rec;
+    MSIHANDLE ret = 0;
+
+    TRACE("%d\n", cParams);
+
+    rec = MSI_CreateRecord( cParams );
+    if( rec )
+        ret = alloc_msihandle( &rec->hdr );
+    return ret;
+}
+
+unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec )
+{
+    return rec->count;
+}
+
+unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld\n", handle );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+    {
+        ERR("Record not found!\n");
+        return 0;
+    }
+
+    ret = MSI_RecordGetFieldCount( rec );
+    msiobj_release( &rec->hdr );
+
+    return ret;
+}
+
+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 MSI_RecordGetInteger( MSIRECORD *rec, unsigned int iField)
+{
+    int ret = 0;
+
+    TRACE("%p %d\n", rec, iField );
+
+    if( iField > rec->count )
+        return MSI_NULL_INTEGER;
+
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        return rec->fields[iField].u.iVal;
+    case MSIFIELD_WSTR:
+        if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
+            return ret;
+        return MSI_NULL_INTEGER;
+    default:
+        break;
+    }
+
+    return MSI_NULL_INTEGER;
+}
+
+int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d\n", handle, iField );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return MSI_NULL_INTEGER;
+
+    ret = MSI_RecordGetInteger( rec, iField );
+    msiobj_release( &rec->hdr );
+
+    return ret;
+}
+
+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 MSI_RecordSetInteger( MSIRECORD *rec, unsigned int iField, int iVal )
+{
+    TRACE("%p %u %d\n", rec, iField, iVal);
+
+    if( iField <= rec->count )
+    {
+        MSI_FreeField( &rec->fields[iField] );
+        rec->fields[iField].type = MSIFIELD_INT;
+        rec->fields[iField].u.iVal = iVal;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %u %d\n", handle, iField, iVal);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_RecordSetInteger( rec, iField, iVal );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+BOOL MSI_RecordIsNull( MSIRECORD *rec, unsigned int iField )
+{
+    BOOL r = TRUE;
+
+    TRACE("%p %d\n", rec, iField );
+
+    r = ( iField > rec->count ) ||
+        ( rec->fields[iField].type == MSIFIELD_NULL );
+
+    return r;
+}
+
+BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d\n", handle, iField );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_RecordIsNull( rec, iField );
+    msiobj_release( &rec->hdr );
+    return ret;
+
+}
+
+UINT MSI_RecordGetStringA(MSIRECORD *rec, unsigned int iField, 
+               LPSTR szValue, DWORD *pcchValue)
+{
+    UINT len=0, ret;
+    CHAR buffer[16];
+
+    TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+    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_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;
+    case MSIFIELD_NULL:
+        len = 1;
+        if( *pcchValue > 0 )
+            szValue[0] = 0;
+        break;
+    default:
+        ret = ERROR_INVALID_PARAMETER;
+        break;
+    }
+
+    if( *pcchValue < len )
+        ret = ERROR_MORE_DATA;
+    *pcchValue = len;
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, 
+               LPSTR szValue, DWORD *pcchValue)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+const WCHAR *MSI_RecordGetString( MSIRECORD *rec, unsigned int iField )
+{
+    if( iField > rec->count )
+        return NULL;
+
+    if( rec->fields[iField].type != MSIFIELD_WSTR )
+        return NULL;
+
+    return rec->fields[iField].u.szwVal;
+}
+
+UINT MSI_RecordGetStringW(MSIRECORD *rec, unsigned int iField,
+               LPWSTR szValue, DWORD *pcchValue)
+{
+    UINT len=0, ret;
+    WCHAR buffer[16];
+    static const WCHAR szFormat[] = { '%','d',0 };
+
+    TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+    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_NULL:
+        len = 1;
+        if( *pcchValue > 0 )
+            szValue[0] = 0;
+    default:
+        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 ret;
+
+    TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT WINAPI MsiRecordDataSize(MSIHANDLE hRecord, unsigned int iField)
+{
+    FIXME("%ld %d\n", hRecord, iField);
+    return 0;
+}
+
+UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
+{
+    LPWSTR str;
+    UINT len;
+
+    TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 );
+    str = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    MultiByteToWideChar( CP_ACP, 0, szValue, -1, str, len );
+    MSI_FreeField( &rec->fields[iField] );
+    rec->fields[iField].type = MSIFIELD_WSTR;
+    rec->fields[iField].u.szwVal = str;
+
+    return 0;
+}
+
+UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_RecordSetStringA( rec, iField, szValue );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
+{
+    LPWSTR str;
+    UINT len;
+
+    TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    len = lstrlenW(szValue) + 1;
+    str = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR));
+    lstrcpyW( str, szValue );
+
+    MSI_FreeField( &rec->fields[iField] );
+    rec->fields[iField].type = MSIFIELD_WSTR;
+    rec->fields[iField].u.szwVal = str;
+
+    return 0;
+}
+
+UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    ret = MSI_RecordSetStringW( rec, iField, szValue );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+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 MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
+{
+    ULONG count;
+    HRESULT r;
+    IStream *stm;
+
+    TRACE("%p %d %p %p\n", rec, iField, buf, sz);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    if( rec->fields[iField].type != MSIFIELD_STREAM )
+    {
+        *sz = 0;
+        return ERROR_INVALID_FIELD;
+    }
+
+    stm = rec->fields[iField].u.stream;
+    if( !stm )
+        return ERROR_INVALID_FIELD;
+
+    /* if there's no buffer pointer, calculate the length to the end */
+    if( !buf )
+    {
+        LARGE_INTEGER ofs;
+        ULARGE_INTEGER end, cur;
+
+        ofs.QuadPart = cur.QuadPart = 0;
+        end.QuadPart = 0;
+        r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+        IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
+        ofs.QuadPart = cur.QuadPart;
+        IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+        *sz = end.QuadPart - cur.QuadPart;
+
+        return ERROR_SUCCESS;
+    }
+
+    /* read the data */
+    count = 0;
+    r = IStream_Read( stm, buf, *sz, &count );
+    if( FAILED( r ) )
+        return ERROR_FUNCTION_FAILED;
+
+    *sz = count;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
+{
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+    ret = MSI_RecordReadStream( rec, iField, buf, sz );
+    msiobj_release( &rec->hdr );
+    return ret;
+}
+
+UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm )
+{
+    TRACE("%p %d %p\n", rec, iField, stm);
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    MSI_FreeField( &rec->fields[iField] );
+
+    rec->fields[iField].type = MSIFIELD_STREAM;
+    rec->fields[iField].u.stream = stm;
+    IStream_AddRef( stm );
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/regsvr.c b/reactos/lib/msi/regsvr.c
new file mode 100644 (file)
index 0000000..fa3cf50
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ *     self-registerable dll functions for msi.dll
+ *
+ * Copyright (C) 2004 Raphael Junqueira
+ *
+ * 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 <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "winerror.h"
+
+#include "ole2.h"
+#include "olectl.h"
+
+#include "wine/debug.h"
+
+#include "msi.h"
+#include "initguid.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * Near the bottom of this file are the exported DllRegisterServer and
+ * DllUnregisterServer, which make all this worthwhile.
+ */
+
+/***********************************************************************
+ *             interface for self-registering
+ */
+struct regsvr_interface {
+    IID const *iid;            /* NULL for end of list */
+    LPCSTR name;               /* can be NULL to omit */
+    IID const *base_iid;       /* can be NULL to omit */
+    int num_methods;           /* can be <0 to omit */
+    CLSID const *ps_clsid;     /* can be NULL to omit */
+    CLSID const *ps_clsid32;   /* can be NULL to omit */
+};
+
+static HRESULT register_interfaces(struct regsvr_interface const *list);
+static HRESULT unregister_interfaces(struct regsvr_interface const *list);
+
+/**
+ * @todo: maybe adding typelibs support here
+ * [Software\\Classes\\CLSID\\{000C1090-0000-0000-C000-000000000046}\\TypeLib] 1080380217
+ * @="{000C1092-0000-0000-C000-000000000046}"
+ */
+struct regsvr_coclass {
+    CLSID const *clsid;                /* NULL for end of list */
+    LPCSTR name;               /* can be NULL to omit */
+    LPCSTR iph32;              /* can be NULL to omit */
+    LPCSTR ips;                        /* can be NULL to omit */
+    LPCSTR ips32;              /* can be NULL to omit */
+    LPCSTR ips32_tmodel;       /* can be NULL to omit, if apartment, iph32 must be set */
+    LPCSTR progid;             /* can be NULL to omit */
+    LPCSTR viprogid;           /* can be NULL to omit */
+    LPCSTR progid_extra;       /* can be NULL to omit */
+};
+
+static HRESULT register_coclasses(struct regsvr_coclass const *list);
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list);
+
+/***********************************************************************
+ *             static string constants
+ */
+static WCHAR const interface_keyname[10] = {
+    'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 };
+static WCHAR const base_ifa_keyname[14] = {
+    'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c',
+    'e', 0 };
+static WCHAR const num_methods_keyname[11] = {
+    'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 };
+static WCHAR const ps_clsid_keyname[15] = {
+    'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+    'i', 'd', 0 };
+static WCHAR const ps_clsid32_keyname[17] = {
+    'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's',
+    'i', 'd', '3', '2', 0 };
+static WCHAR const clsid_keyname[6] = {
+    'C', 'L', 'S', 'I', 'D', 0 };
+static WCHAR const curver_keyname[7] = {
+    'C', 'u', 'r', 'V', 'e', 'r', 0 };
+static WCHAR const iph32_keyname[] = {
+    'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r',
+    '3', '2', 0 };
+static WCHAR const ips_keyname[13] = {
+    'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+    0 };
+static WCHAR const ips32_keyname[15] = {
+    'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r',
+    '3', '2', 0 };
+static WCHAR const progid_keyname[7] = {
+    'P', 'r', 'o', 'g', 'I', 'D', 0 };
+static WCHAR const viprogid_keyname[25] = {
+    'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p',
+    'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D',
+    0 };
+static char const tmodel_valuename[] = "ThreadingModel";
+
+/***********************************************************************
+ *             static helper functions
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid);
+static LONG register_key_defvalueW(HKEY base, WCHAR const *name,
+                                  WCHAR const *value);
+static LONG register_key_defvalueA(HKEY base, WCHAR const *name,
+                                  char const *value);
+static LONG register_progid(WCHAR const *clsid,
+                           char const *progid, char const *curver_progid,
+                           char const *name, char const *extra);
+static LONG recursive_delete_key(HKEY key);
+static LONG recursive_delete_keyA(HKEY base, char const *name);
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name);
+
+/***********************************************************************
+ *             register_interfaces
+ */
+static HRESULT register_interfaces(struct regsvr_interface const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY interface_key;
+
+    res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &interface_key, NULL);
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->iid; ++list) {
+       WCHAR buf[39];
+       HKEY iid_key;
+
+       StringFromGUID2(list->iid, buf, 39);
+       res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0,
+                             KEY_READ | KEY_WRITE, NULL, &iid_key, NULL);
+       if (res != ERROR_SUCCESS) goto error_close_interface_key;
+
+       if (list->name) {
+           res = RegSetValueExA(iid_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)(list->name),
+                                strlen(list->name) + 1);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (list->base_iid) {
+           register_key_guid(iid_key, base_ifa_keyname, list->base_iid);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (0 <= list->num_methods) {
+           static WCHAR const fmt[3] = { '%', 'd', 0 };
+           HKEY key;
+
+           res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0,
+                                 KEY_READ | KEY_WRITE, NULL, &key, NULL);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+
+           wsprintfW(buf, fmt, list->num_methods);
+           res = RegSetValueExW(key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)buf,
+                                (lstrlenW(buf) + 1) * sizeof(WCHAR));
+           RegCloseKey(key);
+
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (list->ps_clsid) {
+           register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+       if (list->ps_clsid32) {
+           register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32);
+           if (res != ERROR_SUCCESS) goto error_close_iid_key;
+       }
+
+    error_close_iid_key:
+       RegCloseKey(iid_key);
+    }
+
+error_close_interface_key:
+    RegCloseKey(interface_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             unregister_interfaces
+ */
+static HRESULT unregister_interfaces(struct regsvr_interface const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY interface_key;
+
+    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0,
+                       KEY_READ | KEY_WRITE, &interface_key);
+    if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->iid; ++list) {
+       WCHAR buf[39];
+
+       StringFromGUID2(list->iid, buf, 39);
+       res = recursive_delete_keyW(interface_key, buf);
+    }
+
+    RegCloseKey(interface_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             register_coclasses
+ */
+static HRESULT register_coclasses(struct regsvr_coclass const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY coclass_key;
+
+    res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL);
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+       WCHAR buf[39];
+       HKEY clsid_key;
+
+       StringFromGUID2(list->clsid, buf, 39);
+       res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0,
+                             KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL);
+       if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+       if (list->name) {
+           res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)(list->name),
+                                strlen(list->name) + 1);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->iph32) {
+           HKEY iph32_key;
+
+           res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0,
+                                 KEY_READ | KEY_WRITE, NULL,
+                                 &iph32_key, NULL);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)list->iph32,
+                                lstrlenA(list->iph32) + 1);
+           RegCloseKey(iph32_key);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->ips) {
+           res = register_key_defvalueA(clsid_key, ips_keyname, list->ips);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->ips32) {
+           HKEY ips32_key;
+
+           res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0,
+                                 KEY_READ | KEY_WRITE, NULL,
+                                 &ips32_key, NULL);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ,
+                                (CONST BYTE*)list->ips32,
+                                lstrlenA(list->ips32) + 1);
+           if (res == ERROR_SUCCESS && list->ips32_tmodel)
+               res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ,
+                                    (CONST BYTE*)list->ips32_tmodel,
+                                    strlen(list->ips32_tmodel) + 1);
+           RegCloseKey(ips32_key);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->progid) {
+           res = register_key_defvalueA(clsid_key, progid_keyname,
+                                        list->progid);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = register_progid(buf, list->progid, NULL,
+                                 list->name, list->progid_extra);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+       if (list->viprogid) {
+           res = register_key_defvalueA(clsid_key, viprogid_keyname,
+                                        list->viprogid);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+
+           res = register_progid(buf, list->viprogid, list->progid,
+                                 list->name, list->progid_extra);
+           if (res != ERROR_SUCCESS) goto error_close_clsid_key;
+       }
+
+    error_close_clsid_key:
+       RegCloseKey(clsid_key);
+    }
+
+error_close_coclass_key:
+    RegCloseKey(coclass_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             unregister_coclasses
+ */
+static HRESULT unregister_coclasses(struct regsvr_coclass const *list) {
+    LONG res = ERROR_SUCCESS;
+    HKEY coclass_key;
+
+    res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0,
+                       KEY_READ | KEY_WRITE, &coclass_key);
+    if (res == ERROR_FILE_NOT_FOUND) return S_OK;
+    if (res != ERROR_SUCCESS) goto error_return;
+
+    for (; res == ERROR_SUCCESS && list->clsid; ++list) {
+       WCHAR buf[39];
+
+       StringFromGUID2(list->clsid, buf, 39);
+       res = recursive_delete_keyW(coclass_key, buf);
+       if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+
+       if (list->progid) {
+           res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid);
+           if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+       }
+
+       if (list->viprogid) {
+           res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid);
+           if (res != ERROR_SUCCESS) goto error_close_coclass_key;
+       }
+    }
+
+error_close_coclass_key:
+    RegCloseKey(coclass_key);
+error_return:
+    return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK;
+}
+
+/***********************************************************************
+ *             regsvr_key_guid
+ */
+static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) {
+    WCHAR buf[39];
+
+    StringFromGUID2(guid, buf, 39);
+    return register_key_defvalueW(base, name, buf);
+}
+
+/***********************************************************************
+ *             regsvr_key_defvalueW
+ */
+static LONG register_key_defvalueW(
+    HKEY base,
+    WCHAR const *name,
+    WCHAR const *value) {
+    LONG res;
+    HKEY key;
+
+    res = RegCreateKeyExW(base, name, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &key, NULL);
+    if (res != ERROR_SUCCESS) return res;
+    res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+                        (lstrlenW(value) + 1) * sizeof(WCHAR));
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             regsvr_key_defvalueA
+ */
+static LONG register_key_defvalueA(
+    HKEY base,
+    WCHAR const *name,
+    char const *value) {
+    LONG res;
+    HKEY key;
+
+    res = RegCreateKeyExW(base, name, 0, NULL, 0,
+                         KEY_READ | KEY_WRITE, NULL, &key, NULL);
+    if (res != ERROR_SUCCESS) return res;
+    res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value,
+                        lstrlenA(value) + 1);
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             regsvr_progid
+ */
+static LONG register_progid(
+    WCHAR const *clsid,
+    char const *progid,
+    char const *curver_progid,
+    char const *name,
+    char const *extra) {
+    LONG res;
+    HKEY progid_key;
+
+    res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0,
+                         NULL, 0, KEY_READ | KEY_WRITE, NULL,
+                         &progid_key, NULL);
+    if (res != ERROR_SUCCESS) return res;
+
+    if (name) {
+       res = RegSetValueExA(progid_key, NULL, 0, REG_SZ,
+                            (CONST BYTE*)name, strlen(name) + 1);
+       if (res != ERROR_SUCCESS) goto error_close_progid_key;
+    }
+
+    if (clsid) {
+       res = register_key_defvalueW(progid_key, clsid_keyname, clsid);
+       if (res != ERROR_SUCCESS) goto error_close_progid_key;
+    }
+
+    if (curver_progid) {
+       res = register_key_defvalueA(progid_key, curver_keyname,
+                                    curver_progid);
+       if (res != ERROR_SUCCESS) goto error_close_progid_key;
+    }
+
+    if (extra) {
+       HKEY extra_key;
+
+       res = RegCreateKeyExA(progid_key, extra, 0,
+                             NULL, 0, KEY_READ | KEY_WRITE, NULL,
+                             &extra_key, NULL);
+       if (res == ERROR_SUCCESS)
+           RegCloseKey(extra_key);
+    }
+
+error_close_progid_key:
+    RegCloseKey(progid_key);
+    return res;
+}
+
+/***********************************************************************
+ *             recursive_delete_key
+ */
+static LONG recursive_delete_key(HKEY key) {
+    LONG res;
+    WCHAR subkey_name[MAX_PATH];
+    DWORD cName;
+    HKEY subkey;
+
+    for (;;) {
+       cName = sizeof(subkey_name) / sizeof(WCHAR);
+       res = RegEnumKeyExW(key, 0, subkey_name, &cName,
+                           NULL, NULL, NULL, NULL);
+       if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) {
+           res = ERROR_SUCCESS; /* presumably we're done enumerating */
+           break;
+       }
+       res = RegOpenKeyExW(key, subkey_name, 0,
+                           KEY_READ | KEY_WRITE, &subkey);
+       if (res == ERROR_FILE_NOT_FOUND) continue;
+       if (res != ERROR_SUCCESS) break;
+
+       res = recursive_delete_key(subkey);
+       RegCloseKey(subkey);
+       if (res != ERROR_SUCCESS) break;
+    }
+
+    if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0);
+    return res;
+}
+
+/***********************************************************************
+ *             recursive_delete_keyA
+ */
+static LONG recursive_delete_keyA(HKEY base, char const *name) {
+    LONG res;
+    HKEY key;
+
+    res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key);
+    if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+    if (res != ERROR_SUCCESS) return res;
+    res = recursive_delete_key(key);
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             recursive_delete_keyW
+ */
+static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) {
+    LONG res;
+    HKEY key;
+
+    res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key);
+    if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS;
+    if (res != ERROR_SUCCESS) return res;
+    res = recursive_delete_key(key);
+    RegCloseKey(key);
+    return res;
+}
+
+/***********************************************************************
+ *             coclass list
+ */
+static struct regsvr_coclass const coclass_list[] = {
+    {     
+        &CLSID_IMsiServer,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+       NULL
+    },    
+    {     
+        &CLSID_IMsiServerMessage,
+       "Wine Installer Message RPC",
+       NULL,
+       NULL,
+       "msi.dll",
+       NULL,
+       "WindowsInstaller.Message",
+       NULL
+    },
+    {     
+        &CLSID_IMsiServerX1,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+       NULL
+    },
+    {     
+        &CLSID_IMsiServerX2,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+       NULL
+    },
+    {     
+        &CLSID_IMsiServerX3,
+       "Msi install server",
+       "ole32.dll",
+       NULL,
+       "msi.dll",
+       "Apartment",
+       "WindowsInstaller.Installer",
+        NULL
+    },
+    { NULL }                   /* list terminator */
+};
+
+/***********************************************************************
+ *             interface list
+ */
+/*
+ * we should declare: (@see ole32/regsvr.c for examples)
+ [-HKEY_CLASSES_ROOT\Interface\{000C101C-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}] 
+ [-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}]
+*/
+static struct regsvr_interface const interface_list[] = {
+    { NULL }                   /* list terminator */
+};
+
+/***********************************************************************
+ *             DllRegisterServer
+ */
+HRESULT WINAPI MSI_DllRegisterServer(void) {
+    HRESULT hr;
+
+    TRACE("\n");
+
+    hr = register_coclasses(coclass_list);
+    if (SUCCEEDED(hr))
+       hr = register_interfaces(interface_list);
+    return hr;
+}
+
+/***********************************************************************
+ *             DllUnregisterServer
+ */
+HRESULT WINAPI MSI_DllUnregisterServer(void) {
+    HRESULT hr;
+
+    TRACE("\n");
+
+    hr = unregister_coclasses(coclass_list);
+    if (SUCCEEDED(hr))
+       hr = unregister_interfaces(interface_list);
+    return hr;
+}
diff --git a/reactos/lib/msi/select.c b/reactos/lib/msi/select.c
new file mode 100644 (file)
index 0000000..c9b53f8
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+
+#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_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm)
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", sv, row, col, stm );
+
+    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_stream( sv->table, row, col, stm );
+}
+
+static UINT SELECT_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %d %04x\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->set_int( sv->table, row, col, val );
+}
+
+static UINT SELECT_insert_row( struct tagMSIVIEW *view, UINT *num )  
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %p\n", sv, num );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->insert_row( sv->table, num );
+}
+
+static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %p\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_fetch_stream,
+    SELECT_set_int,
+    SELECT_insert_row,
+    SELECT_execute,
+    SELECT_close,
+    SELECT_get_dimensions,
+    SELECT_get_column_info,
+    SELECT_modify,
+    SELECT_delete
+};
+
+static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPWSTR name )
+{
+    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;
+}
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                        string_list *columns )
+{
+    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;
+
+    while( columns )
+    {
+        r = SELECT_AddColumn( sv, columns->string );
+        if( r )
+            break;
+        columns = columns->next;
+    }
+
+    if( r != ERROR_SUCCESS )
+    {
+        sv->view.ops->delete( &sv->view );
+        sv = NULL;
+    }
+
+    *view = &sv->view;
+
+    return r;
+}
diff --git a/reactos/lib/msi/sql.y b/reactos/lib/msi/sql.y
new file mode 100644 (file)
index 0000000..18600ed
--- /dev/null
@@ -0,0 +1,828 @@
+%{
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "query.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+extern int SQL_error(const char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_SQL_input
+{
+    MSIDATABASE *db;
+    LPCWSTR command;
+    DWORD n, len;
+    MSIVIEW **view;  /* view structure for the resulting query */
+} SQL_input;
+
+static LPWSTR SQL_getstring( struct sql_str *str );
+static INT SQL_getint( SQL_input *sql );
+static int SQL_lex( void *SQL_lval, SQL_input *info);
+
+static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
+                               string_list *columns );
+static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
+                             string_list *columns );
+
+static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
+                                 string_list *keys);
+
+static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r );
+static struct expr * EXPR_column( LPWSTR );
+static struct expr * EXPR_ival( struct sql_str *);
+static struct expr * EXPR_sval( struct sql_str *);
+static struct expr * EXPR_wildcard();
+
+%}
+
+%pure-parser
+
+%union
+{
+    struct sql_str str;
+    LPWSTR string;
+    string_list *column_list;
+    value_list *val_list;
+    MSIVIEW *query;
+    struct expr *expr;
+    USHORT column_type;
+    create_col_info *column_info;
+    column_assignment update_col_info;
+}
+
+%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_CHAR TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN
+%token TK_COMMA 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 TK_HOLD
+%token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
+%token <str> TK_ID 
+%token TK_INSERT TK_INSTEAD TK_INT 
+%token <str> TK_INTEGER
+%token TK_INTERSECT TK_INTO TK_IS
+%token TK_ISNULL
+%token TK_JOIN TK_JOIN_KW
+%token TK_KEY
+%token TK_LE TK_LIKE TK_LIMIT TK_LONG TK_LONGCHAR TK_LP TK_LSHIFT TK_LT
+%token TK_LOCALIZABLE
+%token TK_MATCH TK_MINUS
+%token TK_NE TK_NOT TK_NOTNULL TK_NULL
+%token TK_OBJECT 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_SHORT TK_SLASH TK_SPACE TK_STAR TK_STATEMENT 
+%token <str> 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 TK_WILDCARD
+
+/*
+ * 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 <string> column table string_or_id
+%type <column_list> selcollist
+%type <query> from unorderedsel oneselect onequery onecreate oneinsert oneupdate
+%type <expr> expr val column_val const_val
+%type <column_type> column_type data_type data_type_l data_count
+%type <column_info> column_def table_def
+%type <val_list> constlist
+%type <update_col_info> column_assignment update_assign_list
+
+%%
+
+onequery:
+    oneselect
+    {
+        SQL_input* sql = (SQL_input*) info;
+        *sql->view = $1;
+    }
+  | onecreate
+    {
+        SQL_input* sql = (SQL_input*) info;
+        *sql->view = $1;
+    }
+  | oneinsert
+    {
+        SQL_input* sql = (SQL_input*) info;
+        *sql->view = $1;
+    }
+  | oneupdate
+    {
+        SQL_input* sql = (SQL_input*) info;
+        *sql->view = $1;
+    }
+    ;
+
+oneinsert:
+    TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP
+    {
+        SQL_input *sql = (SQL_input*) info;
+        MSIVIEW *insert = NULL; 
+
+        INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); 
+        $$ = insert;
+    }
+  | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMP
+    {
+        SQL_input *sql = (SQL_input*) info;
+        MSIVIEW *insert = NULL; 
+
+        INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); 
+        $$ = insert;
+    }
+    ;
+
+onecreate:
+    TK_CREATE TK_TABLE table TK_LP table_def TK_RP
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *create = NULL; 
+
+            if( !$5 )
+                YYABORT;
+            CREATE_CreateView( sql->db, &create, $3, $5, FALSE );
+            $$ = create;
+        }
+  | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *create = NULL; 
+
+            if( !$5 )
+                YYABORT;
+            CREATE_CreateView( sql->db, &create, $3, $5, TRUE );
+            $$ = create;
+        }
+    ;
+
+oneupdate:
+    TK_UPDATE table TK_SET update_assign_list TK_WHERE expr
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *update = NULL; 
+
+            UPDATE_CreateView( sql->db, &update, $2, &$4, $6 );
+            $$ = update;
+        }
+    ;
+
+table_def:
+    column_def TK_PRIMARY TK_KEY selcollist
+        {
+            if( SQL_MarkPrimaryKeys( $1, $4 ) )
+                $$ = $1;
+            else
+                $$ = NULL;
+        }
+    ;
+
+column_def:
+    column_def TK_COMMA column column_type
+        {
+            create_col_info *ci;
+
+            for( ci = $1; ci->next; ci = ci->next )
+                ;
+
+            ci->next = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
+            if( !ci->next )
+            {
+                /* FIXME: free $1 */
+                YYABORT;
+            }
+            ci->next->colname = $3;
+            ci->next->type = $4;
+            ci->next->next = NULL;
+
+            $$ = $1;
+        }
+  | column column_type
+        {
+            $$ = HeapAlloc( GetProcessHeap(), 0, sizeof *$$ );
+            if( ! $$ )
+                YYABORT;
+            $$->colname = $1;
+            $$->type = $2;
+            $$->next = NULL;
+        }
+    ;
+
+column_type:
+    data_type_l
+        {
+            $$ = $1 | MSITYPE_VALID;
+        }
+  | data_type_l TK_LOCALIZABLE
+        {
+            FIXME("LOCALIZABLE ignored\n");
+            $$ = $1 | MSITYPE_VALID;
+        }
+    ;
+
+data_type_l:
+    data_type
+        {
+            $$ |= MSITYPE_NULLABLE;
+        }
+  | data_type TK_NOT TK_NULL
+        {
+            $$ = $1;
+        }
+    ;
+
+data_type:
+    TK_CHAR
+        {
+            $$ = MSITYPE_STRING | 1;
+        }
+  | TK_CHAR TK_LP data_count TK_RP
+        {
+            $$ = MSITYPE_STRING | 0x400 | $3;
+        }
+  | TK_LONGCHAR
+        {
+            $$ = 2;
+        }
+  | TK_SHORT
+        {
+            $$ = 2;
+        }
+  | TK_INT
+        {
+            $$ = 2;
+        }
+  | TK_LONG
+        {
+            $$ = 4;
+        }
+  | TK_OBJECT
+        {
+            $$ = 0;
+        }
+    ;
+
+data_count:
+    TK_INTEGER
+        {
+            SQL_input* sql = (SQL_input*) info;
+            int val = SQL_getint(sql);
+            if( ( val > 255 ) || ( val < 0 ) )
+                YYABORT;
+            $$ = val;
+        }
+    ;
+
+oneselect:
+    unorderedsel TK_ORDER TK_BY selcollist
+        {
+            SQL_input* sql = (SQL_input*) info;
+
+            if( !$1 )
+                YYABORT;
+            if( $4 )
+                $$ = do_order_by( sql->db, $1, $4 );
+            else
+                $$ = $1;
+        }
+  | unorderedsel
+    ;
+
+unorderedsel:
+    TK_SELECT selcollist from 
+        {
+            SQL_input* sql = (SQL_input*) info;
+            if( !$3 )
+                YYABORT;
+            if( $2 )
+            {
+                $$ = do_one_select( sql->db, $3, $2 );
+                if( !$$ )
+                    YYABORT;
+            }
+            else
+                $$ = $3;
+        }
+  | TK_SELECT TK_DISTINCT selcollist from 
+        {
+            SQL_input* sql = (SQL_input*) info;
+            MSIVIEW *view = $4;
+
+            if( !view )
+                YYABORT;
+            if( $3 )
+            {
+                view = do_one_select( sql->db, view, $3 );
+                if( !view )
+                    YYABORT;
+            }
+            DISTINCT_CreateView( sql->db, & $$, view );
+        }
+    ;
+
+selcollist:
+    column 
+        { 
+            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
+        { 
+            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
+        { 
+            SQL_input* sql = (SQL_input*) info;
+            UINT r;
+
+            $$ = NULL;
+            TRACE("From table: %s\n",debugstr_w($2));
+            r = TABLE_CreateView( sql->db, $2, & $$ );
+            if( r != ERROR_SUCCESS )
+                YYABORT;
+        }
+  | TK_FROM table TK_WHERE expr
+        { 
+            SQL_input* sql = (SQL_input*) 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, $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
+  | const_val
+    ;
+
+constlist:
+    const_val
+        {
+            value_list *vals;
+
+            vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
+            if( vals )
+            {
+                vals->val = $1;
+                vals->next = NULL;
+            }
+            $$ = vals;
+        }
+  | constlist TK_COMMA const_val
+        {
+            value_list *vals;
+
+            vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals );
+            if( vals )
+            {
+                vals->val = $3;
+                vals->next = NULL;
+            }
+            $1->next = vals;
+            $$ = $1;
+        }
+    ;
+
+update_assign_list:
+    column_assignment
+  | column_assignment TK_COMMA update_assign_list
+        {
+            $1.col_list->next = $3.col_list;
+            $1.val_list->next = $3.val_list;
+            $$ = $1;
+        }
+    ;
+
+column_assignment:
+    column TK_EQ const_val
+        {
+            $$.col_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.col_list );
+            if( !$$.col_list )
+                YYABORT;
+            $$.col_list->string = $1;
+            $$.col_list->next = NULL;
+            $$.val_list = HeapAlloc( GetProcessHeap(), 0, sizeof *$$.val_list );
+            if( !$$.val_list )
+                YYABORT;
+            $$.val_list->val = $3;
+            $$.val_list->next = 0;
+        }
+    ;
+
+const_val:
+    TK_INTEGER
+        {
+            $$ = EXPR_ival( &$1 );
+        }
+  | TK_STRING
+        {
+            $$ = EXPR_sval( &$1 );
+        }
+  | TK_WILDCARD
+        {
+            $$ = EXPR_wildcard();
+        }
+    ;
+
+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
+        {
+            $$ = SQL_getstring( &$1 );
+        }
+  | TK_STRING
+        {
+            $$ = SQL_getstring( &$1 );
+        }
+    ;
+
+%%
+
+int SQL_lex( void *SQL_lval, SQL_input *sql)
+{
+    int token;
+    struct sql_str * str = SQL_lval;
+
+    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;
+        str->data = &sql->command[sql->n];
+        str->len = sql->len;
+    }
+    while( token == TK_SPACE );
+
+    TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len));
+    
+    return token;
+}
+
+LPWSTR SQL_getstring( struct sql_str *strdata)
+{
+    LPCWSTR p = strdata->data;
+    UINT len = strdata->len;
+    LPWSTR str;
+
+    /* if there's quotes, remove them */
+    if( ( (p[0]=='`') && (p[len-1]=='`') ) || 
+        ( (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 SQL_getint( SQL_input *sql )
+{
+    LPCWSTR p = &sql->command[sql->n];
+
+    return atoiW( p );
+}
+
+int SQL_error(const char *str)
+{
+    return 0;
+}
+
+static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
+                               string_list *columns )
+{
+    MSIVIEW *view = NULL;
+
+    SELECT_CreateView( db, &view, in, columns );
+    delete_string_list( columns );
+    if( !view )
+        ERR("Error creating select query\n");
+    return view;
+}
+
+static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
+                             string_list *columns )
+{
+    MSIVIEW *view = NULL;
+
+    ORDER_CreateView( db, &view, in );
+    if( view )
+    {
+        string_list *x = columns;
+
+        for( x = columns; x ; x = x->next )
+            ORDER_AddColumn( view, x->string );
+    }
+    else
+        ERR("Error creating select query\n");
+    delete_string_list( columns );
+    return view;
+}
+
+static struct expr * EXPR_wildcard()
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_WILDCARD;
+    }
+    return e;
+}
+
+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 str )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COLUMN;
+        e->u.sval = str;
+    }
+    return e;
+}
+
+static struct expr * EXPR_ival( struct sql_str *str )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_IVAL;
+        e->u.ival = atoiW( str->data );
+    }
+    return e;
+}
+
+static struct expr * EXPR_sval( struct sql_str *str )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_SVAL;
+        e->u.sval = SQL_getstring( str );
+    }
+    return e;
+}
+
+void delete_expr( struct expr *e )
+{
+    if( !e )
+        return;
+    if( e->type == EXPR_COMPLEX )
+    {
+        delete_expr( e->u.expr.left );
+        delete_expr( e->u.expr.right );
+    }
+    else if( e->type == EXPR_UTF8 )
+        HeapFree( GetProcessHeap(), 0, e->u.utf8 );
+    else if( e->type == EXPR_SVAL )
+        HeapFree( GetProcessHeap(), 0, e->u.sval );
+    HeapFree( GetProcessHeap(), 0, e );
+}
+
+void delete_string_list( string_list *sl )
+{
+    while( sl )
+    {
+        string_list *t = sl->next;
+        HeapFree( GetProcessHeap(), 0, sl->string );
+        HeapFree( GetProcessHeap(), 0, sl );
+        sl = t;
+    }
+}
+
+void delete_value_list( value_list *vl )
+{
+    while( vl )
+    {
+        value_list *t = vl->next;
+        delete_expr( vl->val );
+        HeapFree( GetProcessHeap(), 0, vl );
+        vl = t;
+    }
+}
+
+static BOOL SQL_MarkPrimaryKeys( create_col_info *cols,
+                                 string_list *keys )
+{
+    string_list *k;
+    BOOL found = TRUE;
+
+    for( k = keys; k && found; k = k->next )
+    {
+        create_col_info *c;
+
+        found = FALSE;
+        for( c = cols; c && !found; c = c->next )
+        {
+             if( lstrcmpW( k->string, c->colname ) )
+                 continue;
+             c->type |= MSITYPE_KEY;
+             found = TRUE;
+        }
+    }
+
+    return found;
+}
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview )
+{
+    SQL_input sql;
+    int r;
+
+    *phview = NULL;
+
+    sql.db = db;
+    sql.command = command;
+    sql.n = 0;
+    sql.len = 0;
+    sql.view = phview;
+
+    r = SQL_parse(&sql);
+
+    TRACE("Parse returned %d\n", r);
+    if( r )
+    {
+        if( *sql.view )
+            (*sql.view)->ops->delete( *sql.view );
+        *sql.view = NULL;
+        return ERROR_BAD_QUERY_SYNTAX;
+    }
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/string.c b/reactos/lib/msi/string.c
new file mode 100644 (file)
index 0000000..8cc4dc1
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004, 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 <stdarg.h>
+#include <assert.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "wine/unicode.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 _msistring
+{
+    UINT hash;
+    UINT refcount;
+    LPWSTR str;
+} msistring;
+
+struct string_table
+{
+    UINT maxcount;         /* the number of strings */
+    UINT freeslot;
+    UINT codepage;
+    msistring *strings; /* an array of strings (in the tree) */
+};
+
+static UINT msistring_makehash( const WCHAR *str )
+{
+    UINT hash = 0;
+
+    if (str==NULL)
+        return hash;
+
+    while( *str )
+    {
+        hash ^= *str++;
+        hash *= 53;
+        hash = (hash<<5) | (hash>>27);
+    }
+    return hash;
+}
+
+string_table *msi_init_stringtable( int entries, UINT codepage )
+{
+    string_table *st;
+
+    st = HeapAlloc( GetProcessHeap(), 0, sizeof (string_table) );
+    if( !st )
+        return NULL;    
+    st->strings = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                              sizeof (msistring) * entries );
+    if( !st )
+    {
+        HeapFree( GetProcessHeap(), 0, st );
+        return NULL;    
+    }
+    if( entries < 1 )
+        entries = 1;
+    st->maxcount = entries;
+    st->freeslot = 1;
+    st->codepage = codepage;
+
+    return st;
+}
+
+VOID msi_destroy_stringtable( string_table *st )
+{
+    UINT i;
+
+    for( i=0; i<st->maxcount; i++ )
+    {
+        if( st->strings[i].refcount )
+            HeapFree( GetProcessHeap(), 0, st->strings[i].str );
+    }
+    HeapFree( GetProcessHeap(), 0, st->strings );
+    HeapFree( GetProcessHeap(), 0, st );
+}
+
+static int st_find_free_entry( string_table *st )
+{
+    UINT i, sz;
+    msistring *p;
+
+    TRACE("%p\n", st);
+
+    if( st->freeslot )
+    {
+        for( i = st->freeslot; i < st->maxcount; i++ )
+            if( !st->strings[i].refcount )
+                return i;
+    }
+    for( i = 1; i < st->maxcount; i++ )
+        if( !st->strings[i].refcount )
+            return i;
+
+    /* dynamically resize */
+    sz = st->maxcount + 1 + st->maxcount/2;
+    p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
+                     st->strings, sz*sizeof(msistring) );
+    if( !p )
+        return -1;
+    st->strings = p;
+    st->freeslot = st->maxcount;
+    st->maxcount = sz;
+    if( st->strings[st->freeslot].refcount )
+        ERR("oops. expected freeslot to be free...\n");
+    return st->freeslot;
+}
+
+static void st_mark_entry_used( string_table *st, UINT n )
+{
+    if( n >= st->maxcount )
+        return;
+    st->freeslot = n + 1;
+}
+
+int msi_addstring( string_table *st, int n, const CHAR *data, int len, UINT refcount )
+{
+    int sz;
+
+    if( !data )
+        return 0;
+    if( !data[0] )
+        return 0;
+    if( n > 0 )
+    {
+        if( st->strings[n].refcount )
+            return -1;
+    }
+    else
+    {
+        if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) )
+        {
+            st->strings[n].refcount++;
+            return n;
+        }
+        n = st_find_free_entry( st );
+        if( n < 0 )
+            return -1;
+    }
+
+    if( n < 1 )
+    {
+        ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n );
+        return -1;
+    }
+
+    /* allocate a new string */
+    if( len < 0 )
+        len = strlen(data);
+    sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 );
+    st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, (sz+1)*sizeof(WCHAR) );
+    if( !st->strings[n].str )
+        return -1;
+    MultiByteToWideChar( st->codepage, 0, data, len, st->strings[n].str, sz );
+    st->strings[n].str[sz] = 0;
+    st->strings[n].refcount = 1;
+    st->strings[n].hash = msistring_makehash( st->strings[n].str );
+
+    st_mark_entry_used( st, n );
+
+    return n;
+}
+
+int msi_addstringW( string_table *st, int n, const WCHAR *data, int len, UINT refcount )
+{
+    /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
+
+    if( !data )
+        return 0;
+    if( !data[0] )
+        return 0;
+    if( n > 0 )
+    {
+        if( st->strings[n].refcount )
+            return -1;
+    }
+    else
+    {
+        if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) )
+        {
+            st->strings[n].refcount++;
+            return n;
+        }
+        n = st_find_free_entry( st );
+        if( n < 0 )
+            return -1;
+    }
+
+    if( n < 1 )
+    {
+        ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n );
+        return -1;
+    }
+
+    /* allocate a new string */
+    if(len<0)
+        len = strlenW(data);
+    TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len );
+
+    st->strings[n].str = HeapAlloc( GetProcessHeap(), 0, (len+1)*sizeof(WCHAR) );
+    if( !st->strings[n].str )
+        return -1;
+    TRACE("%d\n",__LINE__);
+    memcpy( st->strings[n].str, data, len*sizeof(WCHAR) );
+    st->strings[n].str[len] = 0;
+    st->strings[n].refcount = 1;
+    st->strings[n].hash = msistring_makehash( st->strings[n].str );
+
+    st_mark_entry_used( st, n );
+
+    return n;
+}
+
+/* find the string identified by an id - return null if there's none */
+const WCHAR *msi_string_lookup_id( string_table *st, UINT id )
+{
+    static const WCHAR zero[] = { 0 };
+    if( id == 0 )
+        return zero;
+
+    if( id >= st->maxcount )
+        return NULL;
+
+    if( id && !st->strings[id].refcount )
+        return NULL;
+
+    return st->strings[id].str;
+}
+
+/*
+ *  msi_id2stringW
+ *
+ *  [in] st         - pointer to the string table
+ *  [in] id  - id of the string to retrieve
+ *  [out] buffer    - destination of the string
+ *  [in/out] sz     - number of bytes available in the buffer on input
+ *                    number of bytes used on output
+ *
+ *   The size includes the terminating nul character.  Short buffers
+ *  will be filled, but not nul terminated.
+ */
+UINT msi_id2stringW( string_table *st, UINT id, LPWSTR buffer, UINT *sz )
+{
+    UINT len;
+    const WCHAR *str;
+
+    TRACE("Finding string %d of %d\n", id, st->maxcount);
+
+    str = msi_string_lookup_id( st, id );
+    if( !str )
+        return ERROR_FUNCTION_FAILED;
+
+    len = strlenW( str ) + 1;
+
+    if( !buffer )
+    {
+        *sz = len;
+        return ERROR_SUCCESS;
+    }
+
+    if( *sz < len )
+        *sz = len;
+    memcpy( buffer, str, (*sz)*sizeof(WCHAR) ); 
+    *sz = len;
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ *  msi_id2stringA
+ *
+ *  [in] st         - pointer to the string table
+ *  [in] id         - id of the string to retrieve
+ *  [out] buffer    - destination of the UTF8 string
+ *  [in/out] sz     - number of bytes available in the buffer on input
+ *                    number of bytes used on output
+ *
+ *   The size includes the terminating nul character.  Short buffers
+ *  will be filled, but not nul terminated.
+ */
+UINT msi_id2stringA( string_table *st, UINT id, LPSTR buffer, UINT *sz )
+{
+    UINT len;
+    const WCHAR *str;
+    int n;
+
+    TRACE("Finding string %d of %d\n", id, st->maxcount);
+
+    str = msi_string_lookup_id( st, id );
+    if( !str )
+        return ERROR_FUNCTION_FAILED;
+
+    len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL );
+
+    if( !buffer )
+    {
+        *sz = len;
+        return ERROR_SUCCESS;
+    }
+
+    if( len > *sz )
+    {
+        n = strlenW( str ) + 1;
+        while( n && (len > *sz) )
+            len = WideCharToMultiByte( st->codepage, 0, 
+                           str, --n, NULL, 0, NULL, NULL );
+    }
+    else
+        n = -1;
+
+    *sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, NULL, NULL );
+
+    return ERROR_SUCCESS;
+}
+
+/*
+ *  msi_string2idW
+ *
+ *  [in] st         - pointer to the string table
+ *  [in] str        - string to find in the string table
+ *  [out] id        - id of the string, if found
+ */
+UINT msi_string2idW( string_table *st, LPCWSTR str, UINT *id )
+{
+    UINT hash;
+    UINT i, r = ERROR_INVALID_PARAMETER;
+
+    hash = msistring_makehash( str );
+    for( i=0; i<st->maxcount; i++ )
+    {
+        if ( (str == NULL && st->strings[i].str == NULL) || 
+            ( ( st->strings[i].hash == hash ) &&
+            !strcmpW( st->strings[i].str, str ) ))
+        {
+            r = ERROR_SUCCESS;
+            *id = i;
+            break;
+        }
+    }
+
+    return r;
+}
+
+UINT msi_string2idA( string_table *st, LPCSTR buffer, UINT *id )
+{
+    DWORD sz;
+    UINT r = ERROR_INVALID_PARAMETER;
+    LPWSTR str;
+
+    TRACE("Finding string %s in string table\n", debugstr_a(buffer) );
+
+    if( buffer[0] == 0 )
+    {
+        *id = 0;
+        return ERROR_SUCCESS;
+    }
+
+    sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 );
+    if( sz <= 0 )
+        return r;
+    str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof(WCHAR) );
+    if( !str )
+        return ERROR_NOT_ENOUGH_MEMORY;
+    MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz );
+
+    r = msi_string2idW( st, str, id );
+    if( str )
+        HeapFree( GetProcessHeap(), 0, str );
+
+    return r;
+}
+
+UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res )
+{
+    const WCHAR *l_str, *r_str;
+
+    l_str = msi_string_lookup_id( st, lval );
+    if( !l_str )
+        return ERROR_INVALID_PARAMETER;
+    
+    r_str = msi_string_lookup_id( st, rval );
+    if( !r_str )
+        return ERROR_INVALID_PARAMETER;
+
+    /* does this do the right thing for all UTF-8 strings? */
+    *res = strcmpW( l_str, r_str );
+
+    return ERROR_SUCCESS;
+}
+
+UINT msi_string_count( string_table *st )
+{
+    return st->maxcount;
+}
+
+UINT msi_id_refcount( string_table *st, UINT i )
+{
+    if( i >= st->maxcount )
+        return 0;
+    return st->strings[i].refcount;
+}
+
+UINT msi_string_totalsize( string_table *st, UINT *total )
+{
+    UINT size = 0, i, len;
+
+    if( st->strings[0].str || st->strings[0].refcount )
+        ERR("oops. element 0 has a string\n");
+    *total = 0;
+    for( i=1; i<st->maxcount; i++ )
+    {
+        if( st->strings[i].str )
+        {
+            TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str));
+            len = WideCharToMultiByte( st->codepage, 0,
+                     st->strings[i].str, -1, NULL, 0, NULL, NULL);
+            if( len )
+                len--;
+            size += len;
+            *total = (i+1);
+        }
+    }
+    TRACE("%u/%u strings %u bytes codepage %x\n", *total, st->maxcount, size, st->codepage );
+    return size;
+}
+
+UINT msi_string_get_codepage( string_table *st )
+{
+    return st->codepage;
+}
diff --git a/reactos/lib/msi/suminfo.c b/reactos/lib/msi/suminfo.c
new file mode 100644 (file)
index 0000000..29caa7d
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+
+#define PRSPEC_PROPID (1)
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winnls.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);
+
+static 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( MSIOBJECTHDR *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 )
+    {
+        UINT len = MultiByteToWideChar( CP_ACP, 0, szDatabase, -1, NULL, 0 );
+        szwDatabase = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+        if( !szwDatabase )
+            return ERROR_FUNCTION_FAILED;
+        MultiByteToWideChar( CP_ACP, 0, szDatabase, -1, szwDatabase, len );
+    }
+
+    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;
+    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 = MSI_OpenDatabaseW(szDatabase, NULL, &db);
+        if( res != ERROR_SUCCESS )
+            return res;
+    }
+    else
+    {
+        db = msihandle2msiinfo(hDatabase, 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");
+        if (db)
+            msiobj_release(&db->hdr);
+        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;
+    }
+
+    suminfo = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, 
+                  sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
+    if( !suminfo )
+    {
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    IPropertyStorage_AddRef(ps);
+    suminfo->propstg = ps;
+    handle = alloc_msihandle( &suminfo->hdr );
+    if( handle )
+    *phSummaryInfo = handle;
+    else
+        ret = ERROR_FUNCTION_FAILED;
+    msiobj_release( &suminfo->hdr );
+
+end:
+    if( ps )
+        IPropertyStorage_Release(ps);
+    if( psstg )
+        IPropertySetStorage_Release(psstg);
+    if (db)
+        msiobj_release(&db->hdr);
+
+    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.u.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.u.lVal;
+        break;
+    case VT_LPSTR:
+        if( pcchValueBuf && szValueBuf )
+        {
+            lstrcpynA(szValueBuf, var.u.pszVal, *pcchValueBuf );
+            *pcchValueBuf = lstrlenA( var.u.pszVal );
+        }
+        break;
+    case VT_FILETIME:
+        if( pftValue )
+            memcpy(pftValue, &var.u.filetime, sizeof (FILETIME) );
+        break;
+    case VT_EMPTY:
+        break;
+    default:
+        FIXME("Unknown property variant type\n");
+        break;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyW(
+      MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue,
+      FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
+{
+    FIXME("%ld %d %p %p %p %p %p\n",
+        hSummaryInfo, uiProperty, puiDataType, piValue,
+        pftValue, szValueBuf, pcchValueBuf);
+    
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/reactos/lib/msi/table.c b/reactos/lib/msi/table.c
new file mode 100644 (file)
index 0000000..3073bad
--- /dev/null
@@ -0,0 +1,1425 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 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 <stdarg.h>
+
+#define COBJMACROS
+#define NONAMELESSUNION
+#define NONAMELESSSTRUCT
+
+#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 "wine/unicode.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 ref_count;
+    UINT row_count;
+    struct tagMSITABLE *next;
+    struct tagMSITABLE *prev;
+    WCHAR name[1];
+};
+
+#define MAX_STREAM_NAME 0x1f
+
+static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
+       MSICOLUMNINFO **pcols, UINT *pcount );
+static UINT get_tablecolumns( MSIDATABASE *db, 
+       LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
+
+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;
+}
+
+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 LPWSTR encode_streamname(BOOL bTable, LPCWSTR in)
+{
+    DWORD count = MAX_STREAM_NAME;
+    DWORD ch, next;
+    LPWSTR out, p;
+
+    if( !bTable )
+        count = strlenW( in )+2;
+    out = HeapAlloc( GetProcessHeap(), 0, count*sizeof(WCHAR) );
+    p = out;
+
+    if( bTable )
+    {
+         *p++ = 0x4840;
+         count --;
+    }
+    while( count -- ) 
+    {
+        ch = *in++;
+        if( !ch )
+        {
+            *p = ch;
+            return out;
+        }
+        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++;
+                }
+            }
+        }
+        *p++ = ch;
+    }
+    ERR("Failed to encode stream name (%s)\n",debugstr_w(in));
+    HeapFree( GetProcessHeap(), 0, out );
+    return NULL;
+}
+
+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;
+}
+
+void enum_stream_names( IStorage *stg )
+{
+    IEnumSTATSTG *stgenum = NULL;
+    HRESULT r;
+    STATSTG stat;
+    ULONG n, count;
+    WCHAR name[0x40];
+
+    r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
+    if( FAILED( r ) )
+        return;
+
+    n = 0;
+    while( 1 )
+    {
+        count = 0;
+        r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
+        if( FAILED( r ) || !count )
+            break;
+        decode_streamname( stat.pwcsName, name );
+        ERR("stream %2ld -> %s %s\n", n, 
+            debugstr_w(stat.pwcsName), debugstr_w(name) );
+        n++;
+    }
+
+    IEnumSTATSTG_Release( stgenum );
+}
+
+static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
+                              USHORT **pdata, UINT *psz )
+{
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    VOID *data;
+    ULONG sz, count;
+    IStream *stm = NULL;
+    STATSTG stat;
+    LPWSTR encname;
+
+    encname = encode_streamname(TRUE, stname);
+
+    TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+    r = IStorage_OpenStream(stg, encname, NULL, 
+            STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    HeapFree( GetProcessHeap(), 0, encname );
+    if( FAILED( r ) )
+    {
+        WARN("open stream failed r = %08lx - empty table?\n",r);
+        return ret;
+    }
+
+    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);
+        ret = ERROR_NOT_ENOUGH_MEMORY;
+        goto end;
+    }
+        
+    r = IStream_Read(stm, data, sz, &count );
+    if( FAILED( r ) || ( count != sz ) )
+    {
+        HeapFree( GetProcessHeap(), 0, data );
+        ERR("read stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    *pdata = data;
+    *psz = sz;
+    ret = ERROR_SUCCESS;
+
+end:
+    IStream_Release( stm );
+
+    return ret;
+}
+
+UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
+{
+    LPWSTR encname;
+    HRESULT r;
+
+    encname = encode_streamname(FALSE, stname);
+
+    TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+    r = IStorage_OpenStream(db->storage, encname, NULL, 
+            STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
+    HeapFree( GetProcessHeap(), 0, encname );
+    if( FAILED( r ) )
+    {
+        WARN("open stream failed r = %08lx - empty table?\n",r);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname,
+                              USHORT **pdata, UINT *psz )
+{
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    VOID *data;
+    ULONG sz, count;
+    IStream *stm = NULL;
+    STATSTG stat;
+
+    r = db_get_raw_stream( db, stname, &stm );
+    if( r != ERROR_SUCCESS)
+        return ret;
+    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);
+        ret = ERROR_NOT_ENOUGH_MEMORY;
+        goto end;
+    }
+        
+    r = IStream_Read(stm, data, sz, &count );
+    if( FAILED( r ) || ( count != sz ) )
+    {
+        HeapFree( GetProcessHeap(), 0, data );
+        ERR("read stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    *pdata = data;
+    *psz = sz;
+    ret = ERROR_SUCCESS;
+
+end:
+    IStream_Release( stm );
+
+    return ret;
+}
+
+static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
+                               LPVOID data, UINT sz )
+{
+    HRESULT r;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    ULONG count;
+    IStream *stm = NULL;
+    ULARGE_INTEGER size;
+    LARGE_INTEGER pos;
+    LPWSTR encname;
+
+    encname = encode_streamname(TRUE, stname );
+    r = IStorage_OpenStream( stg, encname, NULL, 
+            STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    HeapFree( GetProcessHeap(), 0, encname );
+    if( FAILED(r) )
+    {
+        r = IStorage_CreateStream( stg, encname,
+                STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    }
+    if( FAILED( r ) )
+    {
+        ERR("open stream failed r = %08lx\n",r);
+        return ret;
+    }
+
+    size.QuadPart = sz;
+    r = IStream_SetSize( stm, size );
+    if( FAILED( r ) )
+    {
+        ERR("Failed to SetSize\n");
+        goto end;
+    }
+
+    pos.QuadPart = 0;
+    r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
+    if( FAILED( r ) )
+    {
+        ERR("Failed to Seek\n");
+        goto end;
+    }
+
+    r = IStream_Write(stm, data, sz, &count );
+    if( FAILED( r ) || ( count != sz ) )
+    {
+        ERR("Failed to Write\n");
+        goto end;
+    }
+
+    ret = ERROR_SUCCESS;
+
+end:
+    IStream_Release( stm );
+
+    return ret;
+}
+
+UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
+{
+    MSITABLE *t;
+    USHORT *rawdata = NULL;
+    UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
+    MSICOLUMNINFO *cols, *last_col;
+
+    TRACE("%s\n",debugstr_w(name));
+
+    /* non-existing tables should be interpretted as empty tables */
+    t = HeapAlloc( GetProcessHeap(), 0, 
+                   sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
+    if( !t )
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    r = table_get_column_info( db, name, &cols, &num_cols );
+    if( r != ERROR_SUCCESS )
+    {
+        HeapFree( GetProcessHeap(), 0, t );
+        return r;
+    }
+    last_col = &cols[num_cols-1];
+    row_size = last_col->offset + bytes_per_column( last_col );
+
+    t->row_count = 0;
+    t->data = NULL;
+    lstrcpyW( t->name, name );
+    t->ref_count = 1;
+    *ptable = t;
+
+    /* if we can't read the table, just assume that it's empty */
+    read_stream_data( db->storage, name, &rawdata, &rawsize );
+    if( !rawdata )
+        return ERROR_SUCCESS;
+
+    TRACE("Read %d bytes\n", rawsize );
+
+    if( rawsize % row_size )
+    {
+        ERR("Table size is invalid %d/%d\n", rawsize, row_size );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    t->row_count = rawsize / row_size;
+    t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                         t->row_count * sizeof (USHORT*) );
+    if( !t->data )
+        return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
+
+    /* transpose all the data */
+    TRACE("Transposing data from %d columns\n", t->row_count );
+    for( i=0; i<t->row_count; i++ )
+    {
+        t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
+        if( !t->data[i] )
+            return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
+        for( j=0; j<num_cols; j++ )
+        {
+            UINT ofs = cols[j].offset/2;
+            UINT n = bytes_per_column( &cols[j] );
+
+            switch( n )
+            {
+            case 2:
+                t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
+                break;
+            case 4:
+                t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
+                t->data[i][ofs+1] = rawdata[ofs*t->row_count + i + 1];
+                break;
+            default:
+                ERR("oops - unknown column width %d\n", n);
+                return ERROR_FUNCTION_FAILED;
+            }
+        }
+    }
+
+    HeapFree( GetProcessHeap(), 0, cols );
+    HeapFree( GetProcessHeap(), 0, rawdata );
+
+    return ERROR_SUCCESS;
+}
+
+/* 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;
+}
+
+static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
+{
+    UINT r, column_count;
+    MSICOLUMNINFO *columns;
+
+    /* 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 %s found\n", debugstr_w(name) );
+
+    columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
+    if( !columns )
+        return ERROR_FUNCTION_FAILED;
+
+    r = get_tablecolumns( db, name, columns, &column_count );
+    if( r != ERROR_SUCCESS )
+    {
+        HeapFree( GetProcessHeap(), 0, columns );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    *pcols = columns;
+    *pcount = column_count;
+
+    return r;
+}
+
+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, 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 save_table( MSIDATABASE *db, MSITABLE *t )
+{
+    USHORT *rawdata = NULL, *p;
+    UINT rawsize, r, i, j, row_size, num_cols = 0;
+    MSICOLUMNINFO *cols, *last_col;
+
+    TRACE("Saving %s\n", debugstr_w( t->name ) );
+
+    r = table_get_column_info( db, t->name, &cols, &num_cols );
+    if( r != ERROR_SUCCESS )
+        return r;
+    
+    last_col = &cols[num_cols-1];
+    row_size = last_col->offset + bytes_per_column( last_col );
+
+    rawsize = t->row_count * row_size;
+    rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
+    if( !rawdata )
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    p = rawdata;
+    for( i=0; i<num_cols; i++ )
+    {
+        for( j=0; j<t->row_count; j++ )
+        {
+            UINT offset = cols[i].offset;
+
+            *p++ = t->data[j][offset/2];
+            if( 4 == bytes_per_column( &cols[i] ) )
+                *p++ = t->data[j][offset/2+1];
+        }
+    }
+
+    TRACE("writing %d bytes\n", rawsize);
+    r = write_stream_data( db->storage, t->name, rawdata, rawsize );
+
+    HeapFree( GetProcessHeap(), 0, rawdata );
+
+    return r;
+}
+
+HRESULT init_string_table( IStorage *stg )
+{
+    HRESULT r;
+    static const WCHAR szStringData[] = {
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    static const WCHAR szStringPool[] = {
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+    USHORT zero[2] = { 0, 0 };
+    ULONG count = 0;
+    IStream *stm = NULL;
+    LPWSTR encname;
+
+    encname = encode_streamname(TRUE, szStringPool );
+
+    /* create the StringPool stream... add the zero string to it*/
+    r = IStorage_CreateStream( stg, encname,
+            STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    HeapFree( GetProcessHeap(), 0, encname );
+    if( r ) 
+    {
+        TRACE("Failed\n");
+        return r;
+    }
+
+    r = IStream_Write(stm, zero, sizeof zero, &count );
+    IStream_Release( stm );
+
+    if( FAILED( r ) || ( count != sizeof zero ) )
+    {
+        TRACE("Failed\n");
+        return E_FAIL;
+    }
+
+    /* create the StringData stream... make it zero length */
+    encname = encode_streamname(TRUE, szStringData );
+    r = IStorage_CreateStream( stg, encname,
+            STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
+    HeapFree( GetProcessHeap(), 0, encname );
+    if( r ) 
+    {
+        TRACE("Failed\n");
+        return E_FAIL;
+    }
+    IStream_Release( stm );
+
+    return r;
+}
+
+UINT load_string_table( MSIDATABASE *db )
+{
+    CHAR *data;
+    USHORT *pool;
+    UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0, codepage;
+    DWORD i, count, offset, len, n;
+    static const WCHAR szStringData[] = {
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    static const WCHAR szStringPool[] = {
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+
+    if( db->strings )
+    {
+        msi_destroy_stringtable( db->strings );
+        db->strings = NULL;
+    }
+
+    r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
+    if( r != ERROR_SUCCESS)
+        goto end;
+    r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
+    if( r != ERROR_SUCCESS)
+        goto end;
+
+    count = poolsize/4;
+    if( poolsize > 4 )
+        codepage = pool[0] | ( pool[1] << 16 );
+    else
+        codepage = CP_ACP;
+    db->strings = msi_init_stringtable( count, codepage );
+
+    offset = 0;
+    for( i=1; i<count; i++ )
+    {
+        len = pool[i*2];
+        n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] );
+        if( n != i )
+            ERR("Failed to add string %ld\n", i );
+        offset += len;
+    }
+
+    TRACE("Loaded %ld strings\n", count);
+
+    ret = ERROR_SUCCESS;
+
+end:
+    if( pool )
+        HeapFree( GetProcessHeap(), 0, pool );
+    if( data )
+        HeapFree( GetProcessHeap(), 0, data );
+
+    return ret;
+}
+
+UINT save_string_table( MSIDATABASE *db )
+{
+    UINT i, count, datasize, poolsize, sz, used, r, codepage;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    static const WCHAR szStringData[] = {
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    static const WCHAR szStringPool[] = {
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+    CHAR *data = NULL;
+    USHORT *pool = NULL;
+
+    TRACE("\n");
+
+    /* construct the new table in memory first */
+    datasize = msi_string_totalsize( db->strings, &count );
+    poolsize = count*2*sizeof(USHORT);
+
+    pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
+    if( ! pool )
+    {
+        ERR("Failed to alloc pool %d bytes\n", poolsize );
+        goto err;
+    }
+    data = HeapAlloc( GetProcessHeap(), 0, datasize );
+    if( ! data )
+    {
+        ERR("Failed to alloc data %d bytes\n", poolsize );
+        goto err;
+    }
+
+    used = 0;
+    codepage = msi_string_get_codepage( db->strings );
+    pool[0]=codepage&0xffff;
+    pool[1]=(codepage>>16);
+    for( i=1; i<count; i++ )
+    {
+        sz = datasize - used;
+        r = msi_id2stringA( db->strings, i, data+used, &sz );
+        if( r != ERROR_SUCCESS )
+        {
+            ERR("failed to fetch string\n");
+            sz = 0;
+        }
+        if( sz && (sz < (datasize - used ) ) )
+            sz--;
+        TRACE("adding %u bytes %s\n", sz, data+used );
+        pool[ i*2 ] = sz;
+        pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
+        used += sz;
+        if( used > datasize  )
+        {
+            ERR("oops overran %d >= %d\n", used, datasize);
+            goto err;
+        }
+    }
+
+    if( used != datasize )
+    {
+        ERR("oops used %d != datasize %d\n", used, datasize);
+        goto err;
+    }
+
+    /* write the streams */
+    r = write_stream_data( db->storage, szStringData, data, datasize );
+    TRACE("Wrote StringData r=%08x\n", r);
+    if( r )
+        goto err;
+    r = write_stream_data( db->storage, szStringPool, pool, poolsize );
+    TRACE("Wrote StringPool r=%08x\n", r);
+    if( r )
+        goto err;
+
+    ret = ERROR_SUCCESS;
+
+err:
+    if( data )
+        HeapFree( GetProcessHeap(), 0, data );
+    if( pool )
+        HeapFree( GetProcessHeap(), 0, pool );
+
+    return ret;
+}
+
+static LPWSTR strdupW( LPCWSTR str )
+{
+    UINT len = lstrlenW( str ) + 1;
+    LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
+    if( ret )
+        lstrcpyW( ret, str );
+    return ret;
+}
+
+/* information for default tables */
+static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
+static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
+static const WCHAR szName[]    = { 'N','a','m','e',0 };
+static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
+static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
+static 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_id2stringW( db->strings, stringid, NULL, &sz );
+    if( r != ERROR_SUCCESS )
+        return NULL;
+    str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
+    if( !str )
+        return str;
+    r = msi_id2stringW( db->strings, stringid, str, &sz );
+    if( r == ERROR_SUCCESS )
+        return str;
+    HeapFree(  GetProcessHeap(), 0, str );
+    return NULL;
+}
+
+static UINT get_tablecolumns( MSIDATABASE *db, 
+       LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
+{
+    UINT r, i, n=0, table_id, count, maxcount = *sz;
+    MSITABLE *table = NULL;
+    static 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_string2idW( 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->row_count;
+    for( i=0; i<count; i++ )
+    {
+        if( table->data[ i ][ 0 ] != table_id )
+            continue;
+        if( colinfo )
+        {
+            UINT id = table->data[ i ] [ 2 ];
+            colinfo[n].tablename = MSI_makestring( db, table_id );
+            colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
+            colinfo[n].colname = MSI_makestring( db, id );
+            colinfo[n].type = table->data[ i ] [ 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 )
+{
+    static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
+    static 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_string2idW( db->strings, name, &table_id );
+    if( r != ERROR_SUCCESS )
+    {
+        TRACE("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; */
+    count = table->row_count;
+    for( i=0; i<count; i++ )
+        if( table->data[ i ][ 0 ] == table_id )
+            break;
+
+    release_table( db, table );
+
+    if (i!=count)
+        return TRUE;
+
+    ERR("Searched %d tables, but %d was not found\n", count, table_id );
+
+    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->row_count;
+    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 = tv->columns[col-1].offset/2;
+        *val = tv->table->data[row][offset] + 
+               (tv->table->data[row][offset + 1] << 16);
+        break;
+    case 2:
+        offset = tv->columns[col-1].offset/2;
+        *val = tv->table->data[row][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;
+}
+
+/*
+ * We need a special case for streams, as we need to reference column with
+ * the name of the stream in the same table, and the table name
+ * which may not be available at higher levels of the query
+ */
+static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT ival = 0, refcol = 0, r;
+    LPWSTR sval;
+    LPWSTR full_name;
+    DWORD len;
+    static const WCHAR szDot[] = { '.', 0 };
+
+    if( !view->ops->fetch_int )
+        return ERROR_INVALID_PARAMETER;
+
+    /*
+     * The column marked with the type stream data seems to have a single number
+     * which references the column containing the name of the stream data
+     *
+     * Fetch the column to reference first.
+     */
+    r = view->ops->fetch_int( view, row, col, &ival );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* now get the column with the name of the stream */
+    r = view->ops->fetch_int( view, row, ival, &refcol );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* lookup the string value from the string table */
+    sval = MSI_makestring( tv->db, refcol );
+    if( !sval )
+        return ERROR_INVALID_PARAMETER;
+
+    len = strlenW( tv->name ) + 2 + strlenW( sval );
+    full_name = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
+    strcpyW( full_name, tv->name );
+    strcatW( full_name, szDot );
+    strcatW( full_name, sval );
+
+    r = db_get_raw_stream( tv->db, full_name, stm );
+    if( r )
+        ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r);
+    HeapFree( GetProcessHeap(), 0, full_name );
+    HeapFree( GetProcessHeap(), 0, sval );
+
+    return r;
+}
+
+static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT offset, n;
+
+    if( !tv->table )
+        return ERROR_INVALID_PARAMETER;
+
+    if( (col==0) || (col>tv->num_cols) )
+        return ERROR_INVALID_PARAMETER;
+
+    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;
+    }
+
+    n = bytes_per_column( &tv->columns[col-1] );
+    switch( n )
+    {
+    case 4:
+        offset = tv->columns[col-1].offset/2;
+        tv->table->data[row][offset]     = val & 0xffff;
+        tv->table->data[row][offset + 1] = (val>>16)&0xffff;
+        break;
+    case 2:
+        offset = tv->columns[col-1].offset/2;
+        tv->table->data[row][offset] = val;
+        break;
+    default:
+        ERR("oops! what is %d bytes per column?\n", n );
+        return ERROR_FUNCTION_FAILED;
+    }
+    return ERROR_SUCCESS;
+}
+
+UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    USHORT **p, *row;
+    UINT sz;
+
+    TRACE("%p\n", view);
+
+    if( !tv->table )
+        return ERROR_INVALID_PARAMETER;
+
+    row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
+    if( !row )
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    sz = (tv->table->row_count + 1) * sizeof (UINT*);
+    if( tv->table->data )
+        p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
+    else
+        p = HeapAlloc( GetProcessHeap(), 0, sz );
+    if( !p )
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    tv->table->data = p;
+    tv->table->data[tv->table->row_count] = row;
+    *num = tv->table->row_count;
+    tv->table->row_count++;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT r;
+
+    TRACE("%p %p\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->row_count;
+    }
+
+    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_fetch_stream,
+    TABLE_set_int,
+    TABLE_insert_row,
+    TABLE_execute,
+    TABLE_close,
+    TABLE_get_dimensions,
+    TABLE_get_column_info,
+    TABLE_modify,
+    TABLE_delete
+};
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR 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;
+}
+
+UINT MSI_CommitTables( MSIDATABASE *db )
+{
+    UINT r;
+    MSITABLE *table = NULL;
+
+    TRACE("%p\n",db);
+
+    r = save_string_table( db );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("failed to save string table r=%08x\n",r);
+        return r;
+    }
+
+    for( table = db->first_table; table; table = table->next )
+    {
+        r = save_table( db, table );
+        if( r != ERROR_SUCCESS )
+        {
+            ERR("failed to save table %s (r=%08x)\n",
+                  debugstr_w(table->name), r);
+            return r;
+        }
+    }
+
+    /* force everything to reload next time */
+    free_cached_tables( db );
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/tokenize.c b/reactos/lib/msi/tokenize.c
new file mode 100644 (file)
index 0000000..31040ed
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+** 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 <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "winnls.h"
+#include "query.h"
+#include "sql.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 },
+  { "CHAR", TK_CHAR },
+  { "CHARACTER", TK_CHAR },
+  { "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 },
+  { "HOLD", TK_HOLD },
+  { "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 },
+  { "INT", TK_INT },
+  { "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 },
+  { "LOCALIZABLE", TK_LOCALIZABLE },
+  { "LONG", TK_LONG },
+  { "LONGCHAR", TK_LONGCHAR },
+  { "MATCH", TK_MATCH },
+  { "NATURAL", TK_JOIN_KW },
+  { "NOT", TK_NOT },
+  { "NOTNULL", TK_NOTNULL },
+  { "NULL", TK_NULL },
+  { "OBJECT", TK_OBJECT },
+  { "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 },
+  { "SHORT", TK_SHORT },
+  { "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){
+  UINT i, len;
+  char buffer[0x10];
+
+  len = WideCharToMultiByte( CP_ACP, 0, z, n, buffer, sizeof buffer, NULL, NULL );
+  for(i=0; i<len; i++)
+      buffer[i] = toupper(buffer[i]);
+  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_WILDCARD;
+      return 1;
+    }
+    case ',': {
+      *tokenType = TK_COMMA;
+      return 1;
+    }
+    case '&': {
+      *tokenType = TK_BITAND;
+      return 1;
+    }
+    case '~': {
+      *tokenType = TK_BITNOT;
+      return 1;
+    }
+    case '`': case '\'': case '"': {
+      int delim = z[0];
+      for(i=1; z[i]; i++){
+        if( z[i]==delim ){
+          if( z[i+1]==delim ){
+            i++;
+          }else{
+            break;
+          }
+        }
+      }
+      if( z[i] ) i++;
+      *tokenType = TK_STRING;
+      return i;
+    }
+    case '.': {
+      if( !isdigit(z[1]) ){
+        *tokenType = TK_DOT;
+        return 1;
+      }
+      /* Fall thru into the next case */
+    }
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9': {
+      *tokenType = TK_INTEGER;
+      for(i=1; isdigit(z[i]); i++){}
+      if( z[i]=='.' ){
+        i++;
+        while( isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }
+      if( (z[i]=='e' || z[i]=='E') &&
+           ( isdigit(z[i+1]) 
+            || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
+           )
+      ){
+        i += 2;
+        while( isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }else if( z[0]=='.' ){
+        *tokenType = TK_FLOAT;
+      }
+      return i;
+    }
+    case '[': {
+      for(i=1; z[i] && z[i-1]!=']'; i++){}
+      *tokenType = TK_ID;
+      return i;
+    }
+    default: {
+      if( !isIdChar[*z] ){
+        break;
+      }
+      for(i=1; isIdChar[z[i]]; i++){}
+      *tokenType = sqliteKeywordCode(z, i);
+      return i;
+    }
+  }
+  *tokenType = TK_ILLEGAL;
+  return 1;
+}
diff --git a/reactos/lib/msi/update.c b/reactos/lib/msi/update.c
new file mode 100644 (file)
index 0000000..0066256
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 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 receuved 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 <stdarg.h>
+
+#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 tagMSIUPDATEVIEW
+{
+    MSIVIEW          view;
+    MSIDATABASE     *db;
+    MSIVIEW         *wv;
+    value_list      *vals;
+} MSIUPDATEVIEW;
+
+static UINT UPDATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+
+    TRACE("%p %d %d %p\n", uv, row, col, val );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT UPDATE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    UINT n, type, val, r, row, col_count = 0, row_count = 0;
+    MSIVIEW *wv;
+
+    TRACE("%p %p\n", uv, record );
+
+    if( !record )
+        return ERROR_FUNCTION_FAILED;
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    r = wv->ops->execute( wv, 0 );
+    TRACE("tv execute returned %x\n", r);
+    if( r )
+        return r;
+
+    r = wv->ops->get_dimensions( wv, &row_count, &col_count );
+    if( r )
+        goto err;
+
+    for( row = 0; row < row_count; row++ )
+    {
+        for( n = 1; n <= col_count; n++ )
+        {
+            r = wv->ops->get_column_info( wv, n, NULL, &type );
+            if( r )
+                break;
+
+            if( type & MSITYPE_STRING )
+            {
+                const WCHAR *str = MSI_RecordGetString( record, n );
+                val = msi_addstringW( uv->db->strings, 0, str, -1, 1 );
+            }
+            else
+            {
+                val = MSI_RecordGetInteger( record, n );
+                val |= 0x8000;
+            }
+            r = wv->ops->set_int( wv, row, n, val );
+            if( r )
+                break;
+        }
+    }
+
+err:
+    return ERROR_SUCCESS;
+}
+
+
+static UINT UPDATE_close( struct tagMSIVIEW *view )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p\n", uv);
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    return wv->ops->close( wv );
+}
+
+static UINT UPDATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p %p %p\n", uv, rows, cols );
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    return wv->ops->get_dimensions( wv, rows, cols );
+}
+
+static UINT UPDATE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p %d %p %p\n", uv, n, name, type );
+
+    wv = uv->wv;
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+
+    return wv->ops->get_column_info( wv, n, name, type );
+}
+
+static UINT UPDATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+
+    TRACE("%p %d %ld\n", uv, eModifyMode, hrec );
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+static UINT UPDATE_delete( struct tagMSIVIEW *view )
+{
+    MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view;
+    MSIVIEW *wv;
+
+    TRACE("%p\n", uv );
+
+    wv = uv->wv;
+    if( wv )
+        wv->ops->delete( wv );
+    delete_value_list( uv->vals );
+    msiobj_release( &uv->db->hdr );
+    HeapFree( GetProcessHeap(), 0, uv );
+
+    return ERROR_SUCCESS;
+}
+
+
+static MSIVIEWOPS update_ops =
+{
+    UPDATE_fetch_int,
+    NULL,
+    NULL,
+    NULL,
+    UPDATE_execute,
+    UPDATE_close,
+    UPDATE_get_dimensions,
+    UPDATE_get_column_info,
+    UPDATE_modify,
+    UPDATE_delete
+};
+
+UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table,
+                        column_assignment *list, struct expr *expr )
+{
+    MSIUPDATEVIEW *uv = NULL;
+    UINT r;
+    MSIVIEW *tv = NULL, *sv = NULL, *wv = NULL;
+
+    TRACE("%p\n", uv );
+
+    r = TABLE_CreateView( db, table, &tv );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* add conditions first */
+    r = WHERE_CreateView( db, &wv, tv, expr );
+    if( r != ERROR_SUCCESS )
+    {
+        if( sv )
+            sv->ops->delete( tv );
+        return r;
+    }
+    
+    /* then select the columns we want */
+    r = SELECT_CreateView( db, &sv, wv, list->col_list );
+    if( r != ERROR_SUCCESS )
+    {
+        if( tv )
+            tv->ops->delete( sv );
+        return r;
+    }
+
+    uv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *uv );
+    if( !uv )
+        return ERROR_FUNCTION_FAILED;
+
+    /* fill the structure */
+    uv->view.ops = &update_ops;
+    msiobj_addref( &db->hdr );
+    uv->db = db;
+    uv->vals = list->val_list;
+    uv->wv = sv;
+    *view = (MSIVIEW*) uv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/version.rc b/reactos/lib/msi/version.rc
new file mode 100644 (file)
index 0000000..05b3ba1
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2004 Christian Costa
+ *
+ * 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 WINE_FILEDESCRIPTION_STR "Wine MSI dll"
+#define WINE_FILENAME_STR "msi.dll"
+#define WINE_FILEVERSION 2,0,2600,0
+#define WINE_FILEVERSION_STR "2.0.2600.0"
+#define WINE_PRODUCTVERSION 2,0,2600,0
+#define WINE_PRODUCTVERSION_STR "2.0.2600.0"
+
+#include "wine/wine_common_ver.rc"
diff --git a/reactos/lib/msi/where.c b/reactos/lib/msi/where.c
new file mode 100644 (file)
index 0000000..683ada4
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "wine/unicode.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 WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %d %p\n", wv, row, col, stm );
+
+    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_stream( wv->table, row, col, stm );
+}
+
+static UINT WHERE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %d %04x\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->set_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 const WCHAR *STRING_evaluate( string_table *st,
+              MSIVIEW *table, UINT row, struct expr *expr, MSIRECORD *record )
+{
+    UINT val = 0, r;
+
+    switch( expr->type )
+    {
+    case EXPR_COL_NUMBER:
+        r = table->ops->fetch_int( table, row, expr->u.col_number, &val );
+        if( r != ERROR_SUCCESS )
+            return NULL;
+        return msi_string_lookup_id( st, val );
+
+    case EXPR_SVAL:
+        return expr->u.sval;
+
+    case EXPR_WILDCARD:
+        return MSI_RecordGetString( record, 1 );
+
+    default:
+        ERR("Invalid expression type\n");
+        break;
+    }
+    return NULL;
+}
+
+static UINT STRCMP_Evaluate( string_table *st, MSIVIEW *table, UINT row, 
+                             struct expr *cond, UINT *val, MSIRECORD *record )
+{
+    int sr;
+    const WCHAR *l_str, *r_str;
+
+    l_str = STRING_evaluate( st, table, row, cond->u.expr.left, record );
+    r_str = STRING_evaluate( st, table, row, cond->u.expr.right, record );
+    if( l_str == r_str )
+        sr = 0;
+    else if( l_str && ! r_str )
+        sr = 1;
+    else if( r_str && ! l_str )
+        sr = -1;
+    else
+        sr = strcmpW( l_str, r_str );
+
+    *val = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) ||
+           ( cond->u.expr.op == OP_LT && ( sr < 0 ) ) ||
+           ( cond->u.expr.op == OP_GT && ( sr > 0 ) );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT WHERE_evaluate( MSIDATABASE *db, MSIVIEW *table, UINT row, 
+                             struct expr *cond, UINT *val, MSIRECORD *record )
+{
+    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_UVAL:
+        *val = cond->u.uval;
+        return ERROR_SUCCESS;
+
+    case EXPR_COMPLEX:
+        r = WHERE_evaluate( db, table, row, cond->u.expr.left, &lval, record );
+        if( r != ERROR_SUCCESS )
+            return r;
+        r = WHERE_evaluate( db, table, row, cond->u.expr.right, &rval, record );
+        if( r != ERROR_SUCCESS )
+            return r;
+        *val = INT_evaluate( lval, cond->u.expr.op, rval );
+        return ERROR_SUCCESS;
+
+    case EXPR_STRCMP:
+        return STRCMP_Evaluate( db->strings, table, row, cond, val, record );
+
+    case EXPR_WILDCARD:
+        *val = MSI_RecordGetInteger( record, 1 );
+        return ERROR_SUCCESS;
+
+    default:
+        ERR("Invalid expression type\n");
+        break;
+    } 
+
+    return ERROR_SUCCESS;
+
+}
+
+static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+    UINT count = 0, r, val, i;
+    MSIVIEW *table = wv->table;
+
+    TRACE("%p %p\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( wv->db, table, i, wv->cond, &val, record );
+        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 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;
+
+    if( wv->cond )
+        delete_expr( wv->cond );
+
+    msiobj_release( &wv->db->hdr );
+    HeapFree( GetProcessHeap(), 0, wv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS where_ops =
+{
+    WHERE_fetch_int,
+    WHERE_fetch_stream,
+    WHERE_set_int,
+    NULL,
+    WHERE_execute,
+    WHERE_close,
+    WHERE_get_dimensions,
+    WHERE_get_column_info,
+    WHERE_modify,
+    WHERE_delete
+};
+
+static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond,
+                                   UINT *valid )
+{
+    UINT r, val = 0;
+
+    switch( cond->type )
+    {
+    case EXPR_COLUMN:
+        r = VIEW_find_column( table, cond->u.column, &val );
+        if( r == ERROR_SUCCESS )
+        {
+            *valid = 1;
+            cond->type = EXPR_COL_NUMBER;
+            cond->u.col_number = val;
+        }
+        else
+        {
+            *valid = 0;
+            ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
+        }
+        break;
+    case EXPR_COMPLEX:
+        r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !*valid )
+            return ERROR_SUCCESS;
+        r = WHERE_VerifyCondition( db, table, cond->u.expr.right, valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        /* check the type of the comparison */
+        if( ( cond->u.expr.left->type == EXPR_SVAL ) ||
+            ( cond->u.expr.right->type == EXPR_SVAL ) )
+        {
+            switch( cond->u.expr.op )
+            {
+            case OP_EQ:
+            case OP_GT:
+            case OP_LT:
+                break;
+            default:
+                *valid = FALSE;
+                return ERROR_INVALID_PARAMETER;
+            }
+
+            /* FIXME: check we're comparing a string to a column */
+
+            cond->type = EXPR_STRCMP;
+        }
+
+        break;
+    case EXPR_IVAL:
+        *valid = 1;
+        cond->type = EXPR_UVAL;
+        cond->u.uval = cond->u.ival + (1<<15);
+        break;
+    case EXPR_WILDCARD:
+        *valid = 1;
+        break;
+    case EXPR_SVAL:
+        *valid = 1;
+        break;
+    default:
+        ERR("Invalid expression type\n");
+        *valid = 0;
+        break;
+    } 
+
+    return ERROR_SUCCESS;
+}
+
+UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table,
+                       struct expr *cond )
+{
+    MSIWHEREVIEW *wv = NULL;
+    UINT count = 0, r, valid = 0;
+
+    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;
+    }
+
+    if( cond )
+    {
+        r = WHERE_VerifyCondition( db, table, cond, &valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !valid )
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    wv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *wv );
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    wv->view.ops = &where_ops;
+    msiobj_addref( &db->hdr );
+    wv->db = db;
+    wv->table = table;
+    wv->row_count = 0;
+    wv->reorder = NULL;
+    wv->cond = cond;
+    *view = (MSIVIEW*) wv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/reactos/lib/msi/winehq2ros.patch b/reactos/lib/msi/winehq2ros.patch
new file mode 100644 (file)
index 0000000..8fb2d36
--- /dev/null
@@ -0,0 +1,67 @@
+Only in /root/wine/wine/dlls/msi: .cvsignore
+Only in /root/wine/wine/dlls/msi: CVS
+Only in ./: Makefile
+Only in ./: Makefile.ros-template
+diff -u /root/wine/wine/dlls/msi/action.c ./action.c
+--- /root/wine/wine/dlls/msi/action.c  2004-10-27 20:43:05.000000000 -0500
++++ ./action.c 2004-10-30 12:14:17.000000000 -0500
+@@ -28,6 +28,7 @@
+ #include <stdarg.h>
+ #include <stdio.h>
++#include <fcntl.h>
+ #define COBJMACROS
+@@ -39,7 +40,7 @@
+ #include "fdi.h"
+ #include "msi.h"
+ #include "msiquery.h"
+-#include "msvcrt/fcntl.h"
++//#include "msvcrt/fcntl.h"
+ #include "objbase.h"
+ #include "objidl.h"
+ #include "msipriv.h"
+@@ -3477,7 +3478,7 @@
+             continue;
+         }
+-        res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
++//        res = LoadTypeLib(package->files[index].TargetPath,&ptLib);
+         if (SUCCEEDED(res))
+         {
+             WCHAR help[MAX_PATH];
+@@ -3488,7 +3489,7 @@
+             resolve_folder(package,helpid,help,FALSE,FALSE,NULL);
+-            res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
++//            res = RegisterTypeLib(ptLib,package->files[index].TargetPath,help);
+             if (!SUCCEEDED(res))
+                 ERR("Failed to register type library %s\n",
+                      debugstr_w(package->files[index].TargetPath));
+@@ -3503,8 +3504,9 @@
+                        debugstr_w(package->files[index].TargetPath));
+             }
+-            if (ptLib)
+-                ITypeLib_Release(ptLib);
++            if (ptLib){
++                //ITypeLib_Release(ptLib);
++                }
+         }
+         else
+             ERR("Failed to load type library %s\n",
+Only in ./: patch.diff
+diff -u /root/wine/wine/dlls/msi/suminfo.c ./suminfo.c
+--- /root/wine/wine/dlls/msi/suminfo.c 2004-10-18 13:20:12.000000000 -0500
++++ ./suminfo.c        2004-10-30 00:09:12.000000000 -0500
+@@ -23,6 +23,8 @@
+ #define COBJMACROS
+ #define NONAMELESSUNION
++#define PRSPEC_PROPID (1)
++
+ #include "windef.h"
+ #include "winbase.h"
+ #include "winreg.h"