From 68e1ad07750bd6f9fa7b529398f79db1622c4ad5 Mon Sep 17 00:00:00 2001 From: "Erich E. Hoover" Date: Wed, 16 Sep 2015 11:58:50 -0600 Subject: [PATCH] msidb: Add support for importing database tables. msidb uses a nice CLI syntax for adding multiple database tables in one call with the "-i" mode flag, this patch implements that syntax. For example, this call would import three tables from the current directory (ActionTe.idt, Componen.idt, and InstallE.idt): msidb -d package.msi -f . -i ActionText Component InstallExecuteSequence Signed-off-by: Erich E. Hoover Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- programs/msidb/main.c | 113 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 13 deletions(-) diff --git a/programs/msidb/main.c b/programs/msidb/main.c index a431fd510be..9c4628b7962 100644 --- a/programs/msidb/main.c +++ b/programs/msidb/main.c @@ -27,26 +27,61 @@ #include "wine/debug.h" #include "wine/unicode.h" +#include "wine/list.h" WINE_DEFAULT_DEBUG_CHANNEL(msidb); +struct msidb_listentry +{ + struct list entry; + WCHAR *name; +}; + struct msidb_state { WCHAR *database_file; WCHAR *table_folder; MSIHANDLE database_handle; BOOL create_database; + BOOL import_tables; + struct list table_list; }; +static void list_free( struct list *list ) +{ + struct msidb_listentry *data, *next; + + LIST_FOR_EACH_ENTRY_SAFE( data, next, list, struct msidb_listentry, entry ) + { + list_remove( &data->entry ); + HeapFree( GetProcessHeap(), 0, data ); + } +} + +static void list_append( struct list *list, WCHAR *name ) +{ + struct msidb_listentry *data; + + data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct msidb_listentry) ); + if (!data) + { + ERR( "Out of memory for list.\n" ); + exit( 1 ); + } + data->name = name; + list_add_tail( list, &data->entry ); +} + static void show_usage( void ) { WINE_MESSAGE( - "Usage: msidb [OPTION]...[OPTION]...\n" + "Usage: msidb [OPTION]...[OPTION]... [TABLE]...[TABLE]\n" "Options:\n" " -? Show this usage message and exit.\n" " -c Create database file (instead of opening existing file).\n" " -d package.msi Path to the database file.\n" " -f folder Folder in which to open/save the tables.\n" + " -i Import tables into database.\n" ); } @@ -63,6 +98,17 @@ static int valid_state( struct msidb_state *state ) show_usage(); return 0; } + if (!state->create_database && !state->import_tables) + { + ERR( "No mode flag specified (-c, -i).\n" ); + show_usage(); + return 0; + } + if (list_empty( &state->table_list )) + { + ERR( "No tables specified.\n" ); + return 0; + } return 1; } @@ -70,11 +116,7 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR * { /* msidb accepts either "-" or "/" style flags */ if (strlenW(argv[i]) != 2 || (argv[i][0] != '-' && argv[i][0] != '/')) - { - WINE_FIXME( "Table names are not currently supported.\n" ); - show_usage(); - exit( 1 ); - } + return 0; switch( argv[i][1] ) { case '?': @@ -91,6 +133,9 @@ static int process_argument( struct msidb_state *state, int i, int argc, WCHAR * if (i + 1 >= argc) return 0; state->table_folder = argv[i + 1]; return 2; + case 'i': + state->import_tables = TRUE; + return 1; default: break; } @@ -112,11 +157,14 @@ static int open_database( struct msidb_state *state ) return 1; } -static void close_database( struct msidb_state *state ) +static void close_database( struct msidb_state *state, BOOL commit_changes ) { - UINT ret; + UINT ret = ERROR_SUCCESS; - ret = MsiDatabaseCommit( state->database_handle ); + if (state->database_handle == 0) + return; + if (commit_changes) + ret = MsiDatabaseCommit( state->database_handle ); if (ret != ERROR_SUCCESS) { ERR( "Failed to commit changes to database.\n" ); @@ -130,24 +178,63 @@ static void close_database( struct msidb_state *state ) } } +static int import_table( struct msidb_state *state, const WCHAR *table_name ) +{ + const WCHAR format[] = { '%','.','8','s','.','i','d','t',0 }; /* truncate to 8 characters */ + WCHAR table_path[MAX_PATH]; + UINT ret; + + snprintfW( table_path, sizeof(table_path)/sizeof(WCHAR), format, table_name ); + ret = MsiDatabaseImportW( state->database_handle, state->table_folder, table_path ); + if (ret != ERROR_SUCCESS) + { + ERR( "Failed to import table '%s', error %d.\n", wine_dbgstr_w(table_name), ret ); + return 0; + } + return 1; +} + +static int import_tables( struct msidb_state *state ) +{ + struct msidb_listentry *data; + + LIST_FOR_EACH_ENTRY( data, &state->table_list, struct msidb_listentry, entry ) + { + if (!import_table( state, data->name )) + return 0; /* failed, do not commit changes */ + } + return 1; +} + int wmain( int argc, WCHAR *argv[] ) { struct msidb_state state; int i, n = 1; + int ret = 1; memset( &state, 0x0, sizeof(state) ); + list_init( &state.table_list ); /* process and validate all the command line flags */ for (i = 1; n && i < argc; i += n) n = process_argument( &state, i, argc, argv ); + /* process all remaining arguments as table names */ + for (; i < argc; i++) + list_append( &state.table_list, argv[i] ); if (!valid_state( &state )) - return 1; + goto cleanup; /* perform the requested operations */ if (!open_database( &state )) { ERR( "Failed to open database '%s'.\n", wine_dbgstr_w(state.database_file) ); - return 1; + goto cleanup; } - close_database( &state ); - return 0; + if (state.import_tables && !import_tables( &state )) + goto cleanup; /* failed, do not commit changes */ + ret = 0; + +cleanup: + close_database( &state, ret == 0 ); + list_free( &state.table_list ); + return ret; }