From 43b040164942e4e35928ece1be87f081ad43e9c4 Mon Sep 17 00:00:00 2001 From: J. Ali Harlow Date: Sat, 15 Nov 2014 19:04:45 +0000 Subject: [PATCH] Add GUI front-end to setup and update --- .gitignore | 16 + Makefile.am | 2 +- app-manager/Makefile.am | 34 +- app-manager/app-manager.c | 102 +++- app-manager/app-manager.h | 4 +- app-manager/app-manager.ui | 5 +- app-manager/applications.c | 5 +- app-manager/fetch.c | 8 +- app-manager/localmedia.c | 2 +- app-manager/packagelist.c | 10 +- app-manager/resources.rc.in | 4 + app-manager/setup.c | 88 +++ app-manager/update.c | 89 +++ configure.ac | 45 ++- plover-gtk/Makefile.am | 14 +- plover-gtk/error.c | 30 - plover-gtk/error.h | 12 - plover-gtk/package.c | 180 ------ plover-gtk/package.h | 50 -- plover-gtk/packagefilestore.c | 12 + plover-gtk/packagefilestore.h | 3 + plover-gtk/packageset.c | 259 --------- plover-gtk/packageset.h | 46 -- plover-gtk/packagestore.c | 14 +- plover-gtk/packagestore.h | 2 +- plover-gtk/software-installation.ui | 175 ++++++ plover-gtk/stockicons.c | 141 +++++ plover-gtk/stockicons.h | 6 + plover-gtk/transactionhelper.c | 1037 +++++++++++++++++++++++++++++++++++ plover-gtk/transactionhelper.h | 102 ++++ plover/Makefile.am | 5 +- plover/import-yum.c | 193 ++++--- plover/log.c | 378 +++++++++++++ plover/package.c | 274 +++++++++ plover/package.h | 56 ++ plover/packageset.c | 658 ++++++++++++++++++++++ plover/packageset.h | 73 +++ plover/plover.h | 72 ++- plover/plover.pc.in | 2 +- plover/razor.c | 476 +++------------- plover/repository.c | 168 ++++++ plover/repository.h | 49 ++ plover/transaction.c | 722 ++++++++++++++++++++++++ plover/transaction.h | 87 +++ plover/util.c | 160 +++++-- plover/vector.c | 130 +++++ pre-inst/Makefile.am | 32 ++ pre-inst/icon16.png | Bin 0 -> 821 bytes pre-inst/icon22.png | Bin 0 -> 1237 bytes pre-inst/icon32.png | Bin 0 -> 2005 bytes pre-inst/manifest.xml.in | 29 + pre-inst/pre-inst.c | 504 +++++++++++++++++ pre-inst/resource.h | 6 + pre-inst/resources.rc.in | 47 ++ setup/Makefile.am | 17 +- setup/resources.rc.in | 4 +- setup/setup.c | 90 ++-- setup/setup.js.in | 92 +++ update/Makefile.am | 17 +- update/resources.rc.in | 4 +- update/update.c | 24 +- update/update.js.in | 68 +++ 62 files changed, 5686 insertions(+), 1248 deletions(-) create mode 100644 app-manager/setup.c create mode 100644 app-manager/update.c delete mode 100644 plover-gtk/error.c delete mode 100644 plover-gtk/error.h delete mode 100644 plover-gtk/package.c delete mode 100644 plover-gtk/package.h delete mode 100644 plover-gtk/packageset.c delete mode 100644 plover-gtk/packageset.h create mode 100644 plover-gtk/software-installation.ui create mode 100644 plover-gtk/stockicons.c create mode 100644 plover-gtk/stockicons.h create mode 100644 plover-gtk/transactionhelper.c create mode 100644 plover-gtk/transactionhelper.h create mode 100644 plover/log.c create mode 100644 plover/package.c create mode 100644 plover/package.h create mode 100644 plover/packageset.c create mode 100644 plover/packageset.h create mode 100644 plover/repository.c create mode 100644 plover/repository.h create mode 100644 plover/transaction.c create mode 100644 plover/transaction.h create mode 100644 plover/vector.c create mode 100644 pre-inst/Makefile.am create mode 100644 pre-inst/icon16.png create mode 100644 pre-inst/icon22.png create mode 100644 pre-inst/icon32.png create mode 100644 pre-inst/manifest.xml.in create mode 100644 pre-inst/pre-inst.c create mode 100644 pre-inst/resource.h create mode 100644 pre-inst/resources.rc.in create mode 100644 setup/setup.js.in create mode 100644 update/update.js.in diff --git a/.gitignore b/.gitignore index ecfd2e0..e1dbd73 100644 --- a/.gitignore +++ b/.gitignore @@ -13,10 +13,26 @@ stamp-h1 *.o *.lo *.la +*.ico +*.exe +*.exe.manifest plover/plover.pc plover-gtk/plover-gtk.pc setup/resources.rc setup/setup +setup/setup.js +setup/icon*.pnm update/resources.rc update/update +update/update.js +update/icon*.pnm +pre-inst/resources.rc +pre-inst/pre-inst +pre-inst/icon*.pnm app-manager/resources.rc +app-manager/fetch +app-manager/app-manager +app-manager/plover-applications*.pgm +app-manager/plover-applications*.pnm +app-manager/24x24 +app-manager/48x48 diff --git a/Makefile.am b/Makefile.am index c3475ed..e82944d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1 +1 @@ -SUBDIRS=plover setup update plover-gtk app-manager +SUBDIRS=plover setup update pre-inst plover-gtk app-manager diff --git a/app-manager/Makefile.am b/app-manager/Makefile.am index 4007415..591dd9b 100644 --- a/app-manager/Makefile.am +++ b/app-manager/Makefile.am @@ -3,12 +3,15 @@ LDADD=../plover/libplover.la ../plover-gtk/libplover-gtk.la $(GUI_LIBS) bin_PROGRAMS=app-manager fetch app_manager_SOURCES=app-manager.c app-manager.h packagelist.c applications.c \ - localmedia.c localmedia.h + localmedia.c localmedia.h setup.c update.c fetch_SOURCES=fetch.c fetch_LDADD=$(LDADD) $(FETCH_LIBS) if HAVE_WINDRES app_manager_SOURCES+=resources.rc app-manager.exe.manifest endif +if PLOVER_MINGW +app_manager_LDFLAGS=-mwindows +endif uidir=$(pkgdatadir) ui_DATA=app-manager.ui desktopdir=$(datadir)/applications @@ -20,34 +23,39 @@ smallicon_DATA=24x24/plover-applications.png bigicondir=$(datadir)/icons/hicolor/48x48/apps bigicon_DATA=48x48/plover-applications.png +# PLOVER_V_SKIP: Don't echo anything for this command if V=0 +PLOVER_V_SKIP = $(PLOVER_V_SKIP_$(V)) +PLOVER_V_SKIP_ = $(PLOVER_V_SKIP_$(AM_DEFAULT_VERBOSITY)) +PLOVER_V_SKIP_0 = @ + .rc.$(OBJEXT): - $(WINDRES) $< $@ + $(AM_V_GEN)$(WINDRES) $< $@ resources.$(OBJEXT): app-manager.ico app-manager.exe.manifest plover-applications%.pnm: plover-applications.svg - rsvg -w $* -h $* -f png $< temp.png - pngtopnm temp.png | pnmquant 256 > $@ - $(RM) temp.png + $(PLOVER_V_SKIP)rsvg -w $* -h $* -f png $< temp.png + $(AM_V_GEN)pngtopnm temp.png | pnmquant -quiet 256 > $@ + $(PLOVER_V_SKIP)$(RM) temp.png plover-applications%.pgm: plover-applications.svg - rsvg -w $* -h $* -f png $< temp.png - pngtopnm -alpha temp.png > $@ - $(RM) temp.png + $(PLOVER_V_SKIP)rsvg -w $* -h $* -f png $< temp.png + $(AM_V_GEN)pngtopnm -alpha temp.png > $@ + $(PLOVER_V_SKIP)$(RM) temp.png 24x24/plover-applications.png: plover-applications.svg - mkdir -p 24x24 - rsvg -w 24 -h 24 -f png $< $@ + $(PLOVER_V_SKIP)mkdir -p 24x24 + $(AM_V_GEN)rsvg -w 24 -h 24 -f png $< $@ 48x48/plover-applications.png: plover-applications.svg - mkdir -p 48x48 - rsvg -w 48 -h 48 -f png $< $@ + $(PLOVER_V_SKIP)mkdir -p 48x48 + $(AM_V_GEN)rsvg -w 48 -h 48 -f png $< $@ app-manager.ico: plover-applications16.pnm plover-applications16.pgm \ plover-applications22.pnm plover-applications22.pgm \ plover-applications32.pnm plover-applications32.pgm \ plover-applications46.pnm plover-applications46.pgm - ppmtowinicon -andpgms -output=$@ $^ + $(AM_V_GEN)ppmtowinicon -andpgms -output=$@ $^ clean-local: -rm -rf 24x24 48x48 diff --git a/app-manager/app-manager.c b/app-manager/app-manager.c index 652e71e..479bd7c 100644 --- a/app-manager/app-manager.c +++ b/app-manager/app-manager.c @@ -19,13 +19,21 @@ #include "config.h" #include #include +#ifdef WIN32 +#include +#endif /* WIN32 */ +#include #include #include #include -#include +#include +#include +#include #include "app-manager.h" #include "localmedia.h" +LUALIB_API int luaopen_posix(lua_State *L); + #define LOGO_NAME "plover-applications" GtkBuilder *ui; @@ -103,6 +111,7 @@ static void show_uri(GtkLinkButton *button,const gchar *uri,gpointer data) } } +#if 0 /* Checks whether a loader for SVG files has been registered * with GdkPixbuf. */ @@ -191,21 +200,61 @@ static void install_icons(void) icon_set=gtk_icon_factory_lookup_default(LOGO_NAME); gtk_window_set_default_icon_name(LOGO_NAME); } +#endif + +static void install_icons(void) +{ + GtkIconSet *icon_set; + plover_icons_add_to_stock("apps",LOGO_NAME); + icon_set=gtk_icon_factory_lookup_default(LOGO_NAME); + gtk_window_set_default_icon_name(LOGO_NAME); +} int main(int argc,char **argv) { GError *err=0; GtkWidget *w; gchar *s,*contents; + gchar *setup_base=NULL,*update_base=NULL; gsize len; PloverPackageSet *set; + GSList *objects,*lnk; + gboolean started; GOptionEntry options[]={ + {"setup",0,0,G_OPTION_ARG_FILENAME,&setup_base, + "Setup from installation media","path"}, + {"update",0,0,G_OPTION_ARG_FILENAME,&update_base, + "Update from upgrade media","path"}, {NULL} }; +#ifdef WIN32 + /* + * app-manager is normally a GUI application, but rpm scripts may well + * call console applications and it looks ugly if console windows keep + * popping up. Avoid this by allocating our own console and hiding it. + * Note: + * - If app-manager is a console application (typically for debugging), + * then skip this step. + * - Call ShowWindow twice to negate special handling on first call. + */ + if (!GetConsoleWindow()) + { + AllocConsole(); + ShowWindow(GetConsoleWindow(),SW_HIDE); + ShowWindow(GetConsoleWindow(),SW_HIDE); + } +#endif + razor_set_lua_loader("posix",luaopen_posix); + razor_set_lua_loader("whelk",luaopen_whelk); if (!gtk_init_with_args(&argc,&argv,NULL,options,NULL,&err)) { - g_printerr("%s",err->message); - exit(0); + g_printerr("%s\n",err->message); + exit(1); + } + if (setup_base && update_base) + { + g_printerr("--setup and --update are mutually exclusive\n"); + exit(1); } #ifdef WIN32 prefix=g_win32_get_package_installation_directory_of_module(NULL); @@ -242,23 +291,45 @@ int main(int argc,char **argv) gtk_builder_connect_signals(ui,NULL); gtk_link_button_set_uri_hook(show_uri,NULL,NULL); installed=GTK_TREE_MODEL(plover_package_store_new()); - set=plover_package_set_new_from_installed("",NULL); - if (set) + set=plover_package_set_new(); + (void)plover_package_set_open(set,"",TRUE,NULL); + plover_package_store_add_set(PLOVER_PACKAGE_STORE(installed),set); + if (plover_package_set_get_no_details(set)) { - plover_package_store_add_set(PLOVER_PACKAGE_STORE(installed),set); - if (plover_package_set_get_no_details(set)) - { - w=GTK_WIDGET(gtk_builder_get_object(ui,"ViewFiles")); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),TRUE); - } + w=GTK_WIDGET(gtk_builder_get_object(ui,"ViewFiles")); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(w),TRUE); } applications=plover_applications_model_new(installed); set_package_model(applications); - gtk_main(); - g_object_unref(ui); + if (setup_base) + started=setup(set,setup_base); + else if (update_base) + started=update(set,update_base); + else + { + w=GTK_WIDGET(gtk_builder_get_object(ui,"MainWindow")); + gtk_widget_show(w); + started=TRUE; + } + if (started) + gtk_main(); + g_clear_object(&set); + objects=gtk_builder_get_objects(ui); + for(lnk=objects;lnk;lnk=lnk->next) + if (GTK_IS_WIDGET(lnk->data) && + gtk_widget_is_toplevel(GTK_WIDGET(lnk->data))) + gtk_widget_destroy(GTK_WIDGET(lnk->data)); + g_slist_free(objects); + g_clear_object(&ui); + g_clear_object(&installed); + g_clear_object(&applications); + g_clear_object(&location); + g_clear_object(&local_media); if (relocations) razor_relocations_destroy(relocations); g_free(prefix); + g_free(setup_base); + g_free(update_base); exit(0); } @@ -323,7 +394,7 @@ G_MODULE_EXPORT void on_open_location(GtkWidget *widget) { show_busy_cursor(TRUE); path=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); - set=plover_package_set_new_from_repository(path,relocations,&err); + set=plover_package_set_new_from_yum(path,relocations,&err); if (set) { if (!location) @@ -399,7 +470,8 @@ G_MODULE_EXPORT void on_help_about(GtkWidget *widget) GtkWidget *w=GTK_WIDGET(gtk_builder_get_object(ui,"MainWindow")); gtk_show_about_dialog(GTK_WINDOW(w),"name",PACKAGE_NAME, "version",PACKAGE_VERSION,"comments","Application Manager", - "copyright","Copyright © 2010 J. Ali Harlow","logo-icon-name",LOGO_NAME, + "copyright","Copyright © 2010, 2014 J. Ali Harlow", + "logo-icon-name",LOGO_NAME, NULL); } diff --git a/app-manager/app-manager.h b/app-manager/app-manager.h index 63f077e..9e61cce 100644 --- a/app-manager/app-manager.h +++ b/app-manager/app-manager.h @@ -1,9 +1,11 @@ #include #include -#include +#include extern GtkBuilder *ui; extern struct razor_relocations *relocations; GtkTreeModel *plover_applications_model_new(GtkTreeModel *installed); void set_package_model(GtkTreeModel *model); PloverPackage *get_active_package(void); +gboolean setup(PloverPackageSet *current,const char *base); +gboolean update(PloverPackageSet *current,const char *base); diff --git a/app-manager/app-manager.ui b/app-manager/app-manager.ui index be45944..f5b7b74 100644 --- a/app-manager/app-manager.ui +++ b/app-manager/app-manager.ui @@ -1,9 +1,8 @@ - + - True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK Application Manager 600 @@ -368,7 +367,7 @@ - + True 6 6 diff --git a/app-manager/applications.c b/app-manager/applications.c index ceabefc..1a25680 100644 --- a/app-manager/applications.c +++ b/app-manager/applications.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "app-manager.h" #ifdef WIN32 @@ -63,7 +64,8 @@ static gboolean plover_applications_visible_func(GtkTreeModel *model, gtk_tree_model_get(model,iter,PLOVER_PACKAGE_STORE_OBJ_COLUMN,&package,-1); if (package) { - file_store=GTK_TREE_MODEL(plover_package_get_file_store(package)); + file_store= + GTK_TREE_MODEL(plover_package_file_store_new_from_package(package)); if (gtk_tree_model_get_iter_first(file_store,&fi)) { do @@ -94,6 +96,7 @@ static gboolean plover_applications_visible_func(GtkTreeModel *model, g_free(name); } while(!visible && gtk_tree_model_iter_next(file_store,&fi)); } + g_object_unref(file_store); } g_object_unref(package); return visible; diff --git a/app-manager/fetch.c b/app-manager/fetch.c index 8980899..41ddcf3 100644 --- a/app-manager/fetch.c +++ b/app-manager/fetch.c @@ -158,7 +158,7 @@ gchar *find_prefixed_file(const char *file) const char *name; char *install_root; struct razor_set *set; - struct razor_atomic *atomic; + struct razor_error *error=NULL; struct razor_package *package; struct razor_package_iterator *pi; struct razor_file_iterator *fi; @@ -167,8 +167,7 @@ gchar *find_prefixed_file(const char *file) install_root=getenv("RAZOR_ROOT"); if (!install_root) install_root=""; - atomic=razor_atomic_open("Query packages"); - set=razor_root_open_read_only(install_root,atomic); + set=razor_root_open_read_only(install_root,&error); if (set) { pi=razor_package_iterator_create(set); @@ -192,7 +191,8 @@ gchar *find_prefixed_file(const char *file) razor_package_iterator_destroy(pi); razor_set_unref(set); } - razor_atomic_destroy(atomic); + if (error) + razor_error_free(error); return retval; } diff --git a/app-manager/localmedia.c b/app-manager/localmedia.c index b4f745e..8a52b59 100644 --- a/app-manager/localmedia.c +++ b/app-manager/localmedia.c @@ -90,7 +90,7 @@ static void local_media_scan_mount(PloverLocalMediaStore *store,GMount *mount) #endif if (path) { - set=plover_package_set_new_from_repository(path,relocations,NULL); + set=plover_package_set_new_from_yum(path,relocations,NULL); if (set) { g_object_set_data(G_OBJECT(mount),"plover-local-media-set",set); diff --git a/app-manager/packagelist.c b/app-manager/packagelist.c index 988c7e3..d4041cd 100644 --- a/app-manager/packagelist.c +++ b/app-manager/packagelist.c @@ -20,8 +20,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include "app-manager.h" @@ -36,6 +36,7 @@ void package_present(PloverPackage *package) const char *text,*t; GtkWidget *w; GtkTextBuffer *buf; + PloverPackageFileStore *store; buf=GTK_TEXT_BUFFER(gtk_builder_get_object(ui,"description")); if (package) { @@ -97,8 +98,9 @@ void package_present(PloverPackage *package) { gtk_widget_hide(w); w=GTK_WIDGET(gtk_builder_get_object(ui,"Files")); - gtk_tree_view_set_model(GTK_TREE_VIEW(w), - GTK_TREE_MODEL(plover_package_get_file_store(package))); + store=plover_package_file_store_new_from_package(package); + gtk_tree_view_set_model(GTK_TREE_VIEW(w),GTK_TREE_MODEL(store)); + g_object_unref(store); } else { diff --git a/app-manager/resources.rc.in b/app-manager/resources.rc.in index 8169b6e..3781823 100644 --- a/app-manager/resources.rc.in +++ b/app-manager/resources.rc.in @@ -1,6 +1,8 @@ #include #include +#pragma code_page(65001) + MAINICON ICON "app-manager.ico" VS_VERSION_INFO VERSIONINFO @@ -31,3 +33,5 @@ VS_VERSION_INFO VERSIONINFO VALUE "Translation",0x809,0x4B0 } } + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app-manager.exe.manifest" diff --git a/app-manager/setup.c b/app-manager/setup.c new file mode 100644 index 0000000..047a2ea --- /dev/null +++ b/app-manager/setup.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "app-manager.h" + +gboolean setup(PloverPackageSet *installed,const char *base) +{ + gchar *s; + const char *prefix; + GError *error=NULL; + static PloverTransactionHelper *helper=NULL; + if (!helper) + { + helper=plover_transaction_helper_new(ui); + plover_transaction_helper_set_installed(helper,installed); + plover_transaction_helper_set_base(helper,base); + prefix=plover_transaction_helper_get_prefix(helper,&error); + if (error) + g_clear_error(&error); + else + { + s=g_strconcat(prefix?prefix:"","/var/log/setup",NULL); + plover_log_open(s); + g_free(s); + } + plover_transaction_helper_set_check_vendor(helper,TRUE); + g_signal_connect(helper,"close",G_CALLBACK(gtk_main_quit),NULL); + } + if (!plover_transaction_helper_get_visible(helper)) + { + if (!plover_transaction_helper_install_group(helper,"base",&error)) + { + if (g_error_matches(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK)) + { + g_error_free(error); + error=g_error_new_literal(PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK, + "All packages already installed"); + plover_transaction_helper_set_error(helper,error, + "Software installation"); + } + else if (g_error_matches(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET)) + { + g_error_free(error); + error=g_error_new_literal(PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET, + "Software cannot be installed because of missing updates. " + "Installing all updates first should resolve this problem"); + plover_transaction_helper_set_error(helper,error, + "Software installation failed"); + } + else + plover_transaction_helper_set_error(helper,error, + "Software installation failed"); + g_error_free(error); + } + } + plover_transaction_helper_present(helper); + return TRUE; +} diff --git a/app-manager/update.c b/app-manager/update.c new file mode 100644 index 0000000..0a647c8 --- /dev/null +++ b/app-manager/update.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "app-manager.h" + +gboolean update(PloverPackageSet *installed,const char *base) +{ + gchar *s; + const char *prefix; + GError *error=NULL; + static PloverTransactionHelper *helper=NULL; + if (!helper) + { + helper=plover_transaction_helper_new(ui); + plover_transaction_helper_set_installed(helper,installed); + plover_transaction_helper_set_base(helper,base); + prefix=plover_transaction_helper_get_prefix(helper,&error); + if (error) + g_clear_error(&error); + else + { + s=g_strconcat(prefix?prefix:"","/var/log/update",NULL); + plover_log_open(s); + g_free(s); + } + plover_transaction_helper_set_check_vendor(helper,TRUE); + g_signal_connect(helper,"close",G_CALLBACK(gtk_main_quit),NULL); + } + if (!plover_transaction_helper_get_visible(helper)) + { + if (!plover_transaction_helper_update(helper,&error)) + { + if (g_error_matches(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK)) + { + g_error_free(error); + error=g_error_new_literal(PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK, + "All relevant updates already applied"); + plover_transaction_helper_set_error(helper,error, + "Software update"); + } + else if (g_error_matches(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET)) + { + g_error_free(error); + error=g_error_new_literal(PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET, + "Updates cannot be applied because some earlier updates are " + "missing. Installing updates in sequence should resolve this " + "problem"); + plover_transaction_helper_set_error(helper,error, + "Software update failed"); + } + else + plover_transaction_helper_set_error(helper,error, + "Software update failed"); + g_error_free(error); + } + } + plover_transaction_helper_present(helper); + return TRUE; +} diff --git a/configure.ac b/configure.ac index 0a76556..71c61a8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. -AC_INIT([plover],[0.4.3],[ali@juiblex.co.uk]) +AC_INIT([plover],[0.4.50],[ali@juiblex.co.uk]) AC_PREREQ(2.59) AC_CONFIG_AUX_DIR([config]) AC_CONFIG_SRCDIR([plover/plover.h]) @@ -15,14 +15,18 @@ setup/Makefile setup/resources.rc update/Makefile update/resources.rc +pre-inst/Makefile +pre-inst/resources.rc app-manager/Makefile app-manager/resources.rc ]) PLOVER_MSWIN_MANIFEST([setup/setup.exe.manifest:setup/manifest.xml.in update/update.exe.manifest:update/manifest.xml.in +pre-inst/pre-inst.exe.manifest:pre-inst/manifest.xml.in app-manager/app-manager.exe.manifest:app-manager/manifest.xml.in ]) AM_INIT_AUTOMAKE(no-define) +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) case $VERSION in *.*.*) AC_SUBST(PLOVER_MAJOR_VERSION,[[`echo $VERSION | sed 's/\..*//'`]]) @@ -44,6 +48,15 @@ esac AC_CANONICAL_HOST AC_SUBST(HOST_OS,$host_os) AC_SUBST(HOST_CPU,$host_cpu) +case $host_os in + mingw*) + host_mingw="yes" + ;; + *) + host_mingw="" + ;; +esac +AM_CONDITIONAL(PLOVER_MINGW,[test -n "$host_mingw"]) # libtool versioning for libplover. For a release one of the following # must apply: @@ -54,7 +67,7 @@ AC_SUBST(HOST_CPU,$host_cpu) # increment CURRENT and set AGE and REVISION to 0. # - If the interface is the same as the previous version, increment REVISION. # -lt_current=2 +lt_current=3 lt_revision=0 lt_age=0 LIBPLOVER_LT_VERSION_INFO="$lt_current:$lt_revision:$lt_age" @@ -93,32 +106,44 @@ AC_CHECK_HEADERS([winhttp.h],[],[], ################################################## # Checks for libraries. ################################################## +PKG_CHECK_MODULES(WHELK,[whelk]) PKG_CHECK_MODULES(RAZOR,[razor >= 0.5.4],[:],[RAZOR_LIBS=-lrazor]) PKG_CHECK_MODULES(EXPAT,[expat],[:],[EXPAT_LIBS=-lexpat]) PKG_CHECK_MODULES(ZLIB,[zlib],[:],[ZLIB_LIBS=-lz]) PKG_CHECK_MODULES(GIO,[gio-2.0]) PKG_CHECK_MODULES(GTK,[gtk+-2.0]) PKG_CHECK_MODULES(GMODULE_EXPORT,[gmodule-export-2.0]) -LIBPLOVER_CFLAGS="$RAZOR_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS" -LIBPLOVER_LIBS="$RAZOR_LIBS $EXPAT_LIBS $ZLIB_LIBS" +LIBPLOVER_CFLAGS="$RAZOR_CFLAGS $EXPAT_CFLAGS $ZLIB_CFLAGS $GIO_CFLAGS" +LIBPLOVER_LIBS="$RAZOR_LIBS $EXPAT_LIBS $ZLIB_LIBS $GIO_LIBS" AC_SUBST(LIBPLOVER_CFLAGS) AC_SUBST(LIBPLOVER_LIBS) PLOVER_GTK_CFLAGS="$GTK_CFLAGS $RAZOR_CFLAGS" PLOVER_GTK_LIBS="$GTK_LIBS $RAZOR_LIBS" AC_SUBST(PLOVER_GTK_CFLAGS) AC_SUBST(PLOVER_GTK_LIBS) -GUI_CFLAGS="$GMODULE_EXPORT_CFLAGS $GIO_CFLAGS $PLOVER_GTK_CFLAGS $LIBPLOVER_CFLAGS" -GUI_LIBS="$GMODULE_EXPORT_LIBS $GIO_LIBS $PLOVER_GTK_LIBS $LIBPLOVER_LIBS" +save_LIBS="$LIBS" +AC_SEARCH_LIBS([crypt],[crypt]) +GUI_CFLAGS="$GMODULE_EXPORT_CFLAGS $WHELK_CFLAGS $PLOVER_GTK_CFLAGS \ + $LIBPLOVER_CFLAGS" +GUI_LIBS="-llua-posix $GMODULE_EXPORT_LIBS $WHELK_LIBS $PLOVER_GTK_LIBS \ + $LIBPLOVER_LIBS $LIBS" +LIBS="$save_LIBS" AC_SUBST(GUI_CFLAGS) AC_SUBST(GUI_LIBS) save_PKG_CONFIG="$PKG_CONFIG" PKG_CONFIG="$PKG_CONFIG --static" -PKG_CHECK_MODULES(SETUP,[whelk]) +PKG_CHECK_MODULES(SETUP,[whelk razor >= 0.5.4 expat zlib gio-2.0]) +if test -n "$host_mingw"; then + # Hack: -liconv is required for mingw. This probably stems from our use of + # libiconv rather than win-iconv that Fedora uses, but should be addressed + # somewhere in the stack below us. + SETUP_LIBS="$SETUP_LIBS -liconv" +fi PKG_CONFIG="$save_PKG_CONFIG" save_LIBS="$LIBS" AC_SEARCH_LIBS([crypt],[crypt]) -SETUP_LIBS="-llua-posix $SETUP_LIBS $RAZOR_LIBS $LIBS" -SETUP_CFLAGS="$SETUP_CFLAGS $RAZOR_CFLAGS" +SETUP_LIBS="-llua-posix $SETUP_LIBS $LIBS" +SETUP_CFLAGS="$SETUP_CFLAGS" AC_SUBST(SETUP_LIBS) AC_SUBST(SETUP_CFLAGS) LIBS="$save_LIBS" @@ -139,7 +164,7 @@ LIBS="$save_LIBS" ################################################## # Checks for library functions. ################################################## -AC_CHECK_FUNCS_ONCE([fchdir]) +AC_CHECK_FUNCS_ONCE([fchdir fpathconf dirfd]) ################################################## # Checks for processor independent files. diff --git a/plover-gtk/Makefile.am b/plover-gtk/Makefile.am index f5c8f35..50025e1 100644 --- a/plover-gtk/Makefile.am +++ b/plover-gtk/Makefile.am @@ -1,15 +1,19 @@ -AM_CFLAGS=-g $(PLOVER_GTK_CFLAGS) +AM_CFLAGS=-g $(PLOVER_GTK_CFLAGS) -DPLOVER_DATADIR=\""$(pkgdatadir)"\" LIBS=../plover/libplover.la $(PLOVER_GTK_LIBS) INCLUDES=-I$(top_srcdir) AM_LDFLAGS=-no-undefined -version-info $(PLOVER_GTK_LT_VERSION_INFO) -pkginclude_HEADERS=error.h package.h packageset.h packagestore.h \ - packagefilestore.h +uidir=$(pkgdatadir) +ui_DATA=software-installation.ui + +pkginclude_HEADERS=\ + transactionhelper.h packagestore.h packagefilestore.h stockicons.h lib_LTLIBRARIES=libplover-gtk.la libplover_gtk_la_SOURCES=$(pkginclude_HEADERS) \ - error.c package.c packageset.c packagestore.c \ - packagefilestore.c + transactionhelper.c packagestore.c packagefilestore.c stockicons.c pkgconfigdir=$(libdir)/pkgconfig pkgconfig_DATA=plover-gtk.pc + +EXTRA_DIST=software-installation.ui diff --git a/plover-gtk/error.c b/plover-gtk/error.c deleted file mode 100644 index 237d39a..0000000 --- a/plover-gtk/error.c +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2010 J. Ali Harlow - * - * 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 of the License, 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include -#include - -GQuark plover_razor_error_quark(void) -{ - static GQuark quark=0; - if (!quark) - quark=g_quark_from_static_string("plover-razor-error-quark"); - return quark; -} - diff --git a/plover-gtk/error.h b/plover-gtk/error.h deleted file mode 100644 index 640dd36..0000000 --- a/plover-gtk/error.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __PLOVER_ERROR_H__ -#define __PLOVER_ERROR_H__ - -#define PLOVER_RAZOR_ERROR plover_razor_error_quark() - -typedef enum { - PLOVER_RAZOR_ERROR_FAILED -} PloverRazorError; - -GQuark plover_razor_error_quark(void); - -#endif /* __PLOVER_ERROR_H__ */ diff --git a/plover-gtk/package.c b/plover-gtk/package.c deleted file mode 100644 index 96c8a73..0000000 --- a/plover-gtk/package.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2010 J. Ali Harlow - * - * 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 of the License, 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include "plover-gtk/package.h" - -G_DEFINE_TYPE(PloverPackage,plover_package,G_TYPE_OBJECT); - -typedef struct _PloverPackagePrivate { - struct razor_set *set; - struct razor_package *pkg; - PloverPackageFileStore *file_store; -} PloverPackagePrivate; - -#define PLOVER_PACKAGE_GET_PRIVATE(obj)\ - G_TYPE_INSTANCE_GET_PRIVATE(obj,\ - PLOVER_TYPE_PACKAGE,PloverPackagePrivate) - -enum { - CHANGED=0, - N_SIGNALS -}; - -static guint signals[N_SIGNALS]; - -static void plover_package_dispose(GObject *obj) -{ - PloverPackagePrivate *priv=PLOVER_PACKAGE_GET_PRIVATE(obj); - if (priv->file_store) - { - g_object_unref(priv->file_store); - priv->file_store=NULL; - } - if (G_OBJECT_CLASS(plover_package_parent_class)->dispose) - G_OBJECT_CLASS(plover_package_parent_class)->dispose(obj); -} - -static void plover_package_class_init(PloverPackageClass *klass) -{ - GObjectClass *oclass=G_OBJECT_CLASS(klass); - oclass->dispose=plover_package_dispose; - g_type_class_add_private(klass,sizeof(PloverPackagePrivate)); - signals[CHANGED]=g_signal_newv("changed", - G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, - g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); -} - -static void plover_package_init(PloverPackage *package) -{ -} - -PloverPackage *plover_package_new(struct razor_set *set, - struct razor_package *pkg) -{ - PloverPackage *package; - PloverPackagePrivate *priv; - package=g_object_new(PLOVER_TYPE_PACKAGE,NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - priv->set=set; - priv->pkg=pkg; - return package; -} - -const char *plover_package_get_name(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *name=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_NAME,&name, - RAZOR_DETAIL_LAST); - return name; -} - -const char *plover_package_get_summary(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *summary=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_SUMMARY,&summary, - RAZOR_DETAIL_LAST); - return summary; -} - -const char *plover_package_get_version(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *version=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_VERSION,&version, - RAZOR_DETAIL_LAST); - return version; -} - -const char *plover_package_get_license(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *license=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_LICENSE,&license, - RAZOR_DETAIL_LAST); - return license; -} - -const char *plover_package_get_arch(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *arch=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_ARCH,&arch, - RAZOR_DETAIL_LAST); - return arch; -} - -const char *plover_package_get_description(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *description=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_DESCRIPTION, - &description,RAZOR_DETAIL_LAST); - return description; -} - -const char *plover_package_get_URL(PloverPackage *package) -{ - PloverPackagePrivate *priv; - const char *URL=NULL; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_URL,&URL, - RAZOR_DETAIL_LAST); - return URL; -} - -GdkPixbuf *plover_package_get_icon(PloverPackage *package) -{ - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - return NULL; -} - -PloverPackageFileStore *plover_package_get_file_store(PloverPackage *package) -{ - PloverPackagePrivate *priv; - struct razor_file_iterator *iter; - g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); - priv=PLOVER_PACKAGE_GET_PRIVATE(package); - if (!priv->file_store) - { - iter=razor_file_iterator_create(priv->set,priv->pkg,0); - priv->file_store=plover_package_file_store_new(iter); - razor_file_iterator_destroy(iter); - } - return priv->file_store; -} diff --git a/plover-gtk/package.h b/plover-gtk/package.h deleted file mode 100644 index 23bbff5..0000000 --- a/plover-gtk/package.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef __PLOVER_PACKAGE_H__ -#define __PLOVER_PACKAGE_H__ - -#include -#include -#include -#include -#include - -G_BEGIN_DECLS - -#define PLOVER_TYPE_PACKAGE plover_package_get_type() -#define PLOVER_PACKAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ - PLOVER_TYPE_PACKAGE,PloverPackage) -#define PLOVER_PACKAGE_CLASS(klass)\ - G_TYPE_CHECK_CLASS_CAST(klass,\ - PLOVER_TYPE_PACKAGE,PloverPackageClass) -#define PLOVER_IS_PACKAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj,\ - PLOVER_TYPE_PACKAGE) -#define PLOVER_IS_PACKAGE_CLASS(klass)\ - G_TYPE_CHECK_CLASS_TYPE(obj,\ - PLOVER_TYPE_PACKAGE) -#define PLOVER_PACKAGE_GET_CLASS(obj)\ - G_TYPE_INSTANCE_GET_CLASS(obj,\ - PLOVER_TYPE_PACKAGE,PloverPackageClass) - -typedef struct _PloverPackage { - GObject parent_instance; -} PloverPackage; - -typedef struct _PloverPackageClass { - GObjectClass parent_class; -} PloverPackageClass; - -GType plover_package_get_type(void) G_GNUC_CONST; -PloverPackage *plover_package_new(struct razor_set *set, - struct razor_package *pkg); -const char *plover_package_get_name(PloverPackage *package); -const char *plover_package_get_summary(PloverPackage *package); -const char *plover_package_get_version(PloverPackage *package); -const char *plover_package_get_license(PloverPackage *package); -const char *plover_package_get_arch(PloverPackage *package); -const char *plover_package_get_description(PloverPackage *package); -const char *plover_package_get_URL(PloverPackage *package); -GdkPixbuf *plover_package_get_icon(PloverPackage *package); -PloverPackageFileStore *plover_package_get_file_store(PloverPackage *package); - -G_END_DECLS - -#endif /* __PLOVER_PACKAGE_H__ */ diff --git a/plover-gtk/packagefilestore.c b/plover-gtk/packagefilestore.c index 2b1c4e4..f890e0f 100644 --- a/plover-gtk/packagefilestore.c +++ b/plover-gtk/packagefilestore.c @@ -257,3 +257,15 @@ PloverPackageFileStore * gtk_tree_path_free(path); return store; } + +PloverPackageFileStore * + plover_package_file_store_new_from_package(PloverPackage *package) +{ + PloverPackageFileStore *store; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + struct razor_file_iterator *iter; + iter=plover_package_file_iterator_create(package,FALSE); + store=GTK_TREE_MODEL(plover_package_file_store_new(iter)); + razor_file_iterator_destroy(iter); + return store; +} diff --git a/plover-gtk/packagefilestore.h b/plover-gtk/packagefilestore.h index 65c27c6..e9778f3 100644 --- a/plover-gtk/packagefilestore.h +++ b/plover-gtk/packagefilestore.h @@ -2,6 +2,7 @@ #define __PLOVER_PACKAGE_FILE_STORE_H__ #include +#include #include G_BEGIN_DECLS @@ -44,6 +45,8 @@ typedef struct _PloverPackageFileStoreClass { GType plover_package_file_store_get_type(void) G_GNUC_CONST; PloverPackageFileStore * plover_package_file_store_new(struct razor_file_iterator *files); +PloverPackageFileStore * + plover_package_file_store_new_from_package(PloverPackage *package); G_END_DECLS diff --git a/plover-gtk/packageset.c b/plover-gtk/packageset.c deleted file mode 100644 index e310641..0000000 --- a/plover-gtk/packageset.c +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2010-2012 J. Ali Harlow - * - * 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 of the License, 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include "plover/plover.h" -#include "plover-gtk/error.h" -#include "plover-gtk/packageset.h" -#include "plover-gtk/package.h" - -G_DEFINE_TYPE(PloverPackageSet,plover_package_set,G_TYPE_OBJECT); - -typedef struct _PloverPackageSetPrivate { - struct razor_root *root; - struct razor_set *set; - GSList *packages; - int no_details; -} PloverPackageSetPrivate; - -#define PLOVER_PACKAGE_SET_GET_PRIVATE(obj)\ - G_TYPE_INSTANCE_GET_PRIVATE(obj,\ - PLOVER_TYPE_PACKAGE_SET,\ - PloverPackageSetPrivate) - -enum { - CHANGED=0, - N_SIGNALS -}; - -static guint signals[N_SIGNALS]; - -static void plover_package_set_finalize(GObject *obj) -{ - PloverPackageSetPrivate *priv=PLOVER_PACKAGE_SET_GET_PRIVATE(obj); - if (priv->root) - razor_root_close(priv->root); - if (G_OBJECT_CLASS(plover_package_set_parent_class)->finalize) - G_OBJECT_CLASS(plover_package_set_parent_class)->finalize(obj); -} - -static void plover_package_set_dispose(GObject *obj) -{ - PloverPackageSetPrivate *priv=PLOVER_PACKAGE_SET_GET_PRIVATE(obj); - if (priv->set) - { - razor_set_unref(priv->set); - priv->set=NULL; - } - if (G_OBJECT_CLASS(plover_package_set_parent_class)->dispose) - G_OBJECT_CLASS(plover_package_set_parent_class)->dispose(obj); -} - -static void plover_package_set_class_init(PloverPackageSetClass *klass) -{ - GObjectClass *oclass=G_OBJECT_CLASS(klass); - oclass->finalize=plover_package_set_finalize; - oclass->dispose=plover_package_set_dispose; - g_type_class_add_private(klass,sizeof(PloverPackageSetPrivate)); - signals[CHANGED]=g_signal_newv("changed", - G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, - g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); -} - -static void plover_package_set_init(PloverPackageSet *set) -{ - PloverPackageSetPrivate *priv; - priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); - priv->no_details=-1; -} - -PloverPackageSet *plover_package_set_new(void) -{ - return g_object_new(PLOVER_TYPE_PACKAGE_SET,NULL); -} - -PloverPackageSet *plover_package_set_new_from_installed(const char *root, - GError **err) -{ - PloverPackageSet *set; - PloverPackageSetPrivate *priv; - struct razor_error *error=NULL; - set=plover_package_set_new(); - priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); - priv->root=razor_root_open(root,&error); - if (!priv->root) - { - g_set_error_literal(err,PLOVER_RAZOR_ERROR,PLOVER_RAZOR_ERROR_FAILED, - razor_error_get_msg(error)); - razor_error_free(error); - g_object_unref(set); - return NULL; - } - priv->set=razor_set_ref(razor_root_get_system_set(priv->root)); - if (!priv->set) - { - g_set_error(err,PLOVER_RAZOR_ERROR,PLOVER_RAZOR_ERROR_FAILED, - "Failed to get system set from %s",root); - g_object_unref(set); - return NULL; - } - return set; -} - -PloverPackageSet *plover_package_set_new_from_repository(const char *base, - struct razor_relocations *relocations,GError **err) -{ -#if HAVE_FCHDIR - int fd; -#else - size_t wd_len; - char *wd; -#endif - gchar *s; - struct razor_set *reloc; - struct razor_error *error=NULL; - PloverPackageSet *set; - PloverPackageSetPrivate *priv; - set=plover_package_set_new(); - priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); -#if HAVE_FCHDIR - fd=open(".",O_RDONLY); -#else - wd_len=32; - wd=malloc(wd_len); - while (!getcwd(wd,wd_len) && errno==ERANGE) - { - free(wd); - wd_len*=2; - wd=malloc(wd_len); - } -#endif - s=g_build_filename(base,"repodata",NULL); - if (chdir(s)<0) - { - g_set_error(err,G_FILE_ERROR,g_file_error_from_errno(errno), - "%s: %s",s,g_strerror(errno)); - g_object_unref(set); -#if HAVE_FCHDIR - close(fd); -#else - free(wd); -#endif - return NULL; - } - g_free(s); - priv->set=plover_razor_set_create_from_yum(".."); -#if HAVE_FCHDIR - (void)fchdir(fd); - close(fd); -#else - chdir(wd); - free(wd); -#endif - if (priv->set && relocations) - { - reloc=plover_relocate_packages(priv->set,base,relocations,&error); - if (!reloc) - { - g_set_error_literal(err,PLOVER_RAZOR_ERROR, - PLOVER_RAZOR_ERROR_FAILED,razor_error_get_msg(error)); - razor_error_free(error); - g_object_unref(set); - return NULL; - } - razor_set_unref(priv->set); - priv->set=reloc; - } - if (!priv->set) - { - g_set_error(err,PLOVER_RAZOR_ERROR,PLOVER_RAZOR_ERROR_FAILED, - "Failed to create package set from repository %s",base); - g_object_unref(set); - return NULL; - } - return set; -} - -GSList *plover_package_set_get_packages(PloverPackageSet *set) -{ - struct razor_package_iterator *iter; - struct razor_package *pkg; - PloverPackageSetPrivate *priv; - PloverPackage *package; - g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); - priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); - if (priv->set && !priv->packages) - { - iter=razor_package_iterator_create(priv->set); - while(razor_package_iterator_next(iter,&pkg,RAZOR_DETAIL_LAST)) - { - package=plover_package_new(priv->set,pkg); - priv->packages=g_slist_prepend(priv->packages,package); - } - razor_package_iterator_destroy(iter); - } - return priv->packages; -} - -/* - * Some versions of razor have a bug which causes all detail strings - * to be discarded. If such a version of razor is used to install or - * update a package, then all the detail strings for the installed - * set will be lost. This function tests for this condition and can - * be used to present something more useful than blank details. - */ - -gboolean plover_package_set_get_no_details(PloverPackageSet *set) -{ - PloverPackageSetPrivate *priv; - PloverPackage *package; - GSList *packages,*link; - g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),FALSE); - priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); - if (priv->no_details<0) - { - packages=plover_package_set_get_packages(set); - if (packages) - { - priv->no_details=0; - for(link=packages;link;link=link->next) - { - package=link->data; - priv->no_details+=2; - if (*plover_package_get_summary(package)) - priv->no_details--; - if (*plover_package_get_license(package)) - priv->no_details--; - if (*plover_package_get_description(package)) - priv->no_details--; - if (*plover_package_get_URL(package)) - priv->no_details--; - } - if (priv->no_details<0) /* More than 50% of strings present */ - priv->no_details=0; - } - } - return priv->no_details>0; -} diff --git a/plover-gtk/packageset.h b/plover-gtk/packageset.h deleted file mode 100644 index 66c0529..0000000 --- a/plover-gtk/packageset.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef __PLOVER_PACKAGE_SET_H__ -#define __PLOVER_PACKAGE_SET_H__ - -#include -#include - -G_BEGIN_DECLS - -#define PLOVER_TYPE_PACKAGE_SET plover_package_set_get_type() -#define PLOVER_PACKAGE_SET(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ - PLOVER_TYPE_PACKAGE_SET,PloverPackageSet) -#define PLOVER_PACKAGE_SET_CLASS(klass)\ - G_TYPE_CHECK_CLASS_CAST(klass,\ - PLOVER_TYPE_PACKAGE_SET,\ - PloverPackageSetClass) -#define PLOVER_IS_PACKAGE_SET(obj)\ - G_TYPE_CHECK_INSTANCE_TYPE(obj,\ - PLOVER_TYPE_PACKAGE_SET) -#define PLOVER_IS_PACKAGE_SET_CLASS(klass)\ - G_TYPE_CHECK_CLASS_TYPE(obj,\ - PLOVER_TYPE_PACKAGE_SET) -#define PLOVER_PACKAGE_SET_GET_CLASS(obj)\ - G_TYPE_INSTANCE_GET_CLASS(obj,\ - PLOVER_TYPE_PACKAGE_SET,\ - PloverPackageSetClass) - -typedef struct _PloverPackageSet { - GObject parent_instance; -} PloverPackageSet; - -typedef struct _PloverPackageSetClass { - GObjectClass parent_class; -} PloverPackageSetClass; - -GType plover_package_set_get_type(void) G_GNUC_CONST; -PloverPackageSet *plover_package_set_new(void); -PloverPackageSet *plover_package_set_new_from_installed(const char *root, - GError **err); -PloverPackageSet *plover_package_set_new_from_repository(const char *base, - struct razor_relocations *relocations,GError **err); -GSList *plover_package_set_get_packages(PloverPackageSet *set); -gboolean plover_package_set_get_no_details(PloverPackageSet *set); - -G_END_DECLS - -#endif /* __PLOVER_PACKAGE_SET_H__ */ diff --git a/plover-gtk/packagestore.c b/plover-gtk/packagestore.c index e5cb99a..3611372 100644 --- a/plover-gtk/packagestore.c +++ b/plover-gtk/packagestore.c @@ -24,7 +24,7 @@ #include #include #include "plover/plover.h" -#include "plover-gtk/package.h" +#include "plover/package.h" #include "plover-gtk/packagestore.h" #define VALID_ITER(iter,store) ((iter) && (iter)->user_data && \ @@ -139,6 +139,8 @@ static void plover_package_store_get_value(GtkTreeModel *tree_model, char *s; PloverPackageStore *store=(PloverPackageStore *)tree_model; PloverPackage *package; + GInputStream *stream; + GdkPixbuf *icon; g_return_if_fail(column>=0 && columnuser_data)); @@ -151,7 +153,15 @@ static void plover_package_store_get_value(GtkTreeModel *tree_model, case PLOVER_PACKAGE_STORE_INSTALLED_COLUMN: break; case PLOVER_PACKAGE_STORE_ICON_COLUMN: - g_value_set_object(value,plover_package_get_icon(package)); + stream=plover_package_read_icon(package,NULL); + if (stream) + { + icon=gdk_pixbuf_new_from_stream(stream,NULL,NULL); + g_object_unref(stream); + } + else + icon=NULL; + g_value_set_object(value,icon); break; case PLOVER_PACKAGE_STORE_NAME_COLUMN: g_value_set_string(value,plover_package_get_name(package)); diff --git a/plover-gtk/packagestore.h b/plover-gtk/packagestore.h index 78691a9..bd1b1ba 100644 --- a/plover-gtk/packagestore.h +++ b/plover-gtk/packagestore.h @@ -2,7 +2,7 @@ #define __PLOVER_PACKAGE_STORE_H__ #include -#include +#include G_BEGIN_DECLS diff --git a/plover-gtk/software-installation.ui b/plover-gtk/software-installation.ui new file mode 100644 index 0000000..091e665 --- /dev/null +++ b/plover-gtk/software-installation.ui @@ -0,0 +1,175 @@ + + + + + + 12 + Software Installation + + + + + + + + + + True + 0 + 0 + 16 + 16 + <b>Welcome to the Installation Assistant</b> + +The Installation Assistant will install the software. +To continue, click Forward. + True + + + intro + Software Installation + + + + + True + vertical + + + vertical + + + True + 0 + 0 + 16 + 16 + <b>Incompatible Installation</b> + +The existing installation is not from %s +In order to continue, all the existing packages must be removed. + True + + + False + 0 + + + + + True + 16 + 16 + + + Remove all existing packages + True + True + False + True + + + + + False + 1 + + + + + False + 0 + + + + + 450 + True + 0 + 0 + 16 + 16 + <b>Installation Summary</b> + +Packages to be installed or updated: ... plus dependencies. + True + True + + + 1 + + + + + confirm + Software Installation (2 of 4) + + + + + True + vertical + + + True + 0 + 0 + 16 + 16 + <b>Installing the Software</b> + +Please wait while the Installation Assistant installs the software. +This may take several minutes. + True + + + False + 0 + + + + + True + 16 + 16 + 16 + + + True + True + True + Unpacking files + + + + + False + 1 + + + + + progress + Software Installation (3 of 4) + + + + + True + 0 + 0 + 16 + 16 + <b>Installation Assistant Completed</b> + +The Installation Assistant has successfully completed. +Click Close to exit the Assistant. + True + + + summary + Software Installation (4 of 4) + + + + diff --git a/plover-gtk/stockicons.c b/plover-gtk/stockicons.c new file mode 100644 index 0000000..f5c4011 --- /dev/null +++ b/plover-gtk/stockicons.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2010 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#ifdef WIN32 +#include +#endif /* WIN32 */ +#include +#include +#include + +/* Checks whether a loader for SVG files has been registered + * with GdkPixbuf. + */ +static gboolean plover_pixbuf_supports_svg(void) +{ + GSList *formats; + GSList *tmp_list; + static gint found_svg=-1; + gchar **mime_types,**mime_type; + if (found_svg!=-1) + return found_svg; + formats=gdk_pixbuf_get_formats(); + found_svg=FALSE; + for (tmp_list=formats;tmp_list && !found_svg;tmp_list=tmp_list->next) + { + mime_types=gdk_pixbuf_format_get_mime_types(tmp_list->data); + for (mime_type=mime_types;*mime_type && !found_svg;mime_type++) + if (!strcmp(*mime_type,"image/svg")) + found_svg=TRUE; + g_strfreev(mime_types); + } + g_slist_free(formats); + return found_svg; +} + +static void plover_install_icon_at_size(const char *icon_name, + GtkIconSet *icon_set,GtkIconSize size,const char *filename) +{ + int w,h; + GdkPixbuf *pixbuf; + GtkIconSource *source; + if (gtk_icon_size_lookup(size,&w,&h)) + { + pixbuf=gdk_pixbuf_new_from_file_at_size(filename,w,h,NULL); + if (pixbuf) + { + source=gtk_icon_source_new(); + gtk_icon_source_set_size_wildcarded(source,FALSE); + gtk_icon_source_set_size(source,size); + gtk_icon_source_set_pixbuf(source,pixbuf); + gtk_icon_set_add_source(icon_set,source); + gtk_icon_source_free(source); + g_object_unref(pixbuf); + } + } +} + +/** + * plover_icons_add_to_stock: + * @type: The icon type, typically "apps" or "mimetype" + * @name: The icon name (the basename of files containing the icons) + * + * Find icons in /icons/hicolor and add them to the stock images + * so that, for example, gtk_image_new_from_stock() will be able find them. + * + * If there is an SVG loader registered with GdkPixbuf, then: + * /icons/hicolor/scalable/@type/@name.svg will be used. Otherwise, + * /icons/hicolor/24x24/@type/@name.png and + * /icons/hicolor/48x48/@type/@name.png will be used. + */ +void plover_icons_add_to_stock(const char *type,const char *name) +{ + gchar *prefix,*s,*filename; + GtkIconSource *source; + GtkIconSet *icon_set; + GtkIconFactory *factory; + factory=gtk_icon_factory_new(); + icon_set=gtk_icon_set_new(); +#ifdef WIN32 + prefix=g_win32_get_package_installation_directory_of_module(NULL); +#else + prefix=NULL; +#endif + if (plover_pixbuf_supports_svg()) + { + source=gtk_icon_source_new(); + s=g_strconcat(name,".svg"); + filename=g_build_filename(prefix?prefix:"/usr", + "share/icons/hicolor/scalable",type,s,NULL); + g_free(s); + gtk_icon_source_set_filename(source,filename); + g_free(filename); + gtk_icon_set_add_source(icon_set,source); + gtk_icon_source_free(source); + } + else + { + s=g_strconcat(name,".png"); + filename=g_build_filename(prefix?prefix:"/usr", + "share/icons/hicolor/24x24",type,s,NULL); + plover_install_icon_at_size(name,icon_set,GTK_ICON_SIZE_MENU, + filename); + plover_install_icon_at_size(name,icon_set,GTK_ICON_SIZE_BUTTON, + filename); + plover_install_icon_at_size(name,icon_set,GTK_ICON_SIZE_SMALL_TOOLBAR, + filename); + plover_install_icon_at_size(name,icon_set,GTK_ICON_SIZE_LARGE_TOOLBAR, + filename); + g_free(filename); + filename=g_build_filename(prefix?prefix:"/usr", + "share/icons/hicolor/48x48",type,s,NULL); + plover_install_icon_at_size(name,icon_set,GTK_ICON_SIZE_DND, + filename); + plover_install_icon_at_size(name,icon_set,GTK_ICON_SIZE_DIALOG, + filename); + g_free(filename); + g_free(s); + } + gtk_icon_factory_add(factory,name,icon_set); + gtk_icon_set_unref(icon_set); + //icon_set=gtk_icon_factory_lookup(factory,name); + gtk_icon_factory_add_default(factory); + g_object_unref(factory); +} diff --git a/plover-gtk/stockicons.h b/plover-gtk/stockicons.h new file mode 100644 index 0000000..e2ac76f --- /dev/null +++ b/plover-gtk/stockicons.h @@ -0,0 +1,6 @@ +#ifndef __PLOVER_STOCK_ICONS_H__ +#define __PLOVER_STOCK_ICONS_H__ + +void plover_icons_add_to_stock(const char *type,const char *name); + +#endif /* __PLOVER_STOCK_ICONS_H__ */ diff --git a/plover-gtk/transactionhelper.c b/plover-gtk/transactionhelper.c new file mode 100644 index 0000000..686f6d7 --- /dev/null +++ b/plover-gtk/transactionhelper.c @@ -0,0 +1,1037 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include + +/* + * A PloverTransactionHelper uses a GtkAssistant to help a user run a + * transaction. + */ + +G_DEFINE_TYPE(PloverTransactionHelper,plover_transaction_helper,G_TYPE_OBJECT) + +enum { + CLOSE=0, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +static void plover_transaction_helper_finalize(PloverTransactionHelper *helper) +{ + g_free(helper->error_primary_text); + g_free(helper->base); + g_free(helper->unsatisfied); + if (helper->comps) + plover_comps_free(helper->comps); + plover_vector_free(helper->report_adding); + plover_vector_free(helper->report_removing); +} + +static void plover_transaction_helper_dispose(PloverTransactionHelper *helper) +{ + g_clear_error(&helper->error); + if (helper->error_dialog) + { + g_signal_handlers_disconnect_by_data(helper->error_dialog,helper); + gtk_widget_destroy(helper->error_dialog); + helper->error_dialog=NULL; + } + if (helper->assistant) + { + g_signal_handlers_disconnect_by_data(helper->assistant,helper); + g_clear_object(&helper->assistant); + } + g_clear_object(&helper->ui); + g_slist_foreach(helper->transactions,(GFunc)g_object_unref,NULL); + g_slist_free(helper->transactions); + helper->transactions=NULL; + g_clear_object(&helper->installed); + g_clear_object(&helper->upstream); + g_clear_object(&helper->relocated_upstream); +} + +static void + plover_transaction_helper_class_init(PloverTransactionHelperClass *klass) +{ + GObjectClass *gobject_class=G_OBJECT_CLASS(klass); + gobject_class->finalize= + (void (*)(GObject *))plover_transaction_helper_finalize; + gobject_class->dispose= + (void (*)(GObject *))plover_transaction_helper_dispose; + signals[CLOSE]=g_signal_newv("close", + G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, + g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); +} + +static void plover_transaction_helper_init(PloverTransactionHelper *helper) +{ + helper->report_adding=plover_vector_new(); + helper->report_removing=plover_vector_new(); +} + +static void plover_transaction_helper_assistant_cancel(GtkAssistant *assistant, + PloverTransactionHelper *helper) +{ + gtk_widget_hide(GTK_WIDGET(helper->assistant)); + gtk_assistant_set_current_page(helper->assistant,0); + g_signal_emit(helper,signals[CLOSE],0); +} + +static void plover_transaction_helper_assistant_close(GtkAssistant *assistant, + PloverTransactionHelper *helper) +{ + gtk_widget_hide(GTK_WIDGET(helper->assistant)); + gtk_assistant_set_current_page(helper->assistant,0); + g_signal_emit(helper,signals[CLOSE],0); +} + +static void + plover_transaction_helper_prepare_confirm(PloverTransactionHelper *helper) +{ + gchar *package_list,*add,*remove,*s; + GtkLabel *label; + struct plover_vector *report; + if (helper->report_adding->len) + { + plover_vector_sort(helper->report_adding); + if (helper->report_adding_dependencies) + { + report=plover_vector_dup(helper->report_adding); + if (helper->report_adding->len==1) + plover_vector_append(report,"its dependencies"); + else + plover_vector_append(report,"their dependencies"); + package_list=plover_vector_format_for_display(report); + plover_vector_free(report); + } + else + package_list= + plover_vector_format_for_display(helper->report_adding); + add=g_strconcat("Packages to be installed or updated: ",package_list, + ".",NULL); + g_free(package_list); + } + else + add=NULL; + if (helper->report_removing->len) + { + plover_vector_sort(helper->report_removing); + if (helper->report_removing_dependants) + { + report=plover_vector_dup(helper->report_removing); + if (helper->report_adding->len==1) + plover_vector_append(report,"its dependants"); + else + plover_vector_append(report,"their dependants"); + package_list=plover_vector_format_for_display(report); + plover_vector_free(report); + } + else + package_list= + plover_vector_format_for_display(helper->report_removing); + remove=g_strconcat("Packages to be removed: ",package_list,".",NULL); + g_free(package_list); + } + else + remove=NULL; + label=GTK_LABEL(gtk_builder_get_object(helper->ui,"SISummaryOfWork")); + if (add && remove) + s=g_strconcat("Installation Summary\n\n",remove,"\n\n",add,NULL); + else if (add || remove) + s=g_strconcat("Installation Summary\n\n",add?add:remove,NULL); + else + s=g_strdup("Installation Summary\n\nNo changes scheduled"); + gtk_label_set_markup(label,s); + g_free(s); + g_free(add); + g_free(remove); +} + +static void plover_transaction_helper_run(PloverTransactionHelper *helper); + +static void plover_transaction_helper_callback(GObject *source, + GAsyncResult *result,gpointer user_data) +{ + GError *error=NULL; + PloverTransactionHelper *helper=user_data; + PloverTransaction *transaction=PLOVER_TRANSACTION(source); + if (!plover_transaction_commit_finish(transaction,result,&error)) + { + plover_transaction_helper_set_error(helper,error, + "Software installation failed"); + g_error_free(error); + } + else + plover_transaction_helper_run(helper); + g_signal_handlers_disconnect_by_data(transaction,helper); + g_object_unref(transaction); +} + +static void plover_transaction_helper_transaction_status_changed( + PloverTransaction *transaction,const char *status, + PloverTransactionHelper *helper) +{ + GtkProgressBar *bar; + bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); + gtk_progress_bar_set_text(bar,status); +} + +static void plover_transaction_helper_run(PloverTransactionHelper *helper) +{ + PloverTransaction *transaction; + GtkWidget *page; + page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); + if (helper->transactions) + { + if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,FALSE); + transaction=helper->transactions->data; + helper->transactions=g_slist_delete_link(helper->transactions, + helper->transactions); + g_signal_connect(transaction,"status-changed", + G_CALLBACK(plover_transaction_helper_transaction_status_changed), + helper); + plover_transaction_commit_async(transaction,NULL, + plover_transaction_helper_callback,helper); + } + else if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,TRUE); +} + +static gboolean plover_transaction_helper_pulse(gpointer user_data) +{ + PloverTransactionHelper *helper=user_data; + GtkWidget *w; + GtkProgressBar *bar; + if (!helper->assistant) + return FALSE; + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress")); + bar=GTK_PROGRESS_BAR(gtk_builder_get_object(helper->ui,"SIProgressBar")); + if (gtk_assistant_get_page_complete(helper->assistant,w)) + { + gtk_progress_bar_set_fraction(bar,1.0); + helper->pulse_handler=0; + return FALSE; + } + else + { + gtk_progress_bar_pulse(bar); + return TRUE; + } +} + +static void + plover_transaction_helper_prepare_progress(PloverTransactionHelper *helper) +{ + GError *error=NULL; + GtkToggleButton *button; + PloverTransaction *transaction; + button=GTK_TOGGLE_BUTTON(gtk_builder_get_object(helper->ui, + "SIRemoveExisting")); + if (gtk_toggle_button_get_active(button)) + { + transaction=plover_transaction_new_remove(NULL,&error); + if (!transaction) + { + if (g_error_matches(error,PLOVER_POSIX_ERROR,ENOENT)) + g_clear_error(&error); + if (error) + { + plover_transaction_helper_set_error(helper,error, + "Software installation failed"); + g_error_free(error); + return; + } + } + else + helper->transactions= + g_slist_prepend(helper->transactions,transaction); + } + /* + * Note that PloverTransaction does support cancelling a transaction, but + * there are a number of challenges with using it: + * - cancellation is only supported during the file phase if razor + * has atomic rollback, + * - cancellation is not supported during post-transaction scripts at all + * (since by the time the first script is started the atomic has already + * been committed) and these can take quite some time, + * - where a transaction has an embedded COMMIT, any rollback won't + * go back beyond this point. + * To support user-cancel, then, we would need some mechanism to: + * - Comunicate that the operation is being cancelled and this may take + * some time, + * - Not allow cancellation at all after the last post-transaction script + * phase is started, + * - Report the partially completed transaction where cancellation + * occurred after a COMMIT point. + * At present, this doesn't appear worth the effort. + */ + if (helper->assistant) + gtk_assistant_commit(helper->assistant); + plover_transaction_helper_run(helper); + helper->pulse_handler=g_timeout_add(100,plover_transaction_helper_pulse, + helper); +} + +static void plover_transaction_helper_assistant_prepare(GtkAssistant *assistant, + GtkWidget *page,PloverTransactionHelper *helper) +{ + if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm"))) + plover_transaction_helper_prepare_confirm(helper); + else if (page==GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIProgress"))) + plover_transaction_helper_prepare_progress(helper); +} + +static void + plover_transaction_helper_remove_existing_toggled(GtkToggleButton *button, + PloverTransactionHelper *helper) +{ + GtkWidget *w; + if (helper->assistant) + { + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); + gtk_assistant_set_page_complete(helper->assistant,w, + gtk_toggle_button_get_active(button)); + } +} + +PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui) +{ + gsize len; + gchar *s,*directory,*contents; + GError *error=NULL; + GtkWidget *w; + PloverTransactionHelper *helper; + g_return_val_if_fail(ui == NULL || GTK_IS_BUILDER(ui),NULL); + helper=PLOVER_TRANSACTION_HELPER( + g_object_new(PLOVER_TYPE_TRANSACTION_HELPER,NULL)); + if (ui) + helper->ui=g_object_ref(ui); + else + helper->ui=gtk_builder_new(); + helper->assistant= + GTK_ASSISTANT(gtk_builder_get_object(helper->ui,"SoftwareInstallation")); + if (!helper->assistant) + { + directory=g_strdup(g_getenv("PLOVER_DATADIR")); + if (!directory) + { +#ifdef WIN32 + s=g_win32_get_package_installation_directory_of_module(NULL); + directory=g_build_filename(s,"share","plover",NULL); + g_free(s); +#else + directory=g_strdup(PLOVER_DATADIR); +#endif + } + s=g_build_filename(directory,"software-installation.ui",NULL); + g_free(directory); + (void)g_file_get_contents(s,&contents,&len,&error); + g_free(s); + if (!error) + { + (void)gtk_builder_add_from_string(helper->ui,contents,len,&error); + g_free(contents); + } + if (error) + { + g_critical("software-installation.ui: %s",error->message); + g_clear_error(&error); + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error (no user interface)"); + plover_transaction_helper_set_error(helper,error, + "Can't start installer"); + return helper; + } + helper->assistant=GTK_ASSISTANT(gtk_builder_get_object(helper->ui, + "SoftwareInstallation")); + } + if (!helper->assistant) + { + g_critical("\"SoftwareInstallation\" object not found"); + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error (missing wizard)"); + plover_transaction_helper_set_error(helper,error, + "Can't start installer"); + g_error_free(error); + return helper; + } + else + g_object_ref(helper->assistant); + if (!GTK_IS_ASSISTANT(helper->assistant)) + { + g_critical("\"SoftwareInstallation\" is not a GtkAssistant"); + g_set_error(&error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error (unexpected wizard type)"); + plover_transaction_helper_set_error(helper,error, + "Can't start installer"); + g_error_free(error); + return helper; + } + g_signal_connect(helper->assistant,"cancel", + G_CALLBACK(plover_transaction_helper_assistant_cancel),helper); + g_signal_connect(helper->assistant,"close", + G_CALLBACK(plover_transaction_helper_assistant_close),helper); + g_signal_connect(helper->assistant,"prepare", + G_CALLBACK(plover_transaction_helper_assistant_prepare),helper); + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); + if (w) + g_signal_connect(w,"toggled", + G_CALLBACK(plover_transaction_helper_remove_existing_toggled),helper); + w=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIIntroduction")); + if (w) + gtk_assistant_set_page_complete(helper->assistant,w,TRUE); + return helper; +} + +PloverPackageSet * + plover_transaction_helper_get_installed(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + return helper->installed; +} + +void plover_transaction_helper_set_installed(PloverTransactionHelper *helper, + PloverPackageSet *installed) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed)); + g_return_if_fail(helper->installed == NULL); + helper->installed=g_object_ref(installed); +} + +PloverRepository * + plover_transaction_helper_get_upstream(PloverTransactionHelper *helper, + GError **error) +{ + const char *base; +#if 0 + const char *prefix; + struct razor_relocations *relocations=NULL; +#endif + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + if (!helper->upstream) + { +#if 0 + prefix=plover_transaction_helper_get_prefix(helper,error); + if (!prefix) + return NULL; +#endif + base=plover_transaction_helper_get_base(helper); +#if 0 + if (prefix) + { + relocations=razor_relocations_create(); + razor_relocations_add(relocations,"/usr",prefix); + } +#endif + helper->upstream=plover_repository_new_from_yum(base,error); +#if 0 + if (relocations) + razor_relocations_destroy(relocations); +#endif + } + return helper->upstream; +} + +static PloverPackageSet *plover_transaction_helper_get_relocated_upstream( + PloverTransactionHelper *helper,GError **error) +{ + const char *prefix; + struct razor_relocations *relocations=NULL; + GError *tmp_error=NULL; + PloverRepository *upstream; + PloverPackageSet *set; + if (!helper->relocated_upstream) + { + upstream=plover_transaction_helper_get_upstream(helper,error); + if (!upstream) + return NULL; + prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); + if (tmp_error) + { + g_propagate_error(error,tmp_error); + return NULL; + } + set=plover_repository_get_package_set(upstream); + if (prefix) + { + relocations=razor_relocations_create(); + razor_relocations_add(relocations,"/usr",prefix); + helper->relocated_upstream= + plover_package_set_new_from_repository(upstream,relocations, + error); + if (relocations) + razor_relocations_destroy(relocations); + } + else + helper->relocated_upstream=g_object_ref(set); + } + return helper->relocated_upstream; +} + +void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper, + PloverRepository *upstream) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(PLOVER_IS_REPOSITORY(upstream)); + g_return_if_fail(helper->upstream == NULL); + helper->upstream=g_object_ref(upstream); +} + +const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + return helper->base; +} + +void plover_transaction_helper_set_base(PloverTransactionHelper *helper, + const char *base) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(helper->transactions == NULL); + g_free(helper->base); + helper->base=g_strdup(base); +} + +struct comps * + plover_transaction_helper_get_comps(PloverTransactionHelper *helper, + GError **error) +{ + gchar *s; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + g_return_val_if_fail(helper->base != NULL,NULL); + if (!helper->comps) + { + s=g_strconcat(helper->base,"/repodata/comps.xml",NULL); + helper->comps=plover_comps_new_from_file(s); + if (!helper->comps) + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: %s",s,g_strerror(errno)); + g_free(s); + } + return helper->comps; +} + +const char * + plover_transaction_helper_get_prefix(PloverTransactionHelper *helper, + GError **error) +{ + const char *prefix; + struct comps *comps; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + g_return_val_if_fail(helper->base != NULL || helper->installed != NULL,NULL); + if (helper->base) + { + comps=plover_transaction_helper_get_comps(helper,error); + if (!comps) + return NULL; + return plover_default_prefix_for_vendor(comps->vendor); + } + prefix=plover_package_set_guess_prefix(helper->installed,error); + return prefix; +} + +static int plover_transaction_helper_package_count(void) +{ + int count=0; + char *install_root; + struct razor_set *set; + struct razor_package *package; + struct razor_package_iterator *pi; + install_root=getenv("RAZOR_ROOT"); + if (!install_root) + install_root=""; + set=razor_root_open_read_only(install_root,NULL); + if (set) + { + pi=razor_package_iterator_create(set); + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_LAST)) + count++; + razor_package_iterator_destroy(pi); + razor_set_unref(set); + } + return count; +} + +static gboolean + plover_transaction_helper_check_vendor(PloverTransactionHelper *helper, + GError **error) +{ + int i; + gchar *prefix=NULL,*s; + struct comps *comps=NULL; + GtkWidget *container,*page; + GtkButton *button; + GtkLabel *label; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + if (helper->check_vendor) + { + comps=plover_transaction_helper_get_comps(helper,error); + if (!comps) + return FALSE; + prefix=plover_default_prefix_for_vendor(comps->vendor); + } + button=GTK_BUTTON(gtk_builder_get_object(helper->ui,"SIRemoveExisting")); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),FALSE); + container=GTK_WIDGET(gtk_builder_get_object(helper->ui, + "SIIncompatibleInstallation")); + page=GTK_WIDGET(gtk_builder_get_object(helper->ui,"SIConfirm")); + if (helper->check_vendor && prefix && + !plover_installed_files_match_prefix(prefix)) + { + label=GTK_LABEL(gtk_builder_get_object(helper->ui, + "SIIncompatibleInstallationLabel")); + s=g_strdup_printf("Incompatible Installation\n\n" + "The existing installation is not from %s.\n" + "In order to continue, all the existing packages must be removed.", + comps->vendor); + gtk_label_set_markup(label,s); + g_free(s); + i=plover_transaction_helper_package_count(); + s=g_strdup_printf("Remove %d existing package%s",i,i==1?"":"s"); + gtk_button_set_label(button,s); + g_free(s); + gtk_widget_show(container); + if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,FALSE); + } + else + { + gtk_widget_hide(container); + if (helper->assistant) + gtk_assistant_set_page_complete(helper->assistant,page,TRUE); + } + return TRUE; +} + +void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper, + gboolean check_vendor) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + if (helper->check_vendor!=check_vendor) + { + helper->check_vendor=check_vendor; + if (helper->transactions) + plover_transaction_helper_check_vendor(helper,NULL); + } +} + +/* + * If plover_transaction_helper_add_transaction() failes with an error + * of PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET + * then plover_transaction_helper_get_unsatisfied() can be used to + * retrieve a textual description of the problem. + */ + +const char * + plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + return helper->unsatisfied; +} + +gboolean + plover_transaction_helper_add_transaction(PloverTransactionHelper *helper, + PloverTransaction *transaction,struct plover_vector *report_packages, + enum razor_install_action report_action,GError **error) +{ + int i,count; + gboolean other_packages; + const char *s,*name; + enum razor_install_action action; + struct razor_install_iterator *ii; + struct razor_set *next; + struct razor_package *package; + struct plover_vector *tasked_packages; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(report_action==RAZOR_INSTALL_ACTION_ADD || report_action==RAZOR_INSTALL_ACTION_REMOVE,FALSE); + g_free(helper->unsatisfied); + helper->unsatisfied=NULL; + if (!plover_transaction_resolve(transaction,error)) + { + s=plover_transaction_get_unsatisfied(transaction); + helper->unsatisfied=g_strdup(s); + return FALSE; + } + ii=plover_transaction_get_install_iterator(transaction,error); + if (!ii) + return FALSE; + next=plover_transaction_get_next_set(transaction,error); + if (!next) + return FALSE; + tasked_packages=plover_vector_new(); + other_packages=FALSE; + while (razor_install_iterator_next(ii,&package,&action,&count)) + { + if (action==report_action) + { + razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST); + if (!report_packages || + plover_vector_contains(report_packages,name)) + plover_vector_append(tasked_packages,name); + else + other_packages=TRUE; + } + } + if (!tasked_packages->len) + { + /* + * If there are no reportable packages tasked for action there + * shouldn't by any packages at all, but let's be paranoid. + */ + other_packages=FALSE; + razor_install_iterator_rewind(ii); + while (razor_install_iterator_next(ii,&package,&action,&count)) + { + if (action==report_action) + { + razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST); + plover_vector_append(tasked_packages,name); + } + } + } + if (!tasked_packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK,"Transaction includes no %s actions", + report_action==RAZOR_INSTALL_ACTION_ADD?"add":"remove"); + plover_vector_free(tasked_packages); + return FALSE; + } + if (!helper->transactions) + plover_transaction_helper_check_vendor(helper,error); + g_object_ref(transaction); + helper->transactions=g_slist_append(helper->transactions,transaction); + if (report_action==RAZOR_INSTALL_ACTION_ADD) + { + for(i=0;ilen;i++) + { + s=tasked_packages->strings[i]; + if (!plover_vector_contains(helper->report_adding,s)) + plover_vector_append(helper->report_adding,s); + } + helper->report_adding_dependencies|=other_packages; + } + else + { + for(i=0;ilen;i++) + { + s=tasked_packages->strings[i]; + if (!plover_vector_contains(helper->report_removing,s)) + plover_vector_append(helper->report_removing,s); + } + helper->report_removing_dependants|=other_packages; + } + plover_vector_free(tasked_packages); + return TRUE; +} + +static PloverTransaction * + plover_transaction_helper_new_transaction(PloverTransactionHelper *helper, + GError **error) +{ + gboolean ok; + const char *base,*prefix; + GError *tmp_error=NULL; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),NULL); + prefix=plover_transaction_helper_get_prefix(helper,&tmp_error); + if (tmp_error) + { + g_propagate_error(error,tmp_error); + return NULL; + } + transaction=plover_transaction_new(); + plover_transaction_set_prefix(transaction,prefix); + plover_transaction_set_installed(transaction,helper->installed); + if (helper->upstream) + ok=plover_transaction_set_upstream(transaction,helper->upstream,error); + else + { + base=plover_transaction_helper_get_base(helper); + ok=plover_transaction_set_upstream_from_yum(transaction,base,error); + } + if (!ok) + { + g_object_unref(transaction); + transaction=NULL; + } + return transaction; +} + +struct plover_vector *plover_transaction_helper_group_get_default_packages( + PloverTransactionHelper *helper,const char *group,GError **error) +{ + gboolean changed; + struct comps *comps; + struct comps_group *grp; + struct comps_requirement *pkg; + struct plover_vector *default_packages; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + comps=plover_transaction_helper_get_comps(helper,error); + if (!comps) + return NULL; + grp=plover_comps_lookup_group(comps,group); + if (!grp) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: group not found",group); + return NULL; + } + default_packages=plover_vector_new(); + do + { + changed=FALSE; + for(pkg=grp->packages;pkg;pkg=pkg->next) + { + if (plover_vector_contains(default_packages,pkg->name)) + continue; + if (pkg->type==COMPS_REQUIREMENT_DEFAULT || + pkg->type==COMPS_REQUIREMENT_MANDATORY || + pkg->type==COMPS_REQUIREMENT_CONDITIONAL && + plover_vector_contains(default_packages,pkg->requires)) + { + changed=TRUE; + plover_vector_append(default_packages,pkg->name); + } + } + } while(changed); + return default_packages; +} + +/* + * Returns TRUE if there is work to be done or FALSE if the packages are + * already installed or on error. + */ +gboolean + plover_transaction_helper_install_packages(PloverTransactionHelper *helper, + struct plover_vector *packages,GError **error) +{ + gboolean retval; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + g_return_val_if_fail(packages != NULL,FALSE); + if (!packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK,"No packages listed to be installed"); + return FALSE; + } + transaction=plover_transaction_helper_new_transaction(helper,error); + if (!transaction) + return FALSE; + if (!plover_transaction_install(transaction,packages->strings,error)) + { + g_object_unref(transaction); + return FALSE; + } + retval=plover_transaction_helper_add_transaction(helper,transaction, + packages,RAZOR_INSTALL_ACTION_ADD,error); + g_object_unref(transaction); + return retval; +} + +/* + * Returns TRUE if there is work to be done or FALSE if the group is + * already installed or on error. + */ +gboolean + plover_transaction_helper_install_group(PloverTransactionHelper *helper, + const char *group,GError **error) +{ + gboolean retval; + struct plover_vector *selected_packages; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + selected_packages=plover_transaction_helper_group_get_default_packages( + helper,group,error); + if (!selected_packages) + return FALSE; + if (!selected_packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); + plover_vector_free(selected_packages); + return FALSE; + } + retval=plover_transaction_helper_install_packages(helper,selected_packages, + error); + plover_vector_free(selected_packages); + return retval; +} + +/* + * Returns TRUE if there is work to be done or FALSE if the group is + * not installed or on error. + */ +gboolean plover_transaction_helper_remove_group(PloverTransactionHelper *helper, + const char *group,GError **error) +{ + gboolean retval; + struct plover_vector *selected_packages; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + selected_packages=plover_transaction_helper_group_get_default_packages( + helper,group,error); + if (!selected_packages) + return FALSE; + if (!selected_packages->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_FAILED,"%s: no default packages",group); + plover_vector_free(selected_packages); + return FALSE; + } + transaction=plover_transaction_new(); + plover_transaction_set_installed(transaction,helper->installed); + if (!plover_transaction_remove(transaction,selected_packages->strings, + error)) + { + plover_vector_free(selected_packages); + g_object_unref(transaction); + return FALSE; + } + retval=plover_transaction_helper_add_transaction(helper,transaction, + NULL,RAZOR_INSTALL_ACTION_REMOVE,error); + g_object_unref(transaction); + plover_vector_free(selected_packages); + return retval; +} + +/* + * Returns TRUE if there is work to be done or FALSE if all updates have + * already been applied or on error. + */ +gboolean plover_transaction_helper_update(PloverTransactionHelper *helper, + GError **error) +{ + gboolean retval; + PloverTransaction *transaction; + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + transaction=plover_transaction_helper_new_transaction(helper,error); + if (!transaction) + return FALSE; + if (!plover_transaction_update(transaction,NULL,error)) + { + g_object_unref(transaction); + return FALSE; + } + retval=plover_transaction_helper_add_transaction(helper,transaction, + NULL,RAZOR_INSTALL_ACTION_ADD,error); + g_object_unref(transaction); + return retval; +} + +gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper),FALSE); + if (helper->error_dialog) + return TRUE; + else if (!helper->assistant) + return FALSE; + else + return gtk_widget_get_visible(GTK_WIDGET(helper->assistant)); +} + +void plover_transaction_helper_present(PloverTransactionHelper *helper) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + if (helper->error_dialog) + gtk_window_present(GTK_WINDOW(helper->error_dialog)); + else if (helper->assistant) + gtk_window_present(GTK_WINDOW(helper->assistant)); +} + +static void + plover_transaction_helper_error_dialog_response(GtkDialog *error_dialog, + int response_id,PloverTransactionHelper *helper) +{ + g_signal_handlers_disconnect_by_data(error_dialog,helper); + if ((GtkWidget *)error_dialog==helper->error_dialog) + { + gtk_widget_destroy(helper->error_dialog); + helper->error_dialog=NULL; + if (helper->assistant) + { + gtk_widget_hide(GTK_WIDGET(helper->assistant)); + gtk_assistant_set_current_page(helper->assistant,0); + } + g_signal_emit(helper,signals[CLOSE],0); + } +} + +const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper, + const GError **error) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + if (!helper->error_dialog) + return NULL; + if (error) + *error=helper->error; + return helper->error_primary_text; +} + +void plover_transaction_helper_set_error(PloverTransactionHelper *helper, + const GError *error,const char *primary_text) +{ + GtkMessageType type; + GtkWindow *window; + g_return_if_fail(PLOVER_IS_TRANSACTION_HELPER(helper)); + g_return_if_fail(error != NULL); + g_return_if_fail(primary_text != NULL); + if (helper->pulse_handler) + { + g_source_remove(helper->pulse_handler); + helper->pulse_handler=0; + } + if (helper->error_dialog) + { + gtk_widget_destroy(helper->error_dialog); + helper->error_dialog=NULL; + } + g_free(helper->error_primary_text); + helper->error_primary_text=g_strdup(primary_text); + g_clear_error(&helper->error); + helper->error=g_error_copy(error); + if (g_error_matches(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_WORK)) + type=GTK_MESSAGE_INFO; + else + type=GTK_MESSAGE_ERROR; + if (helper->assistant) + window=GTK_WINDOW(helper->assistant); + else + window=NULL; + helper->error_dialog=gtk_message_dialog_new(window, + GTK_DIALOG_DESTROY_WITH_PARENT,type,GTK_BUTTONS_CLOSE,primary_text); + gtk_message_dialog_format_secondary_text( + GTK_MESSAGE_DIALOG(helper->error_dialog),error->message); + gtk_widget_show(helper->error_dialog); + g_signal_connect(helper->error_dialog,"response", + G_CALLBACK(plover_transaction_helper_error_dialog_response),helper); +} diff --git a/plover-gtk/transactionhelper.h b/plover-gtk/transactionhelper.h new file mode 100644 index 0000000..6452c91 --- /dev/null +++ b/plover-gtk/transactionhelper.h @@ -0,0 +1,102 @@ +#ifndef __PLOVER_TRANSACTION_HELPER_H__ +#define __PLOVER_TRANSACTION_HELPER_H__ + +#include +#include +#include +#include + +#define PLOVER_TYPE_TRANSACTION_HELPER plover_transaction_helper_get_type() +#define PLOVER_TRANSACTION_HELPER(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ + PLOVER_TYPE_TRANSACTION_HELPER,\ + PloverTransactionHelper) +#define PLOVER_TRANSACTION_HELPER_CLASS(klass) \ + G_TYPE_CHECK_CLASS_CAST(klass,\ + PLOVER_TYPE_TRANSACTION_HELPER,\ + PloverTransactionHelperClass) +#define PLOVER_IS_TRANSACTION_HELPER(obj) \ + G_TYPE_CHECK_INSTANCE_TYPE(obj,\ + PLOVER_TYPE_TRANSACTION_HELPER) +#define PLOVER_IS_TRANSACTION_HELPER_CLASS(klass) \ + G_TYPE_CHECK_CLASS_TYPE(obj,\ + PLOVER_TYPE_TRANSACTION_HELPER) +#define PLOVER_TRANSACTION_HELPER_GET_CLASS(obj) \ + G_TYPE_INSTANCE_GET_CLASS(obj,\ + PLOVER_TYPE_TRANSACTION_HELPER,\ + PloverTransactionHelperClass) + +typedef struct _PloverTransactionHelper { + GObject parent_instance; + PloverPackageSet *installed; + PloverRepository *upstream; + PloverPackageSet *relocated_upstream; + gchar *base; + gchar *unsatisfied; + struct comps *comps; + gboolean check_vendor; + gboolean report_adding_dependencies; + gboolean report_removing_dependants; + struct plover_vector *report_adding,*report_removing; + GSList *transactions; + GtkBuilder *ui; + GtkAssistant *assistant; + guint pulse_handler; + GError *error; + gchar *error_primary_text; + GtkWidget *error_dialog; +} PloverTransactionHelper; + +typedef struct _PloverTransactionHelperClass { + GObjectClass parent_class; +} PloverTransactionHelperClass; + +GType plover_transaction_helper_get_type(void); +PloverTransactionHelper *plover_transaction_helper_new(GtkBuilder *ui); +PloverPackageSet * + plover_transaction_helper_get_installed(PloverTransactionHelper *helper); +void plover_transaction_helper_set_installed(PloverTransactionHelper *helper, + PloverPackageSet *installed); +PloverRepository * + plover_transaction_helper_get_upstream(PloverTransactionHelper *helper, + GError **error); +void plover_transaction_helper_set_upstream(PloverTransactionHelper *helper, + PloverRepository *upstream); +const char *plover_transaction_helper_get_base(PloverTransactionHelper *helper); +void plover_transaction_helper_set_base(PloverTransactionHelper *helper, + const char *base); +struct comps * + plover_transaction_helper_get_comps(PloverTransactionHelper *helper, + GError **error); +const char * + plover_transaction_helper_get_prefix(PloverTransactionHelper *helper, + GError **error); +void plover_transaction_helper_set_check_vendor(PloverTransactionHelper *helper, + gboolean check_vendor); +const char * + plover_transaction_helper_get_unsatisfied(PloverTransactionHelper *helper); +gboolean + plover_transaction_helper_add_transaction(PloverTransactionHelper *helper, + PloverTransaction *transaction,struct plover_vector *report_packages, + enum razor_install_action report_action,GError **error); +struct plover_vector *plover_transaction_helper_group_get_default_packages( + PloverTransactionHelper *helper,const char *group,GError **error); +gboolean + plover_transaction_helper_install_packages(PloverTransactionHelper *helper, + struct plover_vector *packages,GError **error); +gboolean + plover_transaction_helper_install_group(PloverTransactionHelper *helper, + const char *group,GError **error); +gboolean + plover_transaction_helper_remove_group(PloverTransactionHelper *helper, + const char *group,GError **error); +gboolean + plover_transaction_helper_update(PloverTransactionHelper *helper, + GError **error); +gboolean plover_transaction_helper_get_visible(PloverTransactionHelper *helper); +void plover_transaction_helper_present(PloverTransactionHelper *helper); +const char *plover_transaction_helper_get_error(PloverTransactionHelper *helper, + const GError **error); +void plover_transaction_helper_set_error(PloverTransactionHelper *helper, + const GError *error,const char *primary_text); + +#endif /* __PLOVER_TRANSACTION_HELPER_H__ */ diff --git a/plover/Makefile.am b/plover/Makefile.am index 03187f0..1ae5c2e 100644 --- a/plover/Makefile.am +++ b/plover/Makefile.am @@ -3,10 +3,11 @@ LIBS=$(LIBPLOVER_LIBS) INCLUDES=-I$(top_srcdir) AM_LDFLAGS=-no-undefined -version-info $(LIBPLOVER_LT_VERSION_INFO) -pkginclude_HEADERS=plover.h +pkginclude_HEADERS=plover.h transaction.h package.h packageset.h repository.h lib_LTLIBRARIES=libplover.la -libplover_la_SOURCES=$(pkginclude_HEADERS) util.c import-yum.c razor.c comps.c +libplover_la_SOURCES=$(pkginclude_HEADERS) util.c import-yum.c razor.c comps.c \ + log.c vector.c transaction.c package.c packageset.c repository.c pkgconfigdir=$(libdir)/pkgconfig pkgconfig_DATA=plover.pc diff --git a/plover/import-yum.c b/plover/import-yum.c index 7ef59fc..ce2ec1a 100644 --- a/plover/import-yum.c +++ b/plover/import-yum.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009, 2011 J. Ali Harlow + * Copyright (C) 2009, 2011, 2014 J. Ali Harlow * * 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 @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -306,94 +307,110 @@ static int plover_system_arch_is_x86(void) #define XML_BUFFER_SIZE 4096 -struct razor_set * -plover_razor_set_create_from_yum(const char *base) +struct razor_set *plover_razor_set_create_from_yum(const char *base, + GError **error) { - struct yum_context ctx; - void *buf; - int len; - gzFile primary, filelists; - XML_ParsingStatus status; - struct razor_set *set; - - ctx.importer = razor_importer_create(); - ctx.state = YUM_STATE_BEGIN; - - ctx.primary_parser = XML_ParserCreate(NULL); - XML_SetUserData(ctx.primary_parser, &ctx); - XML_SetElementHandler(ctx.primary_parser, - yum_primary_start_element, - yum_primary_end_element); - XML_SetCharacterDataHandler(ctx.primary_parser, - yum_character_data); - - ctx.filelists_parser = XML_ParserCreate(NULL); - XML_SetUserData(ctx.filelists_parser, &ctx); - XML_SetElementHandler(ctx.filelists_parser, - yum_filelists_start_element, - yum_filelists_end_element); - XML_SetCharacterDataHandler(ctx.filelists_parser, - yum_character_data); - - primary = gzopen("primary.xml.gz", "rb"); - if (primary == NULL) - return NULL; - filelists = gzopen("filelists.xml.gz", "rb"); - if (filelists == NULL) - return NULL; - - ctx.current_parser = ctx.primary_parser; - - ctx.current = 0; - - do { - XML_GetParsingStatus(ctx.current_parser, &status); - switch (status.parsing) { - case XML_SUSPENDED: - XML_ResumeParser(ctx.current_parser); - break; - case XML_PARSING: - case XML_INITIALIZED: - buf = XML_GetBuffer(ctx.current_parser, - XML_BUFFER_SIZE); - if (ctx.current_parser == ctx.primary_parser) - len = gzread(primary, buf, XML_BUFFER_SIZE); - else - len = gzread(filelists, buf, XML_BUFFER_SIZE); - if (len < 0) { - fprintf(stderr, - "couldn't read input: %s\n", - strerror(errno)); - return NULL; - } - - XML_ParseBuffer(ctx.current_parser, len, len == 0); - break; - case XML_FINISHED: - break; + struct yum_context ctx; + gchar *s; + void *buf; + int len; + gzFile primary, filelists; + XML_ParsingStatus status; + struct razor_set *set; + int errnum; + const char *errstr,*errobj; + ctx.importer=razor_importer_create(); + ctx.state=YUM_STATE_BEGIN; + ctx.primary_parser=XML_ParserCreate(NULL); + XML_SetUserData(ctx.primary_parser,&ctx); + XML_SetElementHandler(ctx.primary_parser,yum_primary_start_element, + yum_primary_end_element); + XML_SetCharacterDataHandler(ctx.primary_parser,yum_character_data); + ctx.filelists_parser=XML_ParserCreate(NULL); + XML_SetUserData(ctx.filelists_parser,&ctx); + XML_SetElementHandler(ctx.filelists_parser,yum_filelists_start_element, + yum_filelists_end_element); + XML_SetCharacterDataHandler(ctx.filelists_parser,yum_character_data); + errno=0; + s=g_build_filename(base,"repodata","primary.xml.gz",NULL); + primary=gzopen(s,"rb"); + if (!primary) + { + g_set_error(error,RAZOR_POSIX_ERROR,errno?errno:ENOMEM, + "%s: %s",s,strerror(errno)); + g_free(s); + return NULL; + } + g_free(s); + s=g_build_filename(base,"repodata","filelists.xml.gz",NULL); + filelists=gzopen(s,"rb"); + if (!filelists) + { + g_set_error(error,RAZOR_POSIX_ERROR,errno?errno:ENOMEM, + "%s: %s",s,strerror(errno)); + g_free(s); + return NULL; + } + g_free(s); + ctx.current_parser=ctx.primary_parser; + ctx.current=0; + do + { + XML_GetParsingStatus(ctx.current_parser,&status); + switch (status.parsing) + { + case XML_SUSPENDED: + XML_ResumeParser(ctx.current_parser); + break; + case XML_PARSING: + case XML_INITIALIZED: + buf=XML_GetBuffer(ctx.current_parser,XML_BUFFER_SIZE); + if (ctx.current_parser==ctx.primary_parser) + len=gzread(primary,buf,XML_BUFFER_SIZE); + else + len=gzread(filelists,buf,XML_BUFFER_SIZE); + if (len<0) + { + if (ctx.current_parser==ctx.primary_parser) + { + errstr=gzerror(primary,&errnum); + errobj="primary.xml.gz"; + } + else + { + errstr=gzerror(filelists,&errnum); + errobj="filelists.xml.gz"; + } + if (errnum==Z_ERRNO) + g_set_error(error,PLOVER_POSIX_ERROR,errno,"%s: %s", + errobj,strerror(errno)); + else + g_set_error(error,RAZOR_ZLIB_ERROR,errnum,"%s: %s", + errobj,errstr); + return NULL; } - } while (status.parsing != XML_FINISHED); - - - XML_ParserFree(ctx.primary_parser); - XML_ParserFree(ctx.filelists_parser); - - gzclose(primary); - gzclose(filelists); - - set = razor_importer_finish(ctx.importer); -#if RAZOR_HEADER_VERSION_MIN <= 1 - /* - * Header version 1 is supported by plover v0.3 and is used on - * 32-bit intel machines which allows the setup and update - * applications from v0.3 to work. On other machines, we don't - * want these old applications to work (since they would do - * the wrong thing) and so we use the current header version - * which they don't support. - */ - if (plover_system_arch_is_x86()) - razor_set_set_header_version(set, 1); + XML_ParseBuffer(ctx.current_parser,len,!len); + break; + case XML_FINISHED: + break; + } + } while (status.parsing!=XML_FINISHED); + XML_ParserFree(ctx.primary_parser); + XML_ParserFree(ctx.filelists_parser); + gzclose(primary); + gzclose(filelists); + set=razor_importer_finish(ctx.importer); +#if RAZOR_HEADER_VERSION_MIN<=1 + /* + * Header version 1 is supported by plover v0.3 and is used on + * 32-bit intel machines which allows the setup and update + * applications from v0.3 to work. On other machines, we don't + * want these old applications to work (since they would do + * the wrong thing) and so we use the current header version + * which they don't support. + */ + if (plover_system_arch_is_x86()) + razor_set_set_header_version(set,1); #endif - - return set; + return set; } diff --git a/plover/log.c b/plover/log.c new file mode 100644 index 0000000..519676e --- /dev/null +++ b/plover/log.c @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#else +#include +#endif +#include "config.h" +#include "plover.h" + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define MAX_OLD_LOGFILES 4 /* (5 including the current) */ + +#ifdef WIN32 + +struct find_suffixed_data { + HANDLE handle; + WIN32_FIND_DATA wfd; + int base_len; + char *suffix; +}; + +static int find_suffixed_first(const char *path,struct find_suffixed_data *data) +{ + const char *t1,*t2; + gchar *s; + s=g_strconcat(path,"-*",NULL); + data->handle=FindFirstFile(s,&data->wfd); + g_free(s); + if (data->handle==INVALID_HANDLE_VALUE) + return FALSE; + t1=strrchr(path,'/'); + t2=strrchr(t1?t1:path,'\\'); + if (t2) + data->base_len=strlen(t2+1); + else if (t1) + data->base_len=strlen(t1+1); + else + data->base_len=strlen(path); + data->suffix=strdup(data->wfd.cFileName+data->base_len); + return TRUE; +} + +static int find_suffixed_next(struct find_suffixed_data *data) +{ + if (!FindNextFile(data->handle,&data->wfd)) + return FALSE; + free(data->suffix); + data->suffix=strdup(data->wfd.cFileName+data->base_len); + return TRUE; +} + +static void find_suffixed_close(struct find_suffixed_data *data) +{ + free(data->suffix); + FindClose(data->handle); +} + +#else /* WIN32 */ + +struct find_suffixed_data { + DIR *dir; + struct dirent *entry; + char *base; + int base_len; + char *suffix; +}; + +static int find_suffixed_next(struct find_suffixed_data *data) +{ + struct dirent *entry_result; + while (!readdir_r(data->dir,data->entry,&entry_result) && entry_result) + { + if (strncmp(data->entry->d_name,data->base,data->base_len) || + data->entry->d_name[data->base_len]!='-') + continue; + free(data->suffix); + data->suffix=strdup(data->entry->d_name+data->base_len); + return TRUE; + } + return FALSE; +} + +/* + * From http://womble.decadent.org.uk/readdir_r-advisory.html + * + * Calculate the required buffer size (in bytes) for directory + * entries read from the given directory handle. Return -1 if this + * this cannot be done. + * + * This code does not trust values of NAME_MAX that are less than + * 255, since some systems (including at least HP-UX) incorrectly + * define it to be a smaller value. + * + * If you use autoconf, include fpathconf and dirfd in your + * AC_CHECK_FUNCS list. Otherwise use some other method to detect + * and use them where available. + */ + +static size_t dirent_buf_size(DIR * dirp) +{ + long name_max; + size_t name_end; +#if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX) + name_max=fpathconf(dirfd(dirp),_PC_NAME_MAX); + if (name_max==-1) +#if defined(NAME_MAX) + name_max=(NAME_MAX>255)?NAME_MAX:255; +#else + return (size_t)-1; +#endif /* NAME_MAX */ +#else +#if defined(NAME_MAX) + name_max=(NAME_MAX>255)?NAME_MAX:255; +#else +#error "buffer size for readdir_r cannot be determined" +#endif /* NAME_MAX */ +#endif /* HAVE_FPATHCONF && HAVE_DIRFD && _PC_NAME_MAX */ + name_end=(size_t)offsetof(struct dirent,d_name)+name_max+1; + return (name_end>sizeof(struct dirent)?name_end:sizeof(struct dirent)); +} + +static int find_suffixed_first(const char *path,struct find_suffixed_data *data) +{ + int len; + char *s,*base; + base=strrchr(path,'/'); + if (base) + { + if (base==path) + data->dir=opendir("/"); + else + { + s=strndup(path,base-path); + data->dir=opendir(s); + free(s); + } + data->base=strdup(path+1); + } + else + { + data->dir=opendir("."); + data->base=strdup(path); + } + if (!data->dir) + { + free(data->base); + return FALSE; + } + data->base_len=strlen(data->base); + len=dirent_buf_size(data->dir); + if (len<0) + { + closedir(data->dir); + return FALSE; + } + data->entry=malloc(len); + if (!data->entry) + { + closedir(data->dir); + return FALSE; + } + if (find_suffixed_next(data)) + return TRUE; + free(data->entry); + closedir(data->dir); + return FALSE; +} + +static void find_suffixed_close(struct find_suffixed_data *data) +{ + free(data->suffix); + free(data->entry); + closedir(data->dir); +} + +#endif /* WIN32 */ + +static int prune_old_logfiles(const char *path) +{ + int i,n_suffixes; + gchar *s; + char *suffix,*suffixes[MAX_OLD_LOGFILES]; + struct find_suffixed_data fsd; + if (find_suffixed_first(path,&fsd)) + { + n_suffixes=0; + do + { + suffix=strdup(fsd.suffix); + if (n_suffixes0) + { + s=suffixes[i]; + suffixes[i]=suffix; + suffix=s; + } + s=g_strconcat(path,suffix,NULL); + (void)remove(s); + g_free(s); + free(suffix); + } + } while(find_suffixed_next(&fsd)); + find_suffixed_close(&fsd); + for(i=0;itm_year+1900,modified->tm_mon+1, + modified->tm_mday); + s=g_strconcat(path,suffix,NULL); + if (rename(path,s)) + { + suffix[10]='\0'; + for(serial='a';serial<='z';serial++) + { + if (errno!=EACCES || serial=='z') + { + perror(s); + free(s); + return -1; + } + free(s); + suffix[9]=serial; + s=g_strconcat(path,suffix,NULL); + if (!rename(path,s)) + break; + } + } + g_free(s); + return prune_old_logfiles(path); +} + +int plover_log_open(const char *path) +{ + int retval; + char *root; + gchar *filename; + struct stat sb; + time_t t; + struct tm today,modified; + struct razor_atomic *atomic; + FILE *fp; + root=getenv("RAZOR_ROOT"); + if (root) + filename=g_strconcat(root,path,NULL); + else + filename=g_strdup(path); + atomic=razor_atomic_open("Open log"); + razor_atomic_make_dirs(atomic,"",filename); + retval=razor_atomic_commit(atomic); + if (retval) + fprintf(stderr,"Can't open log: %s\n", + razor_atomic_get_error_msg(atomic)); + razor_atomic_destroy(atomic); + if (retval) + return retval; + if (stat(filename,&sb)<0) + { + if (errno!=ENOENT) + { + fprintf(stderr,"Can't open log: "); + perror(filename); + g_free(filename); + return -1; + } + } + else if (!S_ISREG(sb.st_mode)) + { + fprintf(stderr,"Can't open log: %s: Not a regular file\n",filename); + g_free(filename); + return -1; + } + else + { + time(&t); +#ifdef WIN32 + localtime_s(&today,&t); + localtime_s(&modified,&sb.st_mtime); +#else + localtime_r(&t,&today); + localtime_r(&sb.st_mtime,&modified); +#endif + if (modified.tm_yday!=today.tm_yday || modified.tm_year!=today.tm_year) + rotate_logfile(filename,&modified); + } + fp=fopen(filename,"a"); + if (!fp) + { + fprintf(stderr,"Can't open log: "); + perror(filename); + g_free(filename); + return -1; + } + g_free(filename); +#ifdef WIN32 + /* + * The situation under MS-Windows is a little complicated. If standard + * output and standard error are valid then the normal code will work. + * This applies in console applications and even in GUI applications + * if standard output and standard error are redirected by the parent + * process (eg., by cmd.exe). However GUI applications started in + * typical fashion will have invalid standard output and standard error. + * See http://support.microsoft.com/kb/105305 for some more detail. + * NB: This solution assumes that fd 1 and 2 are either used for + * standard output/error or are unused. + */ + fclose(stdout); + fclose(stderr); +#else + fflush(stdout); + fflush(stderr); +#endif + if (dup2(fileno(fp),1)<0 || dup2(fileno(fp),2)<0) + { + perror("Failed to redirect standard error/output"); + fclose(fp); + return -1; + } + fclose(fp); +#ifdef WIN32 + *stdout=*fdopen(1,"a"); + setvbuf(stdout,NULL,_IONBF,0); + *stderr=*fdopen(2,"a"); + setvbuf(stderr,NULL,_IONBF,0); + SetStdHandle(STD_OUTPUT_HANDLE,(HANDLE)_get_osfhandle(1)); + SetStdHandle(STD_ERROR_HANDLE,(HANDLE)_get_osfhandle(2)); +#endif + time(&t); + printf("Run started on %s",ctime(&t)); + fflush(stdout); + return 0; +} diff --git a/plover/package.c b/plover/package.c new file mode 100644 index 0000000..507b27f --- /dev/null +++ b/plover/package.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2010 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include "plover/package.h" + +G_DEFINE_TYPE(PloverPackage,plover_package,G_TYPE_OBJECT); + +typedef struct _PloverPackagePrivate { + struct razor_set *set; + struct razor_package *pkg; + GObject *file_store; + const char **prefixes; +} PloverPackagePrivate; + +#define PLOVER_PACKAGE_GET_PRIVATE(obj)\ + G_TYPE_INSTANCE_GET_PRIVATE(obj,\ + PLOVER_TYPE_PACKAGE,PloverPackagePrivate) + +enum { + CHANGED=0, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +static void plover_package_finalize(GObject *obj) +{ + PloverPackagePrivate *priv=PLOVER_PACKAGE_GET_PRIVATE(obj); + g_free(priv->prefixes); + G_OBJECT_CLASS(plover_package_parent_class)->finalize(obj); +} + +static void plover_package_dispose(GObject *obj) +{ + PloverPackagePrivate *priv=PLOVER_PACKAGE_GET_PRIVATE(obj); + if (priv->file_store) + { + g_object_unref(priv->file_store); + priv->file_store=NULL; + } + G_OBJECT_CLASS(plover_package_parent_class)->dispose(obj); +} + +static void plover_package_class_init(PloverPackageClass *klass) +{ + GObjectClass *oclass=G_OBJECT_CLASS(klass); + oclass->finalize=plover_package_finalize; + oclass->dispose=plover_package_dispose; + g_type_class_add_private(klass,sizeof(PloverPackagePrivate)); + signals[CHANGED]=g_signal_newv("changed", + G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, + g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); +} + +static void plover_package_init(PloverPackage *package) +{ +} + +PloverPackage *plover_package_new(struct razor_set *set, + struct razor_package *pkg) +{ + PloverPackage *package; + PloverPackagePrivate *priv; + package=g_object_new(PLOVER_TYPE_PACKAGE,NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + priv->set=set; + priv->pkg=pkg; + return package; +} + +struct razor_set *plover_package_get_razor_set(PloverPackage *package) +{ + PloverPackagePrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + return priv->set; +} + +struct razor_package *plover_package_get_razor_package(PloverPackage *package) +{ + PloverPackagePrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + return priv->pkg; +} + +const char *plover_package_get_name(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *name=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST); + return name; +} + +const char *plover_package_get_summary(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *summary=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_SUMMARY,&summary, + RAZOR_DETAIL_LAST); + return summary; +} + +const char *plover_package_get_version(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *version=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_VERSION,&version, + RAZOR_DETAIL_LAST); + return version; +} + +const char *plover_package_get_license(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *license=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_LICENSE,&license, + RAZOR_DETAIL_LAST); + return license; +} + +const char *plover_package_get_arch(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *arch=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_ARCH,&arch, + RAZOR_DETAIL_LAST); + return arch; +} + +const char *plover_package_get_description(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *description=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_DESCRIPTION, + &description,RAZOR_DETAIL_LAST); + return description; +} + +const char *plover_package_get_URL(PloverPackage *package) +{ + PloverPackagePrivate *priv; + const char *URL=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + razor_package_get_details(priv->set,priv->pkg,RAZOR_DETAIL_URL,&URL, + RAZOR_DETAIL_LAST); + return URL; +} + +/** + * plover_package_read_icon: + * @package: #PloverPackage to read icon from + * @error: a #GError, or %NULL + * + * Opens an icon for reading. The result is a #GInputStream that + * can be used to read the contents of the file. + * + * If the icon does not exist, the %G_IO_ERROR_NOT_FOUND error will be + * returned. Other errors are possible too. + * + * Returns: (transfer full): #GInputStream or %NULL on error. + * Free the returned object with g_object_unref(). + */ +GInputStream *plover_package_read_icon(PloverPackage *package,GError **error) +{ + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + g_set_error_literal(error,G_IO_ERROR,G_IO_ERROR_NOT_SUPPORTED, + "Operation not supported"); + return NULL; +} + +/** + * plover_package_property_iterator_create: + * @package: #PloverPackage to create property iterator for + * + * Creates a property iterator for package. The result is a + * #struct razor_property_iterator that can be used to iterate over the + * properties of a package. + * + * Returns: (transfer full): #struct razor_property_iterator or %NULL on error. + * Free the returned iterator with razor_property_iterator_destroy(). + */ +struct razor_property_iterator * + plover_package_property_iterator_create(PloverPackage *package) +{ + PloverPackagePrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + return razor_property_iterator_create(priv->set,priv->pkg); +} + +/** + * plover_package_file_iterator_create: + * @package: #PloverPackage to create file iterator for + * @reverse: %TRUE if the files should be iterated in reverse order + * + * Creates a file iterator for package. The result is a + * #struct razor_file_iterator that can be used to iterate over the files + * in a package. + * + * Returns: (transfer full): #struct razor_file_iterator or %NULL on error. + * Free the returned iterator with razor_file_iterator_destroy(). + */ +struct razor_file_iterator * + plover_package_file_iterator_create(PloverPackage *package,gboolean reverse) +{ + PloverPackagePrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + return razor_file_iterator_create(priv->set,priv->pkg,reverse); +} + +/** + * plover_package_get_prefixes: + * @package: #PloverPackage to get prefixes for + * + * Retrieve the prefixes for a relocatable package. + * + * Returns: (transfer none): NULL-terminated array of prefixes. + */ +const char *const *plover_package_get_prefixes(PloverPackage *package) +{ + const char *prefix; + struct razor_string_iterator *si; + GPtrArray *prefixes; + PloverPackagePrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_PACKAGE_GET_PRIVATE(package); + if (!priv->prefixes) + { + prefixes=g_ptr_array_new(); + si=razor_install_prefix_iterator_create(priv->set,priv->pkg); + while (razor_string_iterator_next(si,&prefix)) + g_ptr_array_add(prefixes,prefix); + razor_string_iterator_destroy(si); + g_ptr_array_add(prefixes,NULL); + priv->prefixes=g_ptr_array_free(prefixes,FALSE); + } + return priv->prefixes; +} diff --git a/plover/package.h b/plover/package.h new file mode 100644 index 0000000..f164f44 --- /dev/null +++ b/plover/package.h @@ -0,0 +1,56 @@ +#ifndef __PLOVER_PACKAGE_H__ +#define __PLOVER_PACKAGE_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define PLOVER_TYPE_PACKAGE plover_package_get_type() +#define PLOVER_PACKAGE(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ + PLOVER_TYPE_PACKAGE,PloverPackage) +#define PLOVER_PACKAGE_CLASS(klass)\ + G_TYPE_CHECK_CLASS_CAST(klass,\ + PLOVER_TYPE_PACKAGE,PloverPackageClass) +#define PLOVER_IS_PACKAGE(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj,\ + PLOVER_TYPE_PACKAGE) +#define PLOVER_IS_PACKAGE_CLASS(klass)\ + G_TYPE_CHECK_CLASS_TYPE(obj,\ + PLOVER_TYPE_PACKAGE) +#define PLOVER_PACKAGE_GET_CLASS(obj)\ + G_TYPE_INSTANCE_GET_CLASS(obj,\ + PLOVER_TYPE_PACKAGE,PloverPackageClass) + +typedef struct _PloverPackage { + GObject parent_instance; +} PloverPackage; + +typedef struct _PloverPackageClass { + GObjectClass parent_class; +} PloverPackageClass; + +#include + +GType plover_package_get_type(void) G_GNUC_CONST; +PloverPackage *plover_package_new(struct razor_set *set, + struct razor_package *pkg); +struct razor_set *plover_package_get_razor_set(PloverPackage *package); +struct razor_package *plover_package_get_razor_package(PloverPackage *package); +const char *plover_package_get_name(PloverPackage *package); +const char *plover_package_get_summary(PloverPackage *package); +const char *plover_package_get_version(PloverPackage *package); +const char *plover_package_get_license(PloverPackage *package); +const char *plover_package_get_arch(PloverPackage *package); +const char *plover_package_get_description(PloverPackage *package); +const char *plover_package_get_URL(PloverPackage *package); +GInputStream *plover_package_get_icon_stream(PloverPackage *package); +struct razor_property_iterator * + plover_package_property_iterator_create(PloverPackage *package); +struct razor_file_iterator * + plover_package_file_iterator_create(PloverPackage *package,gboolean reverse); +const char *const *plover_package_get_prefixes(PloverPackage *package); + +G_END_DECLS + +#endif /* __PLOVER_PACKAGE_H__ */ diff --git a/plover/packageset.c b/plover/packageset.c new file mode 100644 index 0000000..22c6559 --- /dev/null +++ b/plover/packageset.c @@ -0,0 +1,658 @@ +/* + * Copyright (C) 2010-2012, 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include "plover/plover.h" +#include "plover/package.h" +#include "plover/packageset.h" + +G_DEFINE_TYPE(PloverPackageSet,plover_package_set,G_TYPE_OBJECT); + +typedef struct _PloverPackageSetPrivate { + gchar *install_root; + struct razor_root *root; + struct razor_set *set; + GSList *packages; + int no_details; + gchar *guessed_prefix; +} PloverPackageSetPrivate; + +#define PLOVER_PACKAGE_SET_GET_PRIVATE(obj)\ + G_TYPE_INSTANCE_GET_PRIVATE(obj,\ + PLOVER_TYPE_PACKAGE_SET,\ + PloverPackageSetPrivate) + +enum { + CHANGED=0, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +static void plover_package_set_finalize(GObject *obj) +{ + PloverPackageSetPrivate *priv=PLOVER_PACKAGE_SET_GET_PRIVATE(obj); + g_free(priv->guessed_prefix); + G_OBJECT_CLASS(plover_package_set_parent_class)->finalize(obj); +} + +static void plover_package_set_dispose(GObject *obj) +{ + PloverPackageSetPrivate *priv=PLOVER_PACKAGE_SET_GET_PRIVATE(obj); + if (priv->set) + { + razor_set_unref(priv->set); + priv->set=NULL; + } + if (priv->root) + { + g_free(priv->install_root); + priv->install_root=NULL; + razor_root_close(priv->root); + priv->root=NULL; + } + G_OBJECT_CLASS(plover_package_set_parent_class)->dispose(obj); +} + +static void plover_package_set_class_init(PloverPackageSetClass *klass) +{ + GObjectClass *oclass=G_OBJECT_CLASS(klass); + oclass->finalize=plover_package_set_finalize; + oclass->dispose=plover_package_set_dispose; + g_type_class_add_private(klass,sizeof(PloverPackageSetPrivate)); + signals[CHANGED]=g_signal_newv("changed", + G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,NULL,NULL,NULL, + g_cclosure_marshal_VOID__VOID,G_TYPE_NONE,0,NULL); +} + +static void plover_package_set_init(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + priv->no_details=-1; +} + +PloverPackageSet *plover_package_set_new(void) +{ + return g_object_new(PLOVER_TYPE_PACKAGE_SET,NULL); +} + +void plover_package_set_close(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + g_return_if_fail(PLOVER_IS_PACKAGE_SET(set)); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (priv->root) + { + razor_root_close(priv->root); + priv->root=NULL; + } +} + +gboolean plover_package_set_open(PloverPackageSet *set,const char *install_root, + gboolean exclusive,GError **err) +{ + struct razor_root *root=NULL; + struct razor_set *system=NULL; + PloverPackageSetPrivate *priv; + struct razor_error *error=NULL; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),FALSE); + if (exclusive) + { + root=razor_root_open(install_root,NULL); + if (!root) + { + if (razor_root_create(install_root,&error)) + { + if (razor_error_get_domain(error)==RAZOR_GENERAL_ERROR && + razor_error_get_code(error)== + RAZOR_GENERAL_ERROR_DATABASE_EXISTS) + { + razor_error_free(error); + error=NULL; + root=razor_root_open(install_root,&error); + } + } + else + root=razor_root_open(install_root,&error); + if (!root) + { + plover_propagate_razor_error(err,error); + return FALSE; + } + } + system=razor_root_get_system_set(root); + if (system) + razor_set_ref(system); + } + else + system=razor_root_open_read_only(install_root,&error); + if (error) + { + g_set_error_literal(err,PLOVER_RAZOR_ERROR,RAZOR_GENERAL_ERROR_FAILED, + razor_error_get_msg(error)); + razor_error_free(error); + return FALSE; + } + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (priv->set) + { + razor_set_unref(priv->set); + priv->set=NULL; + } + if (priv->root) + { + razor_root_close(priv->root); + priv->root=NULL; + } + g_free(priv->install_root); + priv->install_root=g_strdup(install_root); + priv->root=root; + priv->set=system; + return TRUE; +} + +const char *plover_package_set_get_install_root(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + return priv->install_root; +} + +gboolean plover_package_set_get_exclusive(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),FALSE); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + return !!priv->root; +} + +gboolean plover_package_set_update(PloverPackageSet *set,struct razor_set *next, + struct razor_atomic *atomic) +{ + gboolean retval; + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),FALSE); + g_return_val_if_fail(plover_package_set_get_exclusive(set) != FALSE,FALSE); + g_return_val_if_fail(next != NULL,FALSE); + g_return_val_if_fail(atomic != NULL,FALSE); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + retval=!razor_root_update(priv->root,next,atomic); + if (retval) + { + razor_set_unref(priv->set); + priv->set=razor_root_get_system_set(priv->root); + if (priv->set) + razor_set_ref(priv->set); + } + return retval; +} + +PloverPackageSet *plover_package_set_new_from_installed(const char *root, + GError **err) +{ + PloverPackageSet *set; + set=plover_package_set_new(); + if (!plover_package_set_open(set,root,FALSE,err)) + { + g_object_unref(set); + return NULL; + } + return set; +} + +PloverPackageSet *plover_package_set_new_from_razor(struct razor_set *razor) +{ + PloverPackageSet *set; + PloverPackageSetPrivate *priv; + g_return_val_if_fail(razor != NULL,NULL); + set=plover_package_set_new(); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + priv->set=razor; + razor_set_ref(priv->set); + return set; +} + +static gboolean + plover_package_set_import_package(PloverRepository *repository, + struct razor_relocations *relocations,struct razor_importer *importer, + PloverPackage *package,GError **error) +{ + struct razor_property_iterator *prop_iter; + struct razor_file_iterator *file_iter; + struct razor_rpm *rpm; + struct razor_property *prop; + const char *name,*version,*arch,*summary,*desc,*url,*license; + uint32_t flags; + rpm=plover_repository_open_rpm(repository,package,error); + if (!rpm) + return FALSE; + razor_relocations_set_rpm(relocations,rpm); + razor_rpm_close(rpm); + name=plover_package_get_name(package); + version=plover_package_get_version(package); + arch=plover_package_get_arch(package); + razor_importer_begin_package(importer,name,version,arch); + summary=plover_package_get_summary(package); + desc=plover_package_get_description(package); + url=plover_package_get_URL(package); + license=plover_package_get_license(package); + razor_importer_add_details(importer,summary,desc,url,license); + prop_iter=plover_package_property_iterator_create(package); + while (razor_property_iterator_next(prop_iter,&prop,&name,&flags,&version)) + razor_importer_add_property(importer,name,flags,version); + razor_property_iterator_destroy(prop_iter); + file_iter=plover_package_file_iterator_create(package,FALSE); + while (razor_file_iterator_next(file_iter,&name)) + { + name=razor_relocations_apply(relocations,name); + razor_importer_add_file(importer,name); + } + razor_file_iterator_destroy(file_iter); + razor_importer_finish_package(importer); + return TRUE; +} + +PloverPackageSet * + plover_package_set_new_from_repository(PloverRepository *repository, + struct razor_relocations *relocations,GError **error) +{ + struct razor_importer *importer; + uint32_t header_version; + GSList *packages,*lnk; + PloverPackageSet *unrelocated,*set; + PloverPackageSetPrivate *priv; + PloverPackage *package; + g_return_val_if_fail(PLOVER_IS_REPOSITORY(repository),NULL); + unrelocated=plover_repository_get_package_set(repository); + set=plover_package_set_new(); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (relocations) + { + importer=razor_importer_create(); + packages=plover_package_set_get_packages(unrelocated); + for(lnk=packages;lnk;lnk=lnk->next) + { + package=lnk->data; + if (!plover_package_set_import_package(repository,relocations, + importer,package,error)) + { + razor_importer_destroy(importer); + g_object_unref(set); + return NULL; + } + } + priv->set=razor_importer_finish(importer); + if (!priv->set) + { + g_object_unref(set); + return NULL; + } + header_version=plover_package_set_get_header_version(unrelocated); + if (header_version) + plover_package_set_set_header_version(set,header_version); + } + else + { + priv->set=plover_package_set_get_razor(unrelocated); + razor_set_ref(priv->set); + } + return set; +} + +PloverPackageSet *plover_package_set_new_from_yum(const char *base, + struct razor_relocations *relocations,GError **error) +{ + PloverPackageSet *set; + PloverRepository *repository; + repository=plover_repository_new_from_yum(base,error); + if (!repository) + return NULL; + set=plover_package_set_new_from_repository(repository,relocations,error); + g_object_unref(repository); + return set; +} + +PloverPackageSet *plover_package_set_new_from_rpms(const char **filenames, + GError **error) +{ + int i; + PloverPackageSet *set; + PloverPackageSetPrivate *priv; + struct razor_importer *importer; + struct razor_rpm *rpm; + struct razor_error *tmp_error=NULL; + importer=razor_importer_create(); + for(i=0;filenames[i];i++) + { + rpm=razor_rpm_open(filenames[i],&tmp_error); + if (!rpm) + { + razor_importer_destroy(importer); + plover_propagate_razor_error(error,tmp_error); + return NULL; + } + if (razor_importer_add_rpm(importer,rpm)) + { + g_set_error(error,PLOVER_RAZOR_ERROR,RAZOR_GENERAL_ERROR_FAILED, + "%s: failed to import",filenames[i]); + razor_importer_destroy(importer); + return NULL; + } + razor_rpm_close(rpm); + } + set=plover_package_set_new(); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + priv->set=razor_importer_finish(importer); + return set; +} + +uint32_t plover_package_set_get_header_version(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),0); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (priv->set) + return razor_set_get_header_version(priv->set); + else + return 0; +} + +gboolean plover_package_set_set_header_version(PloverPackageSet *set, + uint32_t header_version) +{ + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),FALSE); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (priv->set) + return !razor_set_set_header_version(priv->set,header_version); + else + return FALSE; +} + +struct razor_set *plover_package_set_get_razor(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + return priv->set; +} + +GSList *plover_package_set_get_packages(PloverPackageSet *set) +{ + struct razor_package_iterator *iter; + struct razor_package *pkg; + PloverPackageSetPrivate *priv; + PloverPackage *package; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (priv->set && !priv->packages) + { + iter=razor_package_iterator_create(priv->set); + while(razor_package_iterator_next(iter,&pkg,RAZOR_DETAIL_LAST)) + { + package=plover_package_new(priv->set,pkg); + priv->packages=g_slist_prepend(priv->packages,package); + } + razor_package_iterator_destroy(iter); + priv->packages=g_slist_reverse(priv->packages); + } + return priv->packages; +} + +PloverPackage *plover_package_set_lookup(PloverPackageSet *set, + struct razor_package *razor_package) +{ + GSList *packages,*lnk; + PloverPackage *package; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + packages=plover_package_set_get_packages(set); + for(lnk=packages;lnk;lnk=lnk->next) + { + package=lnk->data; + if (plover_package_get_razor_package(package)==razor_package) + return package; + } + return NULL; +} + +PloverPackage *plover_package_set_find_custom(PloverPackageSet *set, + gconstpointer data,GCompareFunc func) +{ + GSList *packages,*lnk; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + packages=plover_package_set_get_packages(set); + lnk=g_slist_find_custom(packages,data,func); + return lnk?lnk->data:NULL; +} + +static gint plover_package_set_compare(gconstpointer a,gconstpointer b) +{ + PloverPackage *pa=(void *)a,*pb=(void *)b; + const char *sa,*sb; + sa=plover_package_get_name(pa); + sb=plover_package_get_name(pb); + if (g_strcmp0(sa,sb)) + return 1; + sa=plover_package_get_arch(pa); + sb=plover_package_get_arch(pb); + if (g_strcmp0(sa,sb)) + return 1; + sa=plover_package_get_version(pa); + sb=plover_package_get_version(pb); + return !!g_strcmp0(sa,sb); +} + +PloverPackage *plover_package_set_find_matching(PloverPackageSet *set, + PloverPackage *template) +{ + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + return plover_package_set_find_custom(set,template, + plover_package_set_compare); +} + +/* + * Some versions of razor have a bug which causes all detail strings + * to be discarded. If such a version of razor is used to install or + * update a package, then all the detail strings for the installed + * set will be lost. This function tests for this condition and can + * be used to present something more useful than blank details. + */ + +gboolean plover_package_set_get_no_details(PloverPackageSet *set) +{ + PloverPackageSetPrivate *priv; + PloverPackage *package; + GSList *packages,*link; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),FALSE); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (priv->no_details<0) + { + packages=plover_package_set_get_packages(set); + if (packages) + { + priv->no_details=0; + for(link=packages;link;link=link->next) + { + package=link->data; + priv->no_details+=2; + if (*plover_package_get_summary(package)) + priv->no_details--; + if (*plover_package_get_license(package)) + priv->no_details--; + if (*plover_package_get_description(package)) + priv->no_details--; + if (*plover_package_get_URL(package)) + priv->no_details--; + } + if (priv->no_details<0) /* More than 50% of strings present */ + priv->no_details=0; + } + } + return priv->no_details>0; +} + +struct plover_package_set_prefix { + gchar *path; + guint count; +}; + +static GArray *plover_package_set_popchart_new(void) +{ + GArray *prefixes; + prefixes=g_array_new(FALSE,FALSE,sizeof(struct plover_package_set_prefix)); + return prefixes; +} + +static void plover_package_set_popchart_free(GArray *popchart) +{ + int i; + struct plover_package_set_prefix *prefix; + for(i=0;ilen;i++) + { + prefix=&g_array_index(popchart,struct plover_package_set_prefix,i); + g_free(prefix->path); + } + g_array_free(popchart,TRUE); +} + +static void plover_package_set_popchart_add(GArray *popchart,const char *path) +{ + int i; + struct plover_package_set_prefix *prefix; + for(i=popchart->len-1;i>=0;i--) + { + prefix=&g_array_index(popchart,struct plover_package_set_prefix,i); + if (!strcmp(prefix->path,path)) + { + prefix->count++; + return; + } + } + g_array_set_size(popchart,popchart->len+1); + prefix=&g_array_index(popchart,struct plover_package_set_prefix, + popchart->len-1); + prefix->path=g_strdup(path); + prefix->count=1; +} + +static const char *plover_package_set_popchart_get_popular(GArray *popchart) +{ + int i; + struct plover_package_set_prefix *prefix,*popular; + if (!popchart->len) + return NULL; + popular=&g_array_index(popchart,struct plover_package_set_prefix,0); + for(i=1;ilen;i++) + { + prefix=&g_array_index(popchart,struct plover_package_set_prefix,i); + if (prefix->count>popular->count) + popular=prefix; + } + return popular->path; +} + +static void plover_package_set_popchart_add_prefixes(PloverPackageSet *set, + GArray *popchart) +{ + int i; + PloverPackage *package; + GSList *packages,*lnk; + const char *const *prefixes; + packages=plover_package_set_get_packages(set); + for(lnk=packages;lnk;lnk=lnk->next) + { + package=lnk->data; + prefixes=plover_package_get_prefixes(package); + for(i=0;prefixes[i];i++) + plover_package_set_popchart_add(popchart,prefixes[i]); + } +} + +static void plover_package_set_popchart_add_from_files(PloverPackageSet *set, + GArray *popchart) +{ + int len; + const char *name,*s; + gchar *default_prefix,*path; + struct razor_file_iterator *fi; + GSList *packages,*lnk; + PloverPackage *package; + default_prefix=plover_default_prefix_for_vendor(""); + if (!default_prefix) + return; + len=strlen(default_prefix); + packages=plover_package_set_get_packages(set); + for(lnk=packages;lnk;lnk=lnk->next) + { + package=lnk->data; + fi=plover_package_file_iterator_create(package,FALSE); + while (razor_file_iterator_next(fi,&name)) + { + if (g_str_has_prefix(name,default_prefix)) + { + for(s=name+len;*s;s++) + if (G_IS_DIR_SEPARATOR(*s)) + break; + path=g_strndup(name,s-name); + plover_package_set_popchart_add(popchart,path); + g_free(path); + } + } + razor_file_iterator_destroy(fi); + } + g_free(default_prefix); +} + +const char *plover_package_set_guess_prefix(PloverPackageSet *set, + GError **error) +{ + GArray *popchart; + const char *prefix; + PloverPackageSetPrivate *priv; + g_return_val_if_fail(PLOVER_IS_PACKAGE_SET(set),NULL); + priv=PLOVER_PACKAGE_SET_GET_PRIVATE(set); + if (!priv->guessed_prefix) + { + popchart=plover_package_set_popchart_new(); + plover_package_set_popchart_add_prefixes(set,popchart); + prefix=plover_package_set_popchart_get_popular(popchart); + if (prefix) + priv->guessed_prefix=g_strdup(prefix); + else + { + plover_package_set_popchart_add_from_files(set,popchart); + prefix=plover_package_set_popchart_get_popular(popchart); + if (prefix) + priv->guessed_prefix=g_strdup(prefix); + } + plover_package_set_popchart_free(popchart); + } + if (!priv->guessed_prefix) + priv->guessed_prefix= + plover_default_prefix_for_vendor("Acme Corporation"); + return priv->guessed_prefix; +} diff --git a/plover/packageset.h b/plover/packageset.h new file mode 100644 index 0000000..3de950a --- /dev/null +++ b/plover/packageset.h @@ -0,0 +1,73 @@ +#ifndef __PLOVER_PACKAGE_SET_H__ +#define __PLOVER_PACKAGE_SET_H__ + +#include +#include + +G_BEGIN_DECLS + +#define PLOVER_TYPE_PACKAGE_SET plover_package_set_get_type() +#define PLOVER_PACKAGE_SET(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ + PLOVER_TYPE_PACKAGE_SET,PloverPackageSet) +#define PLOVER_PACKAGE_SET_CLASS(klass)\ + G_TYPE_CHECK_CLASS_CAST(klass,\ + PLOVER_TYPE_PACKAGE_SET,\ + PloverPackageSetClass) +#define PLOVER_IS_PACKAGE_SET(obj)\ + G_TYPE_CHECK_INSTANCE_TYPE(obj,\ + PLOVER_TYPE_PACKAGE_SET) +#define PLOVER_IS_PACKAGE_SET_CLASS(klass)\ + G_TYPE_CHECK_CLASS_TYPE(obj,\ + PLOVER_TYPE_PACKAGE_SET) +#define PLOVER_PACKAGE_SET_GET_CLASS(obj)\ + G_TYPE_INSTANCE_GET_CLASS(obj,\ + PLOVER_TYPE_PACKAGE_SET,\ + PloverPackageSetClass) + +typedef struct _PloverPackageSet { + GObject parent_instance; +} PloverPackageSet; + +typedef struct _PloverPackageSetClass { + GObjectClass parent_class; +} PloverPackageSetClass; + +#include + +GType plover_package_set_get_type(void) G_GNUC_CONST; +PloverPackageSet *plover_package_set_new(void); +void plover_package_set_close(PloverPackageSet *set); +gboolean plover_package_set_open(PloverPackageSet *set,const char *install_root, + gboolean exclusive,GError **err); +gboolean plover_package_set_update(PloverPackageSet *set,struct razor_set *next, + struct razor_atomic *atomic); +const char *plover_package_set_get_install_root(PloverPackageSet *set); +gboolean plover_package_set_get_exclusive(PloverPackageSet *set); +PloverPackageSet *plover_package_set_new_from_installed(const char *root, + GError **err); +PloverPackageSet *plover_package_set_new_from_razor(struct razor_set *razor); +PloverPackageSet * + plover_package_set_new_from_repository(PloverRepository *repository, + struct razor_relocations *relocations,GError **err); +PloverPackageSet *plover_package_set_new_from_yum(const char *base, + struct razor_relocations *relocations,GError **err); +PloverPackageSet *plover_package_set_new_from_rpms(const char **filenames, + GError **error); +uint32_t plover_package_set_get_header_version(PloverPackageSet *set); +gboolean plover_package_set_set_header_version(PloverPackageSet *set, + uint32_t header_version); +struct razor_set *plover_package_set_get_razor(PloverPackageSet *set); +GSList *plover_package_set_get_packages(PloverPackageSet *set); +PloverPackage *plover_package_set_lookup(PloverPackageSet *set, + struct razor_package *razor_package); +PloverPackage *plover_package_set_find_custom(PloverPackageSet *set, + gconstpointer data,GCompareFunc func); +PloverPackage *plover_package_set_find_matching(PloverPackageSet *set, + PloverPackage *template); +gboolean plover_package_set_get_no_details(PloverPackageSet *set); +const char *plover_package_set_guess_prefix(PloverPackageSet *set, + GError **error); + +G_END_DECLS + +#endif /* __PLOVER_PACKAGE_SET_H__ */ diff --git a/plover/plover.h b/plover/plover.h index f2ae8db..9e3d54d 100644 --- a/plover/plover.h +++ b/plover/plover.h @@ -2,6 +2,26 @@ #define __PLOVER_H__ #include +#include +#include +#include +#include + +#define PLOVER_GENERAL_ERROR RAZOR_ERROR_DOMAIN('P','l','v',0) +#define PLOVER_SCRIPTLET_ERROR RAZOR_ERROR_DOMAIN('P','l','v',1) +#define PLOVER_RAZOR_ERROR plover_razor_error_quark() +#define PLOVER_POSIX_ERROR plover_posix_error_quark() +#define PLOVER_MSWIN_ERROR plover_mswin_error_quark() +#define PLOVER_ZLIB_ERROR plover_zlib_error_quark() + +enum plover_general_error +{ + PLOVER_GENERAL_ERROR_FAILED, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE, + PLOVER_GENERAL_ERROR_NO_WORK, + PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET, + PLOVER_GENERAL_ERROR_CANCELLED, +}; enum comps_requirement_type { @@ -33,25 +53,36 @@ struct comps struct comps_group *groups; }; -char *plover_strconcat(const char *string,...); -char *plover_default_prefix_for_vendor(const char *vendor); +struct plover_vector +{ + int len,alloc; + char **strings; +}; + +gchar *plover_default_prefix_for_vendor(const char *vendor); +gchar *plover_pre_install_prefix(void); char *plover_get_program_directory(const char *argv0); +GQuark plover_razor_error_quark(void); +GQuark plover_posix_error_quark(void); +GQuark plover_mswin_error_quark(void); +GQuark plover_zlib_error_quark(void); +void plover_propagate_razor_error_dup(GError **dest,struct razor_error *src); +void plover_propagate_razor_error(GError **dest,struct razor_error *src); +void plover_propagate_g_error(struct razor_error **dest,GError *src); -struct razor_set *plover_razor_set_create_from_yum(const char *base); +struct razor_set *plover_razor_set_create_from_yum(const char *base, + GError **error); -struct razor_set *plover_relocate_packages(struct razor_set *set, - const char *base,struct razor_relocations *relocations, - struct razor_error **error); int plover_run_transaction(struct razor_transaction *trans, - struct razor_install_iterator *ii,const char *base,const char *install_root, - struct razor_set *system,struct razor_set *next,struct razor_atomic *atomic, - struct razor_relocations *relocations,enum razor_stage_type stage); -int plover_commit_transaction(struct razor_transaction *trans,const char *base, - const char *install_root,struct razor_root *root, - struct razor_relocations *relocations); -int plover_install(const char *base,const char *prefix,char **pkgs); -int plover_update(const char *base,const char *prefix,char **pkgs); -int plover_remove(char **pkgs); + struct razor_install_iterator *ii,const char *install_root, + struct razor_set *system,PloverPackageSet *next,PloverRepository *upstream, + struct razor_atomic *atomic,struct razor_relocations *relocations, + enum razor_stage_type stage,GCancellable *cancellable); +gboolean plover_install(const char *base,const char *prefix,char **pkgs, + GError **error); +gboolean plover_update(const char *base,const char *prefix,char **pkgs, + GError **error); +gboolean plover_remove(char **pkgs,GError **error); int plover_installed_files_match_prefix(const char *prefix); struct comps *plover_comps_new(void); @@ -60,4 +91,15 @@ void plover_comps_free(struct comps *comps); struct comps_group *plover_comps_lookup_group(struct comps *comps, const char *id); +int plover_log_open(const char *path); + +struct plover_vector *plover_vector_new(void); +struct plover_vector *plover_vector_dup(struct plover_vector *old); +void plover_vector_append(struct plover_vector *vector,const char *str); +int plover_vector_contains(struct plover_vector *vector,const char *str); +int plover_vector_remove(struct plover_vector *vector,const char *str); +void plover_vector_sort(struct plover_vector *vector); +char *plover_vector_format_for_display(struct plover_vector *vector); +void plover_vector_free(struct plover_vector *vector); + #endif /* __PLOVER_H__ */ diff --git a/plover/plover.pc.in b/plover/plover.pc.in index bab71c3..7590b4a 100644 --- a/plover/plover.pc.in +++ b/plover/plover.pc.in @@ -6,6 +6,6 @@ includedir=@includedir@ Name: plover Description: Plover packaging system Version: @VERSION@ -Requires: razor expat zlib +Requires: razor expat zlib glib2 Libs: -L${libdir} -lplover Cflags: -I${includedir} diff --git a/plover/razor.c b/plover/razor.c index 3379711..73b8204 100644 --- a/plover/razor.c +++ b/plover/razor.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009, 2011, 2012 J. Ali Harlow + * Copyright (C) 2009, 2011, 2012, 2014 J. Ali Harlow * * 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 @@ -22,10 +22,14 @@ #include #include #include +#include #include #include +#include +#include #include "config.h" #include "plover/plover.h" +#include "plover/transaction.h" static char *rpm_filename(const char *name,const char *version,const char *arch) { @@ -35,65 +39,7 @@ static char *rpm_filename(const char *name,const char *version,const char *arch) v++; else v=version; - return plover_strconcat(name,"-",v,".",arch,".rpm",NULL); -} - -struct razor_set *plover_relocate_packages(struct razor_set *set, - const char *base,struct razor_relocations *relocations, - struct razor_error **error) -{ - struct razor_importer *importer; - struct razor_property_iterator *prop_iter; - struct razor_package_iterator *pkg_iter; - struct razor_file_iterator *file_iter; - struct razor_package *package; - struct razor_property *property; - struct razor_rpm *rpm; - struct razor_set *new; - const char *name,*version,*arch,*summary,*desc,*url,*license; - char *s,*file; - uint32_t flags; - importer=razor_importer_create(); - pkg_iter=razor_package_iterator_create(set); - while (razor_package_iterator_next(pkg_iter,&package,RAZOR_DETAIL_NAME, - &name,RAZOR_DETAIL_VERSION,&version,RAZOR_DETAIL_ARCH,&arch, - RAZOR_DETAIL_SUMMARY,&summary,RAZOR_DETAIL_DESCRIPTION,&desc, - RAZOR_DETAIL_URL,&url,RAZOR_DETAIL_LICENSE,&license,RAZOR_DETAIL_LAST)) - { - s=rpm_filename(name,version,arch); - file=plover_strconcat(base,"/rpms/",s,NULL); - free(s); - rpm=razor_rpm_open(file,error); - free(file); - if (!rpm) - { - razor_package_iterator_destroy(pkg_iter); - razor_importer_destroy(importer); - return NULL; - } - razor_relocations_set_rpm(relocations,rpm); - razor_rpm_close(rpm); - razor_importer_begin_package(importer,name,version,arch); - razor_importer_add_details(importer,summary,desc,url,license); - prop_iter=razor_property_iterator_create(set,package); - while (razor_property_iterator_next(prop_iter,&property,&name,&flags, - &version)) - razor_importer_add_property(importer,name,flags,version); - razor_property_iterator_destroy(prop_iter); - file_iter=razor_file_iterator_create(set,package,0); - while (razor_file_iterator_next(file_iter,&name)) - { - name=razor_relocations_apply(relocations,name); - razor_importer_add_file(importer,name); - } - razor_file_iterator_destroy(file_iter); - razor_importer_finish_package(importer); - } - razor_package_iterator_destroy(pkg_iter); - new=razor_importer_finish(importer); - if (new) - razor_set_set_header_version(new,razor_set_get_header_version(set)); - return new; + return g_strconcat(name,"-",v,".",arch,".rpm",NULL); } /* @@ -101,17 +47,20 @@ struct razor_set *plover_relocate_packages(struct razor_set *set, * is met (in which case the action is consumed). */ int plover_run_transaction(struct razor_transaction *trans, - struct razor_install_iterator *ii,const char *base,const char *install_root, - struct razor_set *system,struct razor_set *next,struct razor_atomic *atomic, - struct razor_relocations *relocations,enum razor_stage_type stage) + struct razor_install_iterator *ii,const char *install_root, + struct razor_set *system,PloverPackageSet *next,PloverRepository *upstream, + struct razor_atomic *atomic,struct razor_relocations *relocations, + enum razor_stage_type stage,GCancellable *cancellable) { - struct razor_package *package; + struct razor_package *pkg; enum razor_install_action action; struct razor_rpm *rpm; struct razor_error *error=NULL; const char *name,*version,*arch; - char *s,*file; + gchar *t; int r,count; + GError *tmp_error=NULL; + PloverPackage *package; switch(stage) { case RAZOR_STAGE_SCRIPTS_PRE: @@ -127,40 +76,41 @@ int plover_run_transaction(struct razor_transaction *trans, /* Keep the compiler happy */ break; } - while (razor_install_iterator_next(ii,&package,&action,&count)) + while (razor_install_iterator_next(ii,&pkg,&action,&count)) { + if (g_cancellable_is_cancelled(cancellable)) + { + razor_atomic_abort(atomic,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_CANCELLED,"Operation was cancelled"); + return -1; + } if (action==RAZOR_INSTALL_ACTION_REMOVE) { - razor_package_get_details(system,package,RAZOR_DETAIL_NAME,&name, + razor_package_get_details(system,pkg,RAZOR_DETAIL_NAME,&name, RAZOR_DETAIL_LAST); if (stage==RAZOR_STAGE_FILES) printf(" Removing : %s ",name); - r=razor_package_remove(system,next,atomic,package,install_root, - count,stage); + r=razor_package_remove(system,plover_package_set_get_razor(next), + atomic,pkg,install_root,count,stage); if (stage==RAZOR_STAGE_FILES) printf("\n"); } else if (action==RAZOR_INSTALL_ACTION_ADD) { - razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, - RAZOR_DETAIL_VERSION,&version,RAZOR_DETAIL_ARCH,&arch, - RAZOR_DETAIL_LAST); - s=rpm_filename(name,version,arch); - file=plover_strconcat(base,"/rpms/",s,NULL); - free(s); - rpm=razor_rpm_open(file,&error); - free(file); + package=plover_package_set_lookup(next,pkg); + rpm=plover_repository_open_rpm(upstream,package,&tmp_error); if (!rpm) { - razor_atomic_abort(atomic,razor_error_get_msg(error)); + plover_propagate_g_error(&error,tmp_error); + razor_atomic_propagate_error(atomic,error,NULL); razor_error_free(error); return -1; } if (stage==RAZOR_STAGE_FILES) - printf(" Installing : %s ",name); + printf(" Installing : %s ",plover_package_get_name(package)); if (relocations) razor_rpm_set_relocations(rpm,relocations); - razor_transaction_fixup_package(trans,package,rpm); + razor_transaction_fixup_package(trans,pkg,rpm); r=razor_rpm_install(rpm,atomic,install_root,1,stage); razor_rpm_close(rpm); if (stage==RAZOR_STAGE_FILES) @@ -174,9 +124,10 @@ int plover_run_transaction(struct razor_transaction *trans, return -1; else if (r) { - razor_package_get_details(next,package,RAZOR_DETAIL_NAME,&name, - RAZOR_DETAIL_VERSION,&version,RAZOR_DETAIL_ARCH,&arch, - RAZOR_DETAIL_LAST); + package=plover_package_set_lookup(next,pkg); + name=plover_package_get_name(package); + version=plover_package_get_version(package); + arch=plover_package_get_arch(package); /* * If a pre or preun script fails, then we should * treat that as a fatal error. post and postun @@ -185,10 +136,12 @@ int plover_run_transaction(struct razor_transaction *trans, */ if (stage==RAZOR_STAGE_SCRIPTS_PRE) { - fprintf(stderr, - "error: %s(%s-%s.%s) scriptlet failed, exit status %d\n", - action==RAZOR_INSTALL_ACTION_ADD?"%pre":"%preun", - name,version,arch,r); + t=g_strconcat(action==RAZOR_INSTALL_ACTION_ADD? + "%pre":"%preun","(",name,"-",version,".",arch, + ") scriptlet failed",NULL); + fprintf(stderr,"error: %s, exit status %d\n",t,r); + razor_atomic_abort(atomic,PLOVER_SCRIPTLET_ERROR,r,t); + g_free(t); return -1; } else @@ -201,339 +154,70 @@ int plover_run_transaction(struct razor_transaction *trans, return 0; } -int plover_commit_transaction(struct razor_transaction *trans,const char *base, - const char *install_root,struct razor_root *root, - struct razor_relocations *relocations) +gboolean plover_install(const char *base,const char *prefix,char **pkgs, + GError **error) { - int r,retval; - size_t pos; - struct razor_set *system,*next,*set; - struct razor_install_iterator *ii; - struct razor_atomic *atomic; - razor_transaction_resolve(trans); - if (razor_transaction_describe(trans)>0) - return -1; - next=razor_transaction_commit(trans); - system=razor_set_ref(razor_root_get_system_set(root)); - ii=razor_set_create_install_iterator(system,next); - do - { - pos=razor_install_iterator_tell(ii); - atomic=razor_atomic_open("package transaction"); - r=plover_run_transaction(trans,ii,base,install_root,system,next,atomic, - relocations,RAZOR_STAGE_SCRIPTS_PRE); - if (r<0) - fprintf(stderr,"Transaction aborted\n"); - else - { - razor_install_iterator_seek(ii,pos); - r=plover_run_transaction(trans,ii,base,install_root,system,next, - atomic,relocations,RAZOR_STAGE_FILES); - if (r==1) - { - set=razor_install_iterator_commit_set(ii); - razor_root_update(root,set,atomic); - razor_set_unref(set); - } - else if (!r) - razor_root_update(root,next,atomic); - retval=razor_atomic_commit(atomic); - if (retval) - fprintf(stderr,"%s\n",razor_atomic_get_error_msg(atomic)); - else - { - razor_install_iterator_seek(ii,pos); - plover_run_transaction(trans,ii,base,install_root,system,next, - atomic,relocations,RAZOR_STAGE_SCRIPTS_POST); - } - } - razor_atomic_destroy(atomic); - } while(!retval && r==1); - razor_set_unref(system); - razor_set_unref(next); - razor_install_iterator_destroy(ii); + gboolean retval; + PloverTransaction *transaction; + transaction=plover_transaction_new_install(base,prefix,pkgs,error); + if (!transaction) + return FALSE; + retval=plover_transaction_commit(transaction,NULL,error); + g_object_unref(transaction); return retval; } -static int plover_mark_package_for_update(struct razor_transaction *trans, - struct razor_set *set,const char *pkg) +gboolean plover_update(const char *base,const char *prefix,char **pkgs, + GError **error) { - struct razor_package_iterator *pi; - struct razor_package *package; - const char *name; - int retval=-1; - pi=razor_package_iterator_create(set); - while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_NAME,&name, - RAZOR_DETAIL_LAST)) + gboolean retval; + GError *tmp_error=NULL; + PloverTransaction *transaction; + transaction=plover_transaction_new_update(base,prefix,pkgs,&tmp_error); + if (!transaction) { - if (!strcmp(name,pkg)) - { - razor_transaction_update_package(trans,package); - retval=0; - break; - } - } - razor_package_iterator_destroy(pi); - return retval; -} - -int plover_install(const char *base,const char *prefix,char **pkgs) -{ - int i,retval; - char *s; - char *install_root; - struct razor_set *system,*set,*upstream; - struct razor_transaction *trans; - struct razor_relocations *relocations; - struct razor_root *root; - struct razor_error *error=NULL; - install_root=getenv("RAZOR_ROOT"); - if (!install_root) - install_root=""; - if (prefix) - { - relocations=razor_relocations_create(); - razor_relocations_add(relocations,"/usr",prefix); - } - else - relocations=NULL; - root=razor_root_open(install_root,NULL); - if (!root) - { - if (razor_root_create(install_root,&error)) - root=NULL; + retval=g_error_matches(tmp_error,PLOVER_POSIX_ERROR,ENOENT); + if (retval) + g_error_free(tmp_error); else - root=razor_root_open(install_root,&error); - if (!root) - { - fprintf(stderr,"%s\n",razor_error_get_msg(error)); - razor_error_free(error); - if (relocations) - razor_relocations_destroy(relocations); - return -1; - } - } - system=razor_root_get_system_set(root); - if (!system) - { - fprintf(stderr,"Internal error: No system set\n"); - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); - return -1; - } - s=plover_strconcat(base,"/repodata",NULL); - if (s) - { - retval=chdir(s); - if (retval<0) - perror(s); - } - else - { - fprintf(stderr,"Not enough memory\n"); - retval=-1; - } - free(s); - if (retval<0) - { - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); - return -1; - } - set=plover_razor_set_create_from_yum(base); - if (set) - { - upstream=plover_relocate_packages(set,base,relocations,&error); - if (!upstream) - { - fprintf(stderr,"%s\n",razor_error_get_msg(error)); - razor_error_free(error); - } - razor_set_unref(set); + g_propagate_error(error,tmp_error); } else - upstream=NULL; - if (!upstream) { - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); - return -1; + retval=plover_transaction_commit(transaction,NULL,error); + g_object_unref(transaction); } - trans=razor_transaction_create(system,upstream); - razor_set_unref(upstream); - for(i=0;pkgs[i];i++) - if (plover_mark_package_for_update(trans,system,pkgs[i]) && - plover_mark_package_for_update(trans,upstream,pkgs[i])) - { - fprintf(stderr,"%s: Package not found\n",pkgs[i]); - retval=-1; - break; - } - if (!retval) - retval=plover_commit_transaction(trans,base,install_root,root, - relocations); - razor_transaction_destroy(trans); - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); return retval; } -int plover_update(const char *base,const char *prefix,char **pkgs) +gboolean plover_remove(char **pkgs,GError **error) { - int i,retval; - char *install_root,*s; - struct razor_set *system,*set,*upstream; - struct razor_transaction *trans; - struct razor_relocations *relocations; - struct razor_root *root; - struct razor_error *error=NULL; - install_root=getenv("RAZOR_ROOT"); - if (!install_root) - install_root=""; - if (prefix) + gboolean retval; + GError *tmp_error=NULL; + PloverTransaction *transaction; + transaction=plover_transaction_new_remove(pkgs,&tmp_error); + if (!transaction) { - relocations=razor_relocations_create(); - razor_relocations_add(relocations,"/usr",prefix); - } - else - relocations=NULL; - root=razor_root_open(install_root,&error); - if (!root) - { - fprintf(stderr,"%s\n",razor_error_get_msg(error)); - razor_error_free(error); - if (relocations) - razor_relocations_destroy(relocations); - return 0; - } - system=razor_root_get_system_set(root); - s=plover_strconcat(base,"/repodata",NULL); - if (s) - { - retval=chdir(s); + retval=g_error_matches(tmp_error,PLOVER_POSIX_ERROR,ENOENT); if (retval) - perror(s); - } - else - { - fprintf(stderr,"Not enough memory"); - retval=-1; - } - free(s); - if (retval) - { - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); - return -1; - } - set=plover_razor_set_create_from_yum(base); - if (set) - { - upstream=plover_relocate_packages(set,base,relocations,&error); - if (!upstream) { - fprintf(stderr,"%s\n",razor_error_get_msg(error)); - razor_error_free(error); - } - razor_set_unref(set); - } - else - upstream=NULL; - if (!upstream) - { - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); - return -1; - } - trans=razor_transaction_create(system,upstream); - razor_set_unref(upstream); - if (pkgs) - for(i=0;pkgs[i];i++) - { - if (plover_mark_package_for_update(trans,system,pkgs[i])) + g_error_free(tmp_error); + if (pkgs) { - fprintf(stderr,"%s: Package not found\n",pkgs[i]); - retval=-1; - break; + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE,"%s: %s",pkgs[0], + "Package not found"); + retval=FALSE; } } - else - razor_transaction_update_all(trans); - if (!retval) - retval=plover_commit_transaction(trans,base,install_root,root, - relocations); - razor_transaction_destroy(trans); - razor_root_close(root); - if (relocations) - razor_relocations_destroy(relocations); - return retval; -} - -static int plover_mark_packages_for_removal(struct razor_transaction *trans, - struct razor_set *set,const char *pkg) -{ - struct razor_package_iterator *pi; - struct razor_package *package; - const char *name; - int retval=pkg?-1:0; - pi=razor_package_iterator_create(set); - while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_NAME,&name, - RAZOR_DETAIL_LAST)) - { - if (!pkg || !strcmp(name,pkg)) - { - razor_transaction_remove_package(trans,package); - retval=0; - } + else + g_propagate_error(error,tmp_error); } - razor_package_iterator_destroy(pi); - return retval; -} - -int plover_remove(char **pkgs) -{ - int i,retval=0; - char *install_root; - struct razor_set *system,*upstream; - struct razor_transaction *trans; - struct razor_root *root; - struct razor_error *error=NULL; - install_root=getenv("RAZOR_ROOT"); - if (!install_root) - install_root=""; - root=razor_root_open(install_root,&error); - if (!root) + else { - fprintf(stderr,"%s\n",razor_error_get_msg(error)); - razor_error_free(error); - return 0; + retval=plover_transaction_commit(transaction,NULL,error); + g_object_unref(transaction); } - system=razor_root_get_system_set(root); - upstream=razor_set_create_without_root(); - trans=razor_transaction_create(system,upstream); - razor_set_unref(upstream); - if (pkgs) - for(i=0;pkgs[i];i++) - { - if (plover_mark_packages_for_removal(trans,system,pkgs[i])) - { - fprintf(stderr,"%s: Package not found\n",pkgs[i]); - retval=-1; - break; - } - } - else - plover_mark_packages_for_removal(trans,system,NULL); - if (!retval) - retval=plover_commit_transaction(trans,NULL,install_root,root,NULL); - razor_transaction_destroy(trans); - razor_root_close(root); return retval; } diff --git a/plover/repository.c b/plover/repository.c new file mode 100644 index 0000000..e9b001c --- /dev/null +++ b/plover/repository.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include + +G_DEFINE_TYPE(PloverRepository,plover_repository,G_TYPE_OBJECT); + +typedef struct _PloverRepositoryPrivate { + PloverPackageSet *set; + gchar **filenames; +} PloverRepositoryPrivate; + +#define PLOVER_REPOSITORY_GET_PRIVATE(obj)\ + G_TYPE_INSTANCE_GET_PRIVATE(obj,\ + PLOVER_TYPE_REPOSITORY,\ + PloverRepositoryPrivate) + +static void plover_repository_finalize(GObject *obj) +{ + PloverRepositoryPrivate *priv=PLOVER_REPOSITORY_GET_PRIVATE(obj); + g_strfreev(priv->filenames); + if (G_OBJECT_CLASS(plover_repository_parent_class)->finalize) + G_OBJECT_CLASS(plover_repository_parent_class)->finalize(obj); +} + +static void plover_repository_dispose(GObject *obj) +{ + PloverRepositoryPrivate *priv=PLOVER_REPOSITORY_GET_PRIVATE(obj); + g_clear_object(&priv->set); + if (G_OBJECT_CLASS(plover_repository_parent_class)->dispose) + G_OBJECT_CLASS(plover_repository_parent_class)->dispose(obj); +} + +static void plover_repository_class_init(PloverRepositoryClass *klass) +{ + GObjectClass *oclass=G_OBJECT_CLASS(klass); + oclass->finalize=plover_repository_finalize; + oclass->dispose=plover_repository_dispose; + g_type_class_add_private(klass,sizeof(PloverRepositoryPrivate)); +} + +static void plover_repository_init(PloverRepository *repository) +{ +} + +PloverRepository *plover_repository_new_from_files(const char **filenames, + GError **error) +{ + PloverPackageSet *set; + PloverRepository *repository; + PloverRepositoryPrivate *priv; + set=plover_package_set_new_from_rpms(filenames,error); + if (!set) + return NULL; + repository=g_object_new(PLOVER_TYPE_REPOSITORY,NULL); + priv=PLOVER_REPOSITORY_GET_PRIVATE(repository); + priv->filenames=g_strdupv((gchar **)filenames); + priv->set=set; + return repository; +} + +static char *rpm_filename(const char *name,const char *version,const char *arch) +{ + const char *v; + v=strchr(version,':'); /* Skip epoch */ + if (v) + v++; + else + v=version; + return g_strconcat(name,"-",v,".",arch,".rpm",NULL); +} + +PloverRepository *plover_repository_new_from_yum(const char *base, + GError **error) +{ + char *s; + const char *name,*version,*arch; + GPtrArray *filenames; + struct razor_set *imported; + struct razor_package *package; + struct razor_package_iterator *pi; + PloverPackageSet *set; + PloverRepository *repository; + PloverRepositoryPrivate *priv; + imported=plover_razor_set_create_from_yum(base,error); + if (!imported) + return NULL; + set=plover_package_set_new_from_razor(imported); + razor_set_unref(imported); + repository=g_object_new(PLOVER_TYPE_REPOSITORY,NULL); + priv=PLOVER_REPOSITORY_GET_PRIVATE(repository); + filenames=g_ptr_array_new(); + pi=razor_package_iterator_create(plover_package_set_get_razor(set)); + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_VERSION,&version,RAZOR_DETAIL_ARCH,&arch,RAZOR_DETAIL_LAST)) + { + s=rpm_filename(name,version,arch); + g_ptr_array_add(filenames,g_build_filename(base,"rpms",s,NULL)); + free(s); + } + razor_package_iterator_destroy(pi); + g_ptr_array_add(filenames,NULL); + priv->filenames=(gchar **)g_ptr_array_free(filenames,FALSE); + priv->set=set; + return repository; +} + +PloverPackageSet * + plover_repository_get_package_set(PloverRepository *repository) +{ + PloverRepositoryPrivate *priv; + g_return_val_if_fail(PLOVER_IS_REPOSITORY(repository),NULL); + priv=PLOVER_REPOSITORY_GET_PRIVATE(repository); + return priv->set; +} + +struct razor_rpm *plover_repository_open_rpm(PloverRepository *repository, + PloverPackage *package,GError **error) +{ + int nth; + struct razor_rpm *rpm; + struct razor_error *tmp_error=NULL; + PloverRepositoryPrivate *priv; + PloverPackage *internal; + g_return_val_if_fail(PLOVER_IS_REPOSITORY(repository),NULL); + g_return_val_if_fail(PLOVER_IS_PACKAGE(package),NULL); + priv=PLOVER_REPOSITORY_GET_PRIVATE(repository); + nth=g_slist_index(plover_package_set_get_packages(priv->set),package); + if (nth<0) + { + internal=plover_package_set_find_matching(priv->set,package); + nth=g_slist_index(plover_package_set_get_packages(priv->set),internal); + } + if (nth<0) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE, + "%s-%s.%s: Package not in repository", + plover_package_get_name(package),plover_package_get_version(package), + plover_package_get_arch(package)); + return NULL; + } + rpm=razor_rpm_open(priv->filenames[nth],&tmp_error); + if (!rpm) + plover_propagate_razor_error(error,tmp_error); + return rpm; +} diff --git a/plover/repository.h b/plover/repository.h new file mode 100644 index 0000000..8409756 --- /dev/null +++ b/plover/repository.h @@ -0,0 +1,49 @@ +#ifndef __PLOVER_REPOSITORY_H__ +#define __PLOVER_REPOSITORY_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define PLOVER_TYPE_REPOSITORY plover_repository_get_type() +#define PLOVER_REPOSITORY(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ + PLOVER_TYPE_REPOSITORY,PloverRepository) +#define PLOVER_REPOSITORY_CLASS(klass)\ + G_TYPE_CHECK_CLASS_CAST(klass,\ + PLOVER_TYPE_REPOSITORY,PloverRepositoryClass) +#define PLOVER_IS_REPOSITORY(obj)\ + G_TYPE_CHECK_INSTANCE_TYPE(obj,\ + PLOVER_TYPE_REPOSITORY) +#define PLOVER_IS_REPOSITORY_CLASS(klass)\ + G_TYPE_CHECK_CLASS_TYPE(obj,\ + PLOVER_TYPE_REPOSITORY) +#define PLOVER_REPOSITORY_GET_CLASS(obj)\ + G_TYPE_INSTANCE_GET_CLASS(obj,\ + PLOVER_TYPE_REPOSITORY,PloverRepositoryClass) + +typedef struct _PloverRepository { + GObject parent_instance; +} PloverRepository; + +typedef struct _PloverRepositoryClass { + GObjectClass parent_class; +} PloverRepositoryClass; + +#include +#include + +GType plover_repository_get_type(void) G_GNUC_CONST; +PloverRepository *plover_repository_new_from_files(const char **filenames, + GError **error); +PloverRepository *plover_repository_new_from_yum(const char *base, + GError **error); +PloverPackageSet * + plover_repository_get_package_set(PloverRepository *repository); +struct razor_rpm *plover_repository_open_rpm(PloverRepository *repository, + PloverPackage *package,GError **error); + +G_END_DECLS + +#endif /* __PLOVER_REPOSITORY_H__ */ diff --git a/plover/transaction.c b/plover/transaction.c new file mode 100644 index 0000000..d22715b --- /dev/null +++ b/plover/transaction.c @@ -0,0 +1,722 @@ +/* + * Copyright (C) 2009, 2011, 2012, 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include "plover/transaction.h" +#include "plover/plover.h" + +G_DEFINE_TYPE(PloverTransaction,plover_transaction,G_TYPE_OBJECT); + +enum { + STATUS_CHANGED=0, + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +static void plover_transaction_finalize(PloverTransaction *transaction) +{ + g_free(transaction->base); + g_free(transaction->prefix); + g_free(transaction->unsatisfied); + if (transaction->trans) + razor_transaction_destroy(transaction->trans); + if (transaction->relocations) + razor_relocations_destroy(transaction->relocations); + if (transaction->install_iterator) + razor_install_iterator_destroy(transaction->install_iterator); + if (transaction->next) + razor_set_unref(transaction->next); + if (transaction->system) + razor_set_unref(transaction->system); +} + +static void plover_transaction_dispose(PloverTransaction *transaction) +{ + g_clear_object(&transaction->installed); + g_clear_object(&transaction->relocated); + g_clear_object(&transaction->upstream); +} + +static void plover_transaction_class_init(PloverTransactionClass *klass) +{ + GObjectClass *gobject_class=G_OBJECT_CLASS(klass); + gobject_class->finalize=(void (*)(GObject *))plover_transaction_finalize; + gobject_class->dispose=(void (*)(GObject *))plover_transaction_dispose; + signals[STATUS_CHANGED]=g_signal_new("status-changed", + G_TYPE_FROM_CLASS(klass),G_SIGNAL_RUN_LAST,0,NULL,NULL, + g_cclosure_marshal_VOID__STRING,G_TYPE_NONE,1,G_TYPE_STRING); +} + +static void plover_transaction_init(PloverTransaction *transaction) +{ +} + +gboolean plover_transaction_resolve(PloverTransaction *transaction, + GError **error) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + if (!(transaction->flags&PLOVER_TRANSACTION_RESOLVED)) + { + razor_transaction_resolve(transaction->trans); + transaction->flags|=PLOVER_TRANSACTION_RESOLVED; + g_free(transaction->unsatisfied); + transaction->unsatisfied=NULL; + if (razor_transaction_describe(transaction->trans)>0) + transaction->flags|=PLOVER_TRANSACTION_UNSATISFIED; + } + if (transaction->flags&PLOVER_TRANSACTION_UNSATISFIED) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_REQUIREMENTS_NOT_MET, + "Package requirements not met"); + return FALSE; + } + return TRUE; +} + +static void plover_transaction_unsatisfied_callback(const char *requirement, + struct razor_package *package,const char *name,const char *version, + const char *arch,void *data) +{ + GString *string=data; + g_string_append_printf(string,"%s is needed by %s-%s.%s\n",requirement,name, + version,arch); +} + +const char *plover_transaction_get_unsatisfied(PloverTransaction *transaction) +{ + GString *unsatisfied; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),NULL); + if (plover_transaction_resolve(transaction,NULL)) + return NULL; + else if (!transaction->unsatisfied) + { + unsatisfied=g_string_new(NULL); + if (!razor_transaction_unsatisfied(transaction->trans, + plover_transaction_unsatisfied_callback,unsatisfied)) + /* Impossible */ + g_string_assign(unsatisfied, + "Unknown package requirements unsatisfied"); + transaction->unsatisfied=g_string_free(unsatisfied,FALSE); + } + return transaction->unsatisfied; +} + +struct razor_set *plover_transaction_get_system_set( + PloverTransaction *transaction) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),NULL); + if (!transaction->system && transaction->installed) + { + transaction->system= + plover_package_set_get_razor(transaction->installed); + if (transaction->system) + razor_set_ref(transaction->system); + } + return transaction->system; +} + +struct razor_set *plover_transaction_get_next_set( + PloverTransaction *transaction,GError **error) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),NULL); + if (!transaction->next) + { + if (!plover_transaction_resolve(transaction,error)) + return NULL; + transaction->next=razor_transaction_commit(transaction->trans); + } + return transaction->next; +} + +struct razor_install_iterator *plover_transaction_get_install_iterator( + PloverTransaction *transaction,GError **error) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),NULL); + if (!transaction->install_iterator) + { + if (!plover_transaction_get_next_set(transaction,error)) + return NULL; + (void)plover_transaction_get_system_set(transaction); + transaction->install_iterator= + razor_set_create_install_iterator(transaction->system, + transaction->next); + } + razor_install_iterator_rewind(transaction->install_iterator); + return transaction->install_iterator; +} + +gboolean plover_transaction_commit(PloverTransaction *transaction, + GCancellable *cancellable,GError **error) +{ + int r; + gboolean retval; + size_t pos; + struct razor_set *set; + struct razor_install_iterator *ii; + struct razor_atomic *atomic; + PloverPackageSet *next; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + if (g_cancellable_set_error_if_cancelled(cancellable,error)) + return FALSE; + ii=plover_transaction_get_install_iterator(transaction,error); + if (!ii) + return FALSE; + do + { + if (g_cancellable_set_error_if_cancelled(cancellable,error)) + return FALSE; + pos=razor_install_iterator_tell(ii); + g_signal_emit(transaction,signals[STATUS_CHANGED],0, + "Running pre-transaction scripts"); + atomic=razor_atomic_open("package transaction"); + next=plover_package_set_new_from_razor(transaction->next); + r=plover_run_transaction(transaction->trans,ii, + plover_package_set_get_install_root(transaction->installed), + transaction->system,next,transaction->upstream,atomic, + transaction->relocations,RAZOR_STAGE_SCRIPTS_PRE,cancellable); + if (r<0) + { + g_signal_emit(transaction,signals[STATUS_CHANGED],0, + "Failed in pre-transaction scripts"); + plover_propagate_razor_error_dup(error, + razor_atomic_get_error(atomic)); + razor_atomic_destroy(atomic); + g_object_unref(next); + return FALSE; + } + else + { + g_signal_emit(transaction,signals[STATUS_CHANGED],0, + "Unpacking files"); + razor_install_iterator_seek(ii,pos); + r=plover_run_transaction(transaction->trans,ii, + plover_package_set_get_install_root(transaction->installed), + transaction->system,next,transaction->upstream, + atomic,transaction->relocations,RAZOR_STAGE_FILES, +#if RAZOR_HAVE_ATOMIC_ROLLBACK + cancellable); +#else + NULL); +#endif + if (r==1) + { + set=razor_install_iterator_commit_set(ii); + plover_package_set_update(transaction->installed,set,atomic); + razor_set_unref(set); + } + else if (!r) + plover_package_set_update(transaction->installed, + transaction->next,atomic); + retval=!razor_atomic_commit(atomic); + if (!retval) + { + g_signal_emit(transaction,signals[STATUS_CHANGED],0, + "Failed to unpack all files correctly"); + plover_propagate_razor_error_dup(error, + razor_atomic_get_error(atomic)); + } + else + { + g_signal_emit(transaction,signals[STATUS_CHANGED],0, + "Running post-transaction scripts"); + razor_install_iterator_seek(ii,pos); + plover_run_transaction(transaction->trans,ii, + plover_package_set_get_install_root(transaction->installed), + transaction->system,next,transaction->upstream, + atomic,transaction->relocations,RAZOR_STAGE_SCRIPTS_POST, + NULL); + } + } + razor_atomic_destroy(atomic); + g_object_unref(next); + } while(retval && r==1); + g_signal_emit(transaction,signals[STATUS_CHANGED],0,"Completed"); + return retval; +} + +static void plover_transaction_commit_async_thread(GTask *task, + gpointer source_object,gpointer task_data,GCancellable *cancellable) +{ + PloverTransaction *transaction=source_object; + GError *error=NULL; + if (!plover_transaction_commit(transaction,cancellable,&error)) + g_task_return_error(task,error); + else + g_task_return_boolean(task,TRUE); + g_object_unref(task); +} + +void plover_transaction_commit_async(PloverTransaction *transaction, + GCancellable *cancellable,GAsyncReadyCallback callback,gpointer user_data) +{ + GTask *task; + g_return_if_fail(PLOVER_IS_TRANSACTION(transaction)); + task=g_task_new(transaction,cancellable,callback,user_data); + g_task_run_in_thread(task,plover_transaction_commit_async_thread); +} + +gboolean plover_transaction_commit_finish(PloverTransaction *transaction, + GAsyncResult *result,GError **error) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(g_task_is_valid(result,transaction),FALSE); + return g_task_propagate_boolean(G_TASK(result),error); +} + +static int plover_mark_package_for_update(struct razor_transaction *trans, + struct razor_set *set,const char *pkg) +{ + struct razor_package_iterator *pi; + struct razor_package *package; + const char *name; + int retval=-1; + pi=razor_package_iterator_create(set); + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST)) + { + if (!strcmp(name,pkg)) + { + razor_transaction_update_package(trans,package); + retval=0; + break; + } + } + razor_package_iterator_destroy(pi); + return retval; +} + +PloverTransaction *plover_transaction_new(void) +{ + return PLOVER_TRANSACTION(g_object_new(PLOVER_TYPE_TRANSACTION,NULL)); +} + +void plover_transaction_set_prefix(PloverTransaction *transaction, + const char *prefix) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION(transaction)); + g_return_if_fail(transaction->upstream == NULL); + if (!g_strcmp0(prefix,transaction->prefix)) + return; + if (transaction->relocations) + razor_relocations_destroy(transaction->relocations); + g_free(transaction->prefix); + if (prefix) + { + transaction->relocations=razor_relocations_create(); + razor_relocations_add(transaction->relocations,"/usr",prefix); + } + else + transaction->relocations=NULL; + transaction->prefix=g_strdup(prefix); +} + +void plover_transaction_set_installed(PloverTransaction *transaction, + PloverPackageSet *installed) +{ + g_return_if_fail(PLOVER_IS_TRANSACTION(transaction)); + g_return_if_fail(PLOVER_IS_PACKAGE_SET(installed)); + if (transaction->installed) + g_object_unref(transaction->installed); + transaction->installed=g_object_ref(installed); +} + +gboolean plover_transaction_root_open(PloverTransaction *transaction, + const char *install_root,GError **error) +{ + PloverPackageSet *installed; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + if (!install_root) + install_root=g_getenv("RAZOR_ROOT"); + if (!install_root) + install_root=""; + if (transaction->installed && !g_strcmp0(install_root, + plover_package_set_get_install_root(transaction->installed))) + return TRUE; + installed=plover_package_set_new(); + if (!plover_package_set_open(installed,install_root,TRUE,error)) + { + g_object_unref(installed); + return FALSE; + } + if (transaction->installed) + g_object_unref(transaction->installed); + transaction->installed=installed; + return TRUE; +} + +struct razor_set *plover_transaction_import_yum(PloverTransaction *transaction, + const char *base,GError **error) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),NULL); + g_return_val_if_fail(transaction->base == NULL,NULL); + transaction->base=g_strdup(base); + return plover_razor_set_create_from_yum(base,error); +} + +gboolean plover_transaction_set_upstream(PloverTransaction *transaction, + PloverRepository *upstream,GError **error) +{ + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(PLOVER_IS_REPOSITORY(upstream),FALSE); + g_return_val_if_fail(transaction->upstream == NULL,FALSE); + transaction->relocated=plover_package_set_new_from_repository(upstream, + transaction->relocations,error); + if (transaction->relocated) + transaction->upstream=g_object_ref(upstream); + return !!transaction->relocated; +} + +gboolean + plover_transaction_set_upstream_from_yum(PloverTransaction *transaction, + const char *base,GError **error) +{ + gboolean retval; + PloverRepository *upstream; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(transaction->upstream == NULL,FALSE); + upstream=plover_repository_new_from_yum(base,error); + if (!upstream) + return FALSE; + retval=plover_transaction_set_upstream(transaction,upstream,error); + g_object_unref(upstream); + return retval; +} + +gboolean plover_transaction_install(PloverTransaction *transaction, + char **pkgs,GError **error) +{ + int i; + struct razor_set *system,*upstream; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(transaction->upstream != NULL,FALSE); + g_return_val_if_fail(transaction->trans == NULL,FALSE); + if (!plover_transaction_root_open(transaction,NULL,error)) + return FALSE; + system=plover_transaction_get_system_set(transaction); + if (!system) + { + g_set_error(error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error: No system set"); + return FALSE; + } + upstream=plover_package_set_get_razor(transaction->relocated); + transaction->trans=razor_transaction_create(system,upstream); + for(i=0;pkgs[i];i++) + if (plover_mark_package_for_update(transaction->trans,system,pkgs[i]) && + plover_mark_package_for_update(transaction->trans,upstream,pkgs[i])) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE,"%s: %s",pkgs[i], + "Package not found"); + razor_transaction_destroy(transaction->trans); + transaction->trans=NULL; + return FALSE; + break; + } + return TRUE; +} + +gboolean plover_transaction_update(PloverTransaction *transaction, + char **pkgs,GError **error) +{ + int i; + struct razor_set *system,*upstream; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(transaction->upstream != NULL,FALSE); + g_return_val_if_fail(transaction->trans == NULL,FALSE); + if (!plover_transaction_root_open(transaction,NULL,error)) + return FALSE; + system=plover_transaction_get_system_set(transaction); + if (!system) + { + g_set_error(error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error: No system set"); + return FALSE; + } + upstream=plover_package_set_get_razor(transaction->relocated); + transaction->trans=razor_transaction_create(system,upstream); + if (!pkgs) + razor_transaction_update_all(transaction->trans); + else + for(i=0;pkgs[i];i++) + if (plover_mark_package_for_update(transaction->trans,system, + pkgs[i])) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE,"%s: %s",pkgs[i], + "Package not found"); + razor_transaction_destroy(transaction->trans); + transaction->trans=NULL; + return FALSE; + break; + } + return TRUE; +} + +PloverTransaction *plover_transaction_new_install(const char *base, + const char *prefix,char **pkgs,GError **error) +{ + PloverTransaction *transaction; + transaction=plover_transaction_new(); + plover_transaction_set_prefix(transaction,prefix); + if (!plover_transaction_set_upstream_from_yum(transaction,base,error)) + { + g_object_unref(transaction); + return NULL; + } + if (!plover_transaction_install(transaction,pkgs,error)) + { + g_object_unref(transaction); + return NULL; + } + return transaction; +} + +PloverTransaction *plover_transaction_new_update(const char *base, + const char *prefix,char **pkgs,GError **error) +{ + PloverTransaction *transaction; + transaction=plover_transaction_new(); + plover_transaction_set_prefix(transaction,prefix); + if (!plover_transaction_set_upstream_from_yum(transaction,base,error)) + { + g_object_unref(transaction); + return NULL; + } + if (!plover_transaction_update(transaction,pkgs,error)) + { + g_object_unref(transaction); + return NULL; + } + return transaction; +} + +static int plover_mark_packages_for_removal(struct razor_transaction *trans, + struct razor_set *set,const char *pkg) +{ + struct razor_package_iterator *pi; + struct razor_package *package; + const char *name; + int retval=pkg?-1:0; + pi=razor_package_iterator_create(set); + while (razor_package_iterator_next(pi,&package,RAZOR_DETAIL_NAME,&name, + RAZOR_DETAIL_LAST)) + { + if (!pkg || !strcmp(name,pkg)) + { + razor_transaction_remove_package(trans,package); + retval=0; + } + } + razor_package_iterator_destroy(pi); + return retval; +} + +gboolean plover_transaction_remove(PloverTransaction *transaction, + char **pkgs,GError **error) +{ + int i; + struct razor_set *system,*empty; + g_return_val_if_fail(PLOVER_IS_TRANSACTION(transaction),FALSE); + g_return_val_if_fail(transaction->trans == NULL,FALSE); + if (!plover_transaction_root_open(transaction,NULL,error)) + return FALSE; + system=plover_transaction_get_system_set(transaction); + if (!system) + { + g_set_error(error,PLOVER_GENERAL_ERROR,PLOVER_GENERAL_ERROR_FAILED, + "Internal error: No system set"); + return FALSE; + } + empty=razor_set_create_without_root(); + transaction->trans=razor_transaction_create(system,empty); + razor_set_unref(empty); + if (!pkgs) + plover_mark_packages_for_removal(transaction->trans,system,NULL); + else + for(i=0;pkgs[i];i++) + if (plover_mark_packages_for_removal(transaction->trans,system, + pkgs[i])) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE,"%s: %s",pkgs[i], + "Package not found"); + razor_transaction_destroy(transaction->trans); + transaction->trans=NULL; + return FALSE; + break; + } + return TRUE; +} + +PloverTransaction *plover_transaction_new_remove(char **pkgs,GError **error) +{ + PloverTransaction *transaction; + transaction=plover_transaction_new(); + if (!plover_transaction_remove(transaction,pkgs,error)) + { + g_object_unref(transaction); + return NULL; + } + return transaction; +} + +static GList *plover_what_requires(struct razor_set *set,const char *ref_name) +{ + const char *name,*version; + uint32_t flags; + GList *list=NULL; + struct razor_property *property; + struct razor_package *package; + struct razor_package_iterator *what_requires; + struct razor_property_iterator *all_props; + all_props=razor_property_iterator_create(set,NULL); + while (razor_property_iterator_next(all_props,&property,&name,&flags, + &version)) + { + if ((flags&RAZOR_PROPERTY_TYPE_MASK)!=RAZOR_PROPERTY_REQUIRES) + continue; + if (strcmp(name,ref_name)) + continue; + what_requires=razor_package_iterator_create_for_property(set,property); + while(razor_package_iterator_next(what_requires,&package, + RAZOR_DETAIL_LAST)) + list=g_list_prepend(list,package); + razor_package_iterator_destroy(what_requires); + } + razor_property_iterator_destroy(all_props); + return list; +} + +/* + * Warning: This code is untested and probably wrong. + */ +PloverTransaction *plover_transaction_new_remove_with_leaves(char **pkgs, + GError **error) +{ + int i,changed,is_leaf; + uint32_t flags; + const char *install_root; + const char *name,*version,*maybe_unused_name; + struct razor_set *system,*upstream; + struct razor_transaction *trans; + PloverPackageSet *installed; + PloverTransaction *transaction; + if (!pkgs) + return plover_transaction_new_remove(NULL,error); + installed=plover_package_set_new(); + install_root=g_getenv("RAZOR_ROOT"); + if (!install_root) + install_root=""; + if (!plover_package_set_open(installed,install_root,TRUE,error)) + { + g_object_unref(installed); + return NULL; + } + system=plover_package_set_get_razor(installed); + struct plover_vector *package_names; + GList *to_remove,*lnk,*what_requires; + struct razor_package *package,*maybe_unused_package; + struct razor_property *property; + struct razor_package_query *query; + struct razor_package_iterator *all_packages,*removed,*maybe_unused; + struct razor_property_iterator *removed_props; + package_names=plover_vector_new(); + for(i=0;pkgs[i];i++) + plover_vector_append(package_names,pkgs[i]); + to_remove=NULL; + all_packages=razor_package_iterator_create(system); + while (razor_package_iterator_next(all_packages,&package, + RAZOR_DETAIL_NAME,&name,RAZOR_DETAIL_LAST)) + if (plover_vector_remove(package_names,name)) + to_remove=g_list_prepend(to_remove,package); + razor_package_iterator_destroy(all_packages); + if (package_names->len) + { + g_set_error(error,PLOVER_GENERAL_ERROR, + PLOVER_GENERAL_ERROR_NO_SUCH_PACKAGE,"%s: %s", + package_names->strings[0],"Package not found"); + plover_vector_free(package_names); + g_list_free(to_remove); + g_object_unref(installed); + return NULL; + } + plover_vector_free(package_names); + do + { + changed=FALSE; + query=razor_package_query_create(system); + for(lnk=to_remove;lnk;lnk=lnk->next) + razor_package_query_add_package(query,lnk->data); + removed=razor_package_query_finish(query); + while(razor_package_iterator_next(removed,&package,RAZOR_DETAIL_LAST)) + { + removed_props=razor_property_iterator_create(system,package); + while (razor_property_iterator_next(removed_props,&property,&name, + &flags,&version)) + { + if ((flags&RAZOR_PROPERTY_TYPE_MASK)!=RAZOR_PROPERTY_REQUIRES) + continue; + maybe_unused=razor_package_iterator_create_for_property(system, + property); + while(razor_package_iterator_next(maybe_unused, + &maybe_unused_package,RAZOR_DETAIL_NAME,&maybe_unused_name, + RAZOR_DETAIL_LAST)) + { + if (g_list_find(to_remove,maybe_unused_package)) + continue; + is_leaf=TRUE; + what_requires=plover_what_requires(system, + maybe_unused_name); + for(lnk=what_requires;lnk;lnk=lnk->next) + if (!g_list_find(to_remove,lnk->data)) + { + is_leaf=FALSE; + break; + } + g_list_free(what_requires); + if (is_leaf) + { + to_remove=g_list_prepend(to_remove, + maybe_unused_package); + changed=TRUE; + } + } + razor_package_iterator_destroy(maybe_unused); + } + razor_property_iterator_destroy(removed_props); + } + razor_package_iterator_destroy(removed); + } while(changed); + upstream=razor_set_create_without_root(); + trans=razor_transaction_create(system,upstream); + razor_set_unref(upstream); + for(lnk=to_remove;lnk;lnk=lnk->next) + razor_transaction_remove_package(trans,lnk->data); + transaction=plover_transaction_new(); + transaction->trans=trans; + transaction->installed=installed; + return transaction; +} diff --git a/plover/transaction.h b/plover/transaction.h new file mode 100644 index 0000000..8fa61f0 --- /dev/null +++ b/plover/transaction.h @@ -0,0 +1,87 @@ +#ifndef __PLOVER_TRANSACTION_H__ +#define __PLOVER_TRANSACTION_H__ + +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define PLOVER_TYPE_TRANSACTION plover_transaction_get_type() +#define PLOVER_TRANSACTION(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,\ + PLOVER_TYPE_TRANSACTION,\ + PloverTransaction) +#define PLOVER_IS_TRANSACTION(obj) G_TYPE_CHECK_INSTANCE_TYPE(obj,\ + PLOVER_TYPE_TRANSACTION) + +typedef enum { + PLOVER_TRANSACTION_RESOLVED=1<<0, + PLOVER_TRANSACTION_UNSATISFIED=1<<1, +} PloverTransactionFlags; + +typedef struct _PloverTransaction { + GObject parent_instance; + PloverTransactionFlags flags; + struct razor_transaction *trans; + char *prefix; + char *base; + char *install_root; + char *unsatisfied; + PloverPackageSet *installed,*relocated; + PloverRepository *upstream; + struct razor_relocations *relocations; + struct razor_set *next,*system; + struct razor_install_iterator *install_iterator; +} PloverTransaction; + +typedef struct _PloverTransactionClass { + GObjectClass parent_class; +} PloverTransactionClass; + +GType plover_transaction_get_type(void) G_GNUC_CONST; +void plover_transaction_commit_async(PloverTransaction *transaction, + GCancellable *cancellable,GAsyncReadyCallback callback,gpointer user_data); +gboolean plover_transaction_commit_finish(PloverTransaction *transaction, + GAsyncResult *result,GError **error); +PloverTransaction *plover_transaction_new(); +void plover_transaction_set_prefix(PloverTransaction *transaction, + const char *prefix); +void plover_transaction_set_installed(PloverTransaction *transaction, + PloverPackageSet *installed); +gboolean plover_transaction_root_open(PloverTransaction *transaction, + const char *install_root,GError **error); +struct razor_set *plover_transaction_import_yum(PloverTransaction *transaction, + const char *base,GError **error); +gboolean plover_transaction_set_upstream(PloverTransaction *transaction, + PloverRepository *upstream,GError **error); +gboolean + plover_transaction_set_upstream_from_yum(PloverTransaction *transaction, + const char *base,GError **error); +gboolean plover_transaction_install(PloverTransaction *transaction, + char **pkgs,GError **error); +PloverTransaction *plover_transaction_new_install(const char *base, + const char *prefix,char **pkgs,GError **error); +gboolean plover_transaction_update(PloverTransaction *transaction, + char **pkgs,GError **error); +PloverTransaction *plover_transaction_new_update(const char *base, + const char *prefix,char **pkgs,GError **error); +gboolean plover_transaction_remove(PloverTransaction *transaction, + char **pkgs,GError **error); +PloverTransaction *plover_transaction_new_remove(char **pkgs, + GError **error); +gboolean plover_transaction_resolve(PloverTransaction *transaction, + GError **error); +const char *plover_transaction_get_unsatisfied(PloverTransaction *transaction); +struct razor_set *plover_transaction_get_system_set( + PloverTransaction *transaction); +struct razor_set *plover_transaction_get_next_set( + PloverTransaction *transaction,GError **error); +struct razor_install_iterator *plover_transaction_get_install_iterator( + PloverTransaction *transaction,GError **error); +gboolean plover_transaction_commit(PloverTransaction *transaction, + GCancellable *cancellable,GError **error); +void plover_transaction_commit_async(PloverTransaction *transaction, + GCancellable *cancellable,GAsyncReadyCallback callback,gpointer user_data); + +#endif /* __PLOVER_TRANSACTION_H__ */ diff --git a/plover/util.c b/plover/util.c index 6a65e55..6678d27 100644 --- a/plover/util.c +++ b/plover/util.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009, 2011 J. Ali Harlow + * Copyright (C) 2009, 2011, 2014 J. Ali Harlow * * 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 @@ -27,37 +27,7 @@ #include "config.h" #include "plover.h" -char *plover_strconcat(const char *string,...) -{ - va_list ap,aq; - size_t n; - char *result=NULL,*t; - const char *s; - if (string) - { - va_start(ap,string); - va_copy(aq,ap); - n=strlen(string); - while((s=va_arg(aq,const char *))) - n+=strlen(s); - va_end(aq); - result=malloc(n+1); - if (result) - { - strcpy(result,string); - t=result+strlen(result); - while((s=va_arg(ap,const char *))) - { - strcpy(t,s); - t+=strlen(t); - } - } - va_end(ap); - } - return result; -} - -char *plover_default_prefix_for_vendor(const char *vendor) +gchar *plover_default_prefix_for_vendor(const char *vendor) { #ifdef WIN32 /* @@ -83,12 +53,59 @@ char *plover_default_prefix_for_vendor(const char *vendor) buf); program_files=buf; } - return plover_strconcat(program_files,"\\",vendor?vendor:"Plover",NULL); + return g_strconcat(program_files,"\\",vendor?vendor:"Plover",NULL); #else return NULL; #endif } +gchar *plover_pre_install_prefix(void) +{ +#ifdef WIN32 + { + HRESULT result; + HKEY key; + DWORD type,nb; + int len; + gunichar2 *str2; + gchar *root=NULL; + result=RegOpenKeyW(HKEY_LOCAL_MACHINE,L"Software\\Plover",&key); + if (result==ERROR_SUCCESS) + { + result=RegQueryValueExW(key,L"Root",0,&type,0,&nb); + if (result==ERROR_SUCCESS && type==REG_SZ) + { + str2=malloc(nb); + result=RegQueryValueExW(key,L"Root",0,NULL,(void *)str2,&nb); + len=nb/2; + if (!str2[len-1]) /* Cope with unterminated strings */ + len--; + root=g_utf16_to_utf8(str2,len,NULL,NULL,NULL); + free(str2); + } + RegCloseKey(key); + } + if (!root) + { + root=plover_default_prefix_for_vendor("Plover Root"); + result=RegCreateKeyExW(HKEY_LOCAL_MACHINE,L"Software\\Plover",0, + NULL,REG_OPTION_NON_VOLATILE,KEY_READ|KEY_WRITE,NULL,&key,NULL); + if (result==ERROR_SUCCESS) + { + str2=g_utf8_to_utf16(root,-1,NULL,NULL,NULL); + RegSetValueExW(key,L"Root",0,REG_SZ,(void *)str2, + (strlen(root)+1)*sizeof(gunichar2)); + g_free(str2); + RegCloseKey(key); + } + } + return root; + } +#else + return g_strdup("/var/lib/plover/root"); +#endif +} + /* * Get the directory containing the program executable. */ @@ -119,3 +136,80 @@ char *plover_get_program_directory(const char *argv0) return strdup("."); #endif } + +G_DEFINE_QUARK(plover-razor-error-quark,plover_razor_error) +G_DEFINE_QUARK(plover-mswin-error-quark,plover_mswin_error) +G_DEFINE_QUARK(plover-posix-error-quark,plover_posix_error) +G_DEFINE_QUARK(plover-zlib-error-quark,plover_zlib_error) + +void plover_propagate_razor_error_dup(GError **dest,struct razor_error *src) +{ + GQuark domain; + int code; + if (dest) + { + code=razor_error_get_code(src); + switch(razor_error_get_domain(src)) + { + case RAZOR_GENERAL_ERROR: + domain=PLOVER_RAZOR_ERROR; + break; + case RAZOR_POSIX_ERROR: + domain=PLOVER_POSIX_ERROR; + break; + case RAZOR_MSWIN_ERROR: + domain=PLOVER_MSWIN_ERROR; + break; + case RAZOR_ZLIB_ERROR: + domain=PLOVER_ZLIB_ERROR; + break; + case PLOVER_GENERAL_ERROR: + if (code==PLOVER_GENERAL_ERROR_CANCELLED) + { + domain=G_IO_ERROR; + code=G_IO_ERROR_CANCELLED; + break; + } + /* else fall though */ + default: + domain=PLOVER_RAZOR_ERROR; + code=RAZOR_GENERAL_ERROR_FAILED; + } + *dest=g_error_new_literal(domain,code,razor_error_get_msg(src)); + } +} + +void plover_propagate_razor_error(GError **dest,struct razor_error *src) +{ + plover_propagate_razor_error_dup(dest,src); + razor_error_free(src); +} + +void plover_propagate_g_error(struct razor_error **dest,GError *src) +{ + int domain,code; + if (dest) + { + code=src->code; + if (src->domain==PLOVER_RAZOR_ERROR) + domain=RAZOR_GENERAL_ERROR; + else if (src->domain==PLOVER_POSIX_ERROR) + domain=RAZOR_POSIX_ERROR; + else if (src->domain==PLOVER_MSWIN_ERROR) + domain=RAZOR_MSWIN_ERROR; + else if (src->domain==PLOVER_ZLIB_ERROR) + domain=RAZOR_ZLIB_ERROR; + else if (src->domain==G_IO_ERROR && code==G_IO_ERROR_CANCELLED) + { + domain=PLOVER_GENERAL_ERROR; + code=PLOVER_GENERAL_ERROR_CANCELLED; + } + else + { + domain=RAZOR_GENERAL_ERROR; + code=RAZOR_GENERAL_ERROR_FAILED; + } + *dest=razor_error_new_str(domain,code,NULL,src->message); + } + g_error_free(src); +} diff --git a/plover/vector.c b/plover/vector.c new file mode 100644 index 0000000..cefced6 --- /dev/null +++ b/plover/vector.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009, 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "config.h" +#include "plover.h" + +struct plover_vector *plover_vector_new(void) +{ + struct plover_vector *vector; + vector=malloc(sizeof(*vector)); + vector->len=0; + vector->alloc=16; + vector->strings=calloc(vector->alloc,sizeof(char *)); + return vector; +} + +struct plover_vector *plover_vector_dup(struct plover_vector *old) +{ + int i; + struct plover_vector *vector; + vector=malloc(sizeof(*vector)); + vector->len=old->len; + vector->alloc=old->alloc; + vector->strings=calloc(vector->alloc,sizeof(char *)); + for(i=0;ilen;i++) + vector->strings[i]=strdup(old->strings[i]); + vector->strings[i]=NULL; + return vector; +} + +void plover_vector_append(struct plover_vector *vector,const char *str) +{ + if (++(vector->len)>=vector->alloc) + { + vector->alloc*=2; + vector->strings=realloc(vector->strings,vector->alloc*sizeof(char *)); + } + vector->strings[vector->len-1]=strdup(str); + vector->strings[vector->len]=NULL; +} + +int plover_vector_contains(struct plover_vector *vector,const char *str) +{ + int i; + for(i=0;ilen;i++) + if (!strcmp(vector->strings[i],str)) + return 1; + return 0; +} + +int plover_vector_remove(struct plover_vector *vector,const char *str) +{ + int i; + for(i=0;ilen;i++) + if (!strcmp(vector->strings[i],str)) + { + free(vector->strings[i]); + vector->len--; + vector->strings[i]=vector->strings[vector->len]; + vector->strings[vector->len]=NULL; + return 1; + } + return 0; +} + +static int string_compar(const void *p1,const void *p2) +{ + return strcmp(*(char * const *)p1,*(char * const *)p2); +} + +void plover_vector_sort(struct plover_vector *vector) +{ + qsort(vector->strings,vector->len,sizeof(char *),string_compar); +} + +char *plover_vector_format_for_display(struct plover_vector *vector) +{ + int i,len; + char *s,*p; + if (!vector->len) + return strdup("none"); + else if (vector->len==1) + return strdup(vector->strings[0]); + else + { + len=(vector->len-1)*2+5+1; + for(i=0;ilen;i++) + len+=strlen(vector->strings[i]); + s=malloc(len); + strcpy(s,vector->strings[0]); + p=s+strlen(s); + for(i=1;ilen-1;i++) + { + *p++=','; + *p++=' '; + strcpy(p,vector->strings[i]); + p+=strlen(p); + } + strcpy(p," and "); + p+=5; + strcpy(p,vector->strings[i]); + return s; + } +} + +void plover_vector_free(struct plover_vector *vector) +{ + int i; + for(i=0;ilen;i++) + free(vector->strings[i]); + free(vector->strings); + free(vector); +} diff --git a/pre-inst/Makefile.am b/pre-inst/Makefile.am new file mode 100644 index 0000000..658ea74 --- /dev/null +++ b/pre-inst/Makefile.am @@ -0,0 +1,32 @@ +AM_CFLAGS=-g $(SETUP_CFLAGS) +LDADD=../plover/libplover.la $(SETUP_LIBS) +if PLOVER_MINGW +LDADD+=-lcomctl32 +# pre-inst is not intended to produce any output (and any that is produced +# can be safely junked). Thus -mwindows is appropriate since if called with +# a console window attached no output should be visible anyway and if called +# when no console window is attached we don't want one to be created. +AM_LDFLAGS=-mwindows +endif +INCLUDES=-I$(top_srcdir) -I$(srcdir) + +bin_PROGRAMS=pre-inst + +pre_inst_SOURCES=pre-inst.c +pre_inst_LDFLAGS=$(AM_LDFLAGS) -all-static +if HAVE_WINDRES +pre_inst_SOURCES+=resources.rc pre-inst.exe.manifest resource.h +endif + +.rc.$(OBJEXT): + $(AM_V_GEN)$(WINDRES) $(INCLUDES) $< $@ + +resources.$(OBJEXT): resources.rc resource.h pre-inst.exe.manifest pre-inst.ico + +.png.pnm: + $(AM_V_GEN)pngtopnm $< | pnmquant -quiet 256 > $@ + +pre-inst.ico: icon16.pnm icon22.pnm icon32.pnm + $(AM_V_GEN)ppmtowinicon -output=$@ $^ + +EXTRA_DIST=icon16.png icon22.png icon32.png diff --git a/pre-inst/icon16.png b/pre-inst/icon16.png new file mode 100644 index 0000000000000000000000000000000000000000..0ca1590b23eaa46ea87ddfedf1433834424c2f74 GIT binary patch literal 821 zcmV-51Iqk~P)3dcc~(o? z#Sj*zlqfriR2D*5m_j0zgQH!9w1AjzW~bUj!D)$UzIf#Er?uhs@#Xv+e>VaEVq$ia zIA*tZOMh}^fkz6@C4M>o_1i}2bvG75+#EgjL2M~)-F1cCSpV^?|JUY4TLQSf_r3Ph z!aJqG{sZVl8av@)>lDszwG%F;DQ=rrzCRboLQP?qZ_1Nrc8t=gE00DZ#qZ{0w8#^9 z3f+x3*xBZRdpuMW!G8eRzI%;oC2x#oxj*ilE0HT#}1M~{wkIG@LH9J<{uzm?0J zTl$vlpCzo|`W-8nruhPJ+9(!_X{A(kVqzj~+cvdY4XrhqOontiO|#i#{_GqNTg#Y4 zBnKYk(n^WR*}4H>Dy62!#>P^%ZBwaK7#QfIw>OKFCZ6XZgy5l(CurM;80^n5IB?+3 zNrVuS`F!5+Jda%NeyY`Ld^%rZ^XgTCz^7iXlgs6});)-&G(uQ=7rg}tf}lrhjqAEt zmSpw9D&vP?dF5v;%c9+GW11%IK$099;riBhfb-=Xkifge_v zIXXH@r{fa@0j*XG<7SKRKAVk%L;^Ly$Xf#MBlXN;=wzg_itAC)iW_x>^jg5^) z7=|D3_mN_0Y3W9xP`I$Rw)S!&k+8DaERN$41Ob&wC0<`&_rfq7pP8B2+z(*viDI!h z>^RQ5hG9G(1VLI#Sr5bTE8xTF>FKTg%mDua(wQ7yN$@|=00000NkvXXu0mjfqMeBn literal 0 HcmV?d00001 diff --git a/pre-inst/icon22.png b/pre-inst/icon22.png new file mode 100644 index 0000000000000000000000000000000000000000..4b7f82c16514a5466bdbabfcea70fbab6b363a04 GIT binary patch literal 1237 zcmV;`1S|y7{`D2%Q-veInSQ$@fDXc+jLtxM@Ct`REU*YffYgp zVHwfr1q9KeurKx@kf6TE%D`DfFJ!HVN?DSkMGJ{6&BkVP>fEO8+2->+dtT1D&*_8P znxceX`on!+7Z*4G>(BLa3r5lWthYc)`Ku5j2*WVqI$_zaWrD(!>zXcp1Rwx*J)d}7 zOZ&a5g{!>8luCq>NU5<)T!;h^A*Dhpjg$%@6;dgL)JQez3pnuc)-az*H5W4%UKGuZ z-n~+)J^$(VADim$tld zz2yZUJ$QkE^QZE;bM4v>{A%&RbBWF{467$s)HEw?J+)}rN)Z-@ua8yQbaotPaG?J+ zQy5v2I@i5gLv&gS)qJ71pvBu0m0wfyf|D zh>Q%{goKeHo-mkb?^1yE?_TT>W6E|l#-kBu4ntEPrhdWAR8?0~R#t}A8sGQnIdg_X z2M^%&eoid+*EOpW=wS9U+waiJ1m)22*Y$jkc)D93R&HZ(LO9LK>JLuqL#$z(ZY zWeGelhSrKO4C(9ZLkK~`!rMsMwM;6HF{#WY5ig^lYuGs}n&G zkV>VPGI=VGJhYY|2pAX`AeBm?wZ?HAPM$nT)O(P9?FRu739Z|QbN-`%nMfRZp7&O3 zYwPB!s;b1;SoywBI-Mbx%aP0F@qHhyHQ8*Ij*gCW*YK>W9Xm3EKsd1kV^9FCt*w4d zP0imYPMla%S68Pz&m)SW@gs!5vMjRMEQb#t_WSz!8nox+tZ{WHNMhbwx*y9`!prJ6~yPYH9&;S5rC$ab;k`h7Hq`$>c`Y zbssAhixs1#>C5GEpXT%Vx3_KEcIH}S{|o*B32ON&%A*i300000NkvXXu0mjfwg6F_ literal 0 HcmV?d00001 diff --git a/pre-inst/icon32.png b/pre-inst/icon32.png new file mode 100644 index 0000000000000000000000000000000000000000..361cd5113f8cf4518087c862e1ff18d1c9088fbf GIT binary patch literal 2005 zcmV;`2P*i9P)B^3n4~8O*X`YQb=lSWm77&+il$1eV^^t%^Z(!f^PJ~A@P9V3bl~MjGJCAx@W%nB+!Q7%Z-4N` z;;9u09Qa<_6OMA9S+k+L#m{zt1uFznNDu-cB%=022!yQl5w&>%O=}u`DF8{OFwVJ; z-YF$<^w>i$%)Znh@XH_IwbTBRpSt4iTfh2~^xy}FIMH{Ea(NyAYcGkSyYGG6b$uVDT#Auke!*NCbOp4F zpoAgDV zD2=SmIgUcA+PI?;QnJ)8jGQ*BT78-FJmCoEdR1j=HhRk8pQn!|=cg{rmkLvDci#J* zWgo9>7Km!CW=X9rS(W=&iPGHEnc>qz*R0O!G_XZgbuk_122Vz_vojOQOYd)A+xa{Q zVS>VCl4t_57Gn~uNia!*F$pG#k$xK&hW}0+CQsssttUVHV4t;SvjT|YWZlg4^hXkQ z$3f{A>#o`+Rt)P)0940JiP?Ymd4+ejQ1i7Tb1&EiV}EGN@6W)hTN#`UvEDenZH`ue`y81TfRMD&|) zXWPA$oZNHYldi>JjbVJ+FqD^EC@M;UAu$Hub!bajI@3iuvjJLEL9qs74K_)z)+{@U z##s7~y;&aWC}w(kFaWA~f!20c62$LN zQG^fz*L6vyQh1(+@B0Knz~RI1aIXIt&g3D4iI#6DKska{jPua1iinlbYa4)CYvMS@aU8-hB$Y~W&pltFZ~c1y_O4>}+}n#XQXNf7iJ4o@ zZ4rTlg3({1*Vo<#GriNTpIZj)PJPDJ72MAf-e~+3@p?H{Qh9=qRP3JRPQg znYK!~oc~V*JkNV!*REYHnM{UUE{E0{Yb{DCT-U|(JUq{Bkf{?X7K?;o$nHJ2^IC3< zZQcZ)2o?c3ZVTs!FF~NEr{`L&^(|dpUF7q5JkM(o$Yxu~WHJrVx)M@K^7%YL5TKMI zolbN84WDNG)EJ*i|6`F0l;6_qm4=TR$y!SJmG16tPfCeWik6lZ(&;qq?d@bT863x9 zVPS!}xp_b!1myF13WWl39FrsoK@hNI>sE5%x&}cZ9fWjnwAcL8vd*ocfY$o14I4H{ zAq2kfH+<{+9@bh;o&1Q>;GalQ7mB_3@nAi4X#L zkw6-#P`x~ox&lH)V+<`VDJs@SI!?7oiBx`_KzogZYF0p4YaQ2h(OLubg2L9+u{hEorBl^ViH0kU zu?+TEWP5TZfA7=!D&=+51YP83P&3d8nB zZr!q(N~J=jQYMa*dK?i&5v5X@bX(FOj1WjigB2EN1?HA2KoA6fnVOoqdF|S@i%xJH zM-&PLZrHJtxy@UM!jRjpTuY@=CJaMLrE)dcguocX{QNvp!m3Yft43KZ<|ju^+EU`2 zt8Lpj1$6>8pU=O3{P^+j@7S@!uPcxw2{SXZRapgOG8wG36pQl&LBPz+EY@1I*3^S{ zcz75!`yq$lcA$ex3?3=F$)U| zjcgZ&5!M=_D5g@WFgG{XIR8QjwAO@S$iTor<&8r__xJS`b4@~;8ekbH)>^oT01%bS z<%eH=_0?mKKKf{?8f>bOjI6Z;LC}bFtu@9NqA0>~9E1?`_xC4Bk{syk>wA;SG^e@q z-h1!O?cBLjjEs!j(%s$dxUSm(Z@$}`$LiT}WMsq+4Gj(T_Vzw7F)>m4ZwP20bL7a8 zGhJO>R}2mgZr`|Zqm#{Mmwi-CKoZw=iQ|}ofdMl*Iy%}ajWi^bv_ + + + Plover pre-inst program + + + + + + + + + + + + + + + + + + + diff --git a/pre-inst/pre-inst.c b/pre-inst/pre-inst.c new file mode 100644 index 0000000..52a4ded --- /dev/null +++ b/pre-inst/pre-inst.c @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2014 J. Ali Harlow + * + * 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 of the License, 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * References: + * http://www.transmissionzero.co.uk/computing/win32-apps-with-mingw/ + */ + +#include "config.h" +#ifndef WIN32 +#define _XOPEN_SOURCE 500 +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#include "resource.h" + +#ifndef FOF_NO_UI +#define FOF_NO_UI (FOF_SILENT|FOF_NOCONFIRMATION|FOF_NOERRORUI|\ + FOF_NOCONFIRMMKDIR) +#endif + +#else /* WIN32 */ +#include +#endif /* WIN32 */ + +#ifdef WIN32 +/* Under WIN32, g_spawn requires a helper program which we'd rather avoid */ +#undef USE_G_SPAWN +#else +#define USE_G_SPAWN +#endif + +LUALIB_API int luaopen_posix(lua_State *L); + +#ifdef WIN32 +DWORD main_thread_id; +#endif + +gchar *prefix; + +int verify_and_fix(const char *root) +{ + return 0; +} + +#ifdef WIN32 +INT_PTR CALLBACK ProgressDialogProc(HWND dialog,UINT msg,WPARAM w_param, + LPARAM l_param) +{ + HWND progress; + DWORD style; + switch (msg) + { + case WM_INITDIALOG: + progress=GetDlgItem(dialog,IDC_PROGRESS); + style=GetWindowLong(progress,GWL_STYLE); + SetWindowLong(progress,GWL_STYLE,style|PBS_MARQUEE); + SendMessage(progress,PBM_SETMARQUEE,(WPARAM)TRUE,(LPARAM)30); + return (INT_PTR)TRUE; + } + return (INT_PTR)FALSE; +} +#endif + +#ifdef WIN32 +__stdcall +#endif +unsigned pre_install_thread(void *data) +{ + int retval; + char *path=data; + gchar *s; + char *install[]={"plover-gtkui",NULL}; + GError *error=NULL; + prefix=plover_pre_install_prefix(); + s=g_strconcat(prefix,"/var/log/pre-install",NULL); + plover_log_open(s); + g_free(s); + s=g_strconcat(prefix,"/var/lib/razor",NULL); + razor_set_database_path(s); + g_free(s); + if (verify_and_fix(prefix)) + { + free(path); + g_free(prefix); + return -1; + } + retval=!plover_install(path,prefix,install,&error); + if (!retval) + retval=!plover_update(path,prefix,NULL,&error); + if (error) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + } + free(path); +#ifdef WIN32 + PostQuitMessage(retval); + PostThreadMessage(main_thread_id,WM_QUIT,retval,0); + _endthreadex(retval); +#endif + return retval; +} + +/* + * The idea is that if pre_install() fails, update/setup should fall back + * to console interfaces. + */ +#ifdef WIN32 +HANDLE +#else +void * +#endif +pre_install(const char *argv0) +{ +#ifdef WIN32 + HANDLE retval; +#else + void *retval; +#endif + char *path; + razor_set_lua_loader("posix",luaopen_posix); + razor_set_lua_loader("whelk",luaopen_whelk); + path=plover_get_program_directory(argv0); +#ifdef WIN32 + retval=(HANDLE)_beginthreadex(NULL,0,pre_install_thread,path,0,NULL); +#else + if (pre_install_thread(path)) + retval=NULL; + else + retval=(void *)1; /* Non-NULL to indicate success */ +#endif + return retval; +} + +#ifndef WIN32 +int remove_ignore(const char *fpath,const struct stat *sb,int typeflag, + struct FTW *ftwbuf) +{ + (void)remove(fpath); + return 0; +} +#endif + +gboolean deltree(const char *path) +{ +#ifdef WIN32 + /* Based on g_local_file_trash() */ + SHFILEOPSTRUCTW op={0}; + gboolean success; + wchar_t *wfilename; + long len; + wfilename=g_utf8_to_utf16(path,-1,NULL,&len,NULL); + /* SHFILEOPSTRUCT.pFrom is double-zero-terminated */ + wfilename=g_renew(wchar_t,wfilename,len+2); + wfilename[len+1]=0; + op.wFunc=FO_DELETE; + op.pFrom=wfilename; + op.fFlags=FOF_NO_UI; + success=!SHFileOperationW(&op); + if (success && op.fAnyOperationsAborted) + success=FALSE; + g_free(wfilename); + return success; +#else + return nftw(path,remove_ignore,64,FTW_DEPTH|FTW_PHYS); +#endif +} + +gboolean pre_uninstall(void) +{ + gboolean success; + gchar *s; + GError *error=NULL; + razor_set_lua_loader("posix",luaopen_posix); + razor_set_lua_loader("whelk",luaopen_whelk); + prefix=plover_pre_install_prefix(); + s=g_strconcat(prefix,"/var/lib/razor",NULL); + razor_set_database_path(s); + g_free(s); + success=plover_remove(NULL,&error); + if (error) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + } + deltree(prefix); + return success; +} + +#if defined(WIN32) && !defined(USE_G_SPAWN) +/* Based on glib's g_spawn_win32.c */ + +static gchar * +win32_cmdline_quote(const char *string) +{ + const gchar *p=string; + gchar *retval,*q; + gint len=0; + gboolean need_dblquotes=FALSE; + while (*p) + { + if (*p==' ' || *p=='\t') + need_dblquotes=TRUE; + else if (*p=='"') + len++; + else if (*p=='\\') + { + const gchar *pp=p; + while (*pp && *pp=='\\') + pp++; + if (*pp=='"') + len++; + } + len++; + p++; + } + q=retval=g_malloc(len+need_dblquotes*2+1); + p=string; + if (need_dblquotes) + *q++='"'; + while (*p) + { + if (*p=='"') + *q++='\\'; + else if (*p=='\\') + { + const gchar *pp=p; + while (*pp && *pp=='\\') + pp++; + if (*pp=='"') + *q++='\\'; + } + *q++=*p; + p++; + } + if (need_dblquotes) + *q++='"'; + *q++='\0'; + return retval; +} + +/* Create a win32-style wide-character argv with suitable quoting */ +wchar_t **win32_argv_import(char **argv,GError **error) +{ + int i,n; + gchar *s; + wchar_t **wargv; + GError *tmp_error=NULL; + n=g_strv_length(argv); + wargv=g_new(wchar_t *,n+1); + for(i=0;imessage); + g_error_free(tmp_error); + for(i--;i>=0;i--) + g_free(wargv[i]); + g_free(wargv); + return FALSE; + } + } + wargv[i]=NULL; + return wargv; +} + +gboolean spawn_sync(char **argv,GError **error) +{ + wchar_t *wargv0,**wargv; + gintptr rc; + GError *tmp_error=NULL; + wargv0=g_utf8_to_utf16(argv[0],-1,NULL,NULL,&tmp_error); + if (!wargv0) + { + fprintf(stderr,"Conversion error in post\n"); + g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED, + "Invalid program name: %s",tmp_error->message); + g_error_free(tmp_error); + return FALSE; + } + wargv=win32_argv_import(argv,error); + if (!wargv) + { + fprintf(stderr,"Conversion error in post\n"); + g_free(wargv0); + return FALSE; + } + errno=0; + rc=_wspawnvp(P_WAIT,wargv0,(const wchar_t **)wargv); + g_free(wargv0); + g_strfreev((gchar **)wargv); + if (rc==-1 && errno!=0) + { + fprintf(stderr,"Failed to start post command (%s)\n",g_strerror(errno)); + g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED, + "Failed to execute post command: %s",g_strerror(errno)); + return FALSE; + } + if (rc!=EXIT_SUCCESS) + { + fprintf(stderr,"post command failed (%ld)\n",(long)rc); + g_set_error(error,G_SPAWN_ERROR,G_SPAWN_ERROR_FAILED, + "Post command exited with code %ld",(long)rc); + return FALSE; + } + return TRUE; +} + +#endif /* defined(WIN32) && !defined(USE_G_SPAWN) */ + +/* + * Run a command after completing request. + * + * Command may refer to %INSTALL_PREFIX% which will be replaced by the + * (first) install prefix used and/or %TEST_RESULT% which will be replaced + * bu either "pass" or "fail" depending as to whether the request succeeded + * or not. Command may also include double quotes which will be used to + * affect how the command is split into arguments much like a shell does. + */ +gboolean run_post(int argc,char **argv,gboolean test_result,GError **error) +{ + int i,post_argc; + char *s; + gchar *expanded; + gchar **post_argv; +#ifdef USE_G_SPAWN + gchar *standard_output,*standard_error; + int exit_status; +#endif + GError *tmp_error=NULL; + if (argc<1) + { + g_set_error_literal(error,G_FILE_ERROR,G_FILE_ERROR_NOENT, + "--post: No command given"); + return FALSE; + } + printf("Running post command: %s\n",argv[1]); + if (!g_shell_parse_argv(argv[1],&post_argc,&post_argv,&tmp_error)) + { + g_propagate_prefixed_error(error,tmp_error,"%s: ",argv[1]); + return FALSE; + } + for(i=0;i0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + WaitForSingleObject(thread,INFINITE); + GetExitCodeThread(thread,&retval); + CloseHandle(thread); + return retval; +} +#endif /* WIN32 */ + +int main(int argc,char **argv) +{ + gboolean success; + GError *error=NULL; +#ifdef WIN32 + /* + * pre-inst is normally a GUI application, but rpm scripts may well + * call console applications and it looks ugly if console windows keep + * popping up. Avoid this by allocating our own console and hiding it. + * Note: + * - If pre-inst is a console application (typically for debugging), + * then skip this step. + * - Call ShowWindow twice to negate special handling on first call. + */ + if (!GetConsoleWindow()) + { + AllocConsole(); + ShowWindow(GetConsoleWindow(),SW_HIDE); + ShowWindow(GetConsoleWindow(),SW_HIDE); + } +#endif + if (argc>1 && !strcmp(argv[1],"-u")) + { + success=pre_uninstall(); + argc--; + argv++; + } + else +#ifdef WIN32 + success=win32_pre_install_gui(argv[0])==EXIT_SUCCESS; +#else + success=!!pre_install(argv[0]); +#endif + if (argc>1 && !strcmp(argv[1],"--post") && + !run_post(argc-1,argv+1,success,&error)) + { +#ifndef WIN32 + fprintf(stderr,"Error in post: %s\n",error->message); +#else + MessageBox(NULL,error->message,"Error in post",MB_ICONERROR|MB_OK); +#endif + g_error_free(error); + success=FALSE; + } +#ifdef WIN32 + return success?EXIT_SUCCESS:EXIT_FAILURE; +#else + return success?0:1; +#endif +} diff --git a/pre-inst/resource.h b/pre-inst/resource.h new file mode 100644 index 0000000..e72598d --- /dev/null +++ b/pre-inst/resource.h @@ -0,0 +1,6 @@ +#define IDD_PROGRESSDIALOG 100 +#define IDC_PROGRESS 1000 + +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif diff --git a/pre-inst/resources.rc.in b/pre-inst/resources.rc.in new file mode 100644 index 0000000..d4d967c --- /dev/null +++ b/pre-inst/resources.rc.in @@ -0,0 +1,47 @@ +#include +#include "resource.h" + +#pragma code_page(65001) + +MAINICON ICON "pre-inst.ico" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @PLOVER_MAJOR_VERSION@,@PLOVER_MINOR_VERSION@, + @PLOVER_MICRO_VERSION@,0 + PRODUCTVERSION @PLOVER_MAJOR_VERSION@,@PLOVER_MINOR_VERSION@, + @PLOVER_MICRO_VERSION@,0 + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + { + BLOCK "StringFileInfo" + { + BLOCK "080904B0" + { + VALUE "CompanyName","The plover development team" + VALUE "FileDescription","Plover pre-inst program" + VALUE "FileVersion","@PACKAGE_VERSION@" + VALUE "InternalName","pre-inst" + VALUE "LegalCopyright", + "Copyright © 2014 J. Ali Harlow et al" + VALUE "OriginalFilename","pre-inst.exe" + VALUE "ProductName","plover" + VALUE "ProductVersion","@PACKAGE_VERSION@" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation",0x809,0x4B0 + } + } + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "pre-inst.exe.manifest" + +IDD_PROGRESSDIALOG DIALOGEX 0,0,167,67 + STYLE DS_SETFONT|DS_MODALFRAME|DS_FIXEDSYS|WS_POPUP|WS_CAPTION + CAPTION "Preparing to install..." + FONT 8,"MS Shell Dlg",400,0,0x1 + { + ICON MAINICON,IDC_STATIC,10,17,20,20 + LTEXT "Unpacking installation program.",IDC_STATIC,45,16,106,8 + CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER,45,28,106,8 + } diff --git a/setup/Makefile.am b/setup/Makefile.am index a1e148b..14e1737 100644 --- a/setup/Makefile.am +++ b/setup/Makefile.am @@ -3,6 +3,7 @@ LDADD=../plover/libplover.la $(SETUP_LIBS) INCLUDES=-I$(top_srcdir) bin_PROGRAMS=setup +bin_SCRIPTS=setup.js setup_SOURCES=setup.c setup_LDFLAGS=-all-static @@ -10,13 +11,19 @@ if HAVE_WINDRES setup_SOURCES+=resources.rc setup.exe.manifest endif -.png.pnm: - pngtopnm $< | pnmquant 256 > $@ +.rc.$(OBJEXT): + $(AM_V_GEN)$(WINDRES) resources.rc $@ resources.$(OBJEXT): resources.rc setup.exe.manifest setup.ico - $(WINDRES) resources.rc $@ + +%.js: $(srcdir)/%.js.in + $(AM_V_GEN)sed -e 's/$$/\r/' $(srcdir)/$@.in > $@ + +.png.pnm: + $(AM_V_GEN)pngtopnm $< | pnmquant -quiet 256 > $@ setup.ico: icon16.pnm icon22.pnm icon32.pnm - ppmtowinicon -output=$@ $^ + $(AM_V_GEN)ppmtowinicon -output=$@ $^ -EXTRA_DIST=icon16.png icon22.png icon32.png +EXTRA_DIST=icon16.png icon22.png icon32.png setup.js.in +CLEANFILES=setup.js diff --git a/setup/resources.rc.in b/setup/resources.rc.in index ce69c18..87666a0 100644 --- a/setup/resources.rc.in +++ b/setup/resources.rc.in @@ -1,6 +1,8 @@ #include #include +#pragma code_page(65001) + MAINICON ICON "setup.ico" VS_VERSION_INFO VERSIONINFO @@ -20,7 +22,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "FileVersion","@PACKAGE_VERSION@" VALUE "InternalName","setup" VALUE "LegalCopyright", - "Copyright (c) 2009,2011,2012 J. Ali Harlow et al" + "Copyright © 2009,2011,2012 J. Ali Harlow et al" VALUE "OriginalFilename","setup.exe" VALUE "ProductName","plover" VALUE "ProductVersion","@PACKAGE_VERSION@" diff --git a/setup/setup.c b/setup/setup.c index c6cf79e..4804d74 100644 --- a/setup/setup.c +++ b/setup/setup.c @@ -31,67 +31,25 @@ LUALIB_API int luaopen_posix(lua_State *L); -struct vector { - int len,alloc; - char **strings; -}; - -struct vector *vector_new(void) -{ - struct vector *vector; - vector=malloc(sizeof(*vector)); - vector->len=0; - vector->alloc=16; - vector->strings=calloc(vector->alloc,sizeof(char *)); - return vector; -} - -void vector_append(struct vector *vector,const char *str) -{ - if (++(vector->len)>=vector->alloc) - { - vector->alloc*=2; - vector->strings=realloc(vector->strings,vector->alloc*sizeof(char *)); - } - vector->strings[vector->len-1]=strdup(str); - vector->strings[vector->len]=NULL; -} - -int vector_contains(struct vector *vector,const char *str) -{ - int i; - for(i=0;ilen;i++) - if (!strcmp(vector->strings[i],str)) - return 1; - return 0; -} - -void vector_free(struct vector *vector) -{ - int i; - for(i=0;ilen;i++) - free(vector->strings[i]); - free(vector->strings); - free(vector); -} - void setup(const char *argv0) { - char *path,*s,*prefix; + char *path; + gchar *s,*prefix; int ch,changed; struct comps *comps; struct comps_group *group; struct comps_requirement *pkg; - struct vector *packages=NULL; + struct plover_vector *packages=NULL; + GError *error=NULL; path=plover_get_program_directory(argv0); - s=plover_strconcat(path,"/repodata/comps.xml",NULL); + s=g_strconcat(path,"/repodata/comps.xml",NULL); comps=plover_comps_new_from_file(s); if (!comps) { perror(s); exit(1); } - free(s); + g_free(s); prefix=plover_default_prefix_for_vendor(comps->vendor); if (!plover_installed_files_match_prefix(prefix)) { @@ -103,7 +61,12 @@ void setup(const char *argv0) exit(1); while(ch!='\n' && ch!=EOF) ch=getchar(); - plover_remove(NULL); + if (plover_remove(NULL,&error)) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + exit(1); + } } group=plover_comps_lookup_group(comps,"base"); if (!group) @@ -111,21 +74,21 @@ void setup(const char *argv0) fprintf(stderr,"No base group found in comps.xml\n"); exit(1); } - packages=vector_new(); + packages=plover_vector_new(); do { changed=0; for(pkg=group->packages;pkg;pkg=pkg->next) { - if (vector_contains(packages,pkg->name)) + if (plover_vector_contains(packages,pkg->name)) continue; if (pkg->type==COMPS_REQUIREMENT_DEFAULT || pkg->type==COMPS_REQUIREMENT_MANDATORY || pkg->type==COMPS_REQUIREMENT_CONDITIONAL && - vector_contains(packages,pkg->requires)) + plover_vector_contains(packages,pkg->requires)) { changed++; - vector_append(packages,pkg->name); + plover_vector_append(packages,pkg->name); } } } while(changed); @@ -135,18 +98,31 @@ void setup(const char *argv0) fprintf(stderr,"No packages to install\n"); exit(1); } - plover_install(path,prefix,packages->strings); - vector_free(packages); - free(prefix); + if (!plover_install(path,prefix,packages->strings,&error)) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + exit(1); + } + plover_vector_free(packages); + g_free(prefix); free(path); } int main(int argc,char **argv) { + GError *error=NULL; razor_set_lua_loader("posix",luaopen_posix); razor_set_lua_loader("whelk",luaopen_whelk); if (argc>1 && !strcmp(argv[1],"-u")) - plover_remove(NULL); + { + if (!plover_remove(NULL,&error)) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + exit(1); + } + } else setup(argv[0]); exit(0); diff --git a/setup/setup.js.in b/setup/setup.js.in new file mode 100644 index 0000000..2b73f07 --- /dev/null +++ b/setup/setup.js.in @@ -0,0 +1,92 @@ +var WshShell = WScript.CreateObject("WScript.Shell"); +var ScriptPath = WScript.ScriptFullName.replace(/\\[^\\]*$/, ""); +var FileSystemObject = new ActiveXObject("Scripting.FileSystemObject"); + +function RunSetup(args) +{ + var OKButton = 0, ErrorIcon = 16, ForReading = 1; + var path, command, i, ExecObject, text; + path = FileSystemObject.BuildPath(ScriptPath, "setup.exe"); + command = "\"" + path + "\""; + for(i = 0; i < args.Length; i++) + command += " \"" + args.Item(i) + "\""; + ExecObject = WshShell.Exec(command); + if (!ExecObject.StdErr.AtEndOfStream) + { + text = "Setup failed for an unexpected reason. Please report " + + "the following error to support@city-occupational.co.uk:\n\n" + + ExecObject.StdErr.ReadAll(); + WshShell.Popup(text, 0, "Setup failed", OKButton + ErrorIcon); + return false; + } + else if (ExecObject.ExitCode) + { + text = "Setup failed without giving a reason. Please report " + + "this to support@city-occupational.co.uk"; + WshShell.Popup(text, 0, "Setup failed", OKButton + ErrorIcon); + return false; + } + return true; +} + +function Install() +{ + var path; + path = FileSystemObject.BuildPath(ScriptPath, "pre-inst.exe"); + WshShell.Run("\"" + path + "\"" + " --post \"wscript.exe" + + " \\\"" + WScript.ScriptFullName + "\\\"" + + " --post %INSTALL_PREFIX% %TEST_RESULT%\"", + 7, true); +} + +function Uninstall() +{ + var path; + path = FileSystemObject.BuildPath(ScriptPath, "pre-inst.exe"); + WshShell.Run("\"" + path + "\"" + " -u --post \"wscript.exe" + + " \\\"" + WScript.ScriptFullName + "\\\"" + " --postun\"", 7, true); +} + +function PostInstall() +{ + var OKButton = 0, InfoIcon = 64, text; + var test_result, install_prefix, path, args; + install_prefix = WScript.Arguments.Item(1); + test_result = WScript.Arguments.Item(2); + if (test_result == "pass") + { + path = FileSystemObject.BuildPath(install_prefix, + "bin\\app-manager.exe"); + WshShell.Run("\"" + path + "\" --setup \"" + ScriptPath + "\""); + } + else + { + args = { Length:0, Item:function(){return nil} }; + if (RunSetup(args)) + { + text = "Software installation completed successfully."; + WshShell.Popup(text, 0, "Software Installation", + OKButton + InfoIcon); + } + } +} + +function PostUninstall() +{ + var args = { Length:1, Item:function(){return "-u"} }; + var OKButton = 0, InfoIcon = 64, text; + if (RunSetup(args)) + { + text = "Uninstall completed successfully."; + WshShell.Popup(text, 0, "Uninstall", OKButton + InfoIcon); + } +} + +if (WScript.Arguments.Length < 1) + Install() +else if (WScript.Arguments.Item(0) == "-u") + Uninstall() +else if (WScript.Arguments.Length >= 3 && WScript.Arguments.Item(0) == "--post") + PostInstall() +else if (WScript.Arguments.Item(0) == "--postun") + PostUninstall() diff --git a/update/Makefile.am b/update/Makefile.am index bd8b841..4b67d18 100644 --- a/update/Makefile.am +++ b/update/Makefile.am @@ -3,6 +3,7 @@ LDADD=../plover/libplover.la $(SETUP_LIBS) INCLUDES=-I$(top_srcdir) bin_PROGRAMS=update +bin_SCRIPTS=update.js update_SOURCES=update.c update_LDFLAGS=-all-static @@ -10,13 +11,19 @@ if HAVE_WINDRES update_SOURCES+=resources.rc update.exe.manifest endif -.png.pnm: - pngtopnm $< | pnmquant 256 > $@ +.rc.$(OBJEXT): + $(AM_V_GEN)$(WINDRES) resources.rc $@ resources.$(OBJEXT): resources.rc update.exe.manifest update.ico - $(WINDRES) resources.rc $@ + +%.js: $(srcdir)/%.js.in + $(AM_V_GEN)sed -e 's/$$/\r/' $(srcdir)/$@.in > $@ + +.png.pnm: + $(AM_V_GEN)pngtopnm $< | pnmquant -quiet 256 > $@ update.ico: icon16.pnm icon22.pnm icon32.pnm - ppmtowinicon -output=$@ $^ + $(AM_V_GEN)ppmtowinicon -output=$@ $^ -EXTRA_DIST=icon16.png icon22.png icon32.png +EXTRA_DIST=icon16.png icon22.png icon32.png update.js.in +CLEANFILES=update.js diff --git a/update/resources.rc.in b/update/resources.rc.in index aa0e5a8..dc417d2 100644 --- a/update/resources.rc.in +++ b/update/resources.rc.in @@ -1,6 +1,8 @@ #include #include +#pragma code_page(65001) + MAINICON ICON "update.ico" VS_VERSION_INFO VERSIONINFO @@ -20,7 +22,7 @@ VS_VERSION_INFO VERSIONINFO VALUE "FileVersion","@PACKAGE_VERSION@" VALUE "InternalName","update" VALUE "LegalCopyright", - "Copyright (c) 2009,2011,2012 J. Ali Harlow et al" + "Copyright © 2009,2011,2012 J. Ali Harlow et al" VALUE "OriginalFilename","update.exe" VALUE "ProductName","plover" VALUE "ProductVersion","@PACKAGE_VERSION@" diff --git a/update/update.c b/update/update.c index 4127239..d1b8ee4 100644 --- a/update/update.c +++ b/update/update.c @@ -27,18 +27,20 @@ LUALIB_API int luaopen_posix(lua_State *L); void update(const char *argv0) { - char *path,*s,*prefix; + char *path; + gchar *s,*prefix; int ch; struct comps *comps; + GError *error=NULL; path=plover_get_program_directory(argv0); - s=plover_strconcat(path,"/repodata/comps.xml",NULL); + s=g_strconcat(path,"/repodata/comps.xml",NULL); comps=plover_comps_new_from_file(s); if (!comps) { perror(s); exit(1); } - free(s); + g_free(s); prefix=plover_default_prefix_for_vendor(comps->vendor); if (!plover_installed_files_match_prefix(prefix)) { @@ -50,11 +52,21 @@ void update(const char *argv0) exit(1); while(ch!='\n' && ch!=EOF) ch=getchar(); - plover_remove(NULL); + if (plover_remove(NULL,&error)) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + exit(1); + } } plover_comps_free(comps); - plover_update(path,prefix,NULL); - free(prefix); + if (!plover_update(path,prefix,NULL,&error)) + { + fprintf(stderr,"%s\n",error->message); + g_error_free(error); + exit(1); + } + g_free(prefix); free(path); } diff --git a/update/update.js.in b/update/update.js.in new file mode 100644 index 0000000..d01c4a0 --- /dev/null +++ b/update/update.js.in @@ -0,0 +1,68 @@ +var WshShell = WScript.CreateObject("WScript.Shell"); +var ScriptPath = WScript.ScriptFullName.replace(/\\[^\\]*$/, ""); +var FileSystemObject = new ActiveXObject("Scripting.FileSystemObject"); + +function RunUpdate(args) +{ + var OKButton = 0, ErrorIcon = 16, ForReading = 1; + var path, command, i, ExecObject, text; + path = FileSystemObject.BuildPath(ScriptPath, "update.exe"); + command = "\"" + path + "\""; + for(i = 0; i < args.Length; i++) + command += " \"" + args.Item(i) + "\""; + ExecObject = WshShell.Exec(command); + if (!ExecObject.StdErr.AtEndOfStream) + { + text = "Update failed for an unexpected reason. Please report " + + "the following error to support@city-occupational.co.uk:\n\n" + + ExecObject.StdErr.ReadAll(); + WshShell.Popup(text, 0, "Update failed", OKButton + ErrorIcon); + return false; + } + else if (ExecObject.ExitCode) + { + text = "Update failed without giving a reason. Please report " + + "this to support@city-occupational.co.uk"; + WshShell.Popup(text, 0, "Update failed", OKButton + ErrorIcon); + return false; + } + return true; +} + +function Update() +{ + var path; + path = FileSystemObject.BuildPath(ScriptPath, "pre-inst.exe"); + WshShell.Run("\"" + path + "\"" + " --post \"wscript.exe" + + " \\\"" + WScript.ScriptFullName + "\\\"" + + " --post %INSTALL_PREFIX% %TEST_RESULT%\"", + 7, true); +} + +function PostUpdate() +{ + var OKButton = 0, InfoIcon = 64, text; + var test_result, install_prefix, path, args; + install_prefix = WScript.Arguments.Item(1); + test_result = WScript.Arguments.Item(2); + if (test_result == "pass") + { + path = FileSystemObject.BuildPath(install_prefix, + "bin\\app-manager.exe"); + WshShell.Run("\"" + path + "\" --update \"" + ScriptPath + "\""); + } + else + { + args = { Length:0, Item:function(){return nil} }; + if (RunUpdate(args)) + { + text = "Software update completed successfully."; + WshShell.Popup(text, 0, "Software Update", OKButton + InfoIcon); + } + } +} + +if (WScript.Arguments.Length < 1) + Update() +else if (WScript.Arguments.Length >= 3 && WScript.Arguments.Item(0) == "--post") + PostUpdate() -- 1.7.1