diff --git a/reactos/lib/msi/Makefile b/reactos/lib/msi/Makefile new file mode 100644 index 00000000000..27f78dcd1af --- /dev/null +++ b/reactos/lib/msi/Makefile @@ -0,0 +1,9 @@ +# $Id: Makefile 11877 2004-11-30 19:10:50Z jimtabor $ + +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 index 00000000000..95f3ecd5b4f --- /dev/null +++ b/reactos/lib/msi/Makefile.in @@ -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 index 00000000000..b54c29ed29d --- /dev/null +++ b/reactos/lib/msi/Makefile.ros-template @@ -0,0 +1,39 @@ +# $Id: Makefile.ros-template + +TARGET_NAME = msi + +TARGET_OBJECTS = @C_SRCS@ @EXTRA_OBJS@ + +TARGET_CFLAGS = @EXTRADEFS@ -D__REACTOS__ + +TARGET_SDKLIBS = @IMPORTS@ winmm.a wine.a wine_uuid.a wine_unicode.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 + +# +# Optional use of bison, this will allow independent building from +# Wine. +# +# 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 index 00000000000..e32e1f9b6dd --- /dev/null +++ b/reactos/lib/msi/action.c @@ -0,0 +1,4878 @@ +/* + * 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 +#include + +#include +#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 UINT HANDLE_CustomType18(MSIPACKAGE *package, const LPWSTR source, + const LPWSTR target, const INT type); +static UINT HANDLE_CustomType50(MSIPACKAGE *package, const LPWSTR source, + const LPWSTR target, const INT type); +static UINT HANDLE_CustomType34(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_FUNCTION_NOT_CALLED) + rc = ERROR_SUCCESS; + + 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_FUNCTION_NOT_CALLED) + rc = ERROR_SUCCESS; + + 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_FUNCTION_NOT_CALLED; + } + + 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 18: /*EXE file installed with package */ + rc = HANDLE_CustomType18(package,source,target,type); + break; + case 50: /*EXE file specified by a property value */ + rc = HANDLE_CustomType50(package,source,target,type); + break; + case 34: /*EXE to be run in specified directory */ + rc = HANDLE_CustomType34(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)); + + 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); + + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + return ERROR_SUCCESS; +} + +static UINT HANDLE_CustomType18(MSIPACKAGE *package, const LPWSTR source, + const LPWSTR target, const INT type) +{ + WCHAR filename[MAX_PATH*2]; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL rc; + WCHAR *deformated; + static const WCHAR spc[] = {' ',0}; + int index; + + memset(&si,0,sizeof(STARTUPINFOW)); + + index = get_loaded_file(package,source); + strcpyW(filename,package->files[index].TargetPath); + + strcatW(filename,spc); + deformat_string(package,target,&deformated); + strcatW(filename,deformated); + + HeapFree(GetProcessHeap(),0,deformated); + + TRACE("executing exe %s \n",debugstr_w(filename)); + + rc = CreateProcessW(NULL, filename, 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); + + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + return ERROR_SUCCESS; +} + +static UINT HANDLE_CustomType50(MSIPACKAGE *package, const LPWSTR source, + const LPWSTR target, const INT type) +{ + WCHAR filename[MAX_PATH*2]; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL rc; + WCHAR *deformated; + static const WCHAR spc[] = {' ',0}; + DWORD sz; + + memset(&si,0,sizeof(STARTUPINFOW)); + + sz = MAX_PATH*2; + if (MSI_GetPropertyW(package,source,filename,&sz) != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + strcatW(filename,spc); + deformat_string(package,target,&deformated); + strcatW(filename,deformated); + + HeapFree(GetProcessHeap(),0,deformated); + + TRACE("executing exe %s \n",debugstr_w(filename)); + + rc = CreateProcessW(NULL, filename, 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); + + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + return ERROR_SUCCESS; +} + +static UINT HANDLE_CustomType34(MSIPACKAGE *package, const LPWSTR source, + const LPWSTR target, const INT type) +{ + WCHAR filename[MAX_PATH*2]; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL rc; + WCHAR *deformated; + + memset(&si,0,sizeof(STARTUPINFOW)); + + rc = resolve_folder(package, source, filename, FALSE, FALSE, NULL); + if (rc != ERROR_SUCCESS) + return rc; + + SetCurrentDirectoryW(filename); + + deformat_string(package,target,&deformated); + strcpyW(filename,deformated); + + HeapFree(GetProcessHeap(),0,deformated); + + TRACE("executing exe %s \n",debugstr_w(filename)); + + rc = CreateProcessW(NULL, filename, 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); + + CloseHandle( info.hProcess ); + CloseHandle( info.hThread ); + 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.tab.c b/reactos/lib/msi/cond.tab.c new file mode 100644 index 00000000000..6a4e64c88c6 --- /dev/null +++ b/reactos/lib/msi/cond.tab.c @@ -0,0 +1,2022 @@ +/* A Bison parser, made from ./cond.y + by GNU bison 1.35. */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define yyparse COND_parse +#define yylex COND_lex +#define yyerror COND_error +#define yylval COND_lval +#define yychar COND_char +#define yydebug COND_debug +#define yynerrs COND_nerrs +# define COND_SPACE 257 +# define COND_EOF 258 +# define COND_OR 259 +# define COND_AND 260 +# define COND_NOT 261 +# define COND_LT 262 +# define COND_GT 263 +# define COND_EQ 264 +# define COND_LPAR 265 +# define COND_RPAR 266 +# define COND_TILDA 267 +# define COND_PERCENT 268 +# define COND_DOLLARS 269 +# define COND_QUESTION 270 +# define COND_AMPER 271 +# define COND_EXCLAM 272 +# define COND_IDENT 273 +# define COND_NUMBER 274 +# define COND_LITER 275 +# define COND_ERROR 276 + +#line 1 "./cond.y" + + +/* + * 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 +#include +#include + +#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); + + +#line 105 "./cond.y" +#ifndef YYSTYPE +typedef 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; +} yystype; +# define YYSTYPE yystype +# define YYSTYPE_IS_TRIVIAL 1 +#endif +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + + + +#define YYFINAL 74 +#define YYFLAG -32768 +#define YYNTBASE 23 + +/* YYTRANSLATE(YYLEX) -- Bison token number corresponding to YYLEX. */ +#define YYTRANSLATE(x) ((unsigned)(x) <= 276 ? yytranslate[x] : 39) + +/* YYTRANSLATE[YYLEX] -- Bison token number corresponding to YYLEX. */ +static const char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22 +}; + +#if YYDEBUG +static const short yyprhs[] = +{ + 0, 0, 2, 4, 8, 10, 14, 16, 19, 21, + 23, 27, 31, 36, 40, 44, 48, 50, 53, 55, + 57, 60, 63, 66, 69, 72, 74, 77, 79, 81, + 84, 87, 90, 93, 96, 98, 101, 103, 105, 108, + 111, 114, 117, 120, 122, 125, 127, 129, 132, 135, + 138, 141, 144, 146, 148, 150, 152, 154, 157, 160, + 163, 166, 168, 171, 173 +}; +static const short yyrhs[] = +{ + 24, 0, 25, 0, 25, 5, 24, 0, 26, 0, + 25, 6, 26, 0, 27, 0, 7, 27, 0, 32, + 0, 33, 0, 32, 28, 32, 0, 33, 29, 33, + 0, 33, 13, 29, 33, 0, 33, 30, 32, 0, + 32, 31, 33, 0, 11, 24, 12, 0, 10, 0, + 8, 9, 0, 8, 0, 9, 0, 8, 10, 0, + 9, 10, 0, 9, 8, 0, 8, 8, 0, 9, + 9, 0, 10, 0, 8, 9, 0, 8, 0, 9, + 0, 8, 10, 0, 9, 10, 0, 9, 8, 0, + 8, 8, 0, 9, 9, 0, 10, 0, 8, 9, + 0, 8, 0, 9, 0, 8, 10, 0, 9, 10, + 0, 9, 8, 0, 8, 8, 0, 9, 9, 0, + 10, 0, 8, 9, 0, 8, 0, 9, 0, 8, + 10, 0, 9, 10, 0, 9, 8, 0, 8, 8, + 0, 9, 9, 0, 35, 0, 38, 0, 36, 0, + 34, 0, 21, 0, 15, 37, 0, 16, 37, 0, + 17, 37, 0, 18, 37, 0, 37, 0, 14, 37, + 0, 19, 0, 20, 0 +}; + +#endif + +#if YYDEBUG +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const short yyrline[] = +{ + 0, 135, 143, 148, 154, 159, 165, 170, 177, 182, + 186, 190, 194, 198, 202, 206, 212, 218, 222, 226, + 230, 234, 239, 243, 247, 253, 259, 263, 267, 271, + 275, 280, 284, 288, 294, 300, 304, 308, 312, 316, + 321, 325, 329, 335, 341, 345, 349, 353, 357, 362, + 366, 370, 376, 381, 387, 392, 398, 407, 417, 426, + 435, 446, 462, 475, 484 +}; +#endif + + +#if (YYDEBUG) || defined YYERROR_VERBOSE + +/* YYTNAME[TOKEN_NUM] -- String name of the token TOKEN_NUM. */ +static const char *const yytname[] = +{ + "$", "error", "$undefined.", "COND_SPACE", "COND_EOF", "COND_OR", + "COND_AND", "COND_NOT", "COND_LT", "COND_GT", "COND_EQ", "COND_LPAR", + "COND_RPAR", "COND_TILDA", "COND_PERCENT", "COND_DOLLARS", + "COND_QUESTION", "COND_AMPER", "COND_EXCLAM", "COND_IDENT", + "COND_NUMBER", "COND_LITER", "COND_ERROR", "condition", "expression", + "boolean_term", "boolean_factor", "term", "comp_op_i", "comp_op_s", + "comp_op_m1", "comp_op_m2", "value_i", "value_s", "literal", "symbol_i", + "symbol_s", "identifier", "integer", 0 +}; +#endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const short yyr1[] = +{ + 0, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 30, 30, 30, 30, 30, 30, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 32, 32, 33, 33, 34, 35, 35, 35, + 35, 36, 36, 37, 38 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const short yyr2[] = +{ + 0, 1, 1, 3, 1, 3, 1, 2, 1, 1, + 3, 3, 4, 3, 3, 3, 1, 2, 1, 1, + 2, 2, 2, 2, 2, 1, 2, 1, 1, 2, + 2, 2, 2, 2, 1, 2, 1, 1, 2, 2, + 2, 2, 2, 1, 2, 1, 1, 2, 2, 2, + 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, + 2, 1, 2, 1, 1 +}; + +/* YYDEFACT[S] -- default rule to reduce with in state S when YYTABLE + doesn't specify something else to do. Zero means the default is an + error. */ +static const short yydefact[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 63, 64, + 56, 1, 2, 4, 6, 8, 9, 55, 52, 54, + 61, 53, 7, 0, 62, 57, 58, 59, 60, 0, + 0, 18, 19, 16, 0, 0, 36, 37, 34, 0, + 0, 0, 15, 3, 5, 23, 17, 20, 22, 24, + 21, 10, 14, 41, 35, 38, 40, 42, 39, 27, + 28, 25, 0, 11, 13, 32, 26, 29, 31, 33, + 30, 12, 0, 0, 0 +}; + +static const short yydefgoto[] = +{ + 72, 11, 12, 13, 14, 34, 40, 41, 35, 15, + 16, 17, 18, 19, 20, 21 +}; + +static const short yypact[] = +{ + -5, 50, -5, -12, -12, -12, -12, -12,-32768,-32768, + -32768,-32768, -2,-32768,-32768, 48, 123,-32768,-32768,-32768, + -32768,-32768,-32768, -4,-32768,-32768,-32768,-32768,-32768, -5, + -5, 10, 24, 33, 110, 58, 27, 41, 59, 135, + 58, 110,-32768,-32768,-32768, 62, 68, 71, 72, 80, + 81,-32768,-32768, 84, 90, 93, 94, 102, 103, 138, + 141,-32768, 58,-32768,-32768,-32768,-32768,-32768,-32768,-32768, + -32768,-32768, 17, 21,-32768 +}; + +static const short yypgoto[] = +{ + -32768, -1,-32768, -8, 25,-32768, -14,-32768,-32768, -11, + -35,-32768,-32768,-32768, 134,-32768 +}; + + +#define YYLAST 151 + + +static const short yytable[] = +{ + 52, 23, 1, 29, 30, 63, 2, 8, 42, 3, + 4, 5, 6, 7, 8, 9, 10, 73, 45, 46, + 47, 74, 44, 51, -45, 62, 22, 71, 43, -45, + 64, -45, 48, 49, 50, 53, 54, 55, -46, 0, + 0, -27, 0, -46, 0, -46, -27, -43, -27, 56, + 57, 58, -43, 0, -43, -28, 31, 32, 33, 0, + -28, 2, -28, 0, 3, 4, 5, 6, 7, 8, + 9, 10, 3, -25, 0, 0, -50, 8, -25, 10, + -25, -50, -44, -50, 0, -47, -49, -44, 0, -44, + -47, -49, -47, -49, -51, -48, 0, 0, -32, -51, + -48, -51, -48, -32, -26, -32, 0, -29, -31, -26, + 0, -26, -29, -31, -29, -31, -33, -30, 0, 0, + 0, -33, -30, -33, -30, 4, 5, 6, 7, 0, + 9, 36, 37, 38, 0, 0, 39, 24, 25, 26, + 27, 28, 0, 59, 60, 61, 65, 66, 67, 68, + 69, 70 +}; + +static const short yycheck[] = +{ + 35, 2, 7, 5, 6, 40, 11, 19, 12, 14, + 15, 16, 17, 18, 19, 20, 21, 0, 8, 9, + 10, 0, 30, 34, 14, 39, 1, 62, 29, 19, + 41, 21, 8, 9, 10, 8, 9, 10, 14, -1, + -1, 14, -1, 19, -1, 21, 19, 14, 21, 8, + 9, 10, 19, -1, 21, 14, 8, 9, 10, -1, + 19, 11, 21, -1, 14, 15, 16, 17, 18, 19, + 20, 21, 14, 14, -1, -1, 14, 19, 19, 21, + 21, 19, 14, 21, -1, 14, 14, 19, -1, 21, + 19, 19, 21, 21, 14, 14, -1, -1, 14, 19, + 19, 21, 21, 19, 14, 21, -1, 14, 14, 19, + -1, 21, 19, 19, 21, 21, 14, 14, -1, -1, + -1, 19, 19, 21, 21, 15, 16, 17, 18, -1, + 20, 8, 9, 10, -1, -1, 13, 3, 4, 5, + 6, 7, -1, 8, 9, 10, 8, 9, 10, 8, + 9, 10 +}; +#define YYPURE 1 + +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ +#line 3 "/usr/share/bison/bison.simple" + +/* Skeleton output parser for bison, + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser when + the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +#if ! defined (yyoverflow) || defined (YYERROR_VERBOSE) + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# else +# ifndef YYSTACK_USE_ALLOCA +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC malloc +# define YYSTACK_FREE free +# endif +#endif /* ! defined (yyoverflow) || defined (YYERROR_VERBOSE) */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (YYLTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; +# if YYLSP_NEEDED + YYLTYPE yyls; +# endif +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAX (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# if YYLSP_NEEDED +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAX) +# else +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAX) +# endif + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAX; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto yyerrlab +#define YYRECOVERING() (!!yyerrstatus) +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yychar1 = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up"); \ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). + + When YYLLOC_DEFAULT is run, CURRENT is set the location of the + first token. By default, to implement support for ranges, extend + its range to the last symbol. */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + Current.last_line = Rhs[N].last_line; \ + Current.last_column = Rhs[N].last_column; +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#if YYPURE +# if YYLSP_NEEDED +# ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +# else +# define YYLEX yylex (&yylval, &yylloc) +# endif +# else /* !YYLSP_NEEDED */ +# ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +# else +# define YYLEX yylex (&yylval) +# endif +# endif /* !YYLSP_NEEDED */ +#else /* !YYPURE */ +# define YYLEX yylex () +#endif /* !YYPURE */ + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +#endif /* !YYDEBUG */ + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + +#ifdef YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif +#endif + +#line 315 "/usr/share/bison/bison.simple" + + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +# define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +# define YYPARSE_PARAM_DECL +# else +# define YYPARSE_PARAM_ARG YYPARSE_PARAM +# define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +# endif +#else /* !YYPARSE_PARAM */ +# define YYPARSE_PARAM_ARG +# define YYPARSE_PARAM_DECL +#endif /* !YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +# ifdef YYPARSE_PARAM +int yyparse (void *); +# else +int yyparse (void); +# endif +#endif + +/* YY_DECL_VARIABLES -- depending whether we use a pure parser, + variables are global, or local to YYPARSE. */ + +#define YY_DECL_NON_LSP_VARIABLES \ +/* The lookahead symbol. */ \ +int yychar; \ + \ +/* The semantic value of the lookahead symbol. */ \ +YYSTYPE yylval; \ + \ +/* Number of parse errors so far. */ \ +int yynerrs; + +#if YYLSP_NEEDED +# define YY_DECL_VARIABLES \ +YY_DECL_NON_LSP_VARIABLES \ + \ +/* Location data for the lookahead symbol. */ \ +YYLTYPE yylloc; +#else +# define YY_DECL_VARIABLES \ +YY_DECL_NON_LSP_VARIABLES +#endif + + +/* If nonreentrant, generate the variables here. */ + +#if !YYPURE +YY_DECL_VARIABLES +#endif /* !YYPURE */ + +int +yyparse (YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + /* If reentrant, generate the variables here. */ +#if YYPURE + YY_DECL_VARIABLES +#endif /* !YYPURE */ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yychar1 = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + +#if YYLSP_NEEDED + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; +#endif + +#if YYLSP_NEEDED +# define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) +#else +# define YYPOPSTACK (yyvsp--, yyssp--) +#endif + + YYSIZE_T yystacksize = YYINITDEPTH; + + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; +#if YYLSP_NEEDED + YYLTYPE yyloc; +#endif + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; +#if YYLSP_NEEDED + yylsp = yyls; +#endif + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyssp >= yyss + yystacksize - 1) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. */ +# if YYLSP_NEEDED + YYLTYPE *yyls1 = yyls; + /* This used to be a conditional around just the two extra args, + but that might be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; +# else + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); +# endif + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (yystacksize >= YYMAXDEPTH) + goto yyoverflowlab; + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); +# if YYLSP_NEEDED + YYSTACK_RELOCATE (yyls); +# endif +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; +#if YYLSP_NEEDED + yylsp = yyls + yysize - 1; +#endif + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyssp >= yyss + yystacksize - 1) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + /* Convert token to internal form (in yychar1) for indexing tables with */ + + if (yychar <= 0) /* This means end of input. */ + { + yychar1 = 0; + yychar = YYEOF; /* Don't call YYLEX any more */ + + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yychar1 = YYTRANSLATE (yychar); + +#if YYDEBUG + /* We have to keep this `#if YYDEBUG', since we use variables + which are defined only if `YYDEBUG' is set. */ + if (yydebug) + { + YYFPRINTF (stderr, "Next token is %d (%s", + yychar, yytname[yychar1]); + /* Give the individual parser a way to print the precise + meaning of a token, for further debugging info. */ +# ifdef YYPRINT + YYPRINT (stderr, yychar, yylval); +# endif + YYFPRINTF (stderr, ")\n"); + } +#endif + } + + yyn += yychar1; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) + goto yydefault; + + yyn = yytable[yyn]; + + /* yyn is what to do for this token type in this state. + Negative => reduce, -yyn is rule number. + Positive => shift, yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrlab; + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %d (%s), ", + yychar, yytname[yychar1])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; +#if YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to the semantic value of + the lookahead token. This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + +#if YYLSP_NEEDED + /* Similarly for the default location. Let the user run additional + commands if for instance locations are ranges. */ + yyloc = yylsp[1-yylen]; + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); +#endif + +#if YYDEBUG + /* We have to keep this `#if YYDEBUG', since we use variables which + are defined only if `YYDEBUG' is set. */ + if (yydebug) + { + int yyi; + + YYFPRINTF (stderr, "Reducing via rule %d (line %d), ", + yyn, yyrline[yyn]); + + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyn]; yyrhs[yyi] > 0; yyi++) + YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]); + YYFPRINTF (stderr, " -> %s\n", yytname[yyr1[yyn]]); + } +#endif + + switch (yyn) { + +case 1: +#line 137 "./cond.y" +{ + COND_input* cond = (COND_input*) info; + cond->result = yyvsp[0].value; + ; + break;} +case 2: +#line 145 "./cond.y" +{ + yyval.value = yyvsp[0].value; + ; + break;} +case 3: +#line 149 "./cond.y" +{ + yyval.value = yyvsp[-2].value || yyvsp[0].value; + ; + break;} +case 4: +#line 156 "./cond.y" +{ + yyval.value = yyvsp[0].value; + ; + break;} +case 5: +#line 160 "./cond.y" +{ + yyval.value = yyvsp[-2].value && yyvsp[0].value; + ; + break;} +case 6: +#line 167 "./cond.y" +{ + yyval.value = yyvsp[0].value; + ; + break;} +case 7: +#line 171 "./cond.y" +{ + yyval.value = ! yyvsp[0].value; + ; + break;} +case 8: +#line 179 "./cond.y" +{ + yyval.value = yyvsp[0].value; + ; + break;} +case 9: +#line 183 "./cond.y" +{ + yyval.value = atoiW(yyvsp[0].string); + ; + break;} +case 10: +#line 187 "./cond.y" +{ + yyval.value = yyvsp[-1].fn_comp_int( yyvsp[-2].value, yyvsp[0].value ); + ; + break;} +case 11: +#line 191 "./cond.y" +{ + yyval.value = yyvsp[-1].fn_comp_str( yyvsp[-2].string, yyvsp[0].string, FALSE ); + ; + break;} +case 12: +#line 195 "./cond.y" +{ + yyval.value = yyvsp[-1].fn_comp_str( yyvsp[-3].string, yyvsp[0].string, TRUE ); + ; + break;} +case 13: +#line 199 "./cond.y" +{ + yyval.value = yyvsp[-1].fn_comp_m1( yyvsp[-2].string, yyvsp[0].value ); + ; + break;} +case 14: +#line 203 "./cond.y" +{ + yyval.value = yyvsp[-1].fn_comp_m2( yyvsp[-2].value, yyvsp[0].string ); + ; + break;} +case 15: +#line 207 "./cond.y" +{ + yyval.value = yyvsp[-1].value; + ; + break;} +case 16: +#line 215 "./cond.y" +{ + yyval.fn_comp_int = comp_eq_i; + ; + break;} +case 17: +#line 219 "./cond.y" +{ + yyval.fn_comp_int = comp_ne_i; + ; + break;} +case 18: +#line 223 "./cond.y" +{ + yyval.fn_comp_int = comp_lt_i; + ; + break;} +case 19: +#line 227 "./cond.y" +{ + yyval.fn_comp_int = comp_gt_i; + ; + break;} +case 20: +#line 231 "./cond.y" +{ + yyval.fn_comp_int = comp_le_i; + ; + break;} +case 21: +#line 235 "./cond.y" +{ + yyval.fn_comp_int = comp_ge_i; + ; + break;} +case 22: +#line 240 "./cond.y" +{ + yyval.fn_comp_int = comp_bitand; + ; + break;} +case 23: +#line 244 "./cond.y" +{ + yyval.fn_comp_int = comp_highcomp; + ; + break;} +case 24: +#line 248 "./cond.y" +{ + yyval.fn_comp_int = comp_lowcomp; + ; + break;} +case 25: +#line 256 "./cond.y" +{ + yyval.fn_comp_str = comp_eq_s; + ; + break;} +case 26: +#line 260 "./cond.y" +{ + yyval.fn_comp_str = comp_ne_s; + ; + break;} +case 27: +#line 264 "./cond.y" +{ + yyval.fn_comp_str = comp_lt_s; + ; + break;} +case 28: +#line 268 "./cond.y" +{ + yyval.fn_comp_str = comp_gt_s; + ; + break;} +case 29: +#line 272 "./cond.y" +{ + yyval.fn_comp_str = comp_le_s; + ; + break;} +case 30: +#line 276 "./cond.y" +{ + yyval.fn_comp_str = comp_ge_s; + ; + break;} +case 31: +#line 281 "./cond.y" +{ + yyval.fn_comp_str = comp_substring; + ; + break;} +case 32: +#line 285 "./cond.y" +{ + yyval.fn_comp_str = comp_start; + ; + break;} +case 33: +#line 289 "./cond.y" +{ + yyval.fn_comp_str = comp_end; + ; + break;} +case 34: +#line 297 "./cond.y" +{ + yyval.fn_comp_m1 = comp_eq_m1; + ; + break;} +case 35: +#line 301 "./cond.y" +{ + yyval.fn_comp_m1 = comp_ne_m1; + ; + break;} +case 36: +#line 305 "./cond.y" +{ + yyval.fn_comp_m1 = comp_lt_m1; + ; + break;} +case 37: +#line 309 "./cond.y" +{ + yyval.fn_comp_m1 = comp_gt_m1; + ; + break;} +case 38: +#line 313 "./cond.y" +{ + yyval.fn_comp_m1 = comp_le_m1; + ; + break;} +case 39: +#line 317 "./cond.y" +{ + yyval.fn_comp_m1 = comp_ge_m1; + ; + break;} +case 40: +#line 322 "./cond.y" +{ + yyval.fn_comp_m1 = 0; + ; + break;} +case 41: +#line 326 "./cond.y" +{ + yyval.fn_comp_m1 = 0; + ; + break;} +case 42: +#line 330 "./cond.y" +{ + yyval.fn_comp_m1 = 0; + ; + break;} +case 43: +#line 338 "./cond.y" +{ + yyval.fn_comp_m2 = comp_eq_m2; + ; + break;} +case 44: +#line 342 "./cond.y" +{ + yyval.fn_comp_m2 = comp_ne_m2; + ; + break;} +case 45: +#line 346 "./cond.y" +{ + yyval.fn_comp_m2 = comp_lt_m2; + ; + break;} +case 46: +#line 350 "./cond.y" +{ + yyval.fn_comp_m2 = comp_gt_m2; + ; + break;} +case 47: +#line 354 "./cond.y" +{ + yyval.fn_comp_m2 = comp_le_m2; + ; + break;} +case 48: +#line 358 "./cond.y" +{ + yyval.fn_comp_m2 = comp_ge_m2; + ; + break;} +case 49: +#line 363 "./cond.y" +{ + yyval.fn_comp_m2 = 0; + ; + break;} +case 50: +#line 367 "./cond.y" +{ + yyval.fn_comp_m2 = 0; + ; + break;} +case 51: +#line 371 "./cond.y" +{ + yyval.fn_comp_m2 = 0; + ; + break;} +case 52: +#line 378 "./cond.y" +{ + yyval.value = yyvsp[0].value; + ; + break;} +case 53: +#line 382 "./cond.y" +{ + yyval.value = yyvsp[0].value; + ; + break;} +case 54: +#line 389 "./cond.y" +{ + yyval.string = yyvsp[0].string; + ; + break;} +case 55: +#line 393 "./cond.y" +{ + yyval.string = yyvsp[0].string; + ; + break;} +case 56: +#line 400 "./cond.y" +{ + yyval.string = COND_GetLiteral(&yyvsp[0].str); + if( !yyval.string ) + YYABORT; + ; + break;} +case 57: +#line 409 "./cond.y" +{ + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = action; + HeapFree( GetProcessHeap(), 0, yyvsp[0].string ); + ; + break;} +case 58: +#line 418 "./cond.y" +{ + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = install; + HeapFree( GetProcessHeap(), 0, yyvsp[0].string ); + ; + break;} +case 59: +#line 427 "./cond.y" +{ + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = action; + HeapFree( GetProcessHeap(), 0, yyvsp[0].string ); + ; + break;} +case 60: +#line 436 "./cond.y" +{ + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = install; + HeapFree( GetProcessHeap(), 0, yyvsp[0].string ); + ; + break;} +case 61: +#line 448 "./cond.y" +{ + DWORD sz; + COND_input* cond = (COND_input*) info; + yyval.string = HeapAlloc( GetProcessHeap(), 0, 0x100*sizeof (WCHAR) ); + + /* Lookup the identifier */ + + sz=0x100; + if (MSI_GetPropertyW(cond->package,yyvsp[0].string,yyval.string,&sz) != ERROR_SUCCESS) + { + yyval.string[0]=0; + } + HeapFree( GetProcessHeap(), 0, yyvsp[0].string ); + ; + break;} +case 62: +#line 463 "./cond.y" +{ + UINT len = GetEnvironmentVariableW( yyvsp[0].string, NULL, 0 ); + if( len++ ) + { + yyval.string = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) ); + if( yyval.string ) + GetEnvironmentVariableW( yyvsp[0].string, yyval.string, len ); + } + HeapFree( GetProcessHeap(), 0, yyvsp[0].string ); + ; + break;} +case 63: +#line 477 "./cond.y" +{ + yyval.string = COND_GetString(&yyvsp[0].str); + if( !yyval.string ) + YYABORT; + ; + break;} +case 64: +#line 486 "./cond.y" +{ + LPWSTR szNum = COND_GetString(&yyvsp[0].str); + if( !szNum ) + YYABORT; + yyval.value = atoiW( szNum ); + HeapFree( GetProcessHeap(), 0, szNum ); + ; + break;} +} + +#line 705 "/usr/share/bison/bison.simple" + + + yyvsp -= yylen; + yyssp -= yylen; +#if YYLSP_NEEDED + yylsp -= yylen; +#endif + +#if YYDEBUG + if (yydebug) + { + short *yyssp1 = yyss - 1; + YYFPRINTF (stderr, "state stack now"); + while (yyssp1 != yyssp) + YYFPRINTF (stderr, " %d", *++yyssp1); + YYFPRINTF (stderr, "\n"); + } +#endif + + *++yyvsp = yyval; +#if YYLSP_NEEDED + *++yylsp = yyloc; +#endif + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTBASE] + *yyssp; + if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTBASE]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + +#ifdef YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (yyn > YYFLAG && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + char *yymsg; + int yyx, yycount; + + yycount = 0; + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) + if (yycheck[yyx + yyn] == yyx) + yysize += yystrlen (yytname[yyx]) + 15, yycount++; + yysize += yystrlen ("parse error, unexpected ") + 1; + yysize += yystrlen (yytname[YYTRANSLATE (yychar)]); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "parse error, unexpected "); + yyp = yystpcpy (yyp, yytname[YYTRANSLATE (yychar)]); + + if (yycount < 5) + { + yycount = 0; + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); + yyx++) + if (yycheck[yyx + yyn] == yyx) + { + const char *yyq = ! yycount ? ", expecting " : " or "; + yyp = yystpcpy (yyp, yyq); + yyp = yystpcpy (yyp, yytname[yyx]); + yycount++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("parse error; also virtual memory exhausted"); + } + else +#endif /* defined (YYERROR_VERBOSE) */ + yyerror ("parse error"); + } + goto yyerrlab1; + + +/*--------------------------------------------------. +| yyerrlab1 -- error raised explicitly by an action | +`--------------------------------------------------*/ +yyerrlab1: + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + /* return failure if at end of input */ + if (yychar == YYEOF) + YYABORT; + YYDPRINTF ((stderr, "Discarding token %d (%s).\n", + yychar, yytname[yychar1])); + yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + + yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto yyerrhandle; + + +/*-------------------------------------------------------------------. +| yyerrdefault -- current state does not do anything special for the | +| error token. | +`-------------------------------------------------------------------*/ +yyerrdefault: +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + + /* If its default is to accept any token, ok. Otherwise pop it. */ + yyn = yydefact[yystate]; + if (yyn) + goto yydefault; +#endif + + +/*---------------------------------------------------------------. +| yyerrpop -- pop the current state because it cannot handle the | +| error token | +`---------------------------------------------------------------*/ +yyerrpop: + if (yyssp == yyss) + YYABORT; + yyvsp--; + yystate = *--yyssp; +#if YYLSP_NEEDED + yylsp--; +#endif + +#if YYDEBUG + if (yydebug) + { + short *yyssp1 = yyss - 1; + YYFPRINTF (stderr, "Error: state stack now"); + while (yyssp1 != yyssp) + YYFPRINTF (stderr, " %d", *++yyssp1); + YYFPRINTF (stderr, "\n"); + } +#endif + +/*--------------. +| yyerrhandle. | +`--------------*/ +yyerrhandle: + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yyerrdefault; + + yyn += YYTERROR; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) + goto yyerrdefault; + + yyn = yytable[yyn]; + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrpop; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrpop; + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; +#if YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +/*---------------------------------------------. +| yyoverflowab -- parser overflow comes here. | +`---------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} +#line 495 "./cond.y" + + + +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_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/cond.tab.h b/reactos/lib/msi/cond.tab.h new file mode 100644 index 00000000000..2392ce59d46 --- /dev/null +++ b/reactos/lib/msi/cond.tab.h @@ -0,0 +1,40 @@ +#ifndef BISON_COND_TAB_H +# define BISON_COND_TAB_H + +#ifndef YYSTYPE +typedef 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; +} yystype; +# define YYSTYPE yystype +# define YYSTYPE_IS_TRIVIAL 1 +#endif +# define COND_SPACE 257 +# define COND_EOF 258 +# define COND_OR 259 +# define COND_AND 260 +# define COND_NOT 261 +# define COND_LT 262 +# define COND_GT 263 +# define COND_EQ 264 +# define COND_LPAR 265 +# define COND_RPAR 266 +# define COND_TILDA 267 +# define COND_PERCENT 268 +# define COND_DOLLARS 269 +# define COND_QUESTION 270 +# define COND_AMPER 271 +# define COND_EXCLAM 272 +# define COND_IDENT 273 +# define COND_NUMBER 274 +# define COND_LITER 275 +# define COND_ERROR 276 + + +#endif /* not BISON_COND_TAB_H */ diff --git a/reactos/lib/msi/cond.y b/reactos/lib/msi/cond.y new file mode 100644 index 00000000000..1cc096a7075 --- /dev/null +++ b/reactos/lib/msi/cond.y @@ -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 +#include +#include + +#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 COND_IDENT COND_NUMBER COND_LITER + +%nonassoc COND_EOF COND_ERROR + +%type expression boolean_term boolean_factor +%type term value_i symbol_i integer +%type identifier value_s symbol_s literal +%type comp_op_i +%type comp_op_s +%type comp_op_m1 +%type 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_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 index 00000000000..356860d4cc3 --- /dev/null +++ b/reactos/lib/msi/create.c @@ -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 + +#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 index 00000000000..c1a463d7666 --- /dev/null +++ b/reactos/lib/msi/distinct.c @@ -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 + +#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; itable->ops->fetch_int( dv->table, i, j, &val ); + if( r != ERROR_SUCCESS ) + { + ERR("Failed to fetch int at %d %d\n", i, j ); + distinct_free( rowset ); + return r; + } + x = distinct_insert( x, val, i ); + if( !*x ) + { + ERR("Failed to insert at %d %d\n", i, j ); + distinct_free( rowset ); + return ERROR_FUNCTION_FAILED; + } + if( j != c_count ) + x = &(*x)->nextcol; + } + + /* check if it was distinct and if so, include it */ + if( (*x)->row == i ) + { + TRACE("Row %d -> %d\n", dv->row_count, i); + dv->translation[dv->row_count++] = i; + } + } + + distinct_free( rowset ); + + return ERROR_SUCCESS; +} + +static UINT DISTINCT_close( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( dv->translation ) + HeapFree( GetProcessHeap(), 0, dv->translation ); + dv->translation = NULL; + dv->row_count = 0; + + return dv->table->ops->close( dv->table ); +} + +static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( rows ) + { + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + *rows = dv->row_count; + } + + return dv->table->ops->get_dimensions( dv->table, NULL, cols ); +} + +static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %p %p\n", dv, n, name, type ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->get_column_info( dv->table, n, name, type ); +} + +static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %ld\n", dv, eModifyMode, hrec ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->modify( dv->table, eModifyMode, hrec ); +} + +static UINT DISTINCT_delete( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + if( dv->translation ) + HeapFree( GetProcessHeap(), 0, dv->translation ); + 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 index 00000000000..ade5e1bf2ba --- /dev/null +++ b/reactos/lib/msi/handle.c @@ -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 + +#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) || 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 %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 + +#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 index 00000000000..48735f43736 --- /dev/null +++ b/reactos/lib/msi/msi.c @@ -0,0 +1,1601 @@ +/* + * 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 + +#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" + +UINT WINAPI MsiGetFileVersionW(LPCWSTR szFilePath, LPWSTR lpVersionBuf, DWORD* pcchVersionBuf, LPWSTR lpLangBuf, DWORD* pcchLangBuf); + + +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 { + hr = ERROR_INVALID_PARAMETER; + goto end; + } + + 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) + { + HeapFree( GetProcessHeap(), 0, szwProduct); + 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) + { + HeapFree( GetProcessHeap(), 0, szwProduct); + 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) + { + ret = ERROR_OUTOFMEMORY; + goto end; + } + } + + if(lpLangBuf && pcchLangBuf && *pcchLangBuf) { + lpwLangBuff = HeapAlloc(GetProcessHeap(), 0, *pcchVersionBuf * sizeof(WCHAR)); + if( !lpwLangBuff) + { + ret = ERROR_OUTOFMEMORY; + goto end; + } + } + + 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); + +end: + 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 index 00000000000..e8a98a30582 --- /dev/null +++ b/reactos/lib/msi/msi.spec @@ -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 index 00000000000..ba36dcb2eda --- /dev/null +++ b/reactos/lib/msi/msipriv.h @@ -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 + +#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 index 00000000000..f856626d024 --- /dev/null +++ b/reactos/lib/msi/msiquery.c @@ -0,0 +1,486 @@ +/* + * 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 + +#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); + + HeapFree( GetProcessHeap(), 0, szwQuery ); + 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; iops->get_column_info( view, i+1, &name, &type ); + if( ret != ERROR_SUCCESS ) + continue; + MsiRecordSetStringW( handle, i+1, name ); + HeapFree( GetProcessHeap(), 0, name ); + } + + *hRec = handle; + + return ERROR_SUCCESS; +} + +UINT WINAPI 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 index 00000000000..f37bdf10972 --- /dev/null +++ b/reactos/lib/msi/order.c @@ -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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +/* below is the query interface to a table */ + +typedef struct tagMSIORDERVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT *reorder; + UINT num_cols; + UINT cols[1]; +} MSIORDERVIEW; + +static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap ) +{ + UINT r, i, a_val = 0, b_val = 0; + + *swap = 0; + for( i=0; inum_cols; i++ ) + { + r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val ); + if( r != ERROR_SUCCESS ) + return r; + + r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val ); + if( r != ERROR_SUCCESS ) + return r; + + if( a_val != b_val ) + { + if( a_val > b_val ) + *swap = 1; + break; + } + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right ) +{ + UINT r, centre = (left + right)/2, temp, swap = 0, i, j; + UINT *array = ov->reorder; + + if( left == right ) + return ERROR_SUCCESS; + + /* sort the left half */ + r = ORDER_mergesort( ov, left, centre ); + if( r != ERROR_SUCCESS ) + return r; + + /* sort the right half */ + r = ORDER_mergesort( ov, centre+1, right ); + if( r != ERROR_SUCCESS ) + return r; + + for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ ) + { + r = ORDER_compare( ov, array[i], array[j], &swap ); + if( r != ERROR_SUCCESS ) + return r; + if( swap ) + { + temp = array[j]; + memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) ); + array[i] = temp; + j++; + centre++; + } + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows ) +{ + UINT i, swap, r; + + for( i=1; ireorder[i-1], ov->reorder[i], &swap ); + if( r != ERROR_SUCCESS ) + return r; + if( !swap ) + continue; + ERR("Bad order! %d\n", i); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %d %p\n", ov, row, col, val ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + row = ov->reorder[ row ]; + + return ov->table->ops->fetch_int( ov->table, row, col, val ); +} + +static UINT ORDER_execute( struct tagMSIVIEW *view, 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; ireorder[i] = i; + + r = ORDER_mergesort( ov, 0, num_rows - 1 ); + if( r != ERROR_SUCCESS ) + return r; + + r = ORDER_verify( ov, num_rows ); + if( r != ERROR_SUCCESS ) + return r; + + return ERROR_SUCCESS; +} + +static UINT ORDER_close( struct tagMSIVIEW *view ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p\n", ov ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + if( ov->reorder ) + HeapFree( GetProcessHeap(), 0, ov->reorder ); + ov->reorder = NULL; + + return ov->table->ops->close( ov->table ); +} + +static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %p %p\n", ov, rows, cols ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->get_dimensions( ov->table, rows, cols ); +} + +static UINT ORDER_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %p %p\n", ov, n, name, type ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->get_column_info( ov->table, n, name, type ); +} + +static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %ld\n", ov, eModifyMode, hrec ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->modify( ov->table, eModifyMode, hrec ); +} + +static UINT ORDER_delete( struct tagMSIVIEW *view ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p\n", ov ); + + if( ov->table ) + ov->table->ops->delete( ov->table ); + + if( ov->reorder ) + HeapFree( GetProcessHeap(), 0, ov->reorder ); + ov->reorder = NULL; + + 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 index 00000000000..cad20ed665b --- /dev/null +++ b/reactos/lib/msi/package.c @@ -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 +#include +#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 index 00000000000..fc8ce425e56 --- /dev/null +++ b/reactos/lib/msi/query.h @@ -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 + +#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 index 00000000000..3def0974a2b --- /dev/null +++ b/reactos/lib/msi/record.c @@ -0,0 +1,576 @@ +/* + * 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 + +#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 ) ) + { + *sz = 0; + 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 index 00000000000..64df6dd6552 --- /dev/null +++ b/reactos/lib/msi/regsvr.c @@ -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 +#include + +#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 index 00000000000..d31ee8f32aa --- /dev/null +++ b/reactos/lib/msi/select.c @@ -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 + +#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.tab.c b/reactos/lib/msi/sql.tab.c new file mode 100644 index 00000000000..dafec32332e --- /dev/null +++ b/reactos/lib/msi/sql.tab.c @@ -0,0 +1,2180 @@ +/* A Bison parser, made from ./sql.y + by GNU bison 1.35. */ + +#define YYBISON 1 /* Identify Bison output. */ + +#define yyparse SQL_parse +#define yylex SQL_lex +#define yyerror SQL_error +#define yylval SQL_lval +#define yychar SQL_char +#define yydebug SQL_debug +#define yynerrs SQL_nerrs +# define TK_ABORT 257 +# define TK_AFTER 258 +# define TK_AGG_FUNCTION 259 +# define TK_ALL 260 +# define TK_AND 261 +# define TK_AS 262 +# define TK_ASC 263 +# define TK_BEFORE 264 +# define TK_BEGIN 265 +# define TK_BETWEEN 266 +# define TK_BITAND 267 +# define TK_BITNOT 268 +# define TK_BITOR 269 +# define TK_BY 270 +# define TK_CASCADE 271 +# define TK_CASE 272 +# define TK_CHAR 273 +# define TK_CHECK 274 +# define TK_CLUSTER 275 +# define TK_COLLATE 276 +# define TK_COLUMN 277 +# define TK_COMMA 278 +# define TK_COMMENT 279 +# define TK_COMMIT 280 +# define TK_CONCAT 281 +# define TK_CONFLICT 282 +# define TK_CONSTRAINT 283 +# define TK_COPY 284 +# define TK_CREATE 285 +# define TK_DEFAULT 286 +# define TK_DEFERRABLE 287 +# define TK_DEFERRED 288 +# define TK_DELETE 289 +# define TK_DELIMITERS 290 +# define TK_DESC 291 +# define TK_DISTINCT 292 +# define TK_DOT 293 +# define TK_DROP 294 +# define TK_EACH 295 +# define TK_ELSE 296 +# define TK_END 297 +# define TK_END_OF_FILE 298 +# define TK_EQ 299 +# define TK_EXCEPT 300 +# define TK_EXPLAIN 301 +# define TK_FAIL 302 +# define TK_FLOAT 303 +# define TK_FOR 304 +# define TK_FOREIGN 305 +# define TK_FROM 306 +# define TK_FUNCTION 307 +# define TK_GE 308 +# define TK_GLOB 309 +# define TK_GROUP 310 +# define TK_GT 311 +# define TK_HAVING 312 +# define TK_HOLD 313 +# define TK_IGNORE 314 +# define TK_ILLEGAL 315 +# define TK_IMMEDIATE 316 +# define TK_IN 317 +# define TK_INDEX 318 +# define TK_INITIALLY 319 +# define TK_ID 320 +# define TK_INSERT 321 +# define TK_INSTEAD 322 +# define TK_INT 323 +# define TK_INTEGER 324 +# define TK_INTERSECT 325 +# define TK_INTO 326 +# define TK_IS 327 +# define TK_ISNULL 328 +# define TK_JOIN 329 +# define TK_JOIN_KW 330 +# define TK_KEY 331 +# define TK_LE 332 +# define TK_LIKE 333 +# define TK_LIMIT 334 +# define TK_LONG 335 +# define TK_LONGCHAR 336 +# define TK_LP 337 +# define TK_LSHIFT 338 +# define TK_LT 339 +# define TK_LOCALIZABLE 340 +# define TK_MATCH 341 +# define TK_MINUS 342 +# define TK_NE 343 +# define TK_NOT 344 +# define TK_NOTNULL 345 +# define TK_NULL 346 +# define TK_OBJECT 347 +# define TK_OF 348 +# define TK_OFFSET 349 +# define TK_ON 350 +# define TK_OR 351 +# define TK_ORACLE_OUTER_JOIN 352 +# define TK_ORDER 353 +# define TK_PLUS 354 +# define TK_PRAGMA 355 +# define TK_PRIMARY 356 +# define TK_RAISE 357 +# define TK_REFERENCES 358 +# define TK_REM 359 +# define TK_REPLACE 360 +# define TK_RESTRICT 361 +# define TK_ROLLBACK 362 +# define TK_ROW 363 +# define TK_RP 364 +# define TK_RSHIFT 365 +# define TK_SELECT 366 +# define TK_SEMI 367 +# define TK_SET 368 +# define TK_SHORT 369 +# define TK_SLASH 370 +# define TK_SPACE 371 +# define TK_STAR 372 +# define TK_STATEMENT 373 +# define TK_STRING 374 +# define TK_TABLE 375 +# define TK_TEMP 376 +# define TK_THEN 377 +# define TK_TRANSACTION 378 +# define TK_TRIGGER 379 +# define TK_UMINUS 380 +# define TK_UNCLOSED_STRING 381 +# define TK_UNION 382 +# define TK_UNIQUE 383 +# define TK_UPDATE 384 +# define TK_UPLUS 385 +# define TK_USING 386 +# define TK_VACUUM 387 +# define TK_VALUES 388 +# define TK_VIEW 389 +# define TK_WHEN 390 +# define TK_WHERE 391 +# define TK_WILDCARD 392 +# define END_OF_FILE 393 +# define ILLEGAL 394 +# define SPACE 395 +# define UNCLOSED_STRING 396 +# define COMMENT 397 +# define FUNCTION 398 +# define COLUMN 399 + +#line 1 "./sql.y" + + +/* + * 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 +#include +#include + +#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(void); + + +#line 73 "./sql.y" +#ifndef YYSTYPE +typedef 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; +} yystype; +# define YYSTYPE yystype +# define YYSTYPE_IS_TRIVIAL 1 +#endif +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + + + +#define YYFINAL 121 +#define YYFLAG -32768 +#define YYNTBASE 147 + +/* YYTRANSLATE(YYLEX) -- Bison token number corresponding to YYLEX. */ +#define YYTRANSLATE(x) ((unsigned)(x) <= 400 ? yytranslate[x] : 171) + +/* YYTRANSLATE[YYLEX] -- Bison token number corresponding to YYLEX. */ +static const short yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, + 146 +}; + +#if YYDEBUG +static const short yyprhs[] = +{ + 0, 0, 2, 4, 6, 8, 19, 31, 38, 46, + 53, 58, 63, 66, 68, 71, 73, 77, 79, 84, + 86, 88, 90, 92, 94, 96, 101, 103, 107, 112, + 114, 118, 120, 123, 128, 132, 136, 140, 144, 148, + 152, 156, 160, 164, 168, 172, 177, 179, 181, 183, + 187, 189, 193, 197, 199, 201, 203, 205, 209, 211, + 213, 215 +}; +static const short yyrhs[] = +{ + 157, 0, 149, 0, 148, 0, 150, 0, 67, 72, + 169, 83, 159, 110, 134, 83, 163, 110, 0, 67, + 72, 169, 83, 159, 110, 134, 83, 163, 110, 122, + 0, 31, 121, 169, 83, 151, 110, 0, 31, 121, + 169, 83, 151, 110, 59, 0, 130, 169, 114, 164, + 137, 161, 0, 152, 102, 77, 159, 0, 152, 24, + 168, 153, 0, 168, 153, 0, 154, 0, 154, 86, + 0, 155, 0, 155, 90, 92, 0, 19, 0, 19, + 83, 156, 110, 0, 82, 0, 115, 0, 69, 0, + 81, 0, 93, 0, 70, 0, 158, 99, 16, 159, + 0, 158, 0, 112, 159, 160, 0, 112, 38, 159, + 160, 0, 168, 0, 168, 24, 159, 0, 118, 0, + 52, 169, 0, 52, 169, 137, 161, 0, 83, 161, + 110, 0, 167, 45, 167, 0, 161, 7, 161, 0, + 161, 97, 161, 0, 167, 45, 162, 0, 167, 57, + 162, 0, 167, 85, 162, 0, 167, 78, 162, 0, + 167, 54, 162, 0, 167, 89, 162, 0, 167, 73, + 92, 0, 167, 73, 90, 92, 0, 167, 0, 166, + 0, 166, 0, 163, 24, 166, 0, 165, 0, 165, + 24, 164, 0, 168, 45, 166, 0, 70, 0, 120, + 0, 138, 0, 168, 0, 169, 39, 170, 0, 170, + 0, 170, 0, 66, 0, 120, 0 +}; + +#endif + +#if YYDEBUG +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const short yyrline[] = +{ + 0, 141, 147, 152, 157, 164, 173, 183, 194, 206, + 217, 227, 247, 258, 263, 270, 275, 281, 286, 290, + 294, 298, 302, 306, 312, 323, 335, 338, 353, 370, + 384, 397, 403, 415, 433, 438, 442, 446, 450, 454, + 458, 462, 466, 470, 474, 478, 484, 486, 489, 502, + 517, 519, 527, 543, 548, 552, 558, 565, 570, 576, + 583, 588 +}; +#endif + + +#if (YYDEBUG) || defined YYERROR_VERBOSE + +/* YYTNAME[TOKEN_NUM] -- String name of the token TOKEN_NUM. */ +static const char *const yytname[] = +{ + "$", "error", "$undefined.", "TK_ABORT", "TK_AFTER", "TK_AGG_FUNCTION", + "TK_ALL", "TK_AND", "TK_AS", "TK_ASC", "TK_BEFORE", "TK_BEGIN", + "TK_BETWEEN", "TK_BITAND", "TK_BITNOT", "TK_BITOR", "TK_BY", + "TK_CASCADE", "TK_CASE", "TK_CHAR", "TK_CHECK", "TK_CLUSTER", + "TK_COLLATE", "TK_COLUMN", "TK_COMMA", "TK_COMMENT", "TK_COMMIT", + "TK_CONCAT", "TK_CONFLICT", "TK_CONSTRAINT", "TK_COPY", "TK_CREATE", + "TK_DEFAULT", "TK_DEFERRABLE", "TK_DEFERRED", "TK_DELETE", + "TK_DELIMITERS", "TK_DESC", "TK_DISTINCT", "TK_DOT", "TK_DROP", + "TK_EACH", "TK_ELSE", "TK_END", "TK_END_OF_FILE", "TK_EQ", "TK_EXCEPT", + "TK_EXPLAIN", "TK_FAIL", "TK_FLOAT", "TK_FOR", "TK_FOREIGN", "TK_FROM", + "TK_FUNCTION", "TK_GE", "TK_GLOB", "TK_GROUP", "TK_GT", "TK_HAVING", + "TK_HOLD", "TK_IGNORE", "TK_ILLEGAL", "TK_IMMEDIATE", "TK_IN", + "TK_INDEX", "TK_INITIALLY", "TK_ID", "TK_INSERT", "TK_INSTEAD", + "TK_INT", "TK_INTEGER", "TK_INTERSECT", "TK_INTO", "TK_IS", "TK_ISNULL", + "TK_JOIN", "TK_JOIN_KW", "TK_KEY", "TK_LE", "TK_LIKE", "TK_LIMIT", + "TK_LONG", "TK_LONGCHAR", "TK_LP", "TK_LSHIFT", "TK_LT", + "TK_LOCALIZABLE", "TK_MATCH", "TK_MINUS", "TK_NE", "TK_NOT", + "TK_NOTNULL", "TK_NULL", "TK_OBJECT", "TK_OF", "TK_OFFSET", "TK_ON", + "TK_OR", "TK_ORACLE_OUTER_JOIN", "TK_ORDER", "TK_PLUS", "TK_PRAGMA", + "TK_PRIMARY", "TK_RAISE", "TK_REFERENCES", "TK_REM", "TK_REPLACE", + "TK_RESTRICT", "TK_ROLLBACK", "TK_ROW", "TK_RP", "TK_RSHIFT", + "TK_SELECT", "TK_SEMI", "TK_SET", "TK_SHORT", "TK_SLASH", "TK_SPACE", + "TK_STAR", "TK_STATEMENT", "TK_STRING", "TK_TABLE", "TK_TEMP", + "TK_THEN", "TK_TRANSACTION", "TK_TRIGGER", "TK_UMINUS", + "TK_UNCLOSED_STRING", "TK_UNION", "TK_UNIQUE", "TK_UPDATE", "TK_UPLUS", + "TK_USING", "TK_VACUUM", "TK_VALUES", "TK_VIEW", "TK_WHEN", "TK_WHERE", + "TK_WILDCARD", "END_OF_FILE", "ILLEGAL", "SPACE", "UNCLOSED_STRING", + "COMMENT", "FUNCTION", "COLUMN", "AGG_FUNCTION.", "onequery", + "oneinsert", "onecreate", "oneupdate", "table_def", "column_def", + "column_type", "data_type_l", "data_type", "data_count", "oneselect", + "unorderedsel", "selcollist", "from", "expr", "val", "constlist", + "update_assign_list", "column_assignment", "const_val", "column_val", + "column", "table", "string_or_id", 0 +}; +#endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const short yyr1[] = +{ + 0, 147, 147, 147, 147, 148, 148, 149, 149, 150, + 151, 152, 152, 153, 153, 154, 154, 155, 155, 155, + 155, 155, 155, 155, 156, 157, 157, 158, 158, 159, + 159, 159, 160, 160, 161, 161, 161, 161, 161, 161, + 161, 161, 161, 161, 161, 161, 162, 162, 163, 163, + 164, 164, 165, 166, 166, 166, 167, 168, 168, 169, + 170, 170 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const short yyr2[] = +{ + 0, 1, 1, 1, 1, 10, 11, 6, 7, 6, + 4, 4, 2, 1, 2, 1, 3, 1, 4, 1, + 1, 1, 1, 1, 1, 4, 1, 3, 4, 1, + 3, 1, 2, 4, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 1, 1, 1, 3, + 1, 3, 3, 1, 1, 1, 1, 3, 1, 1, + 1, 1 +}; + +/* YYDEFACT[S] -- default rule to reduce with in state S when YYTABLE + doesn't specify something else to do. Zero means the default is an + error. */ +static const short yydefact[] = +{ + 0, 0, 0, 0, 0, 3, 2, 4, 1, 26, + 0, 0, 0, 60, 31, 61, 0, 29, 0, 58, + 0, 59, 0, 0, 0, 0, 0, 27, 0, 0, + 0, 0, 0, 0, 28, 32, 30, 57, 0, 50, + 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 0, 0, 17, 21, 22, 19, 23, 20, 12, + 13, 15, 0, 0, 33, 0, 56, 9, 51, 53, + 54, 55, 52, 8, 0, 0, 0, 14, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11, 10, 24, 0, 16, 0, 34, 36, 37, 54, + 38, 47, 35, 42, 46, 39, 0, 44, 41, 40, + 43, 18, 0, 48, 45, 0, 5, 49, 6, 0, + 0, 0 +}; + +static const short yydefgoto[] = +{ + 119, 5, 6, 7, 42, 43, 59, 60, 61, 93, + 8, 9, 16, 27, 64, 100, 112, 38, 39, 101, + 65, 66, 18, 19 +}; + +static const short yypact[] = +{ + -30, -113, -49, -34, -50,-32768,-32768,-32768,-32768, -79, + -50, -50, -52,-32768,-32768,-32768, -25, 4, -10, 0, + -72,-32768, 30, -36, -35, -25, -50,-32768, -52, -50, + -50, -52, -50, -52,-32768, -87,-32768,-32768, -84, 33, + 13,-32768, -51, -15, -17, -41, -53, -53, -50, -58, + 1, -50, -5, -8,-32768,-32768,-32768,-32768,-32768,-32768, + -13, -9, -57, -53, -4, 47,-32768, -4,-32768,-32768, + -32768,-32768,-32768,-32768, -17, -52, 18,-32768, -3, 11, + -7, -53, -53, -59, -59, -59, -71, -59, -59, -59, + -32768,-32768,-32768, -14,-32768, -58,-32768, -4, -4, 56, + -32768,-32768,-32768,-32768,-32768,-32768, 5,-32768,-32768,-32768, + -32768,-32768, -19,-32768,-32768, -58, -23,-32768,-32768, 102, + 108,-32768 +}; + +static const short yypgoto[] = +{ + -32768,-32768,-32768,-32768,-32768,-32768, 40,-32768,-32768,-32768, + -32768,-32768, 10, 93, -37, 28,-32768, 71,-32768, -32, + 22, 3, 14, 45 +}; + + +#define YYLAST 136 + + +static const short yytable[] = +{ + 81, 1, 53, 81, 12, 115, 17, 13, 10, 51, + 67, 69, 69, 13, 13, 17, 13, 72, 20, 106, + 22, 107, 25, 11, 23, 24, 80, 26, 28, 29, + 63, 17, 13, 40, 17, 44, 17, 2, 36, -59, + 35, 41, 30, 45, 97, 98, 31, 32, 33, 21, + 46, 40, 54, 47, 74, 21, 21, 48, 49, 50, + 73, 99, 70, 113, 55, 56, 14, 15, 15, 62, + 15, 21, 75, 77, 37, 76, 57, 79, 17, 71, + 71, 78, 3, 117, 14, 91, 15, 52, 92, 94, + 82, 116, 83, 82, 95, -61, 111, 114, 58, 118, + 4, 84, 120, 96, 85, 102, 104, 104, 121, 104, + 104, 104, 103, 105, 90, 108, 109, 110, 34, 68, + 86, 0, 0, 0, 0, 87, 0, 0, 0, 0, + 0, 0, 88, 0, 0, 0, 89 +}; + +static const short yycheck[] = +{ + 7, 31, 19, 7, 38, 24, 3, 66, 121, 24, + 47, 70, 70, 66, 66, 12, 66, 49, 4, 90, + 99, 92, 12, 72, 10, 11, 63, 52, 24, 39, + 83, 28, 66, 30, 31, 32, 33, 67, 28, 39, + 26, 31, 114, 33, 81, 82, 16, 83, 83, 4, + 137, 48, 69, 137, 51, 10, 11, 24, 45, 110, + 59, 120, 120, 95, 81, 82, 118, 120, 120, 110, + 120, 26, 77, 86, 29, 83, 93, 134, 75, 138, + 138, 90, 112, 115, 118, 75, 120, 102, 70, 92, + 97, 110, 45, 97, 83, 39, 110, 92, 115, 122, + 130, 54, 0, 110, 57, 83, 84, 85, 0, 87, + 88, 89, 84, 85, 74, 87, 88, 89, 25, 48, + 73, -1, -1, -1, -1, 78, -1, -1, -1, -1, + -1, -1, 85, -1, -1, -1, 89 +}; +#define YYPURE 1 + +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ +#line 3 "/usr/share/bison/bison.simple" + +/* Skeleton output parser for bison, + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* This is the parser code that is written into each bison parser when + the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +#if ! defined (yyoverflow) || defined (YYERROR_VERBOSE) + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# else +# ifndef YYSTACK_USE_ALLOCA +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC malloc +# define YYSTACK_FREE free +# endif +#endif /* ! defined (yyoverflow) || defined (YYERROR_VERBOSE) */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (YYLTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; +# if YYLSP_NEEDED + YYLTYPE yyls; +# endif +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAX (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# if YYLSP_NEEDED +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE) + sizeof (YYLTYPE)) \ + + 2 * YYSTACK_GAP_MAX) +# else +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAX) +# endif + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAX; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrlab1 +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto yyerrlab +#define YYRECOVERING() (!!yyerrstatus) +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yychar1 = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up"); \ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). + + When YYLLOC_DEFAULT is run, CURRENT is set the location of the + first token. By default, to implement support for ranges, extend + its range to the last symbol. */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + Current.last_line = Rhs[N].last_line; \ + Current.last_column = Rhs[N].last_column; +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#if YYPURE +# if YYLSP_NEEDED +# ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, &yylloc, YYLEX_PARAM) +# else +# define YYLEX yylex (&yylval, &yylloc) +# endif +# else /* !YYLSP_NEEDED */ +# ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +# else +# define YYLEX yylex (&yylval) +# endif +# endif /* !YYLSP_NEEDED */ +#else /* !YYPURE */ +# define YYLEX yylex () +#endif /* !YYPURE */ + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +#endif /* !YYDEBUG */ + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + +#ifdef YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif +#endif + +#line 315 "/usr/share/bison/bison.simple" + + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +# define YYPARSE_PARAM_ARG void *YYPARSE_PARAM +# define YYPARSE_PARAM_DECL +# else +# define YYPARSE_PARAM_ARG YYPARSE_PARAM +# define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +# endif +#else /* !YYPARSE_PARAM */ +# define YYPARSE_PARAM_ARG +# define YYPARSE_PARAM_DECL +#endif /* !YYPARSE_PARAM */ + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +# ifdef YYPARSE_PARAM +int yyparse (void *); +# else +int yyparse (void); +# endif +#endif + +/* YY_DECL_VARIABLES -- depending whether we use a pure parser, + variables are global, or local to YYPARSE. */ + +#define YY_DECL_NON_LSP_VARIABLES \ +/* The lookahead symbol. */ \ +int yychar; \ + \ +/* The semantic value of the lookahead symbol. */ \ +YYSTYPE yylval; \ + \ +/* Number of parse errors so far. */ \ +int yynerrs; + +#if YYLSP_NEEDED +# define YY_DECL_VARIABLES \ +YY_DECL_NON_LSP_VARIABLES \ + \ +/* Location data for the lookahead symbol. */ \ +YYLTYPE yylloc; +#else +# define YY_DECL_VARIABLES \ +YY_DECL_NON_LSP_VARIABLES +#endif + + +/* If nonreentrant, generate the variables here. */ + +#if !YYPURE +YY_DECL_VARIABLES +#endif /* !YYPURE */ + +int +yyparse (YYPARSE_PARAM_ARG) + YYPARSE_PARAM_DECL +{ + /* If reentrant, generate the variables here. */ +#if YYPURE + YY_DECL_VARIABLES +#endif /* !YYPURE */ + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yychar1 = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + +#if YYLSP_NEEDED + /* The location stack. */ + YYLTYPE yylsa[YYINITDEPTH]; + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; +#endif + +#if YYLSP_NEEDED +# define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) +#else +# define YYPOPSTACK (yyvsp--, yyssp--) +#endif + + YYSIZE_T yystacksize = YYINITDEPTH; + + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; +#if YYLSP_NEEDED + YYLTYPE yyloc; +#endif + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; +#if YYLSP_NEEDED + yylsp = yyls; +#endif + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyssp >= yyss + yystacksize - 1) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. */ +# if YYLSP_NEEDED + YYLTYPE *yyls1 = yyls; + /* This used to be a conditional around just the two extra args, + but that might be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yyls1, yysize * sizeof (*yylsp), + &yystacksize); + yyls = yyls1; +# else + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); +# endif + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (yystacksize >= YYMAXDEPTH) + goto yyoverflowlab; + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); +# if YYLSP_NEEDED + YYSTACK_RELOCATE (yyls); +# endif +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; +#if YYLSP_NEEDED + yylsp = yyls + yysize - 1; +#endif + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyssp >= yyss + yystacksize - 1) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + /* Convert token to internal form (in yychar1) for indexing tables with */ + + if (yychar <= 0) /* This means end of input. */ + { + yychar1 = 0; + yychar = YYEOF; /* Don't call YYLEX any more */ + + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yychar1 = YYTRANSLATE (yychar); + +#if YYDEBUG + /* We have to keep this `#if YYDEBUG', since we use variables + which are defined only if `YYDEBUG' is set. */ + if (yydebug) + { + YYFPRINTF (stderr, "Next token is %d (%s", + yychar, yytname[yychar1]); + /* Give the individual parser a way to print the precise + meaning of a token, for further debugging info. */ +# ifdef YYPRINT + YYPRINT (stderr, yychar, yylval); +# endif + YYFPRINTF (stderr, ")\n"); + } +#endif + } + + yyn += yychar1; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) + goto yydefault; + + yyn = yytable[yyn]; + + /* yyn is what to do for this token type in this state. + Negative => reduce, -yyn is rule number. + Positive => shift, yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrlab; + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %d (%s), ", + yychar, yytname[yychar1])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; +#if YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to the semantic value of + the lookahead token. This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + +#if YYLSP_NEEDED + /* Similarly for the default location. Let the user run additional + commands if for instance locations are ranges. */ + yyloc = yylsp[1-yylen]; + YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen); +#endif + +#if YYDEBUG + /* We have to keep this `#if YYDEBUG', since we use variables which + are defined only if `YYDEBUG' is set. */ + if (yydebug) + { + int yyi; + + YYFPRINTF (stderr, "Reducing via rule %d (line %d), ", + yyn, yyrline[yyn]); + + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyn]; yyrhs[yyi] > 0; yyi++) + YYFPRINTF (stderr, "%s ", yytname[yyrhs[yyi]]); + YYFPRINTF (stderr, " -> %s\n", yytname[yyr1[yyn]]); + } +#endif + + switch (yyn) { + +case 1: +#line 143 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + *sql->view = yyvsp[0].query; + ; + break;} +case 2: +#line 148 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + *sql->view = yyvsp[0].query; + ; + break;} +case 3: +#line 153 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + *sql->view = yyvsp[0].query; + ; + break;} +case 4: +#line 158 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + *sql->view = yyvsp[0].query; + ; + break;} +case 5: +#line 166 "./sql.y" +{ + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + + INSERT_CreateView( sql->db, &insert, yyvsp[-7].string, yyvsp[-5].column_list, yyvsp[-1].val_list, FALSE ); + yyval.query = insert; + ; + break;} +case 6: +#line 174 "./sql.y" +{ + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + + INSERT_CreateView( sql->db, &insert, yyvsp[-8].string, yyvsp[-6].column_list, yyvsp[-2].val_list, TRUE ); + yyval.query = insert; + ; + break;} +case 7: +#line 185 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !yyvsp[-1].column_info ) + YYABORT; + CREATE_CreateView( sql->db, &create, yyvsp[-3].string, yyvsp[-1].column_info, FALSE ); + yyval.query = create; + ; + break;} +case 8: +#line 195 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !yyvsp[-2].column_info ) + YYABORT; + CREATE_CreateView( sql->db, &create, yyvsp[-4].string, yyvsp[-2].column_info, TRUE ); + yyval.query = create; + ; + break;} +case 9: +#line 208 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + MSIVIEW *update = NULL; + + UPDATE_CreateView( sql->db, &update, yyvsp[-4].string, &yyvsp[-2].update_col_info, yyvsp[0].expr ); + yyval.query = update; + ; + break;} +case 10: +#line 219 "./sql.y" +{ + if( SQL_MarkPrimaryKeys( yyvsp[-3].column_info, yyvsp[0].column_list ) ) + yyval.column_info = yyvsp[-3].column_info; + else + yyval.column_info = NULL; + ; + break;} +case 11: +#line 229 "./sql.y" +{ + create_col_info *ci; + + for( ci = yyvsp[-3].column_info; ci->next; ci = ci->next ) + ; + + ci->next = HeapAlloc( GetProcessHeap(), 0, sizeof *yyval.column_info ); + if( !ci->next ) + { + /* FIXME: free $1 */ + YYABORT; + } + ci->next->colname = yyvsp[-1].string; + ci->next->type = yyvsp[0].column_type; + ci->next->next = NULL; + + yyval.column_info = yyvsp[-3].column_info; + ; + break;} +case 12: +#line 248 "./sql.y" +{ + yyval.column_info = HeapAlloc( GetProcessHeap(), 0, sizeof *yyval.column_info ); + if( ! yyval.column_info ) + YYABORT; + yyval.column_info->colname = yyvsp[-1].string; + yyval.column_info->type = yyvsp[0].column_type; + yyval.column_info->next = NULL; + ; + break;} +case 13: +#line 260 "./sql.y" +{ + yyval.column_type = yyvsp[0].column_type | MSITYPE_VALID; + ; + break;} +case 14: +#line 264 "./sql.y" +{ + FIXME("LOCALIZABLE ignored\n"); + yyval.column_type = yyvsp[-1].column_type | MSITYPE_VALID; + ; + break;} +case 15: +#line 272 "./sql.y" +{ + yyval.column_type |= MSITYPE_NULLABLE; + ; + break;} +case 16: +#line 276 "./sql.y" +{ + yyval.column_type = yyvsp[-2].column_type; + ; + break;} +case 17: +#line 283 "./sql.y" +{ + yyval.column_type = MSITYPE_STRING | 1; + ; + break;} +case 18: +#line 287 "./sql.y" +{ + yyval.column_type = MSITYPE_STRING | 0x400 | yyvsp[-1].column_type; + ; + break;} +case 19: +#line 291 "./sql.y" +{ + yyval.column_type = 2; + ; + break;} +case 20: +#line 295 "./sql.y" +{ + yyval.column_type = 2; + ; + break;} +case 21: +#line 299 "./sql.y" +{ + yyval.column_type = 2; + ; + break;} +case 22: +#line 303 "./sql.y" +{ + yyval.column_type = 4; + ; + break;} +case 23: +#line 307 "./sql.y" +{ + yyval.column_type = 0; + ; + break;} +case 24: +#line 314 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + int val = SQL_getint(sql); + if( ( val > 255 ) || ( val < 0 ) ) + YYABORT; + yyval.column_type = val; + ; + break;} +case 25: +#line 325 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + + if( !yyvsp[-3].query ) + YYABORT; + if( yyvsp[0].column_list ) + yyval.query = do_order_by( sql->db, yyvsp[-3].query, yyvsp[0].column_list ); + else + yyval.query = yyvsp[-3].query; + ; + break;} +case 27: +#line 340 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + if( !yyvsp[0].query ) + YYABORT; + if( yyvsp[-1].column_list ) + { + yyval.query = do_one_select( sql->db, yyvsp[0].query, yyvsp[-1].column_list ); + if( !yyval.query ) + YYABORT; + } + else + yyval.query = yyvsp[0].query; + ; + break;} +case 28: +#line 354 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + MSIVIEW *view = yyvsp[0].query; + + if( !view ) + YYABORT; + if( yyvsp[-1].column_list ) + { + view = do_one_select( sql->db, view, yyvsp[-1].column_list ); + if( !view ) + YYABORT; + } + DISTINCT_CreateView( sql->db, & yyval.query, view ); + ; + break;} +case 29: +#line 372 "./sql.y" +{ + string_list *list; + + list = HeapAlloc( GetProcessHeap(), 0, sizeof *list ); + if( !list ) + YYABORT; + list->string = yyvsp[0].string; + list->next = NULL; + + yyval.column_list = list; + TRACE("Collist %s\n",debugstr_w(yyval.column_list->string)); + ; + break;} +case 30: +#line 385 "./sql.y" +{ + string_list *list; + + list = HeapAlloc( GetProcessHeap(), 0, sizeof *list ); + if( !list ) + YYABORT; + list->string = yyvsp[-2].string; + list->next = yyvsp[0].column_list; + + yyval.column_list = list; + TRACE("From table: %s\n",debugstr_w(yyval.column_list->string)); + ; + break;} +case 31: +#line 398 "./sql.y" +{ + yyval.column_list = NULL; + ; + break;} +case 32: +#line 405 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + UINT r; + + yyval.query = NULL; + TRACE("From table: %s\n",debugstr_w(yyvsp[0].string)); + r = TABLE_CreateView( sql->db, yyvsp[0].string, & yyval.query ); + if( r != ERROR_SUCCESS ) + YYABORT; + ; + break;} +case 33: +#line 416 "./sql.y" +{ + SQL_input* sql = (SQL_input*) info; + MSIVIEW *view = NULL; + UINT r; + + yyval.query = NULL; + TRACE("From table: %s\n",debugstr_w(yyvsp[-2].string)); + r = TABLE_CreateView( sql->db, yyvsp[-2].string, &view ); + if( r != ERROR_SUCCESS ) + YYABORT; + r = WHERE_CreateView( sql->db, &view, view, yyvsp[0].expr ); + if( r != ERROR_SUCCESS ) + YYABORT; + yyval.query = view; + ; + break;} +case 34: +#line 435 "./sql.y" +{ + yyval.expr = yyvsp[-1].expr; + ; + break;} +case 35: +#line 439 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_EQ, yyvsp[0].expr ); + ; + break;} +case 36: +#line 443 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_AND, yyvsp[0].expr ); + ; + break;} +case 37: +#line 447 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_OR, yyvsp[0].expr ); + ; + break;} +case 38: +#line 451 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_EQ, yyvsp[0].expr ); + ; + break;} +case 39: +#line 455 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_GT, yyvsp[0].expr ); + ; + break;} +case 40: +#line 459 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_LT, yyvsp[0].expr ); + ; + break;} +case 41: +#line 463 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_LE, yyvsp[0].expr ); + ; + break;} +case 42: +#line 467 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_GE, yyvsp[0].expr ); + ; + break;} +case 43: +#line 471 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_NE, yyvsp[0].expr ); + ; + break;} +case 44: +#line 475 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-2].expr, OP_ISNULL, NULL ); + ; + break;} +case 45: +#line 479 "./sql.y" +{ + yyval.expr = EXPR_complex( yyvsp[-3].expr, OP_NOTNULL, NULL ); + ; + break;} +case 48: +#line 491 "./sql.y" +{ + value_list *vals; + + vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals ); + if( vals ) + { + vals->val = yyvsp[0].expr; + vals->next = NULL; + } + yyval.val_list = vals; + ; + break;} +case 49: +#line 503 "./sql.y" +{ + value_list *vals; + + vals = HeapAlloc( GetProcessHeap(), 0, sizeof *vals ); + if( vals ) + { + vals->val = yyvsp[0].expr; + vals->next = NULL; + } + yyvsp[-2].val_list->next = vals; + yyval.val_list = yyvsp[-2].val_list; + ; + break;} +case 51: +#line 520 "./sql.y" +{ + yyvsp[-2].update_col_info.col_list->next = yyvsp[0].update_col_info.col_list; + yyvsp[-2].update_col_info.val_list->next = yyvsp[0].update_col_info.val_list; + yyval.update_col_info = yyvsp[-2].update_col_info; + ; + break;} +case 52: +#line 529 "./sql.y" +{ + yyval.update_col_info.col_list = HeapAlloc( GetProcessHeap(), 0, sizeof *yyval.update_col_info.col_list ); + if( !yyval.update_col_info.col_list ) + YYABORT; + yyval.update_col_info.col_list->string = yyvsp[-2].string; + yyval.update_col_info.col_list->next = NULL; + yyval.update_col_info.val_list = HeapAlloc( GetProcessHeap(), 0, sizeof *yyval.update_col_info.val_list ); + if( !yyval.update_col_info.val_list ) + YYABORT; + yyval.update_col_info.val_list->val = yyvsp[0].expr; + yyval.update_col_info.val_list->next = 0; + ; + break;} +case 53: +#line 545 "./sql.y" +{ + yyval.expr = EXPR_ival( &yyvsp[0].str ); + ; + break;} +case 54: +#line 549 "./sql.y" +{ + yyval.expr = EXPR_sval( &yyvsp[0].str ); + ; + break;} +case 55: +#line 553 "./sql.y" +{ + yyval.expr = EXPR_wildcard(); + ; + break;} +case 56: +#line 560 "./sql.y" +{ + yyval.expr = EXPR_column( yyvsp[0].string ); + ; + break;} +case 57: +#line 567 "./sql.y" +{ + yyval.string = yyvsp[0].string; /* FIXME */ + ; + break;} +case 58: +#line 571 "./sql.y" +{ + yyval.string = yyvsp[0].string; + ; + break;} +case 59: +#line 578 "./sql.y" +{ + yyval.string = yyvsp[0].string; + ; + break;} +case 60: +#line 585 "./sql.y" +{ + yyval.string = SQL_getstring( &yyvsp[0].str ); + ; + break;} +case 61: +#line 589 "./sql.y" +{ + yyval.string = SQL_getstring( &yyvsp[0].str ); + ; + break;} +} + +#line 705 "/usr/share/bison/bison.simple" + + + yyvsp -= yylen; + yyssp -= yylen; +#if YYLSP_NEEDED + yylsp -= yylen; +#endif + +#if YYDEBUG + if (yydebug) + { + short *yyssp1 = yyss - 1; + YYFPRINTF (stderr, "state stack now"); + while (yyssp1 != yyssp) + YYFPRINTF (stderr, " %d", *++yyssp1); + YYFPRINTF (stderr, "\n"); + } +#endif + + *++yyvsp = yyval; +#if YYLSP_NEEDED + *++yylsp = yyloc; +#endif + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTBASE] + *yyssp; + if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTBASE]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + +#ifdef YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (yyn > YYFLAG && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + char *yymsg; + int yyx, yycount; + + yycount = 0; + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); yyx++) + if (yycheck[yyx + yyn] == yyx) + yysize += yystrlen (yytname[yyx]) + 15, yycount++; + yysize += yystrlen ("parse error, unexpected ") + 1; + yysize += yystrlen (yytname[YYTRANSLATE (yychar)]); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "parse error, unexpected "); + yyp = yystpcpy (yyp, yytname[YYTRANSLATE (yychar)]); + + if (yycount < 5) + { + yycount = 0; + for (yyx = yyn < 0 ? -yyn : 0; + yyx < (int) (sizeof (yytname) / sizeof (char *)); + yyx++) + if (yycheck[yyx + yyn] == yyx) + { + const char *yyq = ! yycount ? ", expecting " : " or "; + yyp = yystpcpy (yyp, yyq); + yyp = yystpcpy (yyp, yytname[yyx]); + yycount++; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("parse error; also virtual memory exhausted"); + } + else +#endif /* defined (YYERROR_VERBOSE) */ + yyerror ("parse error"); + } + goto yyerrlab1; + + +/*--------------------------------------------------. +| yyerrlab1 -- error raised explicitly by an action | +`--------------------------------------------------*/ +yyerrlab1: + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + /* return failure if at end of input */ + if (yychar == YYEOF) + YYABORT; + YYDPRINTF ((stderr, "Discarding token %d (%s).\n", + yychar, yytname[yychar1])); + yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + + yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto yyerrhandle; + + +/*-------------------------------------------------------------------. +| yyerrdefault -- current state does not do anything special for the | +| error token. | +`-------------------------------------------------------------------*/ +yyerrdefault: +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + + /* If its default is to accept any token, ok. Otherwise pop it. */ + yyn = yydefact[yystate]; + if (yyn) + goto yydefault; +#endif + + +/*---------------------------------------------------------------. +| yyerrpop -- pop the current state because it cannot handle the | +| error token | +`---------------------------------------------------------------*/ +yyerrpop: + if (yyssp == yyss) + YYABORT; + yyvsp--; + yystate = *--yyssp; +#if YYLSP_NEEDED + yylsp--; +#endif + +#if YYDEBUG + if (yydebug) + { + short *yyssp1 = yyss - 1; + YYFPRINTF (stderr, "Error: state stack now"); + while (yyssp1 != yyssp) + YYFPRINTF (stderr, " %d", *++yyssp1); + YYFPRINTF (stderr, "\n"); + } +#endif + +/*--------------. +| yyerrhandle. | +`--------------*/ +yyerrhandle: + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yyerrdefault; + + yyn += YYTERROR; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) + goto yyerrdefault; + + yyn = yytable[yyn]; + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrpop; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrpop; + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; +#if YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +/*---------------------------------------------. +| yyoverflowab -- parser overflow comes here. | +`---------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} +#line 594 "./sql.y" + + +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/sql.tab.h b/reactos/lib/msi/sql.tab.h new file mode 100644 index 00000000000..62fdccd2012 --- /dev/null +++ b/reactos/lib/msi/sql.tab.h @@ -0,0 +1,165 @@ +#ifndef BISON_SQL_TAB_H +# define BISON_SQL_TAB_H + +#ifndef YYSTYPE +typedef 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; +} yystype; +# define YYSTYPE yystype +# define YYSTYPE_IS_TRIVIAL 1 +#endif +# define TK_ABORT 257 +# define TK_AFTER 258 +# define TK_AGG_FUNCTION 259 +# define TK_ALL 260 +# define TK_AND 261 +# define TK_AS 262 +# define TK_ASC 263 +# define TK_BEFORE 264 +# define TK_BEGIN 265 +# define TK_BETWEEN 266 +# define TK_BITAND 267 +# define TK_BITNOT 268 +# define TK_BITOR 269 +# define TK_BY 270 +# define TK_CASCADE 271 +# define TK_CASE 272 +# define TK_CHAR 273 +# define TK_CHECK 274 +# define TK_CLUSTER 275 +# define TK_COLLATE 276 +# define TK_COLUMN 277 +# define TK_COMMA 278 +# define TK_COMMENT 279 +# define TK_COMMIT 280 +# define TK_CONCAT 281 +# define TK_CONFLICT 282 +# define TK_CONSTRAINT 283 +# define TK_COPY 284 +# define TK_CREATE 285 +# define TK_DEFAULT 286 +# define TK_DEFERRABLE 287 +# define TK_DEFERRED 288 +# define TK_DELETE 289 +# define TK_DELIMITERS 290 +# define TK_DESC 291 +# define TK_DISTINCT 292 +# define TK_DOT 293 +# define TK_DROP 294 +# define TK_EACH 295 +# define TK_ELSE 296 +# define TK_END 297 +# define TK_END_OF_FILE 298 +# define TK_EQ 299 +# define TK_EXCEPT 300 +# define TK_EXPLAIN 301 +# define TK_FAIL 302 +# define TK_FLOAT 303 +# define TK_FOR 304 +# define TK_FOREIGN 305 +# define TK_FROM 306 +# define TK_FUNCTION 307 +# define TK_GE 308 +# define TK_GLOB 309 +# define TK_GROUP 310 +# define TK_GT 311 +# define TK_HAVING 312 +# define TK_HOLD 313 +# define TK_IGNORE 314 +# define TK_ILLEGAL 315 +# define TK_IMMEDIATE 316 +# define TK_IN 317 +# define TK_INDEX 318 +# define TK_INITIALLY 319 +# define TK_ID 320 +# define TK_INSERT 321 +# define TK_INSTEAD 322 +# define TK_INT 323 +# define TK_INTEGER 324 +# define TK_INTERSECT 325 +# define TK_INTO 326 +# define TK_IS 327 +# define TK_ISNULL 328 +# define TK_JOIN 329 +# define TK_JOIN_KW 330 +# define TK_KEY 331 +# define TK_LE 332 +# define TK_LIKE 333 +# define TK_LIMIT 334 +# define TK_LONG 335 +# define TK_LONGCHAR 336 +# define TK_LP 337 +# define TK_LSHIFT 338 +# define TK_LT 339 +# define TK_LOCALIZABLE 340 +# define TK_MATCH 341 +# define TK_MINUS 342 +# define TK_NE 343 +# define TK_NOT 344 +# define TK_NOTNULL 345 +# define TK_NULL 346 +# define TK_OBJECT 347 +# define TK_OF 348 +# define TK_OFFSET 349 +# define TK_ON 350 +# define TK_OR 351 +# define TK_ORACLE_OUTER_JOIN 352 +# define TK_ORDER 353 +# define TK_PLUS 354 +# define TK_PRAGMA 355 +# define TK_PRIMARY 356 +# define TK_RAISE 357 +# define TK_REFERENCES 358 +# define TK_REM 359 +# define TK_REPLACE 360 +# define TK_RESTRICT 361 +# define TK_ROLLBACK 362 +# define TK_ROW 363 +# define TK_RP 364 +# define TK_RSHIFT 365 +# define TK_SELECT 366 +# define TK_SEMI 367 +# define TK_SET 368 +# define TK_SHORT 369 +# define TK_SLASH 370 +# define TK_SPACE 371 +# define TK_STAR 372 +# define TK_STATEMENT 373 +# define TK_STRING 374 +# define TK_TABLE 375 +# define TK_TEMP 376 +# define TK_THEN 377 +# define TK_TRANSACTION 378 +# define TK_TRIGGER 379 +# define TK_UMINUS 380 +# define TK_UNCLOSED_STRING 381 +# define TK_UNION 382 +# define TK_UNIQUE 383 +# define TK_UPDATE 384 +# define TK_UPLUS 385 +# define TK_USING 386 +# define TK_VACUUM 387 +# define TK_VALUES 388 +# define TK_VIEW 389 +# define TK_WHEN 390 +# define TK_WHERE 391 +# define TK_WILDCARD 392 +# define END_OF_FILE 393 +# define ILLEGAL 394 +# define SPACE 395 +# define UNCLOSED_STRING 396 +# define COMMENT 397 +# define FUNCTION 398 +# define COLUMN 399 + + +#endif /* not BISON_SQL_TAB_H */ diff --git a/reactos/lib/msi/sql.y b/reactos/lib/msi/sql.y new file mode 100644 index 00000000000..709e78e3601 --- /dev/null +++ b/reactos/lib/msi/sql.y @@ -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 +#include +#include + +#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(void); + +%} + +%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 TK_ID +%token TK_INSERT TK_INSTEAD TK_INT +%token 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 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 column table string_or_id +%type selcollist +%type from unorderedsel oneselect onequery onecreate oneinsert oneupdate +%type expr val column_val const_val +%type column_type data_type data_type_l data_count +%type column_def table_def +%type constlist +%type 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 index 00000000000..bec268fe11a --- /dev/null +++ b/reactos/lib/msi/string.c @@ -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 +#include + +#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; imaxcount; 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; imaxcount; 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; imaxcount; 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 index 00000000000..e8c16c625b0 --- /dev/null +++ b/reactos/lib/msi/suminfo.c @@ -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 + +#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 index 00000000000..7f097e9d0ad --- /dev/null +++ b/reactos/lib/msi/table.c @@ -0,0 +1,1428 @@ +/* + * 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 + +#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; irow_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; jdata[i][ofs] = rawdata[ofs*t->row_count + i ]; + break; + case 4: + t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ]; + t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 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; irow_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; istrings, 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; istrings, 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= *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; idata[ 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; idata[ 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 ) + { + HeapFree( GetProcessHeap(), 0, row ); + 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; inum_cols; i++) + { + HeapFree( GetProcessHeap(), 0, tv->columns[i].colname ); + HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename ); + } + HeapFree( GetProcessHeap(), 0, tv->columns ); + } + tv->columns = NULL; + + HeapFree( GetProcessHeap(), 0, tv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS table_ops = +{ + TABLE_fetch_int, + TABLE_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 index 00000000000..d3ad0cfaf8f --- /dev/null +++ b/reactos/lib/msi/tokenize.c @@ -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 +#include +#include + +#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' ){ + *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 index 00000000000..99e5f6b6667 --- /dev/null +++ b/reactos/lib/msi/update.c @@ -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 + +#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 index 00000000000..625c7a7e85a --- /dev/null +++ b/reactos/lib/msi/version.rc @@ -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 index 00000000000..8500deec1eb --- /dev/null +++ b/reactos/lib/msi/where.c @@ -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 + +#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; idb, 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; +}