From 13700720573bdbadb3bbeb25f1e8a88c11dbf605 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 1 Feb 2011 14:01:42 +0100 Subject: [PATCH] programs: Add winemsibuilder. --- configure | 1 + configure.ac | 1 + programs/winemsibuilder/Makefile.in | 8 + programs/winemsibuilder/main.c | 281 ++++++++++++++++++++++++++++ 4 files changed, 291 insertions(+) create mode 100644 programs/winemsibuilder/Makefile.in create mode 100644 programs/winemsibuilder/main.c diff --git a/configure b/configure index 89cbede0d73..64a7c50efdb 100755 --- a/configure +++ b/configure @@ -15427,6 +15427,7 @@ wine_fn_config_program winedevice enable_winedevice install wine_fn_config_program winefile enable_winefile po,install,installbin wine_fn_config_program winemenubuilder enable_winemenubuilder install wine_fn_config_program winemine enable_winemine po,install,installbin +wine_fn_config_program winemsibuilder enable_winemsibuilder install wine_fn_config_program winepath enable_winepath install,installbin wine_fn_config_program winetest enable_winetest wine_fn_config_program winevdm enable_win16 install diff --git a/configure.ac b/configure.ac index 48ed667fbfe..6d8b5740f21 100644 --- a/configure.ac +++ b/configure.ac @@ -2928,6 +2928,7 @@ WINE_CONFIG_PROGRAM(winedevice,,[install]) WINE_CONFIG_PROGRAM(winefile,,[po,install,installbin]) WINE_CONFIG_PROGRAM(winemenubuilder,,[install]) WINE_CONFIG_PROGRAM(winemine,,[po,install,installbin]) +WINE_CONFIG_PROGRAM(winemsibuilder,,[install]) WINE_CONFIG_PROGRAM(winepath,,[install,installbin]) WINE_CONFIG_PROGRAM(winetest) WINE_CONFIG_PROGRAM(winevdm,enable_win16,[install]) diff --git a/programs/winemsibuilder/Makefile.in b/programs/winemsibuilder/Makefile.in new file mode 100644 index 00000000000..14517f1093d --- /dev/null +++ b/programs/winemsibuilder/Makefile.in @@ -0,0 +1,8 @@ +MODULE = winemsibuilder.exe +APPMODE = -mconsole -municode +IMPORTS = msi ole32 +EXTRADEFS = -DWINE_NO_UNICODE_MACROS + +C_SRCS = main.c + +@MAKE_PROG_RULES@ diff --git a/programs/winemsibuilder/main.c b/programs/winemsibuilder/main.c new file mode 100644 index 00000000000..ea4607eb2b8 --- /dev/null +++ b/programs/winemsibuilder/main.c @@ -0,0 +1,281 @@ +/* + * winemsibuilder - tool to build MSI packages + * + * Copyright 2010 Hans Leidekker 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define WIN32_LEAN_AND_MEAN +#define COBJMACROS + +#include +#include +#include +#include +#include + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder); + +static UINT open_database( const WCHAR *msifile, MSIHANDLE *handle ) +{ + UINT r; + MSIHANDLE hdb; + + if (GetFileAttributesW( msifile ) == INVALID_FILE_ATTRIBUTES) + { + r = MsiOpenDatabaseW( msifile, MSIDBOPEN_CREATE, &hdb ); + if (r != ERROR_SUCCESS) + { + WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile), r ); + return r; + } + r = MsiDatabaseCommit( hdb ); + if (r != ERROR_SUCCESS) + { + WINE_ERR( "failed to commit database (%u)\n", r ); + MsiCloseHandle( hdb ); + return r; + } + } + else + { + r = MsiOpenDatabaseW( msifile, MSIDBOPEN_TRANSACT, &hdb ); + if (r != ERROR_SUCCESS) + { + WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile), r ); + return r; + } + } + + *handle = hdb; + return ERROR_SUCCESS; +} + +static int import_tables( const WCHAR *msifile, WCHAR **tables ) +{ + UINT r; + MSIHANDLE hdb; + WCHAR *dir; + DWORD len; + + r = open_database( msifile, &hdb ); + if (r != ERROR_SUCCESS) return 1; + + len = GetCurrentDirectoryW( 0, NULL ); + if (!(dir = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) + { + MsiCloseHandle( hdb ); + return 1; + } + GetCurrentDirectoryW( len + 1, dir ); + + while (*tables) + { + r = MsiDatabaseImportW( hdb, dir, *tables ); + if (r != ERROR_SUCCESS) + { + WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables), r ); + break; + } + tables++; + } + + if (r == ERROR_SUCCESS) + { + r = MsiDatabaseCommit( hdb ); + if (r != ERROR_SUCCESS) + WINE_ERR( "failed to commit changes (%u)\n", r ); + } + + HeapFree( GetProcessHeap(), 0, dir ); + MsiCloseHandle( hdb ); + return (r != ERROR_SUCCESS); +} + +/* taken from dlls/msi/table.c */ +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; +} + +#define MAX_STREAM_NAME 0x1f + +static WCHAR *encode_stream( const WCHAR *in ) +{ + DWORD c, next, count; + WCHAR *out, *p; + + count = strlenW( in ); + if (count > MAX_STREAM_NAME) + return NULL; + + count += 2; + if (!(out = HeapAlloc( GetProcessHeap(), 0, count * sizeof(WCHAR) ))) return NULL; + p = out; + while (count--) + { + c = *in++; + if (!c) + { + *p = c; + return out; + } + if (c < 0x80 && utf2mime( c ) >= 0) + { + c = utf2mime( c ) + 0x4800; + next = *in; + if (next && next < 0x80) + { + next = utf2mime( next ); + if (next != -1) + { + next += 0x3ffffc0; + c += next << 6; + in++; + } + } + } + *p++ = c; + } + HeapFree( GetProcessHeap(), 0, out ); + return NULL; +} + +static int add_stream( const WCHAR *msifile, const WCHAR *stream, const WCHAR *file ) +{ + UINT r; + HRESULT hr; + MSIHANDLE hdb; + IStorage *stg; + IStream *stm = NULL; + HANDLE handle; + char buffer[4096]; + ULARGE_INTEGER size; + DWORD low, high, read; + WCHAR *encname; + int ret = 1; + + /* make sure we have the right type of file */ + r = open_database( msifile, &hdb ); + if (r != ERROR_SUCCESS) return 1; + MsiCloseHandle( hdb ); + + hr = StgOpenStorage( msifile, NULL, STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg ); + if (hr != S_OK) + { + WINE_WARN( "failed to open storage %s (0x%08x)\n", wine_dbgstr_w(msifile), hr ); + return 1; + } + encname = encode_stream( stream ); + if (!encname) + { + WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream) ); + goto done; + } + hr = IStorage_CreateStream( stg, encname, STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm ); + if (hr != S_OK) + { + WINE_WARN( "failed to create stream %s (0x%08x)\n", wine_dbgstr_w(encname), hr ); + goto done; + } + handle = CreateFileW( file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); + if (handle == INVALID_HANDLE_VALUE) + { + WINE_WARN( "failed to open file %s (%u)\n", wine_dbgstr_w(file), GetLastError() ); + goto done; + } + low = GetFileSize( handle, &high ); + if (low == INVALID_FILE_SIZE || high) + { + WINE_WARN( "file %s too big\n", wine_dbgstr_w(file) ); + CloseHandle( handle ); + goto done; + } + size.QuadPart = low; + hr = IStream_SetSize( stm, size ); + if (hr != S_OK) goto done; + + while (ReadFile( handle, buffer, sizeof(buffer), &read, NULL ) && read) + { + hr = IStream_Write( stm, buffer, read, NULL ); + if (hr != S_OK) break; + size.QuadPart -= read; + } + CloseHandle( handle ); + if (size.QuadPart) + { + WINE_WARN( "failed to write stream contents\n" ); + goto done; + } + IStorage_Commit( stg, 0 ); + ret = 0; + +done: + HeapFree( GetProcessHeap(), 0, encname ); + if (stm) IStream_Release( stm ); + IStorage_Release( stg ); + return ret; +} + +static void show_usage( void ) +{ + WINE_MESSAGE( + "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n" + "Options:\n" + " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n" + " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n" + "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n" + "will be created with an empty database.\n" + ); +} + +int wmain( int argc, WCHAR *argv[] ) +{ + if (argc < 3 || argv[1][0] != '-') + { + show_usage(); + return 1; + } + + switch (argv[1][1]) + { + case 'i': + if (argc < 4) break; + return import_tables( argv[2], argv + 3 ); + case 'a': + if (argc < 5) break; + return add_stream( argv[2], argv[3], argv[4] ); + default: + WINE_WARN( "unknown option\n" ); + break; + } + + show_usage(); + return 1; +}