diff options
Diffstat (limited to 'src')
111 files changed, 42067 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..8622fe9 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,193 @@ +SUBDIRS = commands sh + +bin_PROGRAMS = file-roller + +if MKDTEMP_MISSING +MKDTEMP_FILES=mkdtemp.c mkdtemp.h +else +MKDTEMP_FILES= +endif + +if RUN_IN_PLACE +privdatadir = $(top_srcdir)/data/ +uidir = $(top_srcdir)/data/ui +privexecdir = $(abs_top_builddir)/src/commands/ +shdir = $(top_srcdir)/src/sh/ +else +privdatadir = $(datadir)/file-roller/ +uidir = $(datadir)/file-roller/ui +privexecdir = $(libexecdir)/file-roller/ +shdir = $(libexecdir)/file-roller/ +endif + +INCLUDES = \ + -I$(top_srcdir)/copy-n-paste/ \ + -I$(top_srcdir) \ + -I$(top_builddir) \ + -DFR_PREFIX=\"$(prefix)\" \ + -DFR_SYSCONFDIR=\"$(sysconfdir)\" \ + -DFR_DATADIR=\"$(datadir)\" \ + -DPRIVDATADIR=\"$(privdatadir)\" \ + -DUI_DIR=\"$(uidir)\" \ + -DFR_LIBDIR=\"$(libdir)\" \ + -DPKG_DATA_DIR=\"$(pkgdatadir)\" \ + -DPIXMAPSDIR=\""$(datadir)/pixmaps"\" \ + -DGLADEDIR=\""$(gladedir)"\" \ + -DPRIVEXECDIR=\"$(privexecdir)\" \ + -DSHDIR=\"$(shdir)\" \ + $(DISABLE_DEPRECATED) \ + $(FR_CFLAGS) + +BUILT_SOURCES = \ + fr-marshal.c \ + fr-marshal.h \ + fr-enum-types.h \ + fr-enum-types.c + +file_roller_SOURCES = \ + actions.h \ + actions.c \ + dlg-add-files.c \ + dlg-add-files.h \ + dlg-add-folder.c \ + dlg-add-folder.h \ + dlg-ask-password.c \ + dlg-ask-password.h \ + dlg-batch-add.c \ + dlg-batch-add.h \ + dlg-delete.c \ + dlg-delete.h \ + dlg-extract.c \ + dlg-extract.h \ + dlg-new.c \ + dlg-new.h \ + dlg-open-with.c \ + dlg-open-with.h \ + dlg-package-installer.c \ + dlg-package-installer.h \ + dlg-password.c \ + dlg-password.h \ + dlg-prop.c \ + dlg-prop.h \ + dlg-update.c \ + dlg-update.h \ + eggfileformatchooser.c \ + eggfileformatchooser.h \ + egg-macros.h \ + eggtreemultidnd.c \ + eggtreemultidnd.h \ + file-data.c \ + file-data.h \ + file-utils.c \ + file-utils.h \ + fr-archive.c \ + fr-archive.h \ + fr-command.c \ + fr-command.h \ + fr-command-ace.c \ + fr-command-ace.h \ + fr-command-alz.c \ + fr-command-alz.h \ + fr-command-ar.c \ + fr-command-ar.h \ + fr-command-arj.c \ + fr-command-arj.h \ + fr-command-cfile.c \ + fr-command-cfile.h \ + fr-command-cpio.c \ + fr-command-cpio.h \ + fr-command-dpkg.c \ + fr-command-dpkg.h \ + fr-command-iso.c \ + fr-command-iso.h \ + fr-command-jar.h \ + fr-command-jar.c \ + fr-command-lha.c \ + fr-command-lha.h \ + fr-command-rar.c \ + fr-command-rar.h \ + fr-command-rpm.c \ + fr-command-rpm.h \ + fr-command-tar.c \ + fr-command-tar.h \ + fr-command-unstuff.c \ + fr-command-unstuff.h \ + fr-command-zip.c \ + fr-command-zip.h \ + fr-command-lrzip.c \ + fr-command-lrzip.h \ + fr-command-zoo.c \ + fr-command-zoo.h \ + fr-command-7z.c \ + fr-command-7z.h \ + fr-error.c \ + fr-error.h \ + fr-list-model.c \ + fr-list-model.h \ + fr-stock.c \ + fr-stock.h \ + fr-process.c \ + fr-process.h \ + fr-window.c \ + fr-window.h \ + mateconf-utils.c \ + mateconf-utils.h \ + gio-utils.c \ + gio-utils.h \ + glib-utils.c \ + glib-utils.h \ + gtk-utils.c \ + gtk-utils.h \ + java-utils.c \ + java-utils.h \ + main.c \ + main.h \ + open-file.c \ + open-file.h \ + preferences.c \ + preferences.h \ + typedefs.h \ + ui.h \ + $(MKDTEMP_FILES) \ + $(BUILT_SOURCES) + +fr-marshal.h: fr-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN)( $(GLIB_GENMARSHAL) $< --header --prefix=fr_marshal > $@ ) + +fr-marshal.c: fr-marshal.list $(GLIB_GENMARSHAL) + $(AM_V_GEN)( echo "#include \"fr-marshal.h\"" > $@ && \ + $(GLIB_GENMARSHAL) $< --body --prefix=fr_marshal >> $@ ) + +fr-enum-types.h: typedefs.h $(GLIB_MKENUMS) + $(AM_V_GEN)( $(GLIB_MKENUMS) \ + --fhead "#ifndef FR_ENUM__TYPES_H\n#define FR_ENUM_TYPES_H\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \ + --fprod "/* enumerations from \"@filename@\" */\n" \ + --vhead "GType @enum_name@_get_type (void);\n#define FR_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \ + --ftail "G_END_DECLS\n\n#endif /* FR_ENUM_TYPES_H */" \ + $^> xgen-$(@F) \ + && (cmp -s xgen-$(@F) fr-enum-types.h || cp xgen-$(@F) fr-enum-types.h ) \ + && rm -f xgen-$(@F) ) + +fr-enum-types.c: typedefs.h fr-enum-types.h + $(AM_V_GEN)( $(GLIB_MKENUMS) \ + --fhead "#include <glib-object.h>\n" \ + --fprod "\n/* enumerations from \"@filename@\" */\n#include \"@filename@\"" \ + --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \ + --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \ + --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \ + $^> xgen-$(@F) \ + && (cmp -s xgen-$(@F) fr-enum-types.c || cp xgen-$(@F) fr-enum-types.c ) \ + && rm -f xgen-$(@F) ) + +file_roller_LDADD = \ + $(top_builddir)/copy-n-paste/libeggsmclient.la \ + $(FR_LIBS) + +EXTRA_DIST = fr-marshal.list + +CLEANFILES = $(BUILT_SOURCES) + +dist-hook: + cd $(distdir); rm -f $(CLEANFILES) + +-include $(top_srcdir)/git.mk diff --git a/src/actions.c b/src/actions.c new file mode 100644 index 0000000..f44c262 --- /dev/null +++ b/src/actions.c @@ -0,0 +1,873 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <math.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "actions.h" +#include "dlg-add-files.h" +#include "dlg-add-folder.h" +#include "dlg-extract.h" +#include "dlg-delete.h" +#include "dlg-new.h" +#include "dlg-open-with.h" +#include "dlg-password.h" +#include "dlg-prop.h" +#include "gtk-utils.h" +#include "fr-window.h" +#include "file-utils.h" +#include "fr-process.h" +#include "mateconf-utils.h" +#include "glib-utils.h" +#include "main.h" +#include "typedefs.h" + + +/* -- new archive -- */ + + +static void +new_archive (DlgNewData *data, + char *uri) +{ + GtkWidget *archive_window; + gboolean new_window; + const char *password; + gboolean encrypt_header; + int volume_size; + + new_window = fr_window_archive_is_present (data->window) && ! fr_window_is_batch_mode (data->window); + if (new_window) + archive_window = fr_window_new (); + else + archive_window = (GtkWidget *) data->window; + + password = dlg_new_data_get_password (data); + encrypt_header = dlg_new_data_get_encrypt_header (data); + volume_size = dlg_new_data_get_volume_size (data); + + fr_window_set_password (FR_WINDOW (archive_window), password); + fr_window_set_encrypt_header (FR_WINDOW (archive_window), encrypt_header); + fr_window_set_volume_size (FR_WINDOW (archive_window), volume_size); + + if (fr_window_archive_new (FR_WINDOW (archive_window), uri)) { + gtk_widget_destroy (data->dialog); + if (! fr_window_is_batch_mode (FR_WINDOW (archive_window))) + gtk_window_present (GTK_WINDOW (archive_window)); + } + else if (new_window) + gtk_widget_destroy (archive_window); +} + + +/* when on Automatic the user provided extension needs to be supported, + otherwise an existing unsupported archive can be deleted (if the user + provided name matches with its name) before we find out that the + archive is unsupported +*/ +static gboolean +is_supported_extension (GtkWidget *file_sel, + char *filename, + int *file_type) +{ + int i; + for (i = 0; file_type[i] != -1; i++) + if (file_extension_is (filename, mime_type_desc[file_type[i]].default_ext)) + return TRUE; + return FALSE; +} + + +static char * +get_full_uri (DlgNewData *data) +{ + char *full_uri = NULL; + char *uri; + const char *filename; + int idx; + + uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)); + + if ((uri == NULL) || (*uri == 0)) + return NULL; + + filename = file_name_from_path (uri); + if ((filename == NULL) || (*filename == 0)) { + g_free (uri); + return NULL; + } + + idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri); + if (idx > 0) { + const char *uri_ext; + char *default_ext; + + uri_ext = get_archive_filename_extension (uri); + default_ext = mime_type_desc[data->supported_types[idx-1]].default_ext; + if (strcmp_null_tolerant (uri_ext, default_ext) != 0) { + full_uri = g_strconcat (uri, default_ext, NULL); + g_free (uri); + } + } + if (full_uri == NULL) + full_uri = uri; + + return full_uri; +} + + +static char * +get_archive_filename_from_selector (DlgNewData *data) +{ + char *uri = NULL; + GFile *file, *dir; + GFileInfo *info; + GError *err = NULL; + + uri = get_full_uri (data); + if ((uri == NULL) || (*uri == 0)) { + GtkWidget *dialog; + + g_free (uri); + + dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + "%s", + _("You have to specify an archive name.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return NULL; + } + + file = g_file_new_for_uri (uri); + + dir = g_file_get_parent (file); + info = g_file_query_info (dir, + G_FILE_ATTRIBUTE_ACCESS_CAN_READ "," + G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "," + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, + 0, NULL, &err); + if (err != NULL) { + g_warning ("Failed to get permission for extraction dir: %s", + err->message); + g_clear_error (&err); + g_object_unref (info); + g_object_unref (dir); + g_object_unref (file); + g_free (uri); + return NULL; + } + + if (! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) { + GtkWidget *dialog; + + g_object_unref (info); + g_object_unref (dir); + g_object_unref (file); + g_free (uri); + + dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + "%s", + _("You don't have permission to create an archive in this folder")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + return NULL; + } + g_object_unref (info); + g_object_unref (dir); + + /* if the user did not specify a valid extension use the filetype combobox current type + * or tar.gz if automatic is selected. */ + if (get_archive_filename_extension (uri) == NULL) { + int idx; + char *new_uri; + char *ext = NULL; + + idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri); + if (idx > 0) + ext = mime_type_desc[data->supported_types[idx-1]].default_ext; + else + ext = ".tar.gz"; + new_uri = g_strconcat (uri, ext, NULL); + g_free (uri); + uri = new_uri; + } + + debug (DEBUG_INFO, "create/save %s\n", uri); + + if (uri_exists (uri)) { + GtkWidget *dialog; + + if (! is_supported_extension (data->dialog, uri, data->supported_types)) { + dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_MODAL, + NULL, + _("Could not create the archive"), + "%s", + _("Archive type not supported.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_free (uri); + + return NULL; + } + + g_file_delete (file, NULL, &err); + if (err != NULL) { + GtkWidget *dialog; + dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not delete the old archive."), + "%s", + err->message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (GTK_WIDGET (dialog)); + g_error_free (err); + g_free (uri); + g_object_unref (file); + return NULL; + } + } + + g_object_unref (file); + + return uri; +} + + +static void +new_file_response_cb (GtkWidget *w, + int response, + DlgNewData *data) +{ + char *path; + + if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) { + fr_archive_action_completed (data->window->archive, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_PROC_ERROR_STOPPED, + NULL); + gtk_widget_destroy (data->dialog); + return; + } + + if (response == GTK_RESPONSE_HELP) { + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-create"); + return; + } + + path = get_archive_filename_from_selector (data); + if (path != NULL) { + new_archive (data, path); + g_free (path); + } +} + + +void +show_new_archive_dialog (FrWindow *window, + const char *archive_name) +{ + DlgNewData *data; + + if (archive_name != NULL) + data = dlg_save_as (window, archive_name); + else + data = dlg_new (window); + + g_signal_connect (G_OBJECT (data->dialog), + "response", + G_CALLBACK (new_file_response_cb), + data); + gtk_window_present (GTK_WINDOW (data->dialog)); +} + + +void +activate_action_new (GtkAction *action, + gpointer data) +{ + show_new_archive_dialog ((FrWindow*)data, NULL); +} + + +/* -- open archive -- */ + + +static void +window_archive_loaded_cb (FrWindow *window, + gboolean success, + GtkWidget *file_sel) +{ + if (success) { + g_signal_handlers_disconnect_by_data (window, file_sel); + gtk_widget_destroy (file_sel); + } + else { + FrWindow *original_window = g_object_get_data (G_OBJECT (file_sel), "fr_window"); + if (window != original_window) + fr_window_destroy_with_error_dialog (window); + } +} + + +static void +open_file_response_cb (GtkWidget *w, + int response, + GtkWidget *file_sel) +{ + FrWindow *window = NULL; + char *uri; + + if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) { + gtk_widget_destroy (file_sel); + return; + } + + window = g_object_get_data (G_OBJECT (file_sel), "fr_window"); + uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (file_sel)); + + if ((window == NULL) || (uri == NULL)) + return; + + if (fr_window_archive_is_present (window)) + window = (FrWindow *) fr_window_new (); + g_signal_connect (G_OBJECT (window), + "archive_loaded", + G_CALLBACK (window_archive_loaded_cb), + file_sel); + fr_window_archive_open (window, uri, GTK_WINDOW (file_sel)); + + g_free (uri); +} + + +void +activate_action_open (GtkAction *action, + gpointer data) +{ + GtkWidget *file_sel; + FrWindow *window = data; + GtkFileFilter *filter; + int i; + + file_sel = gtk_file_chooser_dialog_new (_("Open"), + GTK_WINDOW (window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (file_sel), fr_window_get_open_default_dir (window)); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All archives")); + for (i = 0; open_type[i] != -1; i++) + gtk_file_filter_add_mime_type (filter, mime_type_desc[open_type[i]].mime_type); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_sel), filter); + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (file_sel), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_sel), filter); + + /**/ + + g_object_set_data (G_OBJECT (file_sel), "fr_window", window); + + g_signal_connect (G_OBJECT (file_sel), + "response", + G_CALLBACK (open_file_response_cb), + file_sel); + + gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE); + gtk_widget_show (file_sel); +} + + +/* -- save archive -- */ + + +static void +save_file_response_cb (GtkWidget *w, + gint response, + DlgNewData *data) +{ + char *path; + const char *password; + gboolean encrypt_header; + int volume_size; + + if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) { + gtk_widget_destroy (data->dialog); + return; + } + + if (response == GTK_RESPONSE_HELP) { + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-convert-archive"); + return; + } + + path = get_archive_filename_from_selector (data); + if (path == NULL) + return; + + password = dlg_new_data_get_password (data); + encrypt_header = dlg_new_data_get_encrypt_header (data); + volume_size = dlg_new_data_get_volume_size (data); + + eel_mateconf_set_integer (PREF_BATCH_VOLUME_SIZE, volume_size); + + fr_window_archive_save_as (data->window, path, password, encrypt_header, volume_size); + gtk_widget_destroy (data->dialog); + + g_free (path); +} + + +void +activate_action_save_as (GtkAction *action, + gpointer callback_data) +{ + FrWindow *window = callback_data; + DlgNewData *data; + char *archive_name = NULL; + + if (fr_window_get_archive_uri (window)) { + const char *uri; + GFile *file; + GFileInfo *info; + GError *err = NULL; + + uri = fr_window_get_archive_uri (window); + file = g_file_new_for_uri (uri); + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, + 0, NULL, &err); + + if (err != NULL) { + g_warning ("Failed to get display name for uri %s: %s", uri, err->message); + g_clear_error (&err); + } + else + archive_name = g_strdup (g_file_info_get_display_name (info)); + + g_object_unref (info); + g_object_unref (file); + } + + data = dlg_save_as (window, archive_name); + g_signal_connect (G_OBJECT (data->dialog), + "response", + G_CALLBACK (save_file_response_cb), + data); + gtk_window_present (GTK_WINDOW (data->dialog)); + + g_free (archive_name); +} + + +void +activate_action_test_archive (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_archive_test (window); +} + + +void +activate_action_properties (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + dlg_prop (window); +} + + +void +activate_action_close (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_close (window); +} + + +void +activate_action_add_files (GtkAction *action, + gpointer data) +{ + add_files_cb (NULL, data); +} + + +void +activate_action_add_folder (GtkAction *action, + gpointer data) +{ + add_folder_cb (NULL, data); +} + + +void +activate_action_extract (GtkAction *action, + gpointer data) +{ + dlg_extract (NULL, data); +} + + +void +activate_action_extract_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + dlg_extract_folder_from_sidebar (NULL, data); +} + + +void +activate_action_copy (GtkAction *action, + gpointer data) +{ + fr_window_copy_selection ((FrWindow*) data, FALSE); +} + + +void +activate_action_cut (GtkAction *action, + gpointer data) +{ + fr_window_cut_selection ((FrWindow*) data, FALSE); +} + + +void +activate_action_paste (GtkAction *action, + gpointer data) +{ + fr_window_paste_selection ((FrWindow*) data, FALSE); +} + + +void +activate_action_rename (GtkAction *action, + gpointer data) +{ + fr_window_rename_selection ((FrWindow*) data, FALSE); +} + + +void +activate_action_delete (GtkAction *action, + gpointer data) +{ + dlg_delete (NULL, data); +} + + +void +activate_action_copy_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + fr_window_copy_selection ((FrWindow*) data, TRUE); +} + + +void +activate_action_cut_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + fr_window_cut_selection ((FrWindow*) data, TRUE); +} + + +void +activate_action_paste_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + fr_window_paste_selection ((FrWindow*) data, TRUE); +} + + +void +activate_action_rename_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + fr_window_rename_selection ((FrWindow*) data, TRUE); +} + + +void +activate_action_delete_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + dlg_delete_from_sidebar (NULL, data); +} + + +void +activate_action_find (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_find (window); +} + + +void +activate_action_select_all (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_select_all (window); +} + + +void +activate_action_deselect_all (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_unselect_all (window); +} + + +void +activate_action_open_with (GtkAction *action, + gpointer data) +{ + open_with_cb (NULL, (FrWindow*) data); +} + + +void +activate_action_view_or_open (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + GList *file_list; + + file_list = fr_window_get_file_list_selection (window, FALSE, NULL); + if (file_list == NULL) + return; + fr_window_open_files (window, file_list, FALSE); + path_list_free (file_list); +} + + +void +activate_action_open_folder (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_current_folder_activated (window, FALSE); +} + + +void +activate_action_open_folder_from_sidebar (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_current_folder_activated (window, TRUE); +} + + +void +activate_action_password (GtkAction *action, + gpointer data) +{ + dlg_password (NULL, (FrWindow*) data); +} + + +void +activate_action_view_toolbar (GtkAction *action, + gpointer data) +{ + eel_mateconf_set_boolean (PREF_UI_TOOLBAR, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); +} + + +void +activate_action_view_statusbar (GtkAction *action, + gpointer data) +{ + eel_mateconf_set_boolean (PREF_UI_STATUSBAR, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); +} + + +void +activate_action_view_folders (GtkAction *action, + gpointer data) +{ + eel_mateconf_set_boolean (PREF_UI_FOLDERS, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))); +} + + +void +activate_action_stop (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_stop (window); +} + + +void +activate_action_reload (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_archive_reload (window); +} + + +void +activate_action_sort_reverse_order (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + fr_window_set_sort_type (window, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING); +} + + +void +activate_action_last_output (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_view_last_output (window, _("Last Output")); +} + + +void +activate_action_go_back (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_go_back (window); +} + + +void +activate_action_go_forward (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_go_forward (window); +} + + +void +activate_action_go_up (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_go_up_one_level (window); +} + + +void +activate_action_go_home (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + fr_window_go_to_location (window, "/", FALSE); +} + + +void +activate_action_manual (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + + show_help_dialog (GTK_WINDOW (window) , NULL); +} + + +void +activate_action_about (GtkAction *action, + gpointer data) +{ + FrWindow *window = data; + const char *authors[] = { + "Paolo Bacchilega <[email protected]>", NULL + }; + const char *documenters [] = { + "Alexander Kirillov", + "Breda McColgan", + NULL + }; + const char *license[] = { + N_("File Roller 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."), + N_("File Roller 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."), + N_("You should have received a copy of the GNU General Public License " + "along with File Roller; if not, write to the Free Software Foundation, Inc., " + "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA") + }; + char *license_text; + + license_text = g_strjoin ("\n\n", _(license[0]), _(license[1]), _(license[2]), NULL); + + gtk_show_about_dialog (GTK_WINDOW (window), + "version", VERSION, + "copyright", _("Copyright \xc2\xa9 2001–2010 Free Software Foundation, Inc."), + "comments", _("An archive manager for MATE."), + "authors", authors, + "documenters", documenters, + "translator-credits", _("translator-credits"), + "logo-icon-name", "file-roller", + "license", license_text, + "wrap-license", TRUE, + NULL); + + g_free (license_text); +} diff --git a/src/actions.h b/src/actions.h new file mode 100644 index 0000000..b0d1def --- /dev/null +++ b/src/actions.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef ACTIONS_H +#define ACTIONS_H + +#include <gtk/gtk.h> +#include "fr-window.h" + +void show_new_archive_dialog (FrWindow *window, const char *archive_name); + +void activate_action_new (GtkAction *action, gpointer data); +void activate_action_open (GtkAction *action, gpointer data); +void activate_action_save_as (GtkAction *action, gpointer data); +void activate_action_test_archive (GtkAction *action, gpointer data); +void activate_action_properties (GtkAction *action, gpointer data); +void activate_action_close (GtkAction *action, gpointer data); +void activate_action_quit (GtkAction *action, gpointer data); + +void activate_action_add_files (GtkAction *action, gpointer data); +void activate_action_add_folder (GtkAction *action, gpointer data); +void activate_action_extract (GtkAction *action, gpointer data); +void activate_action_extract_folder_from_sidebar (GtkAction *action, gpointer data); + +void activate_action_copy (GtkAction *action, gpointer data); +void activate_action_cut (GtkAction *action, gpointer data); +void activate_action_paste (GtkAction *action, gpointer data); +void activate_action_rename (GtkAction *action, gpointer data); +void activate_action_delete (GtkAction *action, gpointer data); + +void activate_action_copy_folder_from_sidebar (GtkAction *action, gpointer data); +void activate_action_cut_folder_from_sidebar (GtkAction *action, gpointer data); +void activate_action_paste_folder_from_sidebar (GtkAction *action, gpointer data); +void activate_action_rename_folder_from_sidebar (GtkAction *action, gpointer data); +void activate_action_delete_folder_from_sidebar (GtkAction *action, gpointer data); + +void activate_action_find (GtkAction *action, gpointer data); +void activate_action_select_all (GtkAction *action, gpointer data); +void activate_action_deselect_all (GtkAction *action, gpointer data); +void activate_action_open_with (GtkAction *action, gpointer data); +void activate_action_view_or_open (GtkAction *action, gpointer data); +void activate_action_open_folder (GtkAction *action, gpointer data); +void activate_action_open_folder_from_sidebar (GtkAction *action, gpointer data); +void activate_action_password (GtkAction *action, gpointer data); + +void activate_action_view_toolbar (GtkAction *action, gpointer data); +void activate_action_view_statusbar (GtkAction *action, gpointer data); +void activate_action_view_folders (GtkAction *action, gpointer data); +void activate_action_stop (GtkAction *action, gpointer data); +void activate_action_reload (GtkAction *action, gpointer data); +void activate_action_sort_reverse_order (GtkAction *action, gpointer data); +void activate_action_last_output (GtkAction *action, gpointer data); + +void activate_action_go_back (GtkAction *action, gpointer data); +void activate_action_go_forward (GtkAction *action, gpointer data); +void activate_action_go_up (GtkAction *action, gpointer data); +void activate_action_go_home (GtkAction *action, gpointer data); + +void activate_action_manual (GtkAction *action, gpointer data); +void activate_action_about (GtkAction *action, gpointer data); + + +#endif /* ACTIONS_H */ diff --git a/src/commands/Makefile.am b/src/commands/Makefile.am new file mode 100644 index 0000000..b51c338 --- /dev/null +++ b/src/commands/Makefile.am @@ -0,0 +1,7 @@ +privexecdir = $(libexecdir)/$(PACKAGE) +privexec_PROGRAMS = rpm2cpio + +INCLUDES = $(DISABLE_DEPRECATED) $(FR_CFLAGS) + +rpm2cpio_SOURCES = rpm2cpio.c +rpm2cpio_LDADD = $(FR_LIBS) diff --git a/src/commands/rpm2cpio.c b/src/commands/rpm2cpio.c new file mode 100644 index 0000000..034cd89 --- /dev/null +++ b/src/commands/rpm2cpio.c @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <glib.h> + + +static const char * +get_mime_type_from_magic_numbers (char *buffer) +{ + static struct { + const char *mime_type; + const char *first_bytes; + int offset; + int len; + } sniffer_data [] = { + { "application/x-bzip2", "BZh", 0, 3 }, + { "application/x-gzip", "\037\213", 0, 2 }, + { "application/x-xz", "\3757zXZ\000", 0, 6 }, + { NULL, NULL, 0 } + }; + int i; + + for (i = 0; sniffer_data[i].mime_type != NULL; i++) + if (memcmp (sniffer_data[i].first_bytes, + buffer + sniffer_data[i].offset, + sniffer_data[i].len) == 0) + { + return sniffer_data[i].mime_type; + } + + return NULL; +} + + +int +main (int argc, char **argv) +{ + const char *filename; + GString *cpio_args; + int i; + FILE *stream; + guchar bytes[8]; + int il, dl, sigsize, offset; + const char *mime_type; + const char *archive_command; + char *command; + + if (argc < 3) + return 0; + + filename = argv[1]; + cpio_args = g_string_new (argv[2]); + for (i = 3; i < argc; i++) { + g_string_append (cpio_args, " "); + g_string_append (cpio_args, argv[i]); + } + + stream = fopen (filename, "r"); + if (stream == NULL) + return 1; + + if (fseek (stream, 104 , SEEK_CUR) != 0) { + fclose (stream); + return 1; + } + if (fread (bytes, 1, 8, stream) == 0) { + fclose (stream); + return 1; + } + il = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3]; + dl = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7]; + sigsize = 8 + 16 * il + dl; + offset = 104 + sigsize + (8 - (sigsize % 8)) % 8 + 8; + if (fseek (stream, offset, SEEK_SET) != 0) { + fclose (stream); + return 1; + } + if (fread (bytes, 1, 8, stream) == 0) { + fclose (stream); + return 1; + } + il = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3]; + dl = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7]; + sigsize = 8 + 16 * il + dl; + offset = offset + sigsize; + + /* get the payload type */ + + if (fseek (stream, offset, SEEK_SET) != 0) { + fclose (stream); + return 1; + } + if (fread (bytes, 1, 8, stream) == 0) { + fclose (stream); + return 1; + } + mime_type = get_mime_type_from_magic_numbers ((char *)bytes); + if (mime_type == NULL) + archive_command = "lzma -dc"; + else if (strcmp (mime_type, "application/x-xz") == 0) + archive_command = "xz -dc"; + else if (strcmp (mime_type, "application/x-gzip") == 0) + archive_command = "gzip -dc"; + else + archive_command = "bzip2 -dc"; + fclose (stream); + + command = g_strdup_printf ("sh -c \"dd if=%s ibs=%u skip=1 2>/dev/null | %s | cpio %s\"", g_shell_quote (filename), offset, archive_command, cpio_args->str); + + return system (command); +} diff --git a/src/dlg-add-files.c b/src/dlg-add-files.c new file mode 100644 index 0000000..06c8be9 --- /dev/null +++ b/src/dlg-add-files.c @@ -0,0 +1,191 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <unistd.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include "file-utils.h" +#include "fr-stock.h" +#include "fr-window.h" +#include "mateconf-utils.h" +#include "gtk-utils.h" +#include "preferences.h" + + +typedef struct { + FrWindow *window; + GtkWidget *dialog; + GtkWidget *add_if_newer_checkbutton; +} DialogData; + + +static void +open_file_destroy_cb (GtkWidget *file_sel, + DialogData *data) +{ + g_free (data); +} + + +static int +file_sel_response_cb (GtkWidget *widget, + int response, + DialogData *data) +{ + GtkFileChooser *file_sel = GTK_FILE_CHOOSER (widget); + FrWindow *window = data->window; + char *current_folder; + char *uri; + gboolean update; + GSList *selections, *iter; + GList *item_list = NULL; + + current_folder = gtk_file_chooser_get_current_folder_uri (file_sel); + uri = gtk_file_chooser_get_uri (file_sel); + eel_mateconf_set_string (PREF_ADD_CURRENT_FOLDER, current_folder); + eel_mateconf_set_string (PREF_ADD_FILENAME, uri); + fr_window_set_add_default_dir (window, current_folder); + g_free (uri); + + if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) { + gtk_widget_destroy (data->dialog); + g_free (current_folder); + return TRUE; + } + + if (response == GTK_RESPONSE_HELP) { + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-add-options"); + g_free (current_folder); + return TRUE; + } + + /* check folder permissions. */ + + if (uri_is_dir (current_folder) && ! check_permissions (current_folder, R_OK)) { + GtkWidget *d; + char *utf8_path; + + utf8_path = g_filename_display_name (current_folder); + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + NULL, + _("Could not add the files to the archive"), + _("You don't have the right permissions to read files from folder \"%s\""), + utf8_path); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (utf8_path); + g_free (current_folder); + return FALSE; + } + + update = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton)); + + /**/ + + selections = gtk_file_chooser_get_uris (file_sel); + for (iter = selections; iter != NULL; iter = iter->next) { + char *uri = iter->data; + item_list = g_list_prepend (item_list, g_file_new_for_uri (uri)); + } + + if (item_list != NULL) + fr_window_archive_add_files (window, item_list, update); + + gio_file_list_free (item_list); + g_slist_foreach (selections, (GFunc) g_free, NULL); + g_slist_free (selections); + g_free (current_folder); + + gtk_widget_destroy (data->dialog); + + return TRUE; +} + + +/* create the "add" dialog. */ +void +add_files_cb (GtkWidget *widget, + void *callback_data) +{ + GtkWidget *file_sel; + DialogData *data; + GtkWidget *main_box; + char *folder; + + data = g_new0 (DialogData, 1); + data->window = callback_data; + data->dialog = file_sel = + gtk_file_chooser_dialog_new (_("Add Files"), + GTK_WINDOW (data->window), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + FR_STOCK_ADD_FILES, GTK_RESPONSE_OK, + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + NULL); + + gtk_window_set_default_size (GTK_WINDOW (data->dialog), 530, 450); + + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_sel), TRUE); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK); + + /* Translators: add a file to the archive only if the disk version is + * newer than the archive version. */ + data->add_if_newer_checkbutton = gtk_check_button_new_with_mnemonic (_("Add only if _newer")); + + main_box = gtk_hbox_new (FALSE, 20); + gtk_container_set_border_width (GTK_CONTAINER (main_box), 0); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel), main_box); + + gtk_box_pack_start (GTK_BOX (main_box), data->add_if_newer_checkbutton, + TRUE, TRUE, 0); + + gtk_widget_show_all (main_box); + + /* set data */ + + folder = eel_mateconf_get_string (PREF_ADD_CURRENT_FOLDER, ""); + if ((folder == NULL) || (strcmp (folder, "") == 0)) + folder = g_strdup (fr_window_get_add_default_dir (data->window)); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (file_sel), folder); + g_free (folder); + + /* signals */ + + g_signal_connect (G_OBJECT (file_sel), + "destroy", + G_CALLBACK (open_file_destroy_cb), + data); + + g_signal_connect (G_OBJECT (file_sel), + "response", + G_CALLBACK (file_sel_response_cb), + data); + + gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE); + gtk_widget_show (file_sel); +} diff --git a/src/dlg-add-files.h b/src/dlg-add-files.h new file mode 100644 index 0000000..dc6c556 --- /dev/null +++ b/src/dlg-add-files.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_ADD_FILES_H +#define DLG_ADD_FILES_H + +#include <gtk/gtk.h> + +void add_files_cb (GtkWidget *widget, void *data); + +#endif /* DLG_ADD_FILES_H */ diff --git a/src/dlg-add-folder.c b/src/dlg-add-folder.c new file mode 100644 index 0000000..cd9f430 --- /dev/null +++ b/src/dlg-add-folder.c @@ -0,0 +1,899 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gio/gio.h> +#include "dlg-add-folder.h" +#include "file-utils.h" +#include "fr-stock.h" +#include "fr-window.h" +#include "mateconf-utils.h" +#include "gtk-utils.h" +#include "preferences.h" + +typedef struct { + FrWindow *window; + GtkWidget *dialog; + GtkWidget *include_subfold_checkbutton; + GtkWidget *add_if_newer_checkbutton; + GtkWidget *exclude_symlinks; + GtkWidget *include_files_checkbutton; + GtkWidget *include_files_entry; + GtkWidget *include_files_label; + GtkWidget *exclude_files_entry; + GtkWidget *exclude_files_label; + GtkWidget *exclude_folders_entry; + GtkWidget *exclude_folders_label; + GtkWidget *load_button; + GtkWidget *save_button; + GtkWidget *clear_button; + char *last_options; +} DialogData; + + +static void +open_file_destroy_cb (GtkWidget *widget, + DialogData *data) +{ + g_free (data->last_options); + g_free (data); +} + + +static gboolean +utf8_only_spaces (const char *text) +{ + const char *scan; + + if (text == NULL) + return TRUE; + + for (scan = text; *scan != 0; scan = g_utf8_next_char (scan)) { + gunichar c = g_utf8_get_char (scan); + if (! g_unichar_isspace (c)) + return FALSE; + } + + return TRUE; +} + + +static void dlg_add_folder_save_last_options (DialogData *data); + + +static int +file_sel_response_cb (GtkWidget *widget, + int response, + DialogData *data) +{ + GtkFileChooser *file_sel = GTK_FILE_CHOOSER (widget); + FrWindow *window = data->window; + char *selected_folder; + gboolean update, recursive, follow_links; + const char *include_files; + const char *exclude_files; + const char *exclude_folders; + char *dest_dir; + char *local_filename; + + + dlg_add_folder_save_last_options (data); + + if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) { + gtk_widget_destroy (data->dialog); + return TRUE; + } + + if (response == GTK_RESPONSE_HELP) { + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-add-options"); + return TRUE; + } + + selected_folder = gtk_file_chooser_get_uri (file_sel); + + /* check folder permissions. */ + + if (! check_permissions (selected_folder, R_OK)) { + GtkWidget *d; + char *utf8_path; + + utf8_path = g_filename_display_name (selected_folder); + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + NULL, + _("Could not add the files to the archive"), + _("You don't have the right permissions to read files from folder \"%s\""), + utf8_path); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (utf8_path); + g_free (selected_folder); + + return FALSE; + } + + update = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton)); + recursive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->include_subfold_checkbutton)); + follow_links = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->exclude_symlinks)); + + include_files = gtk_entry_get_text (GTK_ENTRY (data->include_files_entry)); + if (utf8_only_spaces (include_files)) + include_files = "*"; + + exclude_files = gtk_entry_get_text (GTK_ENTRY (data->exclude_files_entry)); + if (utf8_only_spaces (exclude_files)) + exclude_files = NULL; + + exclude_folders = gtk_entry_get_text (GTK_ENTRY (data->exclude_folders_entry)); + if (utf8_only_spaces (exclude_folders)) + exclude_folders = NULL; + + local_filename = g_filename_from_uri (selected_folder, NULL, NULL); + dest_dir = build_uri (fr_window_get_current_location (window), + file_name_from_path (local_filename), + NULL); + + fr_window_archive_add_with_wildcard (window, + include_files, + exclude_files, + exclude_folders, + selected_folder, + dest_dir, + update, + follow_links); + + g_free (local_filename); + g_free (dest_dir); + g_free (selected_folder); + + gtk_widget_destroy (data->dialog); + + return TRUE; +} + + +static int +include_subfold_toggled_cb (GtkWidget *widget, + gpointer callback_data) +{ + DialogData *data = callback_data; + + gtk_widget_set_sensitive (data->exclude_symlinks, + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))); + + return FALSE; +} + + +static void load_options_cb (GtkWidget *w, DialogData *data); +static void save_options_cb (GtkWidget *w, DialogData *data); +static void clear_options_cb (GtkWidget *w, DialogData *data); +static void dlg_add_folder_load_last_options (DialogData *data); + + +/* create the "add" dialog. */ +void +add_folder_cb (GtkWidget *widget, + void *callback_data) +{ + GtkWidget *file_sel; + DialogData *data; + GtkWidget *main_box; + GtkWidget *vbox; + GtkWidget *table; + GtkWidget *align; + + data = g_new0 (DialogData, 1); + + data->window = callback_data; + + data->dialog = file_sel = + gtk_file_chooser_dialog_new (_("Add a Folder"), + GTK_WINDOW (data->window), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + FR_STOCK_ADD_FOLDER, GTK_RESPONSE_OK, + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + NULL); + + gtk_window_set_default_size (GTK_WINDOW (data->dialog), 530, 510); + + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_sel), FALSE); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK); + + data->add_if_newer_checkbutton = gtk_check_button_new_with_mnemonic (_("Add only if _newer")); + data->include_subfold_checkbutton = gtk_check_button_new_with_mnemonic (_("_Include subfolders")); + data->exclude_symlinks = gtk_check_button_new_with_mnemonic (_("Exclude folders that are symbolic lin_ks")); + + data->include_files_entry = gtk_entry_new (); + gtk_widget_set_tooltip_text (data->include_files_entry, _("example: *.o; *.bak")); + data->include_files_label = gtk_label_new_with_mnemonic (_("Include _files:")); + gtk_misc_set_alignment (GTK_MISC (data->include_files_label), 0.0, 0.5); + gtk_label_set_mnemonic_widget (GTK_LABEL (data->include_files_label), data->include_files_entry); + + data->exclude_files_entry = gtk_entry_new (); + gtk_widget_set_tooltip_text (data->exclude_files_entry, _("example: *.o; *.bak")); + data->exclude_files_label = gtk_label_new_with_mnemonic (_("E_xclude files:")); + gtk_misc_set_alignment (GTK_MISC (data->exclude_files_label), 0.0, 0.5); + gtk_label_set_mnemonic_widget (GTK_LABEL (data->exclude_files_label), data->exclude_files_entry); + + data->exclude_folders_entry = gtk_entry_new (); + gtk_widget_set_tooltip_text (data->exclude_folders_entry, _("example: *.o; *.bak")); + data->exclude_folders_label = gtk_label_new_with_mnemonic (_("_Exclude folders:")); + gtk_misc_set_alignment (GTK_MISC (data->exclude_folders_label), 0.0, 0.5); + gtk_label_set_mnemonic_widget (GTK_LABEL (data->exclude_folders_label), data->exclude_folders_entry); + + data->load_button = gtk_button_new_with_mnemonic (_("_Load Options")); + data->save_button = gtk_button_new_with_mnemonic (_("Sa_ve Options")); + data->clear_button = gtk_button_new_with_mnemonic (_("_Reset Options")); + + main_box = gtk_hbox_new (FALSE, 20); + gtk_container_set_border_width (GTK_CONTAINER (main_box), 0); + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel), main_box); + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 0); + gtk_box_pack_start (GTK_BOX (main_box), vbox, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), data->include_subfold_checkbutton, + TRUE, TRUE, 0); + + align = gtk_alignment_new (0, 0, 0, 0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0); + gtk_container_add (GTK_CONTAINER (align), data->exclude_symlinks); + gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), data->add_if_newer_checkbutton, + TRUE, TRUE, 0); + + table = gtk_table_new (2, 4, FALSE); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (vbox), table, + TRUE, TRUE, 0); + + gtk_table_attach (GTK_TABLE (table), + data->include_files_label, + 0, 1, + 0, 1, + GTK_FILL, 0, + 0, 0); + gtk_table_attach (GTK_TABLE (table), + data->include_files_entry, + 1, 4, + 0, 1, + GTK_FILL|GTK_EXPAND, 0, + 0, 0); + gtk_table_attach (GTK_TABLE (table), + data->exclude_files_label, + 0, 1, + 1, 2, + GTK_FILL, 0, + 0, 0); + gtk_table_attach (GTK_TABLE (table), + data->exclude_files_entry, + 1, 2, + 1, 2, + GTK_FILL|GTK_EXPAND, 0, + 0, 0); + gtk_table_attach (GTK_TABLE (table), + data->exclude_folders_label, + 2, 3, + 1, 2, + GTK_FILL, 0, + 0, 0); + gtk_table_attach (GTK_TABLE (table), + data->exclude_folders_entry, + 3, 4, + 1, 2, + GTK_FILL|GTK_EXPAND, 0, + 0, 0); + + /**/ + + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 0); + gtk_box_pack_start (GTK_BOX (main_box), vbox, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), data->load_button, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), data->save_button, + FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), data->clear_button, + FALSE, FALSE, 0); + + gtk_widget_show_all (main_box); + + /* set data */ + + dlg_add_folder_load_last_options (data); + + /* signals */ + + g_signal_connect (G_OBJECT (file_sel), + "destroy", + G_CALLBACK (open_file_destroy_cb), + data); + g_signal_connect (G_OBJECT (file_sel), + "response", + G_CALLBACK (file_sel_response_cb), + data); + g_signal_connect (G_OBJECT (data->include_subfold_checkbutton), + "toggled", + G_CALLBACK (include_subfold_toggled_cb), + data); + g_signal_connect (G_OBJECT (data->load_button), + "clicked", + G_CALLBACK (load_options_cb), + data); + g_signal_connect (G_OBJECT (data->save_button), + "clicked", + G_CALLBACK (save_options_cb), + data); + g_signal_connect (G_OBJECT (data->clear_button), + "clicked", + G_CALLBACK (clear_options_cb), + data); + + gtk_window_set_modal (GTK_WINDOW (file_sel),TRUE); + gtk_widget_show (file_sel); +} + + +/* load/save the dialog options */ + + +static void +dlg_add_folder_save_last_used_options (DialogData *data, + const char *options_path) +{ + g_free (data->last_options); + data->last_options = g_strdup (file_name_from_path (options_path)); +} + + +static void +sync_widgets_with_options (DialogData *data, + const char *base_dir, + const char *filename, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + gboolean update, + gboolean recursive, + gboolean no_symlinks) +{ + if ((base_dir == NULL) || (strcmp (base_dir, "") == 0)) + base_dir = fr_window_get_add_default_dir (data->window); + + if ((filename != NULL) && (strcmp (filename, base_dir) != 0)) + gtk_file_chooser_select_uri (GTK_FILE_CHOOSER (data->dialog), filename); + else + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->dialog), base_dir); + + if (include_files != NULL) + gtk_entry_set_text (GTK_ENTRY (data->include_files_entry), include_files); + if (exclude_files != NULL) + gtk_entry_set_text (GTK_ENTRY (data->exclude_files_entry), exclude_files); + if (exclude_folders != NULL) + gtk_entry_set_text (GTK_ENTRY (data->exclude_folders_entry), exclude_folders); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton), update); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->include_subfold_checkbutton), recursive); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->exclude_symlinks), no_symlinks); +} + + +static void +clear_options_cb (GtkWidget *w, + DialogData *data) +{ + sync_widgets_with_options (data, + gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (data->dialog)), + gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)), + "", + "", + "", + FALSE, + TRUE, + FALSE); +} + + +static gboolean +dlg_add_folder_load_options (DialogData *data, + const char *name) +{ + GFile *options_dir; + GFile *options_file; + char *file_path; + GKeyFile *key_file; + GError *error = NULL; + char *base_dir = NULL; + char *filename = NULL; + char *include_files = NULL; + char *exclude_files = NULL; + char *exclude_folders = NULL; + gboolean update; + gboolean recursive; + gboolean no_symlinks; + + options_dir = get_home_relative_file (RC_OPTIONS_DIR); + options_file = g_file_get_child (options_dir, name); + file_path = g_file_get_path (options_file); + key_file = g_key_file_new (); + if (! g_key_file_load_from_file (key_file, file_path, G_KEY_FILE_KEEP_COMMENTS, &error)) { + if (error->code != G_IO_ERROR_NOT_FOUND) + g_warning ("Could not load options file: %s\n", error->message); + g_clear_error (&error); + g_object_unref (options_file); + g_object_unref (options_dir); + g_key_file_free (key_file); + return FALSE; + } + + base_dir = g_key_file_get_string (key_file, "Options", "base_dir", NULL); + filename = g_key_file_get_string (key_file, "Options", "filename", NULL); + include_files = g_key_file_get_string (key_file, "Options", "include_files", NULL); + exclude_files = g_key_file_get_string (key_file, "Options", "exclude_files", NULL); + exclude_folders = g_key_file_get_string (key_file, "Options", "exclude_folders", NULL); + update = g_key_file_get_boolean (key_file, "Options", "update", NULL); + recursive = g_key_file_get_boolean (key_file, "Options", "recursive", NULL); + no_symlinks = g_key_file_get_boolean (key_file, "Options", "no_symlinks", NULL); + + sync_widgets_with_options (data, + base_dir, + filename, + include_files, + exclude_files, + exclude_folders, + update, + recursive, + no_symlinks); + + dlg_add_folder_save_last_used_options (data, file_path); + + g_free (base_dir); + g_free (filename); + g_free (include_files); + g_free (exclude_files); + g_free (exclude_folders); + g_key_file_free (key_file); + g_free (file_path); + g_object_unref (options_file); + g_object_unref (options_dir); + + return TRUE; +} + + +static void +dlg_add_folder_load_last_options (DialogData *data) +{ + char *base_dir = NULL; + char *filename = NULL; + char *include_files = NULL; + char *exclude_files = NULL; + char *exclude_folders = NULL; + gboolean update; + gboolean recursive; + gboolean no_symlinks; + + base_dir = eel_mateconf_get_string (PREF_ADD_CURRENT_FOLDER, ""); + filename = eel_mateconf_get_string (PREF_ADD_FILENAME, ""); + include_files = eel_mateconf_get_string (PREF_ADD_INCLUDE_FILES, ""); + exclude_files = eel_mateconf_get_string (PREF_ADD_EXCLUDE_FILES, ""); + exclude_folders = eel_mateconf_get_string (PREF_ADD_EXCLUDE_FOLDERS, ""); + update = eel_mateconf_get_boolean (PREF_ADD_UPDATE, FALSE); + recursive = eel_mateconf_get_boolean (PREF_ADD_RECURSIVE, TRUE); + no_symlinks = eel_mateconf_get_boolean (PREF_ADD_NO_SYMLINKS, FALSE); + + sync_widgets_with_options (data, + base_dir, + filename, + include_files, + exclude_files, + exclude_folders, + update, + recursive, + no_symlinks); + + g_free (base_dir); + g_free (filename); + g_free (include_files); + g_free (exclude_files); + g_free (exclude_folders); +} + + +static void +get_options_from_widgets (DialogData *data, + char **base_dir, + char **filename, + const char **include_files, + const char **exclude_files, + const char **exclude_folders, + gboolean *update, + gboolean *recursive, + gboolean *no_symlinks) +{ + *base_dir = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (data->dialog)); + *filename = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)); + *update = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton)); + *recursive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->include_subfold_checkbutton)); + *no_symlinks = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->exclude_symlinks)); + + *include_files = gtk_entry_get_text (GTK_ENTRY (data->include_files_entry)); + if (utf8_only_spaces (*include_files)) + *include_files = ""; + + *exclude_files = gtk_entry_get_text (GTK_ENTRY (data->exclude_files_entry)); + if (utf8_only_spaces (*exclude_files)) + *exclude_files = ""; + + *exclude_folders = gtk_entry_get_text (GTK_ENTRY (data->exclude_folders_entry)); + if (utf8_only_spaces (*exclude_folders)) + *exclude_folders = ""; +} + + +static void +dlg_add_folder_save_current_options (DialogData *data, + GFile *options_file) +{ + char *base_dir; + char *filename; + const char *include_files; + const char *exclude_files; + const char *exclude_folders; + gboolean update; + gboolean recursive; + gboolean no_symlinks; + GKeyFile *key_file; + + get_options_from_widgets (data, + &base_dir, + &filename, + &include_files, + &exclude_files, + &exclude_folders, + &update, + &recursive, + &no_symlinks); + + fr_window_set_add_default_dir (data->window, base_dir); + + key_file = g_key_file_new (); + g_key_file_set_string (key_file, "Options", "base_dir", base_dir); + g_key_file_set_string (key_file, "Options", "filename", filename); + g_key_file_set_string (key_file, "Options", "include_files", include_files); + g_key_file_set_string (key_file, "Options", "exclude_files", exclude_files); + g_key_file_set_string (key_file, "Options", "exclude_folders", exclude_folders); + g_key_file_set_boolean (key_file, "Options", "update", update); + g_key_file_set_boolean (key_file, "Options", "recursive", recursive); + g_key_file_set_boolean (key_file, "Options", "no_symlinks", no_symlinks); + + g_key_file_save (key_file, options_file); + + g_key_file_free (key_file); + g_free (base_dir); + g_free (filename); +} + + +static void +dlg_add_folder_save_last_options (DialogData *data) +{ + char *base_dir; + char *filename; + const char *include_files; + const char *exclude_files; + const char *exclude_folders; + gboolean update; + gboolean recursive; + gboolean no_symlinks; + + get_options_from_widgets (data, + &base_dir, + &filename, + &include_files, + &exclude_files, + &exclude_folders, + &update, + &recursive, + &no_symlinks); + + eel_mateconf_set_string (PREF_ADD_CURRENT_FOLDER, base_dir); + eel_mateconf_set_string (PREF_ADD_FILENAME, filename); + eel_mateconf_set_string (PREF_ADD_INCLUDE_FILES, include_files); + eel_mateconf_set_string (PREF_ADD_EXCLUDE_FILES, exclude_files); + eel_mateconf_set_string (PREF_ADD_EXCLUDE_FOLDERS, exclude_folders); + eel_mateconf_set_boolean (PREF_ADD_UPDATE, update); + eel_mateconf_set_boolean (PREF_ADD_RECURSIVE, recursive); + eel_mateconf_set_boolean (PREF_ADD_NO_SYMLINKS, no_symlinks); + + g_free (base_dir); + g_free (filename); +} + + +typedef struct { + DialogData *data; + GtkBuilder *builder; + GtkWidget *dialog; + GtkWidget *aod_treeview; + GtkTreeModel *aod_model; +} LoadOptionsDialogData; + + +static void +aod_destroy_cb (GtkWidget *widget, + LoadOptionsDialogData *aod_data) +{ + g_object_unref (aod_data->builder); + g_free (aod_data); +} + + +static void +aod_apply_cb (GtkWidget *widget, + gpointer callback_data) +{ + LoadOptionsDialogData *aod_data = callback_data; + DialogData *data = aod_data->data; + GtkTreeSelection *selection; + GtkTreeIter iter; + char *options_name; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aod_data->aod_treeview)); + if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (aod_data->aod_model, &iter, 1, &options_name, -1); + + dlg_add_folder_load_options (data, options_name); + g_free (options_name); + + gtk_widget_destroy (aod_data->dialog); +} + + +static void +aod_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer callback_data) +{ + aod_apply_cb (NULL, callback_data); +} + + +static void +aod_update_option_list (LoadOptionsDialogData *aod_data) +{ + GtkListStore *list_store = GTK_LIST_STORE (aod_data->aod_model); + GFile *options_dir; + GFileEnumerator *file_enum; + GFileInfo *info; + GError *err = NULL; + + gtk_list_store_clear (list_store); + + options_dir = get_home_relative_file (RC_OPTIONS_DIR); + make_directory_tree (options_dir, 0700, NULL); + + file_enum = g_file_enumerate_children (options_dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &err); + if (err != NULL) { + g_warning ("Failed to enumerate children: %s", err->message); + g_clear_error (&err); + g_object_unref (options_dir); + return; + } + + while ((info = g_file_enumerator_next_file (file_enum, NULL, &err)) != NULL) { + const char *name; + char *display_name; + GtkTreeIter iter; + + if (err != NULL) { + g_warning ("Failed to get info while enumerating: %s", err->message); + g_clear_error (&err); + continue; + } + + name = g_file_info_get_name (info); + display_name = g_filename_display_name (name); + + gtk_list_store_append (GTK_LIST_STORE (aod_data->aod_model), &iter); + gtk_list_store_set (GTK_LIST_STORE (aod_data->aod_model), &iter, + 0, name, + 1, display_name, + -1); + + g_free (display_name); + g_object_unref (info); + } + + if (err != NULL) { + g_warning ("Failed to get info after enumeration: %s", err->message); + g_clear_error (&err); + } + + g_object_unref (options_dir); +} + + +static void +aod_remove_cb (GtkWidget *widget, + LoadOptionsDialogData *aod_data) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + char *filename; + GFile *options_dir; + GFile *options_file; + GError *error = NULL; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aod_data->aod_treeview)); + if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (aod_data->aod_model, &iter, 1, &filename, -1); + gtk_list_store_remove (GTK_LIST_STORE (aod_data->aod_model), &iter); + + options_dir = get_home_relative_file (RC_OPTIONS_DIR); + options_file = g_file_get_child (options_dir, filename); + if (! g_file_delete (options_file, NULL, &error)) { + g_warning ("could not delete the options: %s", error->message); + g_clear_error (&error); + } + + g_object_unref (options_file); + g_object_unref (options_dir); + g_free (filename); +} + + +static void +load_options_cb (GtkWidget *w, + DialogData *data) +{ + LoadOptionsDialogData *aod_data; + GtkWidget *ok_button; + GtkWidget *cancel_button; + GtkWidget *remove_button; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + aod_data = g_new0 (LoadOptionsDialogData, 1); + + aod_data->data = data; + aod_data->builder = _gtk_builder_new_from_file ("add-options.ui"); + if (aod_data->builder == NULL) { + g_free (aod_data); + return; + } + + /* Get the widgets. */ + + aod_data->dialog = _gtk_builder_get_widget (aod_data->builder, "add_options_dialog"); + aod_data->aod_treeview = _gtk_builder_get_widget (aod_data->builder, "aod_treeview"); + + ok_button = _gtk_builder_get_widget (aod_data->builder, "aod_okbutton"); + cancel_button = _gtk_builder_get_widget (aod_data->builder, "aod_cancelbutton"); + remove_button = _gtk_builder_get_widget (aod_data->builder, "aod_remove_button"); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (aod_data->dialog), + "destroy", + G_CALLBACK (aod_destroy_cb), + aod_data); + g_signal_connect (G_OBJECT (aod_data->aod_treeview), + "row_activated", + G_CALLBACK (aod_activated_cb), + aod_data); + g_signal_connect_swapped (G_OBJECT (cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (aod_data->dialog)); + g_signal_connect (G_OBJECT (ok_button), + "clicked", + G_CALLBACK (aod_apply_cb), + aod_data); + g_signal_connect (G_OBJECT (remove_button), + "clicked", + G_CALLBACK (aod_remove_cb), + aod_data); + + /* Set data. */ + + aod_data->aod_model = GTK_TREE_MODEL (gtk_list_store_new (2, + G_TYPE_STRING, + G_TYPE_STRING)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (aod_data->aod_model), + 0, + GTK_SORT_ASCENDING); + gtk_tree_view_set_model (GTK_TREE_VIEW (aod_data->aod_treeview), + aod_data->aod_model); + g_object_unref (aod_data->aod_model); + + /**/ + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, + "text", 0, + NULL); + gtk_tree_view_column_set_sort_column_id (column, 0); + gtk_tree_view_append_column (GTK_TREE_VIEW (aod_data->aod_treeview), + column); + + aod_update_option_list (aod_data); + + /* Run */ + + gtk_window_set_transient_for (GTK_WINDOW (aod_data->dialog), + GTK_WINDOW (data->dialog)); + gtk_window_set_modal (GTK_WINDOW (aod_data->dialog), TRUE); + gtk_widget_show (aod_data->dialog); +} + + +static void +save_options_cb (GtkWidget *w, + DialogData *data) +{ + GFile *options_dir; + GFile *options_file; + char *opt_filename; + + options_dir = get_home_relative_file (RC_OPTIONS_DIR); + make_directory_tree (options_dir, 0700, NULL); + + opt_filename = _gtk_request_dialog_run ( + GTK_WINDOW (data->dialog), + GTK_DIALOG_MODAL, + _("Save Options"), + _("Options Name:"), + (data->last_options != NULL) ? data->last_options : "", + 1024, + GTK_STOCK_CANCEL, + GTK_STOCK_SAVE); + if (opt_filename == NULL) + return; + + options_file = g_file_get_child_for_display_name (options_dir, opt_filename, NULL); + dlg_add_folder_save_current_options (data, options_file); + dlg_add_folder_save_last_used_options (data, opt_filename); + + g_free (opt_filename); + g_object_unref (options_file); + g_object_unref (options_dir); +} diff --git a/src/dlg-add-folder.h b/src/dlg-add-folder.h new file mode 100644 index 0000000..44720a3 --- /dev/null +++ b/src/dlg-add-folder.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_ADD_FOLDER_H +#define DLG_ADD_FOLDER_H + +#include <gtk/gtk.h> + +void add_folder_cb (GtkWidget *widget, void *data); + +#endif /* DLG_ADD_FOLDER_H */ diff --git a/src/dlg-ask-password.c b/src/dlg-ask-password.c new file mode 100644 index 0000000..b5590dc --- /dev/null +++ b/src/dlg-ask-password.c @@ -0,0 +1,170 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> + +#include <gtk/gtk.h> +#include "file-utils.h" +#include "glib-utils.h" +#include "gtk-utils.h" +#include "fr-window.h" + + +typedef enum { + FR_PASSWORD_TYPE_MAIN, + FR_PASSWORD_TYPE_PASTE_FROM +} FrPasswordType; + +typedef struct { + GtkBuilder *builder; + FrWindow *window; + FrPasswordType pwd_type; + GtkWidget *dialog; + GtkWidget *pw_password_entry; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DialogData *data) +{ + g_object_unref (data->builder); + g_free (data); +} + + +static void +ask_password__response_cb (GtkWidget *dialog, + int response_id, + DialogData *data) +{ + char *password; + + switch (response_id) { + case GTK_RESPONSE_OK: + password = _gtk_entry_get_locale_text (GTK_ENTRY (data->pw_password_entry)); + if (data->pwd_type == FR_PASSWORD_TYPE_MAIN) + fr_window_set_password (data->window, password); + else if (data->pwd_type == FR_PASSWORD_TYPE_PASTE_FROM) + fr_window_set_password_for_paste (data->window, password); + g_free (password); + if (fr_window_is_batch_mode (data->window)) + fr_window_resume_batch (data->window); + else + fr_window_restart_current_batch_action (data->window); + break; + + default: + if (fr_window_is_batch_mode (data->window)) + gtk_widget_destroy (GTK_WIDGET (data->window)); + else + fr_window_reset_current_batch_action (data->window); + break; + } + + gtk_widget_destroy (data->dialog); +} + + +static void +dlg_ask_password__common (FrWindow *window, + FrPasswordType pwd_type) +{ + DialogData *data; + GtkWidget *label; + char *text; + char *name; + + data = g_new0 (DialogData, 1); + + data->builder = _gtk_builder_new_from_file ("batch-password.ui"); + if (data->builder == NULL) { + g_free (data); + return; + } + + data->window = window; + data->pwd_type = pwd_type; + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "password_dialog"); + data->pw_password_entry = _gtk_builder_get_widget (data->builder, "pw_password_entry"); + + label = _gtk_builder_get_widget (data->builder, "pw_password_label"); + + /* Set widgets data. */ + + if (data->pwd_type == FR_PASSWORD_TYPE_MAIN) + name = g_uri_display_basename (fr_window_get_archive_uri (window)); + else if (data->pwd_type == FR_PASSWORD_TYPE_PASTE_FROM) + name = g_uri_display_basename (fr_window_get_paste_archive_uri (window)); + text = g_strdup_printf (_("Enter the password for the archive '%s'."), name); + gtk_label_set_label (GTK_LABEL (label), text); + g_free (text); + + if (fr_window_get_password (window) != NULL) + _gtk_entry_set_locale_text (GTK_ENTRY (data->pw_password_entry), + fr_window_get_password (window)); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + + g_signal_connect (G_OBJECT (data->dialog), + "response", + G_CALLBACK (ask_password__response_cb), + data); + + /* Run dialog. */ + + gtk_widget_grab_focus (data->pw_password_entry); + if (gtk_widget_get_realized (GTK_WIDGET (window))) { + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), + GTK_WINDOW (window)); + gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE); + } + else + gtk_window_set_title (GTK_WINDOW (data->dialog), name); + g_free (name); + + gtk_widget_show (data->dialog); +} + + +void +dlg_ask_password (FrWindow *window) +{ + dlg_ask_password__common (window, FR_PASSWORD_TYPE_MAIN); +} + + +void +dlg_ask_password_for_paste_operation (FrWindow *window) +{ + dlg_ask_password__common (window, FR_PASSWORD_TYPE_PASTE_FROM); +} diff --git a/src/dlg-ask-password.h b/src/dlg-ask-password.h new file mode 100644 index 0000000..83fee75 --- /dev/null +++ b/src/dlg-ask-password.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_PASSWORD_H +#define DLG_PASSWORD_H + +#include "fr-window.h" + +void dlg_ask_password (FrWindow *window); +void dlg_ask_password_for_paste_operation (FrWindow *window); + +#endif /* DLG_PASSWORD_H */ diff --git a/src/dlg-batch-add.c b/src/dlg-batch-add.c new file mode 100644 index 0000000..47d11e2 --- /dev/null +++ b/src/dlg-batch-add.c @@ -0,0 +1,611 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <math.h> +#include <unistd.h> +#include <gio/gio.h> +#include <gtk/gtk.h> +#include "file-utils.h" +#include "fr-stock.h" +#include "mateconf-utils.h" +#include "fr-window.h" +#include "typedefs.h" +#include "gtk-utils.h" +#include "glib-utils.h" +#include "preferences.h" +#include "main.h" + + +#define ARCHIVE_ICON_SIZE (48) +#define DEFAULT_EXTENSION ".tar.gz" +#define BAD_CHARS "/\\*" + + +typedef struct { + FrWindow *window; + GtkBuilder *builder; + int *supported_types; + + GtkWidget *dialog; + GtkWidget *a_add_to_entry; + GtkWidget *a_location_filechooserbutton; + GtkWidget *add_image; + GtkWidget *a_archive_type_combo_box; + GtkWidget *a_other_options_expander; + GtkWidget *a_password_entry; + GtkWidget *a_password_label; + GtkWidget *a_encrypt_header_checkbutton; + GtkWidget *a_volume_checkbutton; + GtkWidget *a_volume_spinbutton; + GtkWidget *a_volume_box; + + GList *file_list; + gboolean add_clicked; + const char *last_mime_type; + gboolean single_file; +} DialogData; + + +static const char * +get_ext (DialogData *data) +{ + int idx; + + idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->a_archive_type_combo_box)); + + return mime_type_desc[data->supported_types[idx]].default_ext; +} + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DialogData *data) +{ + eel_mateconf_set_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, get_ext (data)); + /*eel_mateconf_set_boolean (PREF_BATCH_OTHER_OPTIONS, data->add_clicked ? FALSE : gtk_expander_get_expanded (GTK_EXPANDER (data->a_other_options_expander)));*/ + eel_mateconf_set_boolean (PREF_ENCRYPT_HEADER, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton))); + + if (! data->add_clicked) { + fr_window_pop_message (data->window); + fr_window_stop_batch (data->window); + } + + g_object_unref (data->builder); + g_free (data); +} + + +static void +set_archive_options (DialogData *data) +{ + int idx; + + idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->a_archive_type_combo_box)); + if (mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_ENCRYPT) { + const char *pwd; + + pwd = gtk_entry_get_text (GTK_ENTRY (data->a_password_entry)); + if (pwd != NULL) { + if (strcmp (pwd, "") != 0) { + fr_window_set_password (data->window, pwd); + if (mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER) + fr_window_set_encrypt_header (data->window, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton))); + } + } + } + + if ((mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES) + && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_volume_checkbutton))) + { + double value; + int size; + + value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (data->a_volume_spinbutton)); + size = floor (value * MEGABYTE); + eel_mateconf_set_integer (PREF_BATCH_VOLUME_SIZE, size); + fr_window_set_volume_size (data->window, (guint) size); + } +} + + +static void +help_clicked_cb (GtkWidget *widget, + DialogData *data) +{ + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-fmgr-add"); +} + + +static void +add_clicked_cb (GtkWidget *widget, + DialogData *data) +{ + FrWindow *window = data->window; + char *archive_name; + char *archive_dir; + char *archive_file; + char *tmp; + const char *archive_ext; + gboolean do_not_add = FALSE; + GError *error = NULL; + + data->add_clicked = TRUE; + + /* Collect data */ + + archive_name = g_uri_escape_string (gtk_entry_get_text (GTK_ENTRY (data->a_add_to_entry)), NULL, FALSE); + + /* Check whether the user entered a valid archive name. */ + + if ((archive_name == NULL) || (*archive_name == '\0')) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + "%s", + _("You have to specify an archive name.")); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + g_free (archive_name); + + return; + } + else if (strchrs (archive_name, BAD_CHARS)) { + GtkWidget *d; + char *utf8_name = g_filename_display_name (archive_name); + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + _("The name \"%s\" is not valid because it cannot contain the characters: %s\n\n%s"), + utf8_name, + BAD_CHARS, + _("Please use a different name.")); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (utf8_name); + g_free (archive_name); + + return; + } + + /* Check directory existence. */ + + archive_dir = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->a_location_filechooserbutton)); + if (archive_dir == NULL) { + g_free (archive_dir); + g_free (archive_name); + return; + } + + if (! check_permissions (archive_dir, R_OK|W_OK|X_OK)) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + "%s", + _("You don't have the right permissions to create an archive in the destination folder.")); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (archive_dir); + g_free (archive_name); + return; + } + + if (! uri_is_dir (archive_dir)) { + GtkWidget *d; + int r; + char *folder_name; + char *msg; + + folder_name = g_filename_display_name (archive_dir); + msg = g_strdup_printf (_("Destination folder \"%s\" does not exist.\n\nDo you want to create it?"), folder_name); + g_free (folder_name); + + d = _gtk_message_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + msg, + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Create _Folder"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (msg); + + do_not_add = (r != GTK_RESPONSE_YES); + } + + if (! do_not_add && ! ensure_dir_exists (archive_dir, 0755, &error)) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + _("Could not create the destination folder: %s."), + error->message); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_error_free (error); + g_free (archive_dir); + g_free (archive_name); + return; + } + + if (do_not_add) { + GtkWidget *d; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_DIALOG_WARNING, + _("Archive not created"), + NULL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (archive_dir); + g_free (archive_name); + + return; + } + + /**/ + + archive_ext = get_ext (data); + tmp = archive_name; + archive_name = g_strconcat (tmp, archive_ext, NULL); + g_free (tmp); + archive_file = g_strconcat (archive_dir, "/", archive_name, NULL); + + if (uri_is_dir (archive_file)) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Could not create the archive"), + "%s", + _("You have to specify an archive name.")); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (archive_name); + g_free (archive_dir); + g_free (archive_file); + + return; + } + + if (uri_exists (archive_file)) { + GtkWidget *d; + int r; + + d = _gtk_message_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + _("The archive is already present. Do you want to overwrite it?"), + NULL, + GTK_STOCK_NO, GTK_RESPONSE_NO, + _("_Overwrite"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + if (r == GTK_RESPONSE_YES) { + GFile *file; + GError *err = NULL; + + /* FIXME: convert this code in a function in file-utils.c */ + file = g_file_new_for_uri (archive_file); + g_file_delete (file, NULL, &err); + if (err != NULL) { + g_warning ("Failed to delete file %s: %s", + archive_file, + err->message); + g_clear_error (&err); + } + g_object_unref (file); + } + else { + g_free (archive_name); + g_free (archive_dir); + g_free (archive_file); + return; + } + } + set_archive_options (data); + gtk_widget_destroy (data->dialog); + + fr_window_archive_new (window, archive_file); + + g_free (archive_name); + g_free (archive_dir); + g_free (archive_file); +} + + +static void +update_sensitivity_for_mime_type (DialogData *data, + const char *mime_type) +{ + int i; + + if (mime_type == NULL) { + gtk_widget_set_sensitive (data->a_password_entry, FALSE); + gtk_widget_set_sensitive (data->a_password_label, FALSE); + gtk_widget_set_sensitive (data->a_encrypt_header_checkbutton, FALSE); + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton), TRUE); + gtk_widget_set_sensitive (data->a_volume_box, FALSE); + return; + } + + for (i = 0; mime_type_desc[i].mime_type != NULL; i++) { + if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) { + gboolean sensitive; + + sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT; + gtk_widget_set_sensitive (data->a_password_entry, sensitive); + gtk_widget_set_sensitive (data->a_password_label, sensitive); + + sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER; + gtk_widget_set_sensitive (data->a_encrypt_header_checkbutton, sensitive); + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton), ! sensitive); + + sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES; + gtk_widget_set_sensitive (data->a_volume_box, sensitive); + + break; + } + } +} + + +static void +archive_type_combo_box_changed_cb (GtkComboBox *combo_box, + DialogData *data) +{ + const char *mime_type; + int idx = gtk_combo_box_get_active (combo_box); + + mime_type = mime_type_desc[data->supported_types[idx]].mime_type; + + gtk_image_set_from_pixbuf (GTK_IMAGE (data->add_image), get_mime_type_pixbuf (mime_type, ARCHIVE_ICON_SIZE, NULL)); + update_sensitivity_for_mime_type (data, mime_type); +} + + +static void +update_archive_type_combo_box_from_ext (DialogData *data, + const char *ext) +{ + int idx = 0; + int i; + + if (ext == NULL) { + gtk_combo_box_set_active (GTK_COMBO_BOX (data->a_archive_type_combo_box), 0); + return; + } + + for (i = 0; data->supported_types[i] != -1; i++) + if (strcmp (ext, mime_type_desc[data->supported_types[i]].default_ext) == 0) { + idx = i; + break; + } + + gtk_combo_box_set_active (GTK_COMBO_BOX (data->a_archive_type_combo_box), idx); +} + + +static void +update_sensitivity (DialogData *data) +{ + gtk_widget_set_sensitive (data->a_volume_spinbutton, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_volume_checkbutton))); +} + + +static void +password_entry_changed_cb (GtkEditable *editable, + gpointer user_data) +{ + update_sensitivity ((DialogData *) user_data); +} + + +static void +volume_toggled_cb (GtkToggleButton *toggle_button, + gpointer user_data) +{ + update_sensitivity ((DialogData *) user_data); +} + + +void +dlg_batch_add_files (FrWindow *window, + GList *file_list) +{ + DialogData *data; + GtkWidget *cancel_button; + GtkWidget *help_button; + GtkWidget *add_button; + GtkWidget *a_archive_type_box; + GtkSizeGroup *size_group; + char *automatic_name = NULL; + char *default_ext; + const char *first_filename; + char *parent; + int i; + + if (file_list == NULL) + return; + + data = g_new0 (DialogData, 1); + + data->builder = _gtk_builder_new_from_file ("batch-add-files.ui"); + if (data->builder == NULL) { + g_free (data); + return; + } + + data->window = window; + data->file_list = file_list; + data->single_file = ((file_list->next == NULL) && uri_is_file ((char*) file_list->data)); + data->add_clicked = FALSE; + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "batch_add_files_dialog"); + data->a_add_to_entry = _gtk_builder_get_widget (data->builder, "a_add_to_entry"); + data->a_location_filechooserbutton = _gtk_builder_get_widget (data->builder, "a_location_filechooserbutton"); + data->a_password_entry = _gtk_builder_get_widget (data->builder, "a_password_entry"); + data->a_password_label = _gtk_builder_get_widget (data->builder, "a_password_label"); + data->a_other_options_expander = _gtk_builder_get_widget (data->builder, "a_other_options_expander"); + data->a_encrypt_header_checkbutton = _gtk_builder_get_widget (data->builder, "a_encrypt_header_checkbutton"); + + data->a_volume_checkbutton = _gtk_builder_get_widget (data->builder, "a_volume_checkbutton"); + data->a_volume_spinbutton = _gtk_builder_get_widget (data->builder, "a_volume_spinbutton"); + data->a_volume_box = _gtk_builder_get_widget (data->builder, "a_volume_box"); + + add_button = _gtk_builder_get_widget (data->builder, "a_add_button"); + cancel_button = _gtk_builder_get_widget (data->builder, "a_cancel_button"); + help_button = _gtk_builder_get_widget (data->builder, "a_help_button"); + a_archive_type_box = _gtk_builder_get_widget (data->builder, "a_archive_type_box"); + + data->add_image = _gtk_builder_get_widget (data->builder, "a_add_image"); + + /* Set widgets data. */ + + size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "a_archive_label")); + gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "a_location_label")); + gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "a_password_label")); + + gtk_button_set_use_stock (GTK_BUTTON (add_button), TRUE); + gtk_button_set_label (GTK_BUTTON (add_button), FR_STOCK_CREATE_ARCHIVE); + gtk_expander_set_expanded (GTK_EXPANDER (data->a_other_options_expander), FALSE /*eel_mateconf_get_boolean (PREF_BATCH_OTHER_OPTIONS, FALSE)*/); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton), eel_mateconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE)); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->a_volume_spinbutton), (double) eel_mateconf_get_integer (PREF_BATCH_VOLUME_SIZE, 0) / MEGABYTE); + + first_filename = (char*) file_list->data; + parent = remove_level_from_path (first_filename); + + if (file_list->next == NULL) + automatic_name = g_uri_unescape_string (file_name_from_path ((char*) file_list->data), NULL); + else { + automatic_name = g_uri_unescape_string (file_name_from_path (parent), NULL); + if ((automatic_name == NULL) || (automatic_name[0] == '\0')) { + g_free (automatic_name); + automatic_name = g_uri_unescape_string (file_name_from_path (first_filename), NULL); + } + } + + _gtk_entry_set_filename_text (GTK_ENTRY (data->a_add_to_entry), automatic_name); + g_free (automatic_name); + + if (check_permissions (parent, R_OK | W_OK)) + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->a_location_filechooserbutton), parent); + else + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->a_location_filechooserbutton), get_home_uri ()); + g_free (parent); + + /* archive type combobox */ + + data->a_archive_type_combo_box = gtk_combo_box_new_text (); + if (data->single_file) + data->supported_types = single_file_save_type; + else + data->supported_types = save_type; + sort_mime_types_by_extension (data->supported_types); + + for (i = 0; data->supported_types[i] != -1; i++) + gtk_combo_box_append_text (GTK_COMBO_BOX (data->a_archive_type_combo_box), + mime_type_desc[data->supported_types[i]].default_ext); + + gtk_box_pack_start (GTK_BOX (a_archive_type_box), data->a_archive_type_combo_box, TRUE, TRUE, 0); + gtk_widget_show_all (a_archive_type_box); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + g_signal_connect_swapped (G_OBJECT (cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->dialog)); + g_signal_connect (G_OBJECT (add_button), + "clicked", + G_CALLBACK (add_clicked_cb), + data); + g_signal_connect (G_OBJECT (help_button), + "clicked", + G_CALLBACK (help_clicked_cb), + data); + g_signal_connect (G_OBJECT (data->a_archive_type_combo_box), + "changed", + G_CALLBACK (archive_type_combo_box_changed_cb), + data); + g_signal_connect (G_OBJECT (data->a_password_entry), + "changed", + G_CALLBACK (password_entry_changed_cb), + data); + g_signal_connect (G_OBJECT (data->a_volume_checkbutton), + "toggled", + G_CALLBACK (volume_toggled_cb), + data); + + /* Run dialog. */ + + default_ext = eel_mateconf_get_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, DEFAULT_EXTENSION); + update_archive_type_combo_box_from_ext (data, default_ext); + g_free (default_ext); + + gtk_widget_grab_focus (data->a_add_to_entry); + gtk_editable_select_region (GTK_EDITABLE (data->a_add_to_entry), + 0, -1); + + update_sensitivity (data); + + gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE); + gtk_window_present (GTK_WINDOW (data->dialog)); +} diff --git a/src/dlg-batch-add.h b/src/dlg-batch-add.h new file mode 100644 index 0000000..d63a545 --- /dev/null +++ b/src/dlg-batch-add.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_BATCH_ADD_H +#define DLG_BATCH_ADD_H + +#include "fr-window.h" + +void dlg_batch_add_files (FrWindow *window, + GList *file_list); + +#endif /* DLG_BATCH_ADD_H */ diff --git a/src/dlg-delete.c b/src/dlg-delete.c new file mode 100644 index 0000000..dc4f5d7 --- /dev/null +++ b/src/dlg-delete.c @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <gtk/gtk.h> +#include "fr-window.h" +#include "gtk-utils.h" +#include "file-utils.h" + + +typedef struct { + FrWindow *window; + GList *selected_files; + GtkBuilder *builder; + + GtkWidget *dialog; + GtkWidget *d_all_files_radio; + GtkWidget *d_selected_files_radio; + GtkWidget *d_files_radio; + GtkWidget *d_files_entry; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DialogData *data) +{ + path_list_free (data->selected_files); + g_object_unref (G_OBJECT (data->builder)); + g_free (data); +} + + +/* called when the "ok" button is pressed. */ +static void +ok_clicked_cb (GtkWidget *widget, + DialogData *data) +{ + gboolean selected_files; + gboolean pattern_files; + FrWindow *window = data->window; + GList *file_list = NULL; + gboolean do_not_remove_if_null = FALSE; + + selected_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->d_selected_files_radio)); + pattern_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->d_files_radio)); + + /* create the file list. */ + + if (selected_files) { + file_list = data->selected_files; + data->selected_files = NULL; /* do not the list when destroying the dialog. */ + } + else if (pattern_files) { + const char *pattern; + + pattern = gtk_entry_get_text (GTK_ENTRY (data->d_files_entry)); + file_list = fr_window_get_file_list_pattern (window, pattern); + if (file_list == NULL) + do_not_remove_if_null = TRUE; + } + + /* close the dialog. */ + + gtk_widget_destroy (data->dialog); + + /* remove ! */ + + if (! do_not_remove_if_null || (file_list != NULL)) + fr_window_archive_remove (window, file_list); + + path_list_free (file_list); +} + + +static void +entry_changed_cb (GtkWidget *widget, + DialogData *data) +{ + if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->d_files_radio))) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->d_files_radio), TRUE); +} + + +static void +dlg_delete__common (FrWindow *window, + GList *selected_files) +{ + DialogData *data; + GtkWidget *cancel_button; + GtkWidget *ok_button; + + data = g_new (DialogData, 1); + data->window = window; + data->selected_files = selected_files; + + data->builder = _gtk_builder_new_from_file ("delete.ui"); + if (data->builder == NULL) { + g_free (data); + return; + } + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "delete_dialog"); + data->d_all_files_radio = _gtk_builder_get_widget (data->builder, "d_all_files_radio"); + data->d_selected_files_radio = _gtk_builder_get_widget (data->builder, "d_selected_files_radio"); + data->d_files_radio = _gtk_builder_get_widget (data->builder, "d_files_radio"); + data->d_files_entry = _gtk_builder_get_widget (data->builder, "d_files_entry"); + + ok_button = _gtk_builder_get_widget (data->builder, "d_ok_button"); + cancel_button = _gtk_builder_get_widget (data->builder, "d_cancel_button"); + + /* Set widgets data. */ + + if (data->selected_files != NULL) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->d_selected_files_radio), TRUE); + else { + gtk_widget_set_sensitive (data->d_selected_files_radio, FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->d_all_files_radio), TRUE); + } + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + g_signal_connect_swapped (G_OBJECT (cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->dialog)); + g_signal_connect (G_OBJECT (ok_button), + "clicked", + G_CALLBACK (ok_clicked_cb), + data); + g_signal_connect (G_OBJECT (data->d_files_entry), + "changed", + G_CALLBACK (entry_changed_cb), + data); + + /* Run dialog. */ + + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), + GTK_WINDOW (window)); + gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE); + + gtk_widget_show (data->dialog); +} + + +void +dlg_delete (GtkWidget *widget, + gpointer callback_data) +{ + FrWindow *window = callback_data; + dlg_delete__common (window, + fr_window_get_file_list_selection (window, TRUE, NULL)); +} + + +void +dlg_delete_from_sidebar (GtkWidget *widget, + gpointer callback_data) +{ + FrWindow *window = callback_data; + dlg_delete__common (window, + fr_window_get_folder_tree_selection (window, TRUE, NULL)); +} diff --git a/src/dlg-delete.h b/src/dlg-delete.h new file mode 100644 index 0000000..488fad0 --- /dev/null +++ b/src/dlg-delete.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_DELETE_H +#define DLG_DELETE_H + + +void dlg_delete (GtkWidget *widget, gpointer data); +void dlg_delete_from_sidebar (GtkWidget *widget, gpointer data); + + +#endif /* DLG_DELETE_H */ diff --git a/src/dlg-extract.c b/src/dlg-extract.c new file mode 100644 index 0000000..f4dd75b --- /dev/null +++ b/src/dlg-extract.c @@ -0,0 +1,516 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004, 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <unistd.h> +#include "file-utils.h" +#include "fr-stock.h" +#include "main.h" +#include "gtk-utils.h" +#include "fr-window.h" +#include "typedefs.h" +#include "mateconf-utils.h" + + +typedef struct { + FrWindow *window; + GList *selected_files; + char *base_dir_for_selection; + + GtkWidget *dialog; + + GtkWidget *e_main_vbox; + GtkWidget *e_all_radiobutton; + GtkWidget *e_selected_radiobutton; + GtkWidget *e_files_radiobutton; + GtkWidget *e_files_entry; + GtkWidget *e_recreate_dir_checkbutton; + GtkWidget *e_overwrite_checkbutton; + GtkWidget *e_not_newer_checkbutton; + + gboolean extract_clicked; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DialogData *data) +{ + if (! data->extract_clicked) { + fr_window_pop_message (data->window); + fr_window_stop_batch (data->window); + } + path_list_free (data->selected_files); + g_free (data->base_dir_for_selection); + g_free (data); +} + + +static int +extract_cb (GtkWidget *w, + DialogData *data) +{ + FrWindow *window = data->window; + gboolean do_not_extract = FALSE; + char *extract_to_dir; + gboolean overwrite; + gboolean skip_newer; + gboolean selected_files; + gboolean pattern_files; + gboolean junk_paths; + GList *file_list; + char *base_dir = NULL; + GError *error = NULL; + + data->extract_clicked = TRUE; + + /* collect extraction options. */ + + extract_to_dir = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)); + + /* check directory existence. */ + + if (! uri_is_dir (extract_to_dir)) { + if (! ForceDirectoryCreation) { + GtkWidget *d; + int r; + char *folder_name; + char *msg; + + folder_name = g_filename_display_name (extract_to_dir); + msg = g_strdup_printf (_("Destination folder \"%s\" does not exist.\n\nDo you want to create it?"), folder_name); + g_free (folder_name); + + d = _gtk_message_dialog_new (GTK_WINDOW (data->dialog), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + msg, + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Create _Folder"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (msg); + + if (r != GTK_RESPONSE_YES) + do_not_extract = TRUE; + } + + if (! do_not_extract && ! ensure_dir_exists (extract_to_dir, 0755, &error)) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Extraction not performed"), + _("Could not create the destination folder: %s."), + error->message); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_error_free (error); + + return FALSE; + } + } + + if (do_not_extract) { + GtkWidget *d; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_DIALOG_WARNING, + _("Extraction not performed"), + NULL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + if (fr_window_is_batch_mode (data->window)) + gtk_widget_destroy (data->dialog); + + return FALSE; + } + + /* check extraction directory permissions. */ + + if (uri_is_dir (extract_to_dir) + && ! check_permissions (extract_to_dir, R_OK | W_OK)) + { + GtkWidget *d; + char *utf8_path; + + utf8_path = g_filename_display_name (extract_to_dir); + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + _("Extraction not performed"), + _("You don't have the right permissions to extract archives in the folder \"%s\""), + utf8_path); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (utf8_path); + g_free (extract_to_dir); + + return FALSE; + } + + fr_window_set_extract_default_dir (window, extract_to_dir, TRUE); + + overwrite = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton)); + skip_newer = ! gtk_toggle_button_get_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton)) && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton)); + junk_paths = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_recreate_dir_checkbutton)); + + eel_mateconf_set_boolean (PREF_EXTRACT_OVERWRITE, overwrite); + if (!gtk_toggle_button_get_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton))) + eel_mateconf_set_boolean (PREF_EXTRACT_SKIP_NEWER, skip_newer); + eel_mateconf_set_boolean (PREF_EXTRACT_RECREATE_FOLDERS, !junk_paths); + + selected_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton)); + pattern_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_files_radiobutton)); + + /* create the file list. */ + + file_list = NULL; + + if (selected_files) { + file_list = data->selected_files; + data->selected_files = NULL; /* do not the list when destroying the dialog. */ + } + else if (pattern_files) { + const char *pattern; + + pattern = gtk_entry_get_text (GTK_ENTRY (data->e_files_entry)); + file_list = fr_window_get_file_list_pattern (window, pattern); + if (file_list == NULL) { + gtk_widget_destroy (data->dialog); + g_free (extract_to_dir); + return FALSE; + } + } + + if (selected_files) { + base_dir = data->base_dir_for_selection; + data->base_dir_for_selection = NULL; + } + else + base_dir = NULL; + + /* close the dialog. */ + + gtk_widget_destroy (data->dialog); + + /* extract ! */ + + fr_window_archive_extract (window, + file_list, + extract_to_dir, + base_dir, + skip_newer, + overwrite, + junk_paths, + TRUE); + + path_list_free (file_list); + g_free (extract_to_dir); + g_free (base_dir); + + return TRUE; +} + + +static int +file_sel_response_cb (GtkWidget *widget, + int response, + DialogData *data) +{ + if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) { + gtk_widget_destroy (data->dialog); + return TRUE; + } + + if (response == GTK_RESPONSE_HELP) { + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-extract-options"); + return TRUE; + } + + if (response == GTK_RESPONSE_OK) + return extract_cb (widget, data); + + return FALSE; +} + + +static void +files_entry_changed_cb (GtkWidget *widget, + DialogData *data) +{ + if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_files_radiobutton))) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_files_radiobutton), TRUE); +} + + +static void +overwrite_toggled_cb (GtkToggleButton *button, + DialogData *data) +{ + gboolean active = gtk_toggle_button_get_active (button); + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton), !active); + gtk_widget_set_sensitive (data->e_not_newer_checkbutton, active); +} + + +static void +set_bold_label (GtkWidget *label, + const char *label_txt) +{ + char *bold_label; + + bold_label = g_strconcat ("<b>", label_txt, "</b>", NULL); + gtk_label_set_markup (GTK_LABEL (label), bold_label); + g_free (bold_label); +} + + +static GtkWidget * +create_extra_widget (DialogData *data) +{ + GtkWidget *vbox1; + GtkWidget *hbox28; + GtkWidget *vbox19; + GtkWidget *e_files_label; + GtkWidget *hbox29; + GtkWidget *label47; + GtkWidget *table1; + GSList *e_files_radiobutton_group = NULL; + GtkWidget *vbox20; + GtkWidget *e_actions_label; + GtkWidget *hbox30; + GtkWidget *label48; + GtkWidget *vbox15; + + vbox1 = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox1), 0); + + hbox28 = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (vbox1), hbox28, TRUE, TRUE, 0); + + vbox19 = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox28), vbox19, TRUE, TRUE, 0); + + e_files_label = gtk_label_new (""); + set_bold_label (e_files_label, _("Extract")); + gtk_box_pack_start (GTK_BOX (vbox19), e_files_label, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (e_files_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (e_files_label), 0, 0.5); + + hbox29 = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox19), hbox29, TRUE, TRUE, 0); + + label47 = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox29), label47, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label47), GTK_JUSTIFY_LEFT); + + table1 = gtk_table_new (3, 2, FALSE); + gtk_box_pack_start (GTK_BOX (hbox29), table1, TRUE, TRUE, 0); + gtk_table_set_row_spacings (GTK_TABLE (table1), 6); + gtk_table_set_col_spacings (GTK_TABLE (table1), 6); + + data->e_files_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("_Files:")); + gtk_table_attach (GTK_TABLE (table1), data->e_files_radiobutton, 0, 1, 2, 3, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (data->e_files_radiobutton), e_files_radiobutton_group); + e_files_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (data->e_files_radiobutton)); + + data->e_files_entry = gtk_entry_new (); + gtk_table_attach (GTK_TABLE (table1), data->e_files_entry, 1, 2, 2, 3, + (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_widget_set_tooltip_text (data->e_files_entry, _("example: *.txt; *.doc")); + gtk_entry_set_activates_default (GTK_ENTRY (data->e_files_entry), TRUE); + + data->e_all_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("_All files")); + gtk_table_attach (GTK_TABLE (table1), data->e_all_radiobutton, 0, 2, 0, 1, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (data->e_all_radiobutton), e_files_radiobutton_group); + e_files_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (data->e_all_radiobutton)); + + data->e_selected_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("_Selected files")); + gtk_table_attach (GTK_TABLE (table1), data->e_selected_radiobutton, 0, 2, 1, 2, + (GtkAttachOptions) (GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + gtk_radio_button_set_group (GTK_RADIO_BUTTON (data->e_selected_radiobutton), e_files_radiobutton_group); + e_files_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (data->e_selected_radiobutton)); + + vbox20 = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox28), vbox20, TRUE, TRUE, 0); + + e_actions_label = gtk_label_new (""); + set_bold_label (e_actions_label, _("Actions")); + gtk_box_pack_start (GTK_BOX (vbox20), e_actions_label, FALSE, FALSE, 0); + gtk_label_set_use_markup (GTK_LABEL (e_actions_label), TRUE); + gtk_label_set_justify (GTK_LABEL (e_actions_label), GTK_JUSTIFY_LEFT); + gtk_misc_set_alignment (GTK_MISC (e_actions_label), 0, 0.5); + + hbox30 = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox20), hbox30, TRUE, TRUE, 0); + + label48 = gtk_label_new (" "); + gtk_box_pack_start (GTK_BOX (hbox30), label48, FALSE, FALSE, 0); + gtk_label_set_justify (GTK_LABEL (label48), GTK_JUSTIFY_LEFT); + + vbox15 = gtk_vbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (hbox30), vbox15, TRUE, TRUE, 0); + + data->e_recreate_dir_checkbutton = gtk_check_button_new_with_mnemonic (_("Re-crea_te folders")); + gtk_box_pack_start (GTK_BOX (vbox15), data->e_recreate_dir_checkbutton, FALSE, FALSE, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_recreate_dir_checkbutton), TRUE); + + data->e_overwrite_checkbutton = gtk_check_button_new_with_mnemonic (_("Over_write existing files")); + gtk_box_pack_start (GTK_BOX (vbox15), data->e_overwrite_checkbutton, FALSE, FALSE, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton), TRUE); + + data->e_not_newer_checkbutton = gtk_check_button_new_with_mnemonic (_("Do not e_xtract older files")); + gtk_box_pack_start (GTK_BOX (vbox15), data->e_not_newer_checkbutton, FALSE, FALSE, 0); + + gtk_widget_show_all (vbox1); + + return vbox1; +} + + +static void +dlg_extract__common (FrWindow *window, + GList *selected_files, + char *base_dir_for_selection) +{ + DialogData *data; + GtkWidget *file_sel; + + data = g_new0 (DialogData, 1); + + data->window = window; + data->selected_files = selected_files; + data->base_dir_for_selection = base_dir_for_selection; + data->extract_clicked = FALSE; + + data->dialog = file_sel = + gtk_file_chooser_dialog_new (_("Extract"), + GTK_WINDOW (data->window), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + FR_STOCK_EXTRACT, GTK_RESPONSE_OK, + GTK_STOCK_HELP, GTK_RESPONSE_HELP, + NULL); + + gtk_window_set_default_size (GTK_WINDOW (file_sel), 530, 510); + + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_sel), FALSE); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK); + + gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel), create_extra_widget (data)); + + /* Set widgets data. */ + + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (file_sel), fr_window_get_extract_default_dir (window)); + + if (data->selected_files != NULL) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton), TRUE); + else { + gtk_widget_set_sensitive (data->e_selected_radiobutton, FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_all_radiobutton), TRUE); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton), eel_mateconf_get_boolean (PREF_EXTRACT_OVERWRITE, FALSE)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton), eel_mateconf_get_boolean (PREF_EXTRACT_SKIP_NEWER, FALSE)); + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton))) { + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton), TRUE); + gtk_widget_set_sensitive (data->e_not_newer_checkbutton, FALSE); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_recreate_dir_checkbutton), eel_mateconf_get_boolean (PREF_EXTRACT_RECREATE_FOLDERS, TRUE)); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + + g_signal_connect (G_OBJECT (file_sel), + "response", + G_CALLBACK (file_sel_response_cb), + data); + + g_signal_connect (G_OBJECT (data->e_overwrite_checkbutton), + "toggled", + G_CALLBACK (overwrite_toggled_cb), + data); + g_signal_connect (G_OBJECT (data->e_files_entry), + "changed", + G_CALLBACK (files_entry_changed_cb), + data); + + /* Run dialog. */ + + gtk_window_set_modal (GTK_WINDOW (file_sel),TRUE); + gtk_widget_show (file_sel); +} + + +void +dlg_extract (GtkWidget *widget, + gpointer callback_data) +{ + FrWindow *window = callback_data; + GList *files; + char *base_dir; + + files = fr_window_get_selection (window, FALSE, &base_dir); + dlg_extract__common (window, files, base_dir); +} + + +void +dlg_extract_folder_from_sidebar (GtkWidget *widget, + gpointer callback_data) +{ + FrWindow *window = callback_data; + GList *files; + char *base_dir; + + files = fr_window_get_selection (window, TRUE, &base_dir); + dlg_extract__common (window, files, base_dir); +} diff --git a/src/dlg-extract.h b/src/dlg-extract.h new file mode 100644 index 0000000..fcf88e9 --- /dev/null +++ b/src/dlg-extract.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_EXTRACT_H +#define DLG_EXTRACT_H + +#include "fr-archive.h" +#include "fr-window.h" + +void dlg_extract (GtkWidget *widget, gpointer data); +void dlg_extract_folder_from_sidebar (GtkWidget *widget, gpointer data); + +#endif /* DLG_EXTRACT_H */ diff --git a/src/dlg-new.c b/src/dlg-new.c new file mode 100644 index 0000000..2c4caf7 --- /dev/null +++ b/src/dlg-new.c @@ -0,0 +1,526 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <math.h> +#include <unistd.h> +#include <gio/gio.h> +#include "dlg-new.h" +#include "file-utils.h" +#include "fr-stock.h" +#include "mateconf-utils.h" +#include "gtk-utils.h" +#include "main.h" +#include "preferences.h" + + +#define GET_WIDGET(x) (_gtk_builder_get_widget (data->builder, (x))) +#define DEFAULT_EXTENSION ".tar.gz" +#define BAD_CHARS "/\\*" +#define MEGABYTE (1024 * 1024) + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DlgNewData *data) +{ + g_object_unref (data->builder); + g_free (data); +} + + +static void +update_sensitivity (DlgNewData *data) +{ + gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton), ! data->can_encrypt_header); + gtk_widget_set_sensitive (data->n_encrypt_header_checkbutton, data->can_encrypt_header); + gtk_widget_set_sensitive (data->n_volume_spinbutton, ! data->can_create_volumes || gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->n_volume_checkbutton))); +} + + +static void +update_sensitivity_for_ext (DlgNewData *data, + const char *ext) +{ + const char *mime_type; + int i; + + data->can_encrypt = FALSE; + data->can_encrypt_header = FALSE; + data->can_create_volumes = FALSE; + + mime_type = get_mime_type_from_extension (ext); + + if (mime_type == NULL) { + gtk_widget_set_sensitive (data->n_password_entry, FALSE); + gtk_widget_set_sensitive (data->n_password_label, FALSE); + gtk_widget_set_sensitive (data->n_encrypt_header_checkbutton, FALSE); + gtk_widget_set_sensitive (data->n_volume_box, FALSE); + return; + } + + for (i = 0; mime_type_desc[i].mime_type != NULL; i++) { + if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) { + data->can_encrypt = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT; + gtk_widget_set_sensitive (data->n_password_entry, data->can_encrypt); + gtk_widget_set_sensitive (data->n_password_label, data->can_encrypt); + + data->can_encrypt_header = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER; + gtk_widget_set_sensitive (data->n_encrypt_header_checkbutton, data->can_encrypt_header); + + data->can_create_volumes = mime_type_desc[i].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES; + gtk_widget_set_sensitive (data->n_volume_box, data->can_create_volumes); + + break; + } + } + + update_sensitivity (data); +} + + +static int +get_archive_type (DlgNewData *data) +{ + const char *uri; + const char *ext; + + uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)); + if (uri == NULL) + return -1; + + ext = get_archive_filename_extension (uri); + if (ext == NULL) { + int idx; + + idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri); + /*idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box)) - 1;*/ + if (idx >= 0) + return data->supported_types[idx]; + + ext = DEFAULT_EXTENSION; + } + + return get_mime_type_index (get_mime_type_from_extension (ext)); +} + + +/* FIXME +static void +archive_type_combo_box_changed_cb (GtkComboBox *combo_box, + DlgNewData *data) +{ + const char *uri; + const char *ext; + int idx; + + uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)); + + ext = get_archive_filename_extension (uri); + idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box)) - 1; + if ((ext == NULL) && (idx >= 0)) + ext = mime_type_desc[data->supported_types[idx]].default_ext; + + update_sensitivity_for_ext (data, ext); + + if ((idx >= 0) && (uri != NULL)) { + const char *new_ext; + const char *basename; + char *basename_noext; + char *new_basename; + char *new_basename_uft8; + + new_ext = mime_type_desc[data->supported_types[idx]].default_ext; + basename = file_name_from_path (uri); + if (g_str_has_suffix (basename, ext)) + basename_noext = g_strndup (basename, strlen (basename) - strlen (ext)); + else + basename_noext = g_strdup (basename); + new_basename = g_strconcat (basename_noext, new_ext, NULL); + new_basename_uft8 = g_uri_unescape_string (new_basename, NULL); + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), new_basename_uft8); + update_sensitivity_for_ext (data, new_ext); + + g_free (new_basename_uft8); + g_free (new_basename); + g_free (basename_noext); + } +} +*/ + + +static void +password_entry_changed_cb (GtkEditable *editable, + gpointer user_data) +{ + update_sensitivity ((DlgNewData *) user_data); +} + + +static void +volume_toggled_cb (GtkToggleButton *toggle_button, + gpointer user_data) +{ + update_sensitivity ((DlgNewData *) user_data); +} + + +static void +format_chooser_selection_changed_cb (EggFileFormatChooser *format_chooser, + DlgNewData *data) +{ + const char *uri; + const char *ext; + int n_format; + + uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)); + if (uri == NULL) + return; + + ext = get_archive_filename_extension (uri); + n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri); + if (ext == NULL) + ext = mime_type_desc[data->supported_types[n_format - 1]].default_ext; + + update_sensitivity_for_ext (data, ext); + + if (uri != NULL) { + const char *new_ext; + const char *basename; + char *basename_noext; + char *new_basename; + char *new_basename_uft8; + + new_ext = mime_type_desc[data->supported_types[n_format - 1]].default_ext; + basename = file_name_from_path (uri); + if (g_str_has_suffix (basename, ext)) + basename_noext = g_strndup (basename, strlen (basename) - strlen (ext)); + else + basename_noext = g_strdup (basename); + new_basename = g_strconcat (basename_noext, new_ext, NULL); + new_basename_uft8 = g_uri_unescape_string (new_basename, NULL); + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), new_basename_uft8); + update_sensitivity_for_ext (data, new_ext); + + g_free (new_basename_uft8); + g_free (new_basename); + g_free (basename_noext); + } +} + + +static char * +get_icon_name_for_type (const char *mime_type) +{ + char *name = NULL; + + if (mime_type != NULL) { + char *s; + + name = g_strconcat ("mate-mime-", mime_type, NULL); + for (s = name; *s; ++s) + if (! g_ascii_isalpha (*s)) + *s = '-'; + } + + if ((name == NULL) || ! gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name)) { + g_free (name); + name = g_strdup ("package-x-generic"); + } + + return name; +} + + +static void +options_expander_unmap_cb (GtkWidget *widget, + gpointer user_data) +{ + egg_file_format_chooser_emit_size_changed ((EggFileFormatChooser *) user_data); +} + + +static DlgNewData * +dlg_new_archive (FrWindow *window, + int *supported_types, + const char *default_name) +{ + DlgNewData *data; + GtkWidget *n_new_button; + GtkFileFilter *filter; + /*char *default_ext;*/ + int i; + + data = g_new0 (DlgNewData, 1); + + data->builder = _gtk_builder_new_from_file ("new.ui"); + if (data->builder == NULL) { + g_free (data); + return NULL; + } + + data->window = window; + data->supported_types = supported_types; + sort_mime_types_by_description (data->supported_types); + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "filechooserdialog"); + + data->n_password_entry = _gtk_builder_get_widget (data->builder, "n_password_entry"); + data->n_password_label = _gtk_builder_get_widget (data->builder, "n_password_label"); + data->n_other_options_expander = _gtk_builder_get_widget (data->builder, "n_other_options_expander"); + data->n_encrypt_header_checkbutton = _gtk_builder_get_widget (data->builder, "n_encrypt_header_checkbutton"); + + data->n_volume_checkbutton = _gtk_builder_get_widget (data->builder, "n_volume_checkbutton"); + data->n_volume_spinbutton = _gtk_builder_get_widget (data->builder, "n_volume_spinbutton"); + data->n_volume_box = _gtk_builder_get_widget (data->builder, "n_volume_box"); + + n_new_button = _gtk_builder_get_widget (data->builder, "n_new_button"); + + /* Set widgets data. */ + + gtk_dialog_set_default_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK); + gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->dialog), fr_window_get_open_default_dir (window)); + + if (default_name != NULL) { + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), default_name); + /*char *ext, *name_ext; + + ext = eel_mateconf_get_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, ".tgz"); + name_ext = g_strconcat (default_name, ext, NULL); + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), name_ext); + g_free (name_ext); + g_free (ext);*/ + } + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All archives")); + for (i = 0; data->supported_types[i] != -1; i++) + gtk_file_filter_add_mime_type (filter, mime_type_desc[data->supported_types[i]].mime_type); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (data->dialog), filter); + gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (data->dialog), filter); + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, _("All files")); + gtk_file_filter_add_pattern (filter, "*"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (data->dialog), filter); + + /**/ + + gtk_button_set_use_stock (GTK_BUTTON (n_new_button), TRUE); + gtk_button_set_label (GTK_BUTTON (n_new_button), FR_STOCK_CREATE_ARCHIVE); + gtk_expander_set_expanded (GTK_EXPANDER (data->n_other_options_expander), FALSE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton), eel_mateconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE)); + gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->n_volume_spinbutton), (double) eel_mateconf_get_integer (PREF_BATCH_VOLUME_SIZE, 0) / MEGABYTE); + + /* format chooser */ + + data->format_chooser = (EggFileFormatChooser *) egg_file_format_chooser_new (); + for (i = 0; data->supported_types[i] != -1; i++) { + int idx = data->supported_types[i]; + char *exts[4]; + int e; + int n_exts; + char *icon_name; + + n_exts = 0; + for (e = 0; (n_exts < 4) && file_ext_type[e].ext != NULL; e++) { + if (strcmp (file_ext_type[e].ext, mime_type_desc[idx].default_ext) == 0) + continue; + if (strcmp (file_ext_type[e].mime_type, mime_type_desc[idx].mime_type) == 0) + exts[n_exts++] = file_ext_type[e].ext; + } + while (n_exts < 4) + exts[n_exts++] = NULL; + + /* g_print ("%s => %s, %s, %s, %s\n", mime_type_desc[idx].mime_type, exts[0], exts[1], exts[2], exts[3]); */ + + icon_name = get_icon_name_for_type (mime_type_desc[idx].mime_type); + egg_file_format_chooser_add_format (data->format_chooser, + 0, + _(mime_type_desc[idx].name), + icon_name, + mime_type_desc[idx].default_ext, + exts[0], + exts[1], + exts[2], + exts[3], + NULL); + + g_free (icon_name); + } + egg_file_format_chooser_set_format (data->format_chooser, 0); + gtk_widget_show (GTK_WIDGET (data->format_chooser)); + gtk_box_pack_start (GTK_BOX (GET_WIDGET ("format_chooser_box")), GTK_WIDGET (data->format_chooser), TRUE, TRUE, 0); + + /* Set the signals handlers. */ + + /*g_signal_connect (G_OBJECT (data->dialog), + "response", + G_CALLBACK (new_file_response_cb), + data);*/ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + + /* + g_signal_connect_swapped (G_OBJECT (cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->dialog)); + g_signal_connect (G_OBJECT (add_button), + "clicked", + G_CALLBACK (add_clicked_cb), + data);*/ + + /* FIXME g_signal_connect (G_OBJECT (data->n_archive_type_combo_box), + "changed", + G_CALLBACK (archive_type_combo_box_changed_cb), + data); */ + g_signal_connect (G_OBJECT (data->n_password_entry), + "changed", + G_CALLBACK (password_entry_changed_cb), + data); + g_signal_connect (G_OBJECT (data->n_volume_checkbutton), + "toggled", + G_CALLBACK (volume_toggled_cb), + data); + g_signal_connect (G_OBJECT (data->format_chooser), + "selection-changed", + G_CALLBACK (format_chooser_selection_changed_cb), + data); + g_signal_connect_after (GET_WIDGET ("other_oprtions_alignment"), + "unmap", + G_CALLBACK (options_expander_unmap_cb), + data->format_chooser); + + /* Run dialog. */ + +/* default_ext = eel_mateconf_get_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, DEFAULT_EXTENSION); + update_archive_type_combo_box_from_ext (data, default_ext); + g_free (default_ext);*/ + + update_sensitivity (data); + + gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE); + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->window)); + /*gtk_window_present (GTK_WINDOW (data->dialog));*/ + + return data; +} + + +DlgNewData * +dlg_new (FrWindow *window) +{ + DlgNewData *data; + + data = dlg_new_archive (window, create_type, NULL); + gtk_window_set_title (GTK_WINDOW (data->dialog), C_("File", "New")); + + return data; +} + + +DlgNewData * +dlg_save_as (FrWindow *window, + const char *default_name) +{ + DlgNewData *data; + + data = dlg_new_archive (window, save_type, default_name); + gtk_window_set_title (GTK_WINDOW (data->dialog), C_("File", "Save")); + + return data; +} + + +const char * +dlg_new_data_get_password (DlgNewData *data) +{ + const char *password = NULL; + int idx; + + idx = get_archive_type (data); + if (idx < 0) + return NULL; + + if (mime_type_desc[idx].capabilities & FR_COMMAND_CAN_ENCRYPT) + password = (char*) gtk_entry_get_text (GTK_ENTRY (data->n_password_entry)); + + return password; +} + + +gboolean +dlg_new_data_get_encrypt_header (DlgNewData *data) +{ + gboolean encrypt_header = FALSE; + int idx; + + idx = get_archive_type (data); + if (idx < 0) + return FALSE; + + if (mime_type_desc[idx].capabilities & FR_COMMAND_CAN_ENCRYPT) { + const char *password = gtk_entry_get_text (GTK_ENTRY (data->n_password_entry)); + if (password != NULL) { + if (strcmp (password, "") != 0) { + if (mime_type_desc[idx].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER) + encrypt_header = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton)); + } + } + } + + return encrypt_header; +} + + +int +dlg_new_data_get_volume_size (DlgNewData *data) +{ + guint volume_size = 0; + int idx; + + idx = get_archive_type (data); + if (idx < 0) + return 0; + + if ((mime_type_desc[idx].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES) + && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->n_volume_checkbutton))) + { + double value; + + value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (data->n_volume_spinbutton)); + volume_size = floor (value * MEGABYTE); + + } + + return volume_size; +} diff --git a/src/dlg-new.h b/src/dlg-new.h new file mode 100644 index 0000000..58bc86d --- /dev/null +++ b/src/dlg-new.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2008 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_NEW_H +#define DLG_NEW_H + +#include <gtk/gtk.h> +#include "eggfileformatchooser.h" +#include "fr-window.h" + + +typedef struct { + FrWindow *window; + int *supported_types; + gboolean can_encrypt; + gboolean can_encrypt_header; + gboolean can_create_volumes; + GtkBuilder *builder; + + GtkWidget *dialog; + /*GtkWidget *n_archive_type_combo_box;*/ + GtkWidget *n_other_options_expander; + GtkWidget *n_password_entry; + GtkWidget *n_password_label; + GtkWidget *n_encrypt_header_checkbutton; + GtkWidget *n_volume_checkbutton; + GtkWidget *n_volume_spinbutton; + GtkWidget *n_volume_box; + EggFileFormatChooser *format_chooser; +} DlgNewData; + + +DlgNewData * dlg_new (FrWindow *window); +DlgNewData * dlg_save_as (FrWindow *window, + const char *default_name); +const char * dlg_new_data_get_password (DlgNewData *data); +gboolean dlg_new_data_get_encrypt_header (DlgNewData *data); +int dlg_new_data_get_volume_size (DlgNewData *data); + +#endif /* DLG_NEW_H */ diff --git a/src/dlg-open-with.c b/src/dlg-open-with.c new file mode 100644 index 0000000..014e59c --- /dev/null +++ b/src/dlg-open-with.c @@ -0,0 +1,517 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> + +#include <gtk/gtk.h> +#include "file-utils.h" +#include "mateconf-utils.h" +#include "glib-utils.h" +#include "gtk-utils.h" +#include "main.h" +#include "fr-window.h" + + +#define TEMP_DOCS "temp_docs" + +enum { ICON_COLUMN, TEXT_COLUMN, DATA_COLUMN, N_COLUMNS }; + +typedef struct { + FrWindow *window; + GtkBuilder *builder; + + GtkWidget *dialog; + GtkWidget *o_app_tree_view; + GtkWidget *o_recent_tree_view; + GtkWidget *o_app_entry; + GtkWidget *o_del_button; + GtkWidget *ok_button; + + GList *app_list; + GList *file_list; + + GtkTreeModel *app_model; + GtkTreeModel *recent_model; + + GtkWidget *last_clicked_list; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +open_with__destroy_cb (GtkWidget *widget, + DialogData *data) +{ + g_object_unref (G_OBJECT (data->builder)); + + if (data->app_list != NULL) + g_list_free (data->app_list); + + if (data->file_list != NULL) + path_list_free (data->file_list); + + g_free (data); +} + + +static void +open_cb (GtkWidget *widget, + gpointer callback_data) +{ + DialogData *data = callback_data; + const char *application; + gboolean present = FALSE; + char *command = NULL; + GList *scan; + GSList *sscan, *editors; + + application = gtk_entry_get_text (GTK_ENTRY (data->o_app_entry)); + + for (scan = data->app_list; scan; scan = scan->next) { + GAppInfo *app = scan->data; + if (strcmp (g_app_info_get_executable (app), application) == 0) { + fr_window_open_files_with_application (data->window, data->file_list, app); + gtk_widget_destroy (data->dialog); + return; + } + } + + /* add the command to the editors list if not already present. */ + + editors = eel_mateconf_get_string_list (PREF_EDIT_EDITORS); + for (sscan = editors; sscan && ! present; sscan = sscan->next) { + char *recent_command = sscan->data; + if (strcmp (recent_command, application) == 0) { + command = g_strdup (recent_command); + present = TRUE; + } + } + + if (! present) { + editors = g_slist_prepend (editors, g_strdup (application)); + command = g_strdup (application); + eel_mateconf_set_string_list (PREF_EDIT_EDITORS, editors); + } + + g_slist_foreach (editors, (GFunc) g_free, NULL); + g_slist_free (editors); + + /* exec the application */ + + if (command != NULL) { + fr_window_open_files_with_command (data->window, data->file_list, command); + g_free (command); + } + + gtk_widget_destroy (data->dialog); +} + + +static void +app_list_selection_changed_cb (GtkTreeSelection *selection, + gpointer p) +{ + DialogData *data = p; + GtkTreeIter iter; + GAppInfo *app; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_app_tree_view)); + if (selection == NULL) + return; + + if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (data->app_model, &iter, + DATA_COLUMN, &app, + -1); + _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), g_app_info_get_executable (app)); + data->last_clicked_list = data->o_app_tree_view; +} + + +static void +app_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer callback_data) +{ + DialogData *data = callback_data; + GtkTreeIter iter; + GAppInfo *app; + + if (! gtk_tree_model_get_iter (data->app_model, &iter, path)) + return; + + gtk_tree_model_get (data->app_model, &iter, + DATA_COLUMN, &app, + -1); + + _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), g_app_info_get_executable (app)); + + open_cb (NULL, data); +} + + +static void +recent_list_selection_changed_cb (GtkTreeSelection *selection, + gpointer p) +{ + DialogData *data = p; + GtkTreeIter iter; + char *editor; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_recent_tree_view)); + if (selection == NULL) + return; + + if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (data->recent_model, &iter, + 0, &editor, + -1); + _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), editor); + g_free (editor); + data->last_clicked_list = data->o_recent_tree_view; +} + + +static void +recent_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer callback_data) +{ + DialogData *data = callback_data; + GtkTreeIter iter; + char *editor; + + if (! gtk_tree_model_get_iter (data->recent_model, &iter, path)) + return; + + gtk_tree_model_get (data->recent_model, &iter, + 0, &editor, + -1); + _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), editor); + g_free (editor); + + open_cb (NULL, data); +} + + +static void +app_entry__changed_cb (GtkEditable *editable, + gpointer user_data) +{ + DialogData *data = user_data; + const char *text; + + text = eat_void_chars (gtk_entry_get_text (GTK_ENTRY (data->o_app_entry))); + gtk_widget_set_sensitive (data->ok_button, strlen (text) > 0); +} + + +static void +delete_recent_cb (GtkWidget *widget, + gpointer callback_data) +{ + DialogData *data = callback_data; + GtkTreeSelection *selection; + GtkTreeIter iter; + + + if (data->last_clicked_list == data->o_recent_tree_view) { + char *editor; + GSList *editors, *link; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_recent_tree_view)); + if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (data->recent_model, &iter, + 0, &editor, + -1); + gtk_list_store_remove (GTK_LIST_STORE (data->recent_model), &iter); + + /**/ + + editors = eel_mateconf_get_string_list (PREF_EDIT_EDITORS); + link = g_slist_find_custom (editors, editor, (GCompareFunc) strcmp); + if (link != NULL) { + editors = g_slist_remove_link (editors, link); + eel_mateconf_set_string_list (PREF_EDIT_EDITORS, editors); + g_free (link->data); + g_slist_free (link); + } + g_slist_foreach (editors, (GFunc) g_free, NULL); + g_slist_free (editors); + + g_free (editor); + } + else if (data->last_clicked_list == data->o_app_tree_view) { + GAppInfo *app; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_app_tree_view)); + if (! gtk_tree_selection_get_selected (selection, NULL, &iter)) + return; + + gtk_tree_model_get (data->app_model, &iter, + DATA_COLUMN, &app, + -1); + gtk_list_store_remove (GTK_LIST_STORE (data->app_model), &iter); + + if (g_app_info_can_remove_supports_type (app)) { + const char *mime_type; + + mime_type = get_file_mime_type_for_path ((char*) data->file_list->data, FALSE); + g_app_info_remove_supports_type (app, mime_type, NULL); + } + } +} + + +/* create the "open with" dialog. */ +void +dlg_open_with (FrWindow *window, + GList *file_list) +{ + DialogData *data; + GAppInfo *app; + GList *scan, *app_names = NULL; + GSList *sscan, *editors; + GtkWidget *cancel_button; + GtkTreeIter iter; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkIconTheme *theme; + int icon_size; + + if (file_list == NULL) + return; + + data = g_new0 (DialogData, 1); + + data->builder = _gtk_builder_new_from_file ("open-with.ui"); + if (data->builder == NULL) { + g_free (data); + return; + } + + data->file_list = path_list_dup (file_list); + data->window = window; + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "open_with_dialog"); + data->o_app_tree_view = _gtk_builder_get_widget (data->builder, "o_app_list_tree_view"); + data->o_recent_tree_view = _gtk_builder_get_widget (data->builder, "o_recent_tree_view"); + data->o_app_entry = _gtk_builder_get_widget (data->builder, "o_app_entry"); + data->o_del_button = _gtk_builder_get_widget (data->builder, "o_del_button"); + data->ok_button = _gtk_builder_get_widget (data->builder, "o_ok_button"); + cancel_button = _gtk_builder_get_widget (data->builder, "o_cancel_button"); + + gtk_widget_set_sensitive (data->ok_button, FALSE); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (open_with__destroy_cb), + data); + + g_signal_connect (G_OBJECT (data->o_app_entry), + "changed", + G_CALLBACK (app_entry__changed_cb), + data); + + g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_app_tree_view))), + "changed", + G_CALLBACK (app_list_selection_changed_cb), + data); + g_signal_connect (G_OBJECT (data->o_app_tree_view), + "row_activated", + G_CALLBACK (app_activated_cb), + data); + + g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_recent_tree_view))), + "changed", + G_CALLBACK (recent_list_selection_changed_cb), + data); + g_signal_connect (G_OBJECT (data->o_recent_tree_view), + "row_activated", + G_CALLBACK (recent_activated_cb), + data); + + g_signal_connect (G_OBJECT (data->ok_button), + "clicked", + G_CALLBACK (open_cb), + data); + g_signal_connect_swapped (G_OBJECT (cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->dialog)); + g_signal_connect (G_OBJECT (data->o_del_button), + "clicked", + G_CALLBACK (delete_recent_cb), + data); + + /* Set data. */ + + /* * registered applications list. */ + + data->app_list = NULL; + for (scan = data->file_list; scan; scan = scan->next) { + const char *mime_type; + const char *name = scan->data; + + mime_type = get_file_mime_type_for_path (name, FALSE); + if ((mime_type != NULL) && ! g_content_type_is_unknown (mime_type)) + data->app_list = g_list_concat (data->app_list, g_app_info_get_all_for_type (mime_type)); + } + + data->app_model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_POINTER)); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->app_model), + TEXT_COLUMN, + GTK_SORT_ASCENDING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (data->o_app_tree_view), + data->app_model); + g_object_unref (G_OBJECT (data->app_model)); + + theme = gtk_icon_theme_get_default (); + icon_size = get_folder_pixbuf_size_for_list (GTK_WIDGET (data->dialog)); + + for (scan = data->app_list; scan; scan = scan->next) { + gboolean found; + char *utf8_name; + GdkPixbuf *icon_image = NULL; + + app = scan->data; + + found = FALSE; + if (app_names != NULL) { + GList *p; + for (p = app_names; p && !found; p = p->next) + if (strcmp ((char*)p->data, g_app_info_get_executable (app)) == 0) + found = TRUE; + } + + if (found) + continue; + + app_names = g_list_prepend (app_names, (char*) g_app_info_get_executable (app)); + + utf8_name = g_locale_to_utf8 (g_app_info_get_name (app), -1, NULL, NULL, NULL); + icon_image = get_icon_pixbuf (g_app_info_get_icon (app), icon_size, theme); + + gtk_list_store_append (GTK_LIST_STORE (data->app_model), + &iter); + gtk_list_store_set (GTK_LIST_STORE (data->app_model), + &iter, + ICON_COLUMN, icon_image, + TEXT_COLUMN, utf8_name, + DATA_COLUMN, app, + -1); + + g_free (utf8_name); + } + + column = gtk_tree_view_column_new (); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", ICON_COLUMN, + NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, + renderer, + TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", TEXT_COLUMN, + NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (data->o_app_tree_view), + column); + + if (app_names) + g_list_free (app_names); + + /* * recent editors list. */ + + data->recent_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->recent_model), 0, GTK_SORT_ASCENDING); + + gtk_tree_view_set_model (GTK_TREE_VIEW (data->o_recent_tree_view), + data->recent_model); + g_object_unref (G_OBJECT (data->recent_model)); + + editors = eel_mateconf_get_string_list (PREF_EDIT_EDITORS); + for (sscan = editors; sscan; sscan = sscan->next) { + char *editor = sscan->data; + + gtk_list_store_append (GTK_LIST_STORE (data->recent_model), &iter); + gtk_list_store_set (GTK_LIST_STORE (data->recent_model), &iter, + 0, editor, + -1); + } + g_slist_foreach (editors, (GFunc) g_free, NULL); + g_slist_free (editors); + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (NULL, + renderer, + "text", 0, + NULL); + gtk_tree_view_column_set_sort_column_id (column, 0); + gtk_tree_view_append_column (GTK_TREE_VIEW (data->o_recent_tree_view), + column); + + /* Run dialog. */ + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), + GTK_WINDOW (window)); + gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE); + gtk_widget_show_all (data->dialog); +} + + +void +open_with_cb (GtkWidget *widget, + void *callback_data) +{ + FrWindow *window = callback_data; + GList *file_list; + + file_list = fr_window_get_file_list_selection (window, FALSE, NULL); + if (file_list == NULL) + return; + + fr_window_open_files (window, file_list, TRUE); + path_list_free (file_list); +} diff --git a/src/dlg-open-with.h b/src/dlg-open-with.h new file mode 100644 index 0000000..9f537d4 --- /dev/null +++ b/src/dlg-open-with.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_OPEN_WITH_H +#define DLG_OPEN_WITH_H + +#include <gtk/gtk.h> +#include "fr-window.h" + +void open_with_cb (GtkWidget *widget, void *data); +void dlg_open_with (FrWindow *window, GList *file_list); + +#endif /* DLG_OPEN_WITH_H */ diff --git a/src/dlg-package-installer.c b/src/dlg-package-installer.c new file mode 100644 index 0000000..d1f9e94 --- /dev/null +++ b/src/dlg-package-installer.c @@ -0,0 +1,294 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001-2009 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <glib/gi18n.h> +#include <gdk/gdkx.h> +#include <gtk/gtk.h> +#include "dlg-package-installer.h" +#include "gtk-utils.h" +#include "main.h" + + +typedef struct { + FrWindow *window; + FrArchive *archive; + FrAction action; + const char *packages; +} InstallerData; + + +static void +installer_data_free (InstallerData *idata) +{ + g_object_unref (idata->archive); + g_object_unref (idata->window); + g_free (idata); +} + + +static void +package_installer_terminated (InstallerData *idata, + const char *error) +{ + GdkWindow *window; + + window = gtk_widget_get_window (GTK_WIDGET (idata->window)); + if (window != NULL) + gdk_window_set_cursor (window, NULL); + + if (error != NULL) { + fr_archive_action_completed (idata->archive, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_PROC_ERROR_GENERIC, + error); + } + else { + update_registered_commands_capabilities (); + if (fr_window_is_batch_mode (idata->window)) + fr_window_resume_batch (idata->window); + else + fr_window_restart_current_batch_action (idata->window); + } + + installer_data_free (idata); +} + + +#ifdef ENABLE_PACKAGEKIT + + +static void +packagekit_install_package_names_ready_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + InstallerData *idata = user_data; + GDBusProxy *proxy; + GVariant *values; + GError *error = NULL; + char *message = NULL; + + proxy = G_DBUS_PROXY (source_object); + values = g_dbus_proxy_call_finish (proxy, res, &error); + if (values == NULL) { + message = g_strdup_printf ("%s\n%s", + _("There was an internal error trying to search for applications:"), + error->message); + g_clear_error (&error); + } + + package_installer_terminated (idata, message); + + g_free (message); + if (values != NULL) + g_variant_unref (values); + g_object_unref (proxy); +} + + +static char ** +get_packages_real_names (char **names) +{ + char **real_names; + GKeyFile *key_file; + char *filename; + int i; + + real_names = g_new0 (char *, g_strv_length (names)); + key_file = g_key_file_new (); + filename = g_build_filename (PRIVDATADIR, "packages.match", NULL); + g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL); + + for (i = 0; names[i] != NULL; i++) { + char *real_name; + + real_name = g_key_file_get_string (key_file, "Package Matches", names[i], NULL); + if (real_name != NULL) + real_name = g_strstrip (real_name); + if ((real_name == NULL) || (strncmp (real_name, "", 1) == 0)) + real_names[i] = g_strdup (real_name); + + g_free (real_name); + } + + g_free (filename); + g_key_file_free (key_file); + + return real_names; +} + + +static void +install_packages (InstallerData *idata) +{ + GDBusConnection *connection; + GError *error = NULL; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + if (connection != NULL) { + GdkWindow *window; + GDBusProxy *proxy; + + window = gtk_widget_get_window (GTK_WIDGET (idata->window)); + if (window != NULL) { + GdkCursor *cursor; + + cursor = gdk_cursor_new (GDK_WATCH); + gdk_window_set_cursor (window, cursor); + gdk_cursor_unref (cursor); + } + + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + "org.freedesktop.PackageKit", + "/org/freedesktop/PackageKit", + "org.freedesktop.PackageKit.Modify", + NULL, + &error); + + if (proxy != NULL) { + guint xid; + char **names; + char **real_names; + + if (window != NULL) + xid = GDK_WINDOW_XID (window); + else + xid = 0; + + names = g_strsplit (idata->packages, ",", -1); + real_names = get_packages_real_names (names); + + g_dbus_proxy_call (proxy, + "InstallPackageNames", + g_variant_new ("(u^ass)", + xid, + names, + "hide-confirm-search,hide-finished,hide-warning"), + G_DBUS_CALL_FLAGS_NONE, + G_MAXINT, + NULL, + packagekit_install_package_names_ready_cb, + idata); + + g_strfreev (real_names); + g_strfreev (names); + } + } + + if (error != NULL) { + char *message; + + message = g_strdup_printf ("%s\n%s", + _("There was an internal error trying to search for applications:"), + error->message); + package_installer_terminated (idata, message); + + g_clear_error (&error); + } +} + + +static void +confirm_search_dialog_response_cb (GtkDialog *dialog, + int response_id, + gpointer user_data) +{ + InstallerData *idata = user_data; + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + if (response_id == GTK_RESPONSE_YES) { + install_packages (idata); + } + else { + fr_window_stop_batch (idata->window); + installer_data_free (idata); + } +} + + +#endif /* ENABLE_PACKAGEKIT */ + + +void +dlg_package_installer (FrWindow *window, + FrArchive *archive, + FrAction action) +{ + InstallerData *idata; + GType command_type; + FrCommand *command; + + idata = g_new0 (InstallerData, 1); + idata->window = g_object_ref (window); + idata->archive = g_object_ref (archive); + idata->action = action; + + command_type = get_preferred_command_for_mime_type (idata->archive->content_type, FR_COMMAND_CAN_READ_WRITE); + if (command_type == 0) + command_type = get_preferred_command_for_mime_type (idata->archive->content_type, FR_COMMAND_CAN_READ); + if (command_type == 0) { + package_installer_terminated (idata, _("Archive type not supported.")); + return; + } + + command = g_object_new (command_type, 0); + idata->packages = fr_command_get_packages (command, idata->archive->content_type); + g_object_unref (command); + + if (idata->packages == NULL) { + package_installer_terminated (idata, _("Archive type not supported.")); + return; + } + +#ifdef ENABLE_PACKAGEKIT + + { + char *secondary_text; + GtkWidget *dialog; + + secondary_text = g_strdup_printf (_("There is no command installed for %s files.\nDo you want to search for a command to open this file?"), + g_content_type_get_description (idata->archive->content_type)); + dialog = _gtk_message_dialog_new (GTK_WINDOW (idata->window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_ERROR, + _("Could not open this file type"), + secondary_text, + GTK_STOCK_CANCEL, GTK_RESPONSE_NO, + _("_Search Command"), GTK_RESPONSE_YES, + NULL); + g_signal_connect (dialog, "response", G_CALLBACK (confirm_search_dialog_response_cb), idata); + gtk_widget_show (dialog); + + g_free (secondary_text); + } + +#else /* ! ENABLE_PACKAGEKIT */ + + package_installer_terminated (idata, _("Archive type not supported.")); + +#endif /* ENABLE_PACKAGEKIT */ +} diff --git a/src/dlg-package-installer.h b/src/dlg-package-installer.h new file mode 100644 index 0000000..f7df874 --- /dev/null +++ b/src/dlg-package-installer.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001-2009 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_PACKAGE_INSTALLER_H +#define DLG_PACKAGE_INSTALLER_H + +#include "fr-window.h" + +void dlg_package_installer (FrWindow *window, + FrArchive *archive, + FrAction action); + +#endif /* DLG_PACKAGE_INSTALLER_H */ diff --git a/src/dlg-password.c b/src/dlg-password.c new file mode 100644 index 0000000..db2c5ac --- /dev/null +++ b/src/dlg-password.c @@ -0,0 +1,126 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <gtk/gtk.h> +#include "fr-window.h" +#include "mateconf-utils.h" +#include "gtk-utils.h" +#include "preferences.h" + + +typedef struct { + GtkBuilder *builder; + FrWindow *window; + GtkWidget *dialog; + GtkWidget *pw_password_entry; + GtkWidget *pw_encrypt_header_checkbutton; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DialogData *data) +{ + g_object_unref (data->builder); + g_free (data); +} + + +static void +response_cb (GtkWidget *dialog, + int response_id, + DialogData *data) +{ + char *password; + gboolean encrypt_header; + + switch (response_id) { + case GTK_RESPONSE_OK: + password = _gtk_entry_get_locale_text (GTK_ENTRY (data->pw_password_entry)); + fr_window_set_password (data->window, password); + g_free (password); + + encrypt_header = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->pw_encrypt_header_checkbutton)); + eel_mateconf_set_boolean (PREF_ENCRYPT_HEADER, encrypt_header); + fr_window_set_encrypt_header (data->window, encrypt_header); + break; + default: + break; + } + + gtk_widget_destroy (data->dialog); +} + + +void +dlg_password (GtkWidget *widget, + gpointer callback_data) +{ + FrWindow *window = callback_data; + DialogData *data; + + data = g_new0 (DialogData, 1); + + data->builder = _gtk_builder_new_from_file ("password.ui"); + if (data->builder == NULL) { + g_free (data); + return; + } + + data->window = window; + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "password_dialog"); + data->pw_password_entry = _gtk_builder_get_widget (data->builder, "pw_password_entry"); + data->pw_encrypt_header_checkbutton = _gtk_builder_get_widget (data->builder, "pw_encrypt_header_checkbutton"); + + /* Set widgets data. */ + + _gtk_entry_set_locale_text (GTK_ENTRY (data->pw_password_entry), fr_window_get_password (window)); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->pw_encrypt_header_checkbutton), fr_window_get_encrypt_header (window)); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + + g_signal_connect (G_OBJECT (data->dialog), + "response", + G_CALLBACK (response_cb), + data); + + /* Run dialog. */ + + gtk_widget_grab_focus (data->pw_password_entry); + if (gtk_widget_get_realized (GTK_WIDGET (window))) + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), + GTK_WINDOW (window)); + gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE); + + gtk_widget_show (data->dialog); +} diff --git a/src/dlg-password.h b/src/dlg-password.h new file mode 100644 index 0000000..b804078 --- /dev/null +++ b/src/dlg-password.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_PASSWORD_H +#define DLG_PASSWORD_H + +#include "fr-window.h" + +void dlg_password (GtkWidget *widget, + gpointer callback_data); + +#endif /* DLG_PASSWORD_H */ diff --git a/src/dlg-prop.c b/src/dlg-prop.c new file mode 100644 index 0000000..e359743 --- /dev/null +++ b/src/dlg-prop.c @@ -0,0 +1,219 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <gtk/gtk.h> +#include "glib-utils.h" +#include "file-utils.h" +#include "gtk-utils.h" +#include "fr-window.h" + + +typedef struct { + GtkBuilder *builder; + GtkWidget *dialog; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +destroy_cb (GtkWidget *widget, + DialogData *data) +{ + g_object_unref (G_OBJECT (data->builder)); + g_free (data); +} + + +static void +set_label_type (GtkWidget *label, const char *text, const char *type) +{ + char *t; + + t = g_strdup_printf ("<%s>%s</%s>", type, text, type); + gtk_label_set_markup (GTK_LABEL (label), t); + g_free (t); +} + + +static void +set_label (GtkWidget *label, const char *text) +{ + set_label_type (label, text, "b"); +} + + +static int +help_cb (GtkWidget *w, + DialogData *data) +{ + show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-view-archive-properties"); + return TRUE; +} + + +void +dlg_prop (FrWindow *window) +{ + DialogData *data; + GtkWidget *ok_button; + GtkWidget *help_button; + GtkWidget *label_label; + GtkWidget *label; + char *s; + goffset size, uncompressed_size; + char *utf8_name; + char *title_txt; + double ratio; + + data = g_new (DialogData, 1); + + data->builder = _gtk_builder_new_from_file ("properties.ui"); + if (data->builder == NULL) { + g_free (data); + return; + } + + /* Get the widgets. */ + + data->dialog = _gtk_builder_get_widget (data->builder, "prop_dialog"); + ok_button = _gtk_builder_get_widget (data->builder, "p_ok_button"); + help_button = _gtk_builder_get_widget (data->builder, "p_help_button"); + + /* Set widgets data. */ + + label_label = _gtk_builder_get_widget (data->builder, "p_path_label_label"); + /* Translators: after the colon there is a folder name. */ + set_label (label_label, _("Location:")); + + label = _gtk_builder_get_widget (data->builder, "p_path_label"); + s = remove_level_from_path (fr_window_get_archive_uri (window)); + utf8_name = g_filename_display_name (s); + gtk_label_set_text (GTK_LABEL (label), utf8_name); + g_free (utf8_name); + g_free (s); + + /**/ + + label_label = _gtk_builder_get_widget (data->builder, "p_name_label_label"); + set_label (label_label, C_("File", "Name:")); + + label = _gtk_builder_get_widget (data->builder, "p_name_label"); + utf8_name = g_uri_display_basename (fr_window_get_archive_uri (window)); + gtk_label_set_text (GTK_LABEL (label), utf8_name); + + title_txt = g_strdup_printf (_("%s Properties"), utf8_name); + gtk_window_set_title (GTK_WINDOW (data->dialog), title_txt); + g_free (title_txt); + + g_free (utf8_name); + + /**/ + + label_label = _gtk_builder_get_widget (data->builder, "p_date_label_label"); + set_label (label_label, _("Modified on:")); + + label = _gtk_builder_get_widget (data->builder, "p_date_label"); + s = get_time_string (get_file_mtime (fr_window_get_archive_uri (window))); + gtk_label_set_text (GTK_LABEL (label), s); + g_free (s); + + /**/ + + label_label = _gtk_builder_get_widget (data->builder, "p_size_label_label"); + set_label (label_label, _("Archive size:")); + + label = _gtk_builder_get_widget (data->builder, "p_size_label"); + size = get_file_size (fr_window_get_archive_uri (window)); + s = g_format_size_for_display (size); + gtk_label_set_text (GTK_LABEL (label), s); + g_free (s); + + /**/ + + label_label = _gtk_builder_get_widget (data->builder, "p_uncomp_size_label_label"); + set_label (label_label, _("Content size:")); + + uncompressed_size = 0; + if (fr_window_archive_is_present (window)) { + int i; + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + uncompressed_size += fd->size; + } + } + + label = _gtk_builder_get_widget (data->builder, "p_uncomp_size_label"); + s = g_format_size_for_display (uncompressed_size); + gtk_label_set_text (GTK_LABEL (label), s); + g_free (s); + + /**/ + + label_label = _gtk_builder_get_widget (data->builder, "p_cratio_label_label"); + set_label (label_label, _("Compression ratio:")); + + label = _gtk_builder_get_widget (data->builder, "p_cratio_label"); + + if (uncompressed_size != 0) + ratio = (double) uncompressed_size / size; + else + ratio = 0.0; + s = g_strdup_printf ("%0.2f", ratio); + gtk_label_set_text (GTK_LABEL (label), s); + g_free (s); + + /**/ + + label_label = _gtk_builder_get_widget (data->builder, "p_files_label_label"); + set_label (label_label, _("Number of files:")); + + label = _gtk_builder_get_widget (data->builder, "p_files_label"); + s = g_strdup_printf ("%d", window->archive->command->n_regular_files); + gtk_label_set_text (GTK_LABEL (label), s); + g_free (s); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->dialog), + "destroy", + G_CALLBACK (destroy_cb), + data); + g_signal_connect_swapped (G_OBJECT (ok_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->dialog)); + g_signal_connect (G_OBJECT (help_button), + "clicked", + G_CALLBACK (help_cb), + data); + + /* Run dialog. */ + + gtk_window_set_transient_for (GTK_WINDOW (data->dialog), + GTK_WINDOW (window)); + gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE); + + gtk_widget_show (data->dialog); +} diff --git a/src/dlg-prop.h b/src/dlg-prop.h new file mode 100644 index 0000000..50849f5 --- /dev/null +++ b/src/dlg-prop.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_PROP_H +#define DLG_PROP_H + +#include "fr-window.h" + +void dlg_prop (FrWindow *window); + +#endif /* DLG_DELETE_H */ diff --git a/src/dlg-update.c b/src/dlg-update.c new file mode 100644 index 0000000..bd46517 --- /dev/null +++ b/src/dlg-update.c @@ -0,0 +1,406 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <gtk/gtk.h> +#include "dlg-update.h" +#include "file-utils.h" +#include "mateconf-utils.h" +#include "glib-utils.h" +#include "gtk-utils.h" +#include "main.h" +#include "fr-window.h" + + +enum { + IS_SELECTED_COLUMN, + NAME_COLUMN, + DATA_COLUMN, + N_COLUMNS +}; + +typedef struct { + FrWindow *window; + GtkBuilder *builder; + + GtkWidget *update_file_dialog; + GtkWidget *update_file_primary_text_label; + GtkWidget *update_file_secondary_text_label; + + GtkWidget *update_files_dialog; + GtkWidget *update_files_primary_text_label; + GtkWidget *update_files_secondary_text_label; + GtkWidget *update_files_treeview; + GtkWidget *update_files_ok_button; + + GList *file_list; + GtkTreeModel *list_model; +} DialogData; + + +/* called when the main dialog is closed. */ +static void +dlg_update__destroy_cb (GtkWidget *widget, + DialogData *data) +{ + fr_window_update_dialog_closed (data->window); + g_object_unref (G_OBJECT (data->builder)); + if (data->file_list != NULL) + g_list_free (data->file_list); + g_free (data); +} + + +static GList* +get_selected_files (DialogData *data) +{ + GList *selection = NULL; + GtkTreeIter iter; + + if (! gtk_tree_model_get_iter_first (data->list_model, &iter)) + return NULL; + + do { + gboolean is_selected; + OpenFile *file; + + gtk_tree_model_get (data->list_model, &iter, + IS_SELECTED_COLUMN, &is_selected, + DATA_COLUMN, &file, + -1); + if (is_selected) + selection = g_list_prepend (selection, file); + } while (gtk_tree_model_iter_next (data->list_model, &iter)); + + return g_list_reverse (selection); +} + + +static void +update_cb (GtkWidget *widget, + gpointer callback_data) +{ + DialogData *data = callback_data; + GList *selection; + + selection = get_selected_files (data); + if (fr_window_update_files (data->window, selection)) { + int n_files; + + n_files = g_list_length (data->file_list); + if (n_files == 1) + gtk_widget_destroy (data->update_file_dialog); + else + gtk_widget_destroy (data->update_files_dialog); + } + if (selection != NULL) + g_list_free (selection); +} + + +static void +update_file_list (DialogData *data) +{ + gboolean n_files; + GList *scan; + GtkTreeIter iter; + + n_files = g_list_length (data->file_list); + + /* update the file list */ + + gtk_list_store_clear (GTK_LIST_STORE (data->list_model)); + for (scan = data->file_list; scan; scan = scan->next) { + char *utf8_name; + OpenFile *file = scan->data; + + gtk_list_store_append (GTK_LIST_STORE (data->list_model), + &iter); + + utf8_name = g_filename_display_name (file_name_from_path (file->path)); + gtk_list_store_set (GTK_LIST_STORE (data->list_model), + &iter, + IS_SELECTED_COLUMN, TRUE, + NAME_COLUMN, utf8_name, + DATA_COLUMN, file, + -1); + g_free (utf8_name); + } + + /* update the labels */ + + if (n_files == 1) { + OpenFile *file = data->file_list->data; + char *file_name; + char *unescaped; + char *archive_name; + char *label; + char *markup; + + /* primary text */ + + file_name = g_filename_display_name (file_name_from_path (file->path)); + unescaped = g_uri_unescape_string (fr_window_get_archive_uri (data->window), NULL); + archive_name = g_path_get_basename (unescaped); + label = g_markup_printf_escaped (_("Update the file \"%s\" in the archive \"%s\"?"), file_name, archive_name); + markup = g_strdup_printf ("<big><b>%s</b></big>", label); + gtk_label_set_markup (GTK_LABEL (data->update_file_primary_text_label), markup); + + g_free (markup); + g_free (label); + g_free (archive_name); + g_free (unescaped); + g_free (file_name); + + /* secondary text */ + + label = g_strdup_printf (ngettext ("The file has been modified with an external application. If you don't update the file in the archive, all of your changes will be lost.", + "%d files have been modified with an external application. If you don't update the files in the archive, all of your changes will be lost.", + n_files), + n_files); + gtk_label_set_text (GTK_LABEL (data->update_file_secondary_text_label), label); + g_free (label); + } + else if (n_files > 1) { + char *unescaped; + char *archive_name; + char *label; + char *markup; + + /* primary text */ + + unescaped = g_uri_unescape_string (fr_window_get_archive_uri (data->window), NULL); + archive_name = g_path_get_basename (unescaped); + label = g_markup_printf_escaped (_("Update the files in the archive \"%s\"?"), archive_name); + markup = g_strdup_printf ("<big><b>%s</b></big>", label); + gtk_label_set_markup (GTK_LABEL (data->update_files_primary_text_label), markup); + + g_free (markup); + g_free (label); + g_free (archive_name); + g_free (unescaped); + + /* secondary text */ + + label = g_strdup_printf (ngettext ("The file has been modified with an external application. If you don't update the file in the archive, all of your changes will be lost.", + "%d files have been modified with an external application. If you don't update the files in the archive, all of your changes will be lost.", + n_files), + n_files); + gtk_label_set_text (GTK_LABEL (data->update_files_secondary_text_label), label); + g_free (label); + } + + /* show the appropriate dialog */ + + if (n_files == 1) { + /*gtk_window_set_modal (GTK_WINDOW (data->update_files_dialog), FALSE);*/ + gtk_widget_hide (data->update_files_dialog); + /*gtk_window_set_modal (GTK_WINDOW (data->update_file_dialog), TRUE);*/ + gtk_widget_show (data->update_file_dialog); + } + else if (n_files > 1) { + /*gtk_window_set_modal (GTK_WINDOW (data->update_file_dialog), FALSE);*/ + gtk_widget_hide (data->update_file_dialog); + /*gtk_window_set_modal (GTK_WINDOW (data->update_files_dialog), TRUE);*/ + gtk_widget_show (data->update_files_dialog); + } + else { /* n_files == 0 */ + /*gtk_window_set_modal (GTK_WINDOW (data->update_files_dialog), FALSE);*/ + gtk_widget_hide (data->update_files_dialog); + /*gtk_window_set_modal (GTK_WINDOW (data->update_file_dialog), FALSE);*/ + gtk_widget_hide (data->update_file_dialog); + } +} + + +static int +n_selected (DialogData *data) +{ + int n = 0; + GtkTreeIter iter; + + if (! gtk_tree_model_get_iter_first (data->list_model, &iter)) + return 0; + + do { + gboolean is_selected; + gtk_tree_model_get (data->list_model, &iter, IS_SELECTED_COLUMN, &is_selected, -1); + if (is_selected) + n++; + } while (gtk_tree_model_iter_next (data->list_model, &iter)); + + return n; +} + + +static void +is_selected_toggled (GtkCellRendererToggle *cell, + char *path_string, + gpointer callback_data) +{ + DialogData *data = callback_data; + GtkTreeModel *model = GTK_TREE_MODEL (data->list_model); + GtkTreeIter iter; + GtkTreePath *path = gtk_tree_path_new_from_string (path_string); + guint value; + + gtk_tree_model_get_iter (model, &iter, path); + value = ! gtk_cell_renderer_toggle_get_active (cell); + gtk_list_store_set (GTK_LIST_STORE (model), &iter, IS_SELECTED_COLUMN, value, -1); + + gtk_tree_path_free (path); + + gtk_widget_set_sensitive (data->update_files_ok_button, n_selected (data) > 0); +} + + +gpointer +dlg_update (FrWindow *window) +{ + DialogData *data; + GtkWidget *update_file_ok_button; + GtkWidget *update_file_cancel_button; + GtkWidget *update_files_cancel_button; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + data = g_new0 (DialogData, 1); + + data->builder = _gtk_builder_new_from_file ("update.ui"); + if (data->builder == NULL) { + g_free (data); + return NULL; + } + + data->file_list = NULL; + data->window = window; + + /* Get the widgets. */ + + data->update_file_dialog = _gtk_builder_get_widget (data->builder, "update_file_dialog"); + data->update_file_primary_text_label = _gtk_builder_get_widget (data->builder, "update_file_primary_text_label"); + data->update_file_secondary_text_label = _gtk_builder_get_widget (data->builder, "update_file_secondary_text_label"); + + update_file_ok_button = _gtk_builder_get_widget (data->builder, "update_file_ok_button"); + update_file_cancel_button = _gtk_builder_get_widget (data->builder, "update_file_cancel_button"); + + data->update_files_dialog = _gtk_builder_get_widget (data->builder, "update_files_dialog"); + data->update_files_primary_text_label = _gtk_builder_get_widget (data->builder, "update_files_primary_text_label"); + data->update_files_secondary_text_label = _gtk_builder_get_widget (data->builder, "update_files_secondary_text_label"); + data->update_files_treeview = _gtk_builder_get_widget (data->builder, "update_files_treeview"); + data->update_files_ok_button = _gtk_builder_get_widget (data->builder, "update_files_ok_button"); + update_files_cancel_button = _gtk_builder_get_widget (data->builder, "update_files_cancel_button"); + + /* Set the signals handlers. */ + + g_signal_connect (G_OBJECT (data->update_file_dialog), + "destroy", + G_CALLBACK (dlg_update__destroy_cb), + data); + g_signal_connect (G_OBJECT (update_file_ok_button), + "clicked", + G_CALLBACK (update_cb), + data); + g_signal_connect_swapped (G_OBJECT (update_file_cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->update_file_dialog)); + g_signal_connect (G_OBJECT (data->update_files_dialog), + "destroy", + G_CALLBACK (dlg_update__destroy_cb), + data); + g_signal_connect (G_OBJECT (data->update_files_ok_button), + "clicked", + G_CALLBACK (update_cb), + data); + g_signal_connect_swapped (G_OBJECT (update_files_cancel_button), + "clicked", + G_CALLBACK (gtk_widget_destroy), + G_OBJECT (data->update_files_dialog)); + + /* Set dialog data. */ + + data->list_model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS, + G_TYPE_BOOLEAN, + G_TYPE_STRING, + G_TYPE_POINTER)); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->list_model), + NAME_COLUMN, + GTK_SORT_ASCENDING); + gtk_tree_view_set_model (GTK_TREE_VIEW (data->update_files_treeview), + data->list_model); + g_object_unref (G_OBJECT (data->list_model)); + + column = gtk_tree_view_column_new (); + + renderer = gtk_cell_renderer_toggle_new (); + g_signal_connect (G_OBJECT (renderer), + "toggled", + G_CALLBACK (is_selected_toggled), + data); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "active", IS_SELECTED_COLUMN, + NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", NAME_COLUMN, + NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (data->update_files_treeview), column); + + /* Run dialog. */ + + gtk_window_set_transient_for (GTK_WINDOW (data->update_file_dialog), + GTK_WINDOW (window)); + gtk_window_set_transient_for (GTK_WINDOW (data->update_files_dialog), + GTK_WINDOW (window)); + + update_file_list (data); + + return data; +} + + +void +dlg_update_add_file (gpointer dialog, + OpenFile *file) +{ + DialogData *data = dialog; + GList *scan; + + /* avoid duplicates */ + + for (scan = data->file_list; scan; scan = scan->next) { + OpenFile *test = scan->data; + if (uricmp (test->extracted_uri, file->extracted_uri) == 0) + return; + } + + /**/ + + data->file_list = g_list_append (data->file_list, file); + update_file_list (data); +} diff --git a/src/dlg-update.h b/src/dlg-update.h new file mode 100644 index 0000000..6133887 --- /dev/null +++ b/src/dlg-update.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2008 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef DLG_UPDATE_H +#define DLG_UPDATE_H + +#include <gtk/gtk.h> +#include "fr-window.h" +#include "open-file.h" + +gpointer dlg_update (FrWindow *window); +void dlg_update_add_file (gpointer dialog, + OpenFile *file); + +#endif /* DLG_UPDATE_H */ diff --git a/src/egg-macros.h b/src/egg-macros.h new file mode 100644 index 0000000..6e56e2e --- /dev/null +++ b/src/egg-macros.h @@ -0,0 +1,154 @@ +/** + * Useful macros. + * + * Author: + * Darin Adler <[email protected]> + * + * Copyright 2001 Ben Tea Spoons, Inc. + */ +#ifndef _EGG_MACROS_H_ +#define _EGG_MACROS_H_ + +#include <glib.h> + +G_BEGIN_DECLS + +/* Macros for defining classes. Ideas taken from Caja and GOB. */ + +/* Define the boilerplate type stuff to reduce typos and code size. Defines + * the get_type method and the parent_class static variable. */ + +#define EGG_BOILERPLATE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro, \ + register_type_macro) \ +static void type_as_function ## _class_init (type ## Class *klass); \ +static void type_as_function ## _instance_init (type *object); \ +static parent_type ## Class *parent_class = NULL; \ +static void \ +type_as_function ## _class_init_trampoline (gpointer klass, \ + gpointer data) \ +{ \ + parent_class = (parent_type ## Class *)g_type_class_ref ( \ + parent_type_macro); \ + type_as_function ## _class_init ((type ## Class *)klass); \ +} \ +GType \ +type_as_function ## _get_type (void) \ +{ \ + static GType object_type = 0; \ + if (object_type == 0) { \ + static const GTypeInfo object_info = { \ + sizeof (type ## Class), \ + NULL, /* base_init */ \ + NULL, /* base_finalize */ \ + type_as_function ## _class_init_trampoline, \ + NULL, /* class_finalize */ \ + NULL, /* class_data */ \ + sizeof (type), \ + 0, /* n_preallocs */ \ + (GInstanceInitFunc) type_as_function ## _instance_init \ + }; \ + object_type = register_type_macro \ + (type, type_as_function, corba_type, \ + parent_type, parent_type_macro); \ + } \ + return object_type; \ +} + +/* Just call the parent handler. This assumes that there is a variable + * named parent_class that points to the (duh!) parent class. Note that + * this macro is not to be used with things that return something, use + * the _WITH_DEFAULT version for that */ +#define EGG_CALL_PARENT(parent_class_cast, name, args) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : (void)0) + +/* Same as above, but in case there is no implementation, it evaluates + * to def_return */ +#define EGG_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \ + name, args, def_return) \ + ((parent_class_cast(parent_class)->name != NULL) ? \ + parent_class_cast(parent_class)->name args : def_return) + +/* Call a virtual method */ +#define EGG_CALL_VIRTUAL(object, get_class_cast, method, args) \ + (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0) + +/* Call a virtual method with default */ +#define EGG_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \ + (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default) + +#define EGG_CLASS_BOILERPLATE(type, type_as_function, \ + parent_type, parent_type_macro) \ + EGG_BOILERPLATE(type, type_as_function, type, \ + parent_type, parent_type_macro, \ + EGG_REGISTER_TYPE) + +#define EGG_REGISTER_TYPE(type, type_as_function, corba_type, \ + parent_type, parent_type_macro) \ + g_type_register_static (parent_type_macro, #type, &object_info, 0) + + +#define EGG_DEFINE_BOXED_TYPE(TN, t_n) \ +EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, {}); + +#define EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, _C_) \ +\ +static gpointer t_n##_copy (gpointer boxed); \ +static void t_n##_free (gpointer boxed); \ +\ +EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, t_n##_copy, t_n##_free, _C_); + +#define EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, b_c, b_f, _C_) \ +\ +_EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TN, t_n, b_c, b_f) {_C_;} \ +_EGG_DEFINE_BOXED_TYPE_EXTENDED_END() + +#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TypeName, type_name, boxed_copy, boxed_free) \ +\ +GType \ +type_name##_get_type (void) \ +{ \ + static volatile gsize g_define_type_id__volatile = 0; \ + if (g_once_init_enter (&g_define_type_id__volatile)) \ + { \ + GType g_define_type_id = \ + g_boxed_type_register_static (g_intern_static_string (#TypeName), \ + boxed_copy, boxed_free); \ + { /* custom code follows */ +#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_END() \ + /* following custom code */ \ + } \ + g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \ + } \ + return g_define_type_id__volatile; \ +} /* closes type_name##_get_type() */ + +#define EGG_DEFINE_QUARK(QN, q_n) \ +\ +GQuark \ +q_n##_quark (void) \ +{ \ + static volatile gsize g_define_quark__volatile = 0; \ + if (g_once_init_enter (&g_define_quark__volatile)) \ + { \ + GQuark g_define_quark = g_quark_from_string (#QN); \ + g_once_init_leave (&g_define_quark__volatile, g_define_quark); \ + } \ + return g_define_quark__volatile; \ +} + +#define EGG_IS_POSITIVE_RESPONSE(response_id) \ + ((response_id) == GTK_RESPONSE_ACCEPT || \ + (response_id) == GTK_RESPONSE_OK || \ + (response_id) == GTK_RESPONSE_YES || \ + (response_id) == GTK_RESPONSE_APPLY) + +#define EGG_IS_NEGATIVE_RESPONSE(response_id) \ + ((response_id) == GTK_RESPONSE_REJECT || \ + (response_id) == GTK_RESPONSE_CANCEL || \ + (response_id) == GTK_RESPONSE_NO) + +G_END_DECLS + +#endif /* _EGG_MACROS_H_ */ diff --git a/src/eggfileformatchooser.c b/src/eggfileformatchooser.c new file mode 100644 index 0000000..894da54 --- /dev/null +++ b/src/eggfileformatchooser.c @@ -0,0 +1,1223 @@ +/* EggFileFormatChooser + * Copyright (C) 2007 Mathias Hasselmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#include "eggfileformatchooser.h" +#include "egg-macros.h" + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <string.h> +#include <ctype.h> + +typedef struct _EggFileFormatFilterInfo EggFileFormatFilterInfo; +typedef struct _EggFileFormatSearch EggFileFormatSearch; + +enum +{ + MODEL_COLUMN_ID, + MODEL_COLUMN_NAME, + MODEL_COLUMN_ICON, + MODEL_COLUMN_EXTENSIONS, + MODEL_COLUMN_FILTER, + MODEL_COLUMN_DATA, + MODEL_COLUMN_DESTROY +}; + +enum +{ + SIGNAL_SELECTION_CHANGED, + SIGNAL_LAST +}; + +struct _EggFileFormatChooserPrivate +{ + GtkTreeStore *model; + GtkTreeSelection *selection; + guint idle_hack; + guint last_id; + gulong size_changed_event; + + GtkFileChooser *chooser; + GtkFileFilter *all_files; + GtkFileFilter *supported_files; +}; + +struct _EggFileFormatFilterInfo +{ + GHashTable *extension_set; + GSList *extension_list; + gboolean show_extensions; + gchar *name; +}; + +struct _EggFileFormatSearch +{ + gboolean success; + GtkTreeIter iter; + + guint format; + const gchar *extension; +}; + +static guint signals[SIGNAL_LAST]; + +G_DEFINE_TYPE (EggFileFormatChooser, + egg_file_format_chooser, + GTK_TYPE_EXPANDER); +static EGG_DEFINE_QUARK (EggFileFormatFilterInfo, + egg_file_format_filter_info); + +static EggFileFormatFilterInfo* +egg_file_format_filter_info_new (const gchar *name, + gboolean show_extensions) +{ + EggFileFormatFilterInfo *self; + + self = g_new0 (EggFileFormatFilterInfo, 1); + self->extension_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + self->show_extensions = show_extensions; + self->name = g_strdup (name); + + return self; +} + +static void +egg_file_format_filter_info_free (gpointer boxed) +{ + EggFileFormatFilterInfo *self; + + if (boxed) + { + self = boxed; + + g_hash_table_unref (self->extension_set); + g_slist_foreach (self->extension_list, (GFunc) g_free, NULL); + g_slist_free (self->extension_list); + g_free (self->name); + g_free (self); + } +} + +static gboolean +egg_file_format_filter_find (gpointer key, + gpointer value G_GNUC_UNUSED, + gpointer data) +{ + const GtkFileFilterInfo *info = data; + const gchar *pattern = key; + + return g_str_has_suffix (info->filename, pattern + 1); +} + +static gboolean +egg_file_format_filter_filter (const GtkFileFilterInfo *info, + gpointer data) +{ + EggFileFormatFilterInfo *self = data; + + return NULL != g_hash_table_find (self->extension_set, + egg_file_format_filter_find, + (gpointer) info); +} + +static GtkFileFilter* +egg_file_format_filter_new (const gchar *name, + gboolean show_extensions) +{ + GtkFileFilter *filter; + EggFileFormatFilterInfo *info; + + filter = gtk_file_filter_new (); + gtk_file_filter_set_name (filter, name); + + info = egg_file_format_filter_info_new (name, show_extensions); + + gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME, + egg_file_format_filter_filter, + info, NULL); + g_object_set_qdata_full (G_OBJECT (filter), + egg_file_format_filter_info_quark (), + info, egg_file_format_filter_info_free); + + return filter; +} + +static void +egg_file_format_filter_add_extensions (GtkFileFilter *filter, + const gchar *extensions) +{ + EggFileFormatFilterInfo *info; + GString *filter_name; + const gchar *extptr; + gchar *pattern; + gsize length; + + g_assert (NULL != extensions); + + info = g_object_get_qdata (G_OBJECT (filter), + egg_file_format_filter_info_quark ()); + + info->extension_list = g_slist_prepend (info->extension_list, + g_strdup (extensions)); + + if (info->show_extensions) + { + filter_name = g_string_new (info->name); + g_string_append (filter_name, " ("); + } + else + filter_name = NULL; + + extptr = extensions; + while (*extptr) + { + length = strcspn (extptr, ","); + pattern = g_new (gchar, length + 3); + + memcpy (pattern, "*.", 2); + memcpy (pattern + 2, extptr, length); + pattern[length + 2] = '\0'; + + if (filter_name) + { + if (extptr != extensions) + g_string_append (filter_name, ", "); + + g_string_append (filter_name, pattern); + } + + extptr += length; + + if (*extptr) + extptr += 2; + + g_hash_table_replace (info->extension_set, pattern, pattern); + } + + if (filter_name) + { + g_string_append (filter_name, ")"); + gtk_file_filter_set_name (filter, filter_name->str); + g_string_free (filter_name, TRUE); + } +} + +static void +selection_changed_cb (GtkTreeSelection *selection, + EggFileFormatChooser *self) +{ + gchar *label; + gchar *name; + + GtkFileFilter *filter; + GtkTreeModel *model; + GtkTreeIter parent; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, &model, &iter)) + { + gtk_tree_model_get (model, &iter, MODEL_COLUMN_NAME, &name, -1); + + label = g_strdup_printf (_("File _Format: %s"), name); + gtk_expander_set_use_underline (GTK_EXPANDER (self), TRUE); + gtk_expander_set_label (GTK_EXPANDER (self), label); + + g_free (name); + g_free (label); + + if (self->priv->chooser) + { + while (gtk_tree_model_iter_parent (model, &parent, &iter)) + iter = parent; + + gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1); + gtk_file_chooser_set_filter (self->priv->chooser, filter); + g_object_unref (filter); + } + + g_signal_emit (self, signals[SIGNAL_SELECTION_CHANGED], 0); + } +} + +/* XXX This hack is needed, as gtk_expander_set_label seems + * not to work from egg_file_format_chooser_init */ +static gboolean +select_default_file_format (gpointer data) +{ + EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (data); + egg_file_format_chooser_set_format (self, 0); + self->priv->idle_hack = 0; + return FALSE; +} + +static gboolean +find_by_format (GtkTreeModel *model, + GtkTreePath *path G_GNUC_UNUSED, + GtkTreeIter *iter, + gpointer data) +{ + EggFileFormatSearch *search = data; + guint id; + + gtk_tree_model_get (model, iter, MODEL_COLUMN_ID, &id, -1); + + if (id == search->format) + { + search->success = TRUE; + search->iter = *iter; + } + + return search->success; +} + +static gboolean +find_in_list (gchar *list, + const gchar *needle) +{ + gchar *saveptr; + gchar *token; + + for (token = strtok_r (list, ",", &saveptr); NULL != token; + token = strtok_r (NULL, ",", &saveptr)) + { + token = g_strstrip (token); + + if (strcasecmp (needle, token) == 0) + return TRUE; + } + + return FALSE; +} + +static gboolean +accept_filename (gchar *extensions, + const gchar *filename) +{ + const gchar *extptr; + gchar *saveptr; + gchar *token; + gsize length; + + length = strlen (filename); + + for (token = strtok_r (extensions, ",", &saveptr); NULL != token; + token = strtok_r (NULL, ",", &saveptr)) + { + token = g_strstrip (token); + extptr = filename + length - strlen (token) - 1; + + if (extptr > filename && '.' == *extptr && + !strcmp (extptr + 1, token)) + return TRUE; + } + + return FALSE; +} + +static gboolean +find_by_extension (GtkTreeModel *model, + GtkTreePath *path G_GNUC_UNUSED, + GtkTreeIter *iter, + gpointer data) +{ + EggFileFormatSearch *search = data; + + gchar *extensions = NULL; + guint format = 0; + + gtk_tree_model_get (model, iter, + MODEL_COLUMN_EXTENSIONS, &extensions, + MODEL_COLUMN_ID, &format, + -1); + + if (extensions && find_in_list (extensions, search->extension)) + { + search->format = format; + search->success = TRUE; + search->iter = *iter; + } + + g_free (extensions); + return search->success; +} + +static int +emit_default_size_changed (gpointer user_data) +{ + EggFileFormatChooser *self = user_data; + + self->priv->size_changed_event = 0; + g_signal_emit_by_name (self->priv->chooser, "default-size-changed"); + return FALSE; +} + +static void +expander_unmap_cb (GtkWidget *widget, + gpointer user_data) +{ + EggFileFormatChooser *self = user_data; + + if (self->priv->size_changed_event == 0) + self->priv->size_changed_event = gdk_threads_add_idle (emit_default_size_changed, self); +} + +static void +egg_file_format_chooser_init (EggFileFormatChooser *self) +{ + GtkWidget *scroller; + GtkWidget *view; + + GtkTreeViewColumn *column; + GtkCellRenderer *cell; + GtkTreeIter iter; + + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EGG_TYPE_FILE_FORMAT_CHOOSER, + EggFileFormatChooserPrivate); + + self->priv->size_changed_event = 0; + +/* file filters */ + + self->priv->all_files = g_object_ref_sink (gtk_file_filter_new ()); + gtk_file_filter_set_name (self->priv->all_files, _("All Files")); + self->priv->supported_files = egg_file_format_filter_new (_("All Supported Files"), FALSE); + +/* tree model */ + + self->priv->model = gtk_tree_store_new (7, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, + GTK_TYPE_FILE_FILTER, G_TYPE_POINTER, G_TYPE_POINTER); + + gtk_tree_store_append (self->priv->model, &iter, NULL); + gtk_tree_store_set (self->priv->model, &iter, + MODEL_COLUMN_NAME, _("By Extension"), + MODEL_COLUMN_FILTER, self->priv->supported_files, + MODEL_COLUMN_ID, 0, + -1); + +/* tree view */ + + view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->priv->model)); + self->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view)); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE); + +/* file format column */ + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_expand (column, TRUE); + gtk_tree_view_column_set_title (column, _("File Format")); + gtk_tree_view_append_column (GTK_TREE_VIEW (view), column); + + cell = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, cell, FALSE); + gtk_tree_view_column_set_attributes (column, cell, + "icon-name", MODEL_COLUMN_ICON, + NULL); + + cell = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, cell, TRUE); + gtk_tree_view_column_set_attributes (column, cell, + "text", MODEL_COLUMN_NAME, + NULL); + +/* extensions column */ + + column = gtk_tree_view_column_new_with_attributes ( + _("Extension(s)"), gtk_cell_renderer_text_new (), + "text", MODEL_COLUMN_EXTENSIONS, NULL); + gtk_tree_view_column_set_expand (column, FALSE); + gtk_tree_view_append_column (GTK_TREE_VIEW (view), column); + +/* selection */ + + gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_BROWSE); + g_signal_connect (self->priv->selection, "changed", + G_CALLBACK (selection_changed_cb), self); + self->priv->idle_hack = g_idle_add (select_default_file_format, self); + +/* scroller */ + + scroller = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller), + GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller), + GTK_SHADOW_IN); + gtk_widget_set_size_request (scroller, -1, 150); + gtk_container_add (GTK_CONTAINER (scroller), view); + gtk_widget_show_all (scroller); + + gtk_container_add (GTK_CONTAINER (self), scroller); + + g_signal_connect_after (scroller, "unmap", G_CALLBACK (expander_unmap_cb), self); +} + +static void +reset_model (EggFileFormatChooser *self) +{ + GtkTreeModel *model = GTK_TREE_MODEL (self->priv->model); + GtkTreeIter iter; + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + GDestroyNotify destroy = NULL; + gpointer data = NULL; + + gtk_tree_model_get (model, &iter, + MODEL_COLUMN_DESTROY, &destroy, + MODEL_COLUMN_DATA, &data, + -1); + + if (destroy) + destroy (data); + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + gtk_tree_store_clear (self->priv->model); +} + +static void +egg_file_format_chooser_dispose (GObject *obj) +{ + EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj); + + if (NULL != self) + { + if (self->priv->idle_hack) + { + g_source_remove (self->priv->idle_hack); + self->priv->idle_hack = 0; + } + if (self->priv->size_changed_event != 0) + { + g_source_remove (self->priv->size_changed_event); + self->priv->size_changed_event = 0; + } + } + + G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->dispose (obj); +} + +static void +egg_file_format_chooser_finalize (GObject *obj) +{ + EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj); + + if (NULL != self) + { + if (self->priv->model) + { + reset_model (self); + + g_object_unref (self->priv->model); + self->priv->model = NULL; + + g_object_unref (self->priv->all_files); + self->priv->all_files = NULL; + } + } + + G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->finalize (obj); +} + +static void +filter_changed_cb (GObject *object, + GParamSpec *spec, + gpointer data) +{ + EggFileFormatChooser *self; + + GtkFileFilter *current_filter; + GtkFileFilter *format_filter; + + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreeIter parent; + + self = EGG_FILE_FORMAT_CHOOSER (data); + + format_filter = NULL; + current_filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (object)); + model = GTK_TREE_MODEL (self->priv->model); + + if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter)) + { + while (gtk_tree_model_iter_parent (model, &parent, &iter)) + iter = parent; + + gtk_tree_model_get (model, &iter, + MODEL_COLUMN_FILTER, + &format_filter, -1); + g_object_unref (format_filter); + } + + if (current_filter && current_filter != format_filter && + gtk_tree_model_get_iter_first (model, &iter)) + { + if (current_filter == self->priv->all_files) + format_filter = current_filter; + else + { + format_filter = NULL; + + do + { + gtk_tree_model_get (model, &iter, + MODEL_COLUMN_FILTER, + &format_filter, -1); + g_object_unref (format_filter); + + if (format_filter == current_filter) + break; + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + if (format_filter) + gtk_tree_selection_select_iter (self->priv->selection, &iter); + } +} + +/* Shows an error dialog set as transient for the specified window */ +static void +error_message_with_parent (GtkWindow *parent, + const char *msg, + const char *detail) +{ + gboolean first_call = TRUE; + GtkWidget *dialog; + + if (first_call) + { + g_warning ("%s: Merge with the code in Gtk{File,Recent}ChooserDefault.", G_STRLOC); + first_call = FALSE; + } + + dialog = gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", + msg); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + "%s", detail); + + if (gtk_window_get_group (parent)) + gtk_window_group_add_window (gtk_window_get_group (parent), GTK_WINDOW (dialog)); + + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +/* Returns a toplevel GtkWindow, or NULL if none */ +static GtkWindow * +get_toplevel (GtkWidget *widget) +{ + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (widget); + if (!gtk_widget_is_toplevel (toplevel)) + return NULL; + else + return GTK_WINDOW (toplevel); +} + +/* Shows an error dialog for the file chooser */ +static void +error_message (EggFileFormatChooser *impl, + const char *msg, + const char *detail) +{ + error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail); +} + +static void +chooser_response_cb (GtkDialog *dialog, + gint response_id, + gpointer data) +{ + EggFileFormatChooser *self; + gchar *filename, *basename; + gchar *message; + guint format; + + self = EGG_FILE_FORMAT_CHOOSER (data); + + if (EGG_IS_POSITIVE_RESPONSE (response_id)) + { + filename = gtk_file_chooser_get_filename (self->priv->chooser); + basename = g_filename_display_basename (filename); + g_free (filename); + + format = egg_file_format_chooser_get_format (self, basename); + g_print ("%s: %s - %d\n", G_STRFUNC, basename, format); + + if (0 == format) + { + + message = g_strdup_printf ( + _("The program was not able to find out the file format " + "you want to use for `%s'. Please make sure to use a " + "known extension for that file or manually choose a " + "file format from the list below."), + basename); + + error_message (self, + _("File format not recognized"), + message); + + g_free (message); + + g_signal_stop_emission_by_name (dialog, "response"); + } + else + { + filename = egg_file_format_chooser_append_extension (self, basename, format); + + if (strcmp (filename, basename)) + { + gtk_file_chooser_set_current_name (self->priv->chooser, filename); + g_signal_stop_emission_by_name (dialog, "response"); + } + + g_free (filename); + } + + g_free (basename); + } + +} + +static void +egg_file_format_chooser_realize (GtkWidget *widget) +{ + EggFileFormatChooser *self; + GtkWidget *parent; + + GtkFileFilter *filter; + GtkTreeModel *model; + GtkTreeIter iter; + + GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->realize (widget); + + self = EGG_FILE_FORMAT_CHOOSER (widget); + + g_return_if_fail (NULL == self->priv->chooser); + + parent = gtk_widget_get_parent (widget); + while ((parent != NULL) && !GTK_IS_FILE_CHOOSER (parent)) + parent = gtk_widget_get_parent (parent); + + self->priv->chooser = GTK_FILE_CHOOSER (parent); + + g_return_if_fail (GTK_IS_FILE_CHOOSER (self->priv->chooser)); + g_return_if_fail (gtk_file_chooser_get_action (self->priv->chooser) == + GTK_FILE_CHOOSER_ACTION_SAVE); + + g_object_ref (self->priv->chooser); + + g_signal_connect (self->priv->chooser, "notify::filter", + G_CALLBACK (filter_changed_cb), self); + gtk_file_chooser_add_filter (self->priv->chooser, self->priv->all_files); + + model = GTK_TREE_MODEL (self->priv->model); + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1); + gtk_file_chooser_add_filter (self->priv->chooser, filter); + g_object_unref (filter); + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + gtk_file_chooser_set_filter (self->priv->chooser, + self->priv->supported_files); + + if (GTK_IS_DIALOG (self->priv->chooser)) + g_signal_connect (self->priv->chooser, "response", + G_CALLBACK (chooser_response_cb), self); +} + +static void +egg_file_format_chooser_unrealize (GtkWidget *widget) +{ + EggFileFormatChooser *self; + + GtkFileFilter *filter; + GtkTreeModel *model; + GtkTreeIter iter; + + GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->unrealize (widget); + + self = EGG_FILE_FORMAT_CHOOSER (widget); + model = GTK_TREE_MODEL (self->priv->model); + + g_signal_handlers_disconnect_by_func (self->priv->chooser, + filter_changed_cb, self); + g_signal_handlers_disconnect_by_func (self->priv->chooser, + chooser_response_cb, self); + + if (gtk_tree_model_get_iter_first (model, &iter)) + { + do + { + gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1); + gtk_file_chooser_remove_filter (self->priv->chooser, filter); + g_object_unref (filter); + } + while (gtk_tree_model_iter_next (model, &iter)); + } + + gtk_file_chooser_remove_filter (self->priv->chooser, self->priv->all_files); + g_object_unref (self->priv->chooser); +} + +static void +egg_file_format_chooser_class_init (EggFileFormatChooserClass *cls) +{ + GObjectClass *object_class = G_OBJECT_CLASS (cls); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (cls); + + g_type_class_add_private (cls, sizeof (EggFileFormatChooserPrivate)); + + object_class->dispose = egg_file_format_chooser_dispose; + object_class->finalize = egg_file_format_chooser_finalize; + + widget_class->realize = egg_file_format_chooser_realize; + widget_class->unrealize = egg_file_format_chooser_unrealize; + + signals[SIGNAL_SELECTION_CHANGED] = g_signal_new ( + "selection-changed", EGG_TYPE_FILE_FORMAT_CHOOSER, G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EggFileFormatChooserClass, selection_changed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +GtkWidget* +egg_file_format_chooser_new (void) +{ + return g_object_new (EGG_TYPE_FILE_FORMAT_CHOOSER, NULL); +} + +static guint +egg_file_format_chooser_add_format_impl (EggFileFormatChooser *self, + guint parent, + const gchar *name, + const gchar *icon, + const gchar *extensions) +{ + EggFileFormatSearch search; + GtkFileFilter *filter; + GtkTreeIter iter; + + search.success = FALSE; + search.format = parent; + filter = NULL; + + if (parent > 0) + { + gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model), + find_by_format, &search); + g_return_val_if_fail (search.success, -1); + } + else + filter = egg_file_format_filter_new (name, TRUE); + + gtk_tree_store_append (self->priv->model, &iter, + parent > 0 ? &search.iter : NULL); + + gtk_tree_store_set (self->priv->model, &iter, + MODEL_COLUMN_ID, ++self->priv->last_id, + MODEL_COLUMN_EXTENSIONS, extensions, + MODEL_COLUMN_FILTER, filter, + MODEL_COLUMN_NAME, name, + MODEL_COLUMN_ICON, icon, + -1); + + if (extensions) + { + if (parent > 0) + gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model), &search.iter, + MODEL_COLUMN_FILTER, &filter, -1); + + egg_file_format_filter_add_extensions (self->priv->supported_files, extensions); + egg_file_format_filter_add_extensions (filter, extensions); + + if (parent > 0) + g_object_unref (filter); + } + + return self->priv->last_id; +} + +guint +egg_file_format_chooser_add_format (EggFileFormatChooser *self, + guint parent, + const gchar *name, + const gchar *icon, + ...) +{ + GString *buffer = NULL; + const gchar* extptr; + va_list extensions; + guint id; + + g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), 0); + g_return_val_if_fail (NULL != name, 0); + + va_start (extensions, icon); + + while (NULL != (extptr = va_arg (extensions, const gchar*))) + { + if (NULL == buffer) + buffer = g_string_new (NULL); + else + g_string_append (buffer, ", "); + + g_string_append (buffer, extptr); + } + + va_end (extensions); + + id = egg_file_format_chooser_add_format_impl (self, parent, name, icon, + buffer ? buffer->str : NULL); + + if (buffer) + g_string_free (buffer, TRUE); + + return id; +} + +static gchar* +get_icon_name (const gchar *mime_type) +{ + static gboolean first_call = TRUE; + gchar *name = NULL; + gchar *s; + + if (first_call) + { + g_warning ("%s: Replace by g_content_type_get_icon " + "when GVFS is merged into GLib.", G_STRLOC); + first_call = FALSE; + } + + if (mime_type) + { + name = g_strconcat ("mate-mime-", mime_type, NULL); + + for(s = name; *s; ++s) + { + if (!isalpha (*s) || !isascii (*s)) + *s = '-'; + } + } + + if (!name || + !gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name)) + { + g_free (name); + name = g_strdup ("mate-mime-image"); + } + + return name; +} + +void +egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self, + guint parent G_GNUC_UNUSED, + guint **formats) +{ + GSList *pixbuf_formats = NULL; + GSList *iter; + gint i; + + g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self)); + + pixbuf_formats = gdk_pixbuf_get_formats (); + + if (formats) + *formats = g_new0 (guint, g_slist_length (pixbuf_formats) + 1); + + for(iter = pixbuf_formats, i = 0; iter; iter = iter->next, ++i) + { + GdkPixbufFormat *format = iter->data; + + gchar *description, *name, *extensions, *icon; + gchar **mime_types, **extension_list; + guint id; + + if (gdk_pixbuf_format_is_disabled (format) || + !gdk_pixbuf_format_is_writable (format)) + continue; + + mime_types = gdk_pixbuf_format_get_mime_types (format); + icon = get_icon_name (mime_types[0]); + g_strfreev (mime_types); + + extension_list = gdk_pixbuf_format_get_extensions (format); + extensions = g_strjoinv (", ", extension_list); + g_strfreev (extension_list); + + description = gdk_pixbuf_format_get_description (format); + name = gdk_pixbuf_format_get_name (format); + + id = egg_file_format_chooser_add_format_impl (self, parent, description, + icon, extensions); + + g_free (description); + g_free (extensions); + g_free (icon); + + egg_file_format_chooser_set_format_data (self, id, name, g_free); + + if (formats) + *formats[i] = id; + } + + g_slist_free (pixbuf_formats); +} + +void +egg_file_format_chooser_remove_format (EggFileFormatChooser *self, + guint format) +{ + GDestroyNotify destroy = NULL; + gpointer data = NULL; + + EggFileFormatSearch search; + GtkFileFilter *filter; + GtkTreeModel *model; + + g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self)); + + search.success = FALSE; + search.format = format; + + model = GTK_TREE_MODEL (self->priv->model); + gtk_tree_model_foreach (model, find_by_format, &search); + + g_return_if_fail (search.success); + + gtk_tree_model_get (model, &search.iter, + MODEL_COLUMN_FILTER, &filter, + MODEL_COLUMN_DESTROY, &destroy, + MODEL_COLUMN_DATA, &data, + -1); + + if (destroy) + destroy (data); + + if (filter) + { + if (self->priv->chooser) + gtk_file_chooser_remove_filter (self->priv->chooser, filter); + + g_object_unref (filter); + } + else + g_warning ("TODO: Remove extensions from parent filter"); + + gtk_tree_store_remove (self->priv->model, &search.iter); +} + +void +egg_file_format_chooser_set_format (EggFileFormatChooser *self, + guint format) +{ + EggFileFormatSearch search; + + GtkTreeModel *model; + GtkTreePath *path; + GtkTreeView *view; + + g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self)); + + search.success = FALSE; + search.format = format; + + model = GTK_TREE_MODEL (self->priv->model); + gtk_tree_model_foreach (model, find_by_format, &search); + + g_return_if_fail (search.success); + + path = gtk_tree_model_get_path (model, &search.iter); + view = gtk_tree_selection_get_tree_view (self->priv->selection); + + gtk_tree_view_expand_to_path (view, path); + gtk_tree_selection_unselect_all (self->priv->selection); + gtk_tree_selection_select_path (self->priv->selection, path); + + gtk_tree_path_free (path); + + if (self->priv->idle_hack > 0) + { + g_source_remove (self->priv->idle_hack); + self->priv->idle_hack = 0; + } +} + +guint +egg_file_format_chooser_get_format (EggFileFormatChooser *self, + const gchar *filename) +{ + GtkTreeModel *model; + GtkTreeIter iter; + guint format = 0; + + g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), -1); + + if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter)) + gtk_tree_model_get (model, &iter, MODEL_COLUMN_ID, &format, -1); + + if (0 == format && NULL != filename) + { + EggFileFormatSearch search; + + search.extension = strrchr(filename, '.'); + search.success = FALSE; + + if (search.extension++) + gtk_tree_model_foreach (model, find_by_extension, &search); + if (search.success) + format = search.format; + } + + return format; +} + +void +egg_file_format_chooser_set_format_data (EggFileFormatChooser *self, + guint format, + gpointer data, + GDestroyNotify destroy) +{ + EggFileFormatSearch search; + + g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self)); + + search.success = FALSE; + search.format = format; + + gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model), + find_by_format, &search); + + g_return_if_fail (search.success); + + gtk_tree_store_set (self->priv->model, &search.iter, + MODEL_COLUMN_DESTROY, destroy, + MODEL_COLUMN_DATA, data, + -1); +} + +gpointer +egg_file_format_chooser_get_format_data (EggFileFormatChooser *self, + guint format) +{ + EggFileFormatSearch search; + gpointer data = NULL; + GtkTreeModel *model; + + g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL); + + search.success = FALSE; + search.format = format; + + model = GTK_TREE_MODEL (self->priv->model); + gtk_tree_model_foreach (model, find_by_format, &search); + + g_return_val_if_fail (search.success, NULL); + + gtk_tree_model_get (model, &search.iter, + MODEL_COLUMN_DATA, &data, + -1); + return data; +} + +gchar* +egg_file_format_chooser_append_extension (EggFileFormatChooser *self, + const gchar *filename, + guint format) +{ + EggFileFormatSearch search; + GtkTreeModel *model; + GtkTreeIter child; + + gchar *extensions; + gchar *result; + + g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL); + g_return_val_if_fail (NULL != filename, NULL); + + if (0 == format) + format = egg_file_format_chooser_get_format (self, NULL); + + if (0 == format) + { + g_warning ("%s: No file format selected. Cannot append extension.", G_STRFUNC); + return NULL; + } + + search.success = FALSE; + search.format = format; + + model = GTK_TREE_MODEL (self->priv->model); + gtk_tree_model_foreach (model, find_by_format, &search); + + g_return_val_if_fail (search.success, NULL); + + gtk_tree_model_get (model, &search.iter, + MODEL_COLUMN_EXTENSIONS, &extensions, + -1); + + if (NULL == extensions && + gtk_tree_model_iter_nth_child (model, &child, &search.iter, 0)) + { + gtk_tree_model_get (model, &child, + MODEL_COLUMN_EXTENSIONS, &extensions, + -1); + } + + if (NULL == extensions) + { + g_warning ("%s: File format %d doesn't provide file extensions. " + "Cannot append extension.", G_STRFUNC, format); + return NULL; + } + + if (accept_filename (extensions, filename)) + result = g_strdup (filename); + else + result = g_strconcat (filename, ".", extensions, NULL); + + g_assert (NULL == strchr(extensions, ',')); + g_free (extensions); + return result; +} + +void +egg_file_format_chooser_emit_size_changed (EggFileFormatChooser *self) +{ + if (self->priv->size_changed_event == 0) + self->priv->size_changed_event = gdk_threads_add_idle (emit_default_size_changed, self); +} + +/* vim: set sw=2 sta et: */ diff --git a/src/eggfileformatchooser.h b/src/eggfileformatchooser.h new file mode 100644 index 0000000..6c78891 --- /dev/null +++ b/src/eggfileformatchooser.h @@ -0,0 +1,85 @@ +/* EggFileFormatChooser + * Copyright (C) 2007 Mathias Hasselmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef __EGG_FILE_FORMAT_CHOOSER_H__ +#define __EGG_FILE_FORMAT_CHOOSER_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_FILE_FORMAT_CHOOSER (egg_file_format_chooser_get_type()) +#define EGG_FILE_FORMAT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooser)) +#define EGG_FILE_FORMAT_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass)) +#define EGG_IS_FILE_FORMAT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER)) +#define EGG_IS_FILE_FORMAT_CHOOSER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER)) +#define EGG_FILE_FORMAT_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass)) + +typedef struct _EggFileFormatChooser EggFileFormatChooser; +typedef struct _EggFileFormatChooserClass EggFileFormatChooserClass; +typedef struct _EggFileFormatChooserPrivate EggFileFormatChooserPrivate; + +struct _EggFileFormatChooser +{ + GtkExpander parent; + EggFileFormatChooserPrivate *priv; +}; + +struct _EggFileFormatChooserClass +{ + GtkExpanderClass parent; + + void (*selection_changed)(EggFileFormatChooser *self); +}; + +GType egg_file_format_chooser_get_type (void) G_GNUC_CONST; +GtkWidget* egg_file_format_chooser_new (void); + +guint egg_file_format_chooser_add_format (EggFileFormatChooser *self, + guint parent, + const gchar *name, + const gchar *icon, + ...) G_GNUC_NULL_TERMINATED; +void egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self, + guint parent, + guint **formats); +void egg_file_format_chooser_remove_format (EggFileFormatChooser *self, + guint format); + +void egg_file_format_chooser_set_format (EggFileFormatChooser *self, + guint format); +guint egg_file_format_chooser_get_format (EggFileFormatChooser *self, + const gchar *filename); + +void egg_file_format_chooser_set_format_data (EggFileFormatChooser *self, + guint format, + gpointer data, + GDestroyNotify destroy); +gpointer egg_file_format_chooser_get_format_data (EggFileFormatChooser *self, + guint format); + +gchar* egg_file_format_chooser_append_extension (EggFileFormatChooser *self, + const gchar *filename, + guint format); +void egg_file_format_chooser_emit_size_changed (EggFileFormatChooser *self); + +G_END_DECLS + +#endif /* __EGG_FILE_FORMAT_CHOOSER_H__ */ + +/* vim: set sw=2 sta et: */ diff --git a/src/eggtreemultidnd.c b/src/eggtreemultidnd.c new file mode 100644 index 0000000..5bf4698 --- /dev/null +++ b/src/eggtreemultidnd.c @@ -0,0 +1,459 @@ +/* + * File-Roller + * + * Copyright (C) 2005 Free Software Foundation, Inc. + * Author: Paolo Bacchilega + */ + +/* eggtreemultidnd.c + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#include <string.h> +#include <gtk/gtk.h> +#include "eggtreemultidnd.h" + +#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString" + +static GtkTargetEntry target_table[] = { + { "XdndDirectSave0", 0, 0 }, + { "XdndFileRoller0", 0, 1 } +}; + +typedef struct +{ + guint pressed_button; + gint x; + gint y; + guint motion_notify_handler; + guint button_release_handler; + guint drag_data_get_handler; + GSList *event_list; + gboolean pending_event; +} EggTreeMultiDndData; + + +GType +egg_tree_multi_drag_source_get_type (void) +{ + static GType our_type = 0; + + if (!our_type) + { + static const GTypeInfo our_info = + { + sizeof (EggTreeMultiDragSourceIface), /* class_size */ + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + 0, + 0, /* n_preallocs */ + NULL + }; + + our_type = g_type_register_static (G_TYPE_INTERFACE, + "EggTreeMultiDragSource", + &our_info, + 0); + } + + return our_type; +} + + +/** + * egg_tree_multi_drag_source_row_draggable: + * @drag_source: a #EggTreeMultiDragSource + * @path: row on which user is initiating a drag + * + * Asks the #EggTreeMultiDragSource whether a particular row can be used as + * the source of a DND operation. If the source doesn't implement + * this interface, the row is assumed draggable. + * + * Return value: %TRUE if the row can be dragged + **/ +gboolean +egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source, + GList *path_list) +{ + EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source); + + g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE); + g_return_val_if_fail (iface->row_draggable != NULL, FALSE); + g_return_val_if_fail (path_list != NULL, FALSE); + + if (iface->row_draggable) + return (* iface->row_draggable) (drag_source, path_list); + else + return TRUE; +} + + +/** + * egg_tree_multi_drag_source_drag_data_delete: + * @drag_source: a #EggTreeMultiDragSource + * @path: row that was being dragged + * + * Asks the #EggTreeMultiDragSource to delete the row at @path, because + * it was moved somewhere else via drag-and-drop. Returns %FALSE + * if the deletion fails because @path no longer exists, or for + * some model-specific reason. Should robustly handle a @path no + * longer found in the model! + * + * Return value: %TRUE if the row was successfully deleted + **/ +gboolean +egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source, + GList *path_list) +{ + EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source); + + g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE); + g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE); + g_return_val_if_fail (path_list != NULL, FALSE); + + return (* iface->drag_data_delete) (drag_source, path_list); +} + + +/** + * egg_tree_multi_drag_source_drag_data_get: + * @drag_source: a #EggTreeMultiDragSource + * @path: row that was dragged + * @selection_data: a #EggSelectionData to fill with data from the dragged row + * + * Asks the #EggTreeMultiDragSource to fill in @selection_data with a + * representation of the row at @path. @selection_data->target gives + * the required type of the data. Should robustly handle a @path no + * longer found in the model! + * + * Return value: %TRUE if data of the required type was provided + **/ +gboolean +egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list) +{ + EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source); + + g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE); + g_return_val_if_fail (iface->drag_data_get != NULL, FALSE); + g_return_val_if_fail (path_list != NULL, FALSE); + g_return_val_if_fail (selection_data != NULL, FALSE); + + return (* iface->drag_data_get) (drag_source, context, selection_data, path_list); +} + + +static void +stop_drag_check (GtkWidget *widget) +{ + EggTreeMultiDndData *priv_data; + GSList *l; + + priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING); + + for (l = priv_data->event_list; l != NULL; l = l->next) + gdk_event_free (l->data); + + g_slist_free (priv_data->event_list); + priv_data->event_list = NULL; + priv_data->pending_event = FALSE; + + if (priv_data->motion_notify_handler) { + g_signal_handler_disconnect (widget, priv_data->motion_notify_handler); + priv_data->motion_notify_handler = 0; + } + if (priv_data->button_release_handler) { + g_signal_handler_disconnect (widget, priv_data->button_release_handler); + priv_data->button_release_handler = 0; + } +} + + +static gboolean +egg_tree_multi_drag_button_release_event (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + EggTreeMultiDndData *priv_data; + GSList *l; + + priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING); + + for (l = priv_data->event_list; l != NULL; l = l->next) + gtk_propagate_event (widget, l->data); + + stop_drag_check (widget); + + return FALSE; +} + + +static void +selection_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list_ptr; + + list_ptr = (GList **) data; + + *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path)); +} + + +static void +path_list_free (GList *path_list) +{ + g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL); + g_list_free (path_list); +} + + +static void +set_context_data (GdkDragContext *context, + GList *path_list) +{ + g_object_set_data_full (G_OBJECT (context), + "egg-tree-view-multi-source-row", + path_list, + (GDestroyNotify) path_list_free); +} + + +static GList * +get_context_data (GdkDragContext *context) +{ + return g_object_get_data (G_OBJECT (context), + "egg-tree-view-multi-source-row"); +} + + +static gboolean +egg_tree_multi_drag_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + GtkTreeView *tree_view; + GtkTreeModel *model; + GList *path_list; + + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); + if (model == NULL) + return FALSE; + + path_list = get_context_data (context); + if (path_list == NULL) + return FALSE; + + /* We can implement the GTK_TREE_MODEL_ROW target generically for + * any model; for DragSource models there are some other targets + * we also support. + */ + + if (! EGG_IS_TREE_MULTI_DRAG_SOURCE (model)) + return FALSE; + + return egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model), + context, + selection_data, + path_list); +} + + +static gboolean +egg_tree_multi_drag_motion_event (GtkWidget *widget, + GdkEventMotion *event, + gpointer data) +{ + EggTreeMultiDndData *priv_data; + + priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING); + + if (gtk_drag_check_threshold (widget, + priv_data->x, + priv_data->y, + event->x, + event->y)) + { + GList *path_list = NULL; + GtkTreeSelection *selection; + GtkTreeModel *model; + GdkDragContext *context; + + stop_drag_check (widget); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); + gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list); + if (path_list == NULL) + return FALSE; + + path_list = g_list_reverse (path_list); + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list)) + { + GtkTargetList *target_list = gtk_target_list_new (target_table, + G_N_ELEMENTS (target_table)); + + context = gtk_drag_begin (widget, + target_list, + GDK_ACTION_COPY, + priv_data->pressed_button, + (GdkEvent*)event); + set_context_data (context, path_list); + gtk_drag_set_icon_default (context); + + gtk_target_list_unref (target_list); + } + else + { + path_list_free (path_list); + } + } + + return TRUE; +} + + +static gboolean +egg_tree_multi_drag_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + GtkTreeView *tree_view; + GtkTreePath *path = NULL; + GtkTreeViewColumn *column = NULL; + gint cell_x, cell_y; + GtkTreeSelection *selection; + EggTreeMultiDndData *priv_data; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))) + return FALSE; + + if (event->button == 3) + return FALSE; + + tree_view = GTK_TREE_VIEW (widget); + priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING); + if (priv_data == NULL) + { + priv_data = g_new0 (EggTreeMultiDndData, 1); + priv_data->pending_event = FALSE; + g_object_set_data (G_OBJECT (tree_view), + EGG_TREE_MULTI_DND_STRING, + priv_data); + } + + if (g_slist_find (priv_data->event_list, event)) + return FALSE; + + if (priv_data->pending_event) + { + /* save the event to be propagated in order */ + priv_data->event_list = g_slist_append (priv_data->event_list, + gdk_event_copy ((GdkEvent*)event)); + return TRUE; + } + + if (event->type == GDK_2BUTTON_PRESS) + return FALSE; + + gtk_tree_view_get_path_at_pos (tree_view, + event->x, event->y, + &path, &column, + &cell_x, &cell_y); + + selection = gtk_tree_view_get_selection (tree_view); + + if (path) + { + gboolean call_parent = (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) || + !gtk_tree_selection_path_is_selected (selection, path) || + event->button != 1); + + if (call_parent) + (GTK_WIDGET_GET_CLASS (tree_view))->button_press_event (widget, event); + + if (gtk_tree_selection_path_is_selected (selection, path)) + { + priv_data->pressed_button = event->button; + priv_data->x = event->x; + priv_data->y = event->y; + + priv_data->pending_event = TRUE; + if (!call_parent) + priv_data->event_list = g_slist_append (priv_data->event_list, + gdk_event_copy ((GdkEvent*)event)); + + if (priv_data->motion_notify_handler == 0) + { + priv_data->motion_notify_handler = + g_signal_connect (G_OBJECT (tree_view), + "motion_notify_event", + G_CALLBACK (egg_tree_multi_drag_motion_event), + NULL); + } + + if (priv_data->button_release_handler == 0) + { + priv_data->button_release_handler = + g_signal_connect (G_OBJECT (tree_view), + "button_release_event", + G_CALLBACK (egg_tree_multi_drag_button_release_event), + NULL); + } + + if (priv_data->drag_data_get_handler == 0) + { + priv_data->drag_data_get_handler = + g_signal_connect (G_OBJECT (tree_view), + "drag_data_get", + G_CALLBACK (egg_tree_multi_drag_drag_data_get), + NULL); + } + } + + gtk_tree_path_free (path); + /* We called the default handler so we don't let the default handler run */ + return TRUE; + } + + return FALSE; +} + + +void +egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view) +{ + g_return_if_fail (GTK_IS_TREE_VIEW (tree_view)); + g_signal_connect (G_OBJECT (tree_view), + "button_press_event", + G_CALLBACK (egg_tree_multi_drag_button_press_event), + NULL); +} + diff --git a/src/eggtreemultidnd.h b/src/eggtreemultidnd.h new file mode 100644 index 0000000..0e8a790 --- /dev/null +++ b/src/eggtreemultidnd.h @@ -0,0 +1,75 @@ +/* eggtreednd.h + * Copyright (C) 2001 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __EGG_TREE_MULTI_DND_H__ +#define __EGG_TREE_MULTI_DND_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE (egg_tree_multi_drag_source_get_type ()) +#define EGG_TREE_MULTI_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource)) +#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE)) +#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface)) + +typedef struct _EggTreeMultiDragSource EggTreeMultiDragSource; /* Dummy typedef */ +typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface; + +struct _EggTreeMultiDragSourceIface +{ + GTypeInterface g_iface; + + /* VTable - not signals */ + gboolean (* row_draggable) (EggTreeMultiDragSource *drag_source, + GList *path_list); + + gboolean (* drag_data_get) (EggTreeMultiDragSource *drag_source, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list); + + gboolean (* drag_data_delete) (EggTreeMultiDragSource *drag_source, + GList *path_list); +}; + +GType egg_tree_multi_drag_source_get_type (void) G_GNUC_CONST; + +/* Returns whether the given row can be dragged */ +gboolean egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source, + GList *path_list); + +/* Deletes the given row, or returns FALSE if it can't */ +gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source, + GList *path_list); + + +/* Fills in selection_data with type selection_data->target based on the row + * denoted by path, returns TRUE if it does anything + */ +gboolean egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list); + +void egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view); + +G_END_DECLS + +#endif /* __EGG_TREE_MULTI_DND_H__ */ diff --git a/src/file-data.c b/src/file-data.c new file mode 100644 index 0000000..620accb --- /dev/null +++ b/src/file-data.c @@ -0,0 +1,152 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001-2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include "glib-utils.h" +#include "file-utils.h" +#include "file-data.h" + + +FileData * +file_data_new (void) +{ + FileData *fdata; + + fdata = g_new0 (FileData, 1); + fdata->content_type = NULL; + fdata->free_original_path = FALSE; + fdata->dir_size = 0; + + return fdata; +} + + +void +file_data_free (FileData *fdata) +{ + if (fdata == NULL) + return; + if (fdata->free_original_path) + g_free (fdata->original_path); + g_free (fdata->full_path); + g_free (fdata->name); + g_free (fdata->path); + g_free (fdata->link); + g_free (fdata->list_name); + g_free (fdata); +} + + +FileData * +file_data_copy (FileData *src) +{ + FileData *fdata; + + fdata = g_new0 (FileData, 1); + + fdata->original_path = g_strdup (src->original_path); + fdata->free_original_path = TRUE; + + fdata->full_path = g_strdup (src->full_path); + fdata->link = g_strdup (src->link); + fdata->size = src->size; + fdata->modified = src->modified; + fdata->name = g_strdup (src->name); + fdata->path = g_strdup (src->path); + fdata->content_type = src->content_type; + fdata->encrypted = src->encrypted; + fdata->dir = src->dir; + fdata->dir_size = src->dir_size; + + fdata->list_dir = src->list_dir; + fdata->list_name = g_strdup (src->list_name); + + return fdata; +} + + +GType +file_data_get_type (void) +{ + static GType type = 0; + + if (type == 0) + type = g_boxed_type_register_static ("FRFileData", (GBoxedCopyFunc) file_data_copy, (GBoxedFreeFunc) file_data_free); + + return type; +} + + +void +file_data_update_content_type (FileData *fdata) +{ + + if (fdata->dir) + fdata->content_type = MIME_TYPE_DIRECTORY; + else + fdata->content_type = get_static_string (g_content_type_guess (fdata->full_path, NULL, 0, NULL)); +} + + +gboolean +file_data_is_dir (FileData *fdata) +{ + return fdata->dir || fdata->list_dir; +} + + +int +file_data_compare_by_path (gconstpointer a, + gconstpointer b) +{ + FileData *data_a = *((FileData **) a); + FileData *data_b = *((FileData **) b); + + return strcmp (data_a->full_path, data_b->full_path); +} + + +int +find_path_in_file_data_array (GPtrArray *array, + const char *path) +{ + int l, r, p, cmp = -1; + FileData *fd; + + l = 0; + r = array->len; + while (l < r) { + p = l + ((r - l) / 2); + fd = (FileData *) g_ptr_array_index (array, p); + cmp = strcmp (path, fd->original_path); + if (cmp == 0) + return p; + else if (cmp < 0) + r = p; + else + l = p + 1; + } + + return -1; +} diff --git a/src/file-data.h b/src/file-data.h new file mode 100644 index 0000000..cd0147a --- /dev/null +++ b/src/file-data.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FILE_DATA_H +#define FILE_DATA_H + +#include <glib.h> +#include <glib-object.h> +#include <time.h> + +typedef struct { + char *original_path; /* path read from command line. */ + char *full_path; /* "/" + original_path. */ + char *link; + goffset size; + time_t modified; + + char *name; /* The file name. */ + char *path; /* The directory. */ + gboolean encrypted; /* Whether the file is encrypted. */ + gboolean dir; /* Whether this is a directory listed in the archive */ + goffset dir_size; + const char *content_type; + + /* Additional data. */ + + gboolean list_dir; /* Whether this entry is used to show + * a directory. */ + char *list_name; /* The string visualized in the list + * view. */ + + /* Private data */ + + gboolean free_original_path; +} FileData; + +#define FR_TYPE_FILE_DATA (file_data_get_type ()) + +GType file_data_get_type (void); +FileData * file_data_new (void); +FileData * file_data_copy (FileData *src); +void file_data_free (FileData *fdata); +void file_data_update_content_type (FileData *fdata); +gboolean file_data_is_dir (FileData *fdata); + +int file_data_compare_by_path (gconstpointer a, + gconstpointer b); +int find_path_in_file_data_array (GPtrArray *array, + const char *path); + +#endif /* FILE_DATA_H */ diff --git a/src/file-utils.c b/src/file-utils.c new file mode 100644 index 0000000..9c3bff5 --- /dev/null +++ b/src/file-utils.c @@ -0,0 +1,1395 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <dirent.h> + +#include <glib.h> +#include <gio/gio.h> +#include <mateconf/mateconf-client.h> +#include "file-utils.h" +#include "glib-utils.h" +#include "main.h" + + +#ifndef HAVE_MKDTEMP +#include "mkdtemp.h" +#endif + +#define BUF_SIZE 4096 +#define FILE_PREFIX "file://" +#define FILE_PREFIX_L 7 +#define SPECIAL_DIR(x) ((strcmp ((x), "..") == 0) || (strcmp ((x), ".") == 0)) + + +gboolean +uri_exists (const char *uri) +{ + GFile *file; + gboolean exists; + + if (uri == NULL) + return FALSE; + + file = g_file_new_for_uri (uri); + exists = g_file_query_exists (file, NULL); + g_object_unref (file); + + return exists; +} + + +static gboolean +uri_is_filetype (const char *uri, + GFileType file_type) +{ + gboolean result = FALSE; + GFile *file; + GFileInfo *info; + GError *error = NULL; + + file = g_file_new_for_uri (uri); + + if (! g_file_query_exists (file, NULL)) { + g_object_unref (file); + return FALSE; + } + + info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, &error); + if (error == NULL) { + result = (g_file_info_get_file_type (info) == file_type); + } + else { + g_warning ("Failed to get file type for uri %s: %s", uri, error->message); + g_error_free (error); + } + + g_object_unref (info); + g_object_unref (file); + + return result; +} + + +gboolean +uri_is_file (const char *uri) +{ + return uri_is_filetype (uri, G_FILE_TYPE_REGULAR); +} + + +gboolean +uri_is_dir (const char *uri) +{ + return uri_is_filetype (uri, G_FILE_TYPE_DIRECTORY); +} + + +gboolean +path_is_dir (const char *path) +{ + char *uri; + gboolean result; + + uri = g_filename_to_uri (path, NULL, NULL); + result = uri_is_dir (uri); + g_free (uri); + + return result; +} + +gboolean +uri_is_local (const char *uri) +{ + return strncmp (uri, "file://", 7) == 0; +} + + +gboolean +dir_is_empty (const char *uri) +{ + GFile *file; + GFileEnumerator *file_enum; + GFileInfo *info; + GError *error = NULL; + int n = 0; + + file = g_file_new_for_uri (uri); + + if (! g_file_query_exists (file, NULL)) { + g_object_unref (file); + return TRUE; + } + + file_enum = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error); + if (error != NULL) { + g_warning ("Failed to enumerate children of %s: %s", uri, error->message); + g_error_free (error); + g_object_unref (file_enum); + g_object_unref (file); + return TRUE; + } + + while ((n == 0) && ((info = g_file_enumerator_next_file (file_enum, NULL, &error)) != NULL)) { + if (error != NULL) { + g_warning ("Encountered error while enumerating children of %s (ignoring): %s", uri, error->message); + g_error_free (error); + } + else if (! SPECIAL_DIR (g_file_info_get_name (info))) + n++; + g_object_unref (info); + } + + g_object_unref (file); + g_object_unref (file_enum); + + return (n == 0); +} + + +gboolean +dir_contains_one_object (const char *uri) +{ + GFile *file; + GFileEnumerator *file_enum; + GFileInfo *info; + GError *err = NULL; + int n = 0; + + file = g_file_new_for_uri (uri); + + if (! g_file_query_exists (file, NULL)) { + g_object_unref (file); + return FALSE; + } + + file_enum = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &err); + if (err != NULL) { + g_warning ("Failed to enumerate children of %s: %s", uri, err->message); + g_error_free (err); + g_object_unref (file_enum); + g_object_unref (file); + return FALSE; + } + + while ((info = g_file_enumerator_next_file (file_enum, NULL, &err)) != NULL) { + const char *name; + + if (err != NULL) { + g_warning ("Encountered error while enumerating children of %s, ignoring: %s", uri, err->message); + g_error_free (err); + g_object_unref (info); + continue; + } + + name = g_file_info_get_name (info); + if (strcmp (name, ".") == 0 || strcmp (name, "..") == 0) { + g_object_unref (info); + continue; + } + + g_object_unref (info); + + if (++n > 1) + break; + } + + g_object_unref (file); + g_object_unref (file_enum); + + return (n == 1); +} + + +char * +get_dir_content_if_unique (const char *uri) +{ + GFile *file; + GFileEnumerator *file_enum; + GFileInfo *info; + GError *err = NULL; + char *content_uri = NULL; + + file = g_file_new_for_uri (uri); + + if (! g_file_query_exists (file, NULL)) { + g_object_unref (file); + return NULL; + } + + file_enum = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &err); + if (err != NULL) { + g_warning ("Failed to enumerate children of %s: %s", uri, err->message); + g_error_free (err); + return NULL; + } + + while ((info = g_file_enumerator_next_file (file_enum, NULL, &err)) != NULL) { + const char *name; + + if (err != NULL) { + g_warning ("Failed to get info while enumerating children: %s", err->message); + g_clear_error (&err); + g_object_unref (info); + continue; + } + + name = g_file_info_get_name (info); + if ((strcmp (name, ".") == 0) || (strcmp (name, "..") == 0)) { + g_object_unref (info); + continue; + } + + if (content_uri != NULL) { + g_free (content_uri); + g_object_unref (info); + content_uri = NULL; + break; + } + + content_uri = build_uri (uri, name, NULL); + g_object_unref (info); + } + + if (err != NULL) { + g_warning ("Failed to get info after enumerating children: %s", err->message); + g_clear_error (&err); + } + + g_object_unref (file_enum); + g_object_unref (file); + + return content_uri; +} + + +/* Check whether the dirname is contained in filename */ +gboolean +path_in_path (const char *dirname, + const char *filename) +{ + int dirname_l, filename_l, separator_position; + + if ((dirname == NULL) || (filename == NULL)) + return FALSE; + + dirname_l = strlen (dirname); + filename_l = strlen (filename); + + if ((dirname_l == filename_l + 1) + && (dirname[dirname_l - 1] == '/')) + return FALSE; + + if ((filename_l == dirname_l + 1) + && (filename[filename_l - 1] == '/')) + return FALSE; + + if (dirname[dirname_l - 1] == '/') + separator_position = dirname_l - 1; + else + separator_position = dirname_l; + + return ((filename_l > dirname_l) + && (strncmp (dirname, filename, dirname_l) == 0) + && (filename[separator_position] == '/')); +} + + +goffset +get_file_size (const char *uri) +{ + goffset size = 0; + GFile *file; + GFileInfo *info; + GError *err = NULL; + + if ((uri == NULL) || (*uri == '\0')) + return 0; + + file = g_file_new_for_uri (uri); + info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, 0, NULL, &err); + if (err == NULL) { + size = g_file_info_get_size (info); + } + else { + g_warning ("Failed to get file size for %s: %s", uri, err->message); + g_error_free (err); + } + + g_object_unref (info); + g_object_unref (file); + + return size; +} + + +goffset +get_file_size_for_path (const char *path) +{ + char *uri; + goffset result; + + uri = g_filename_to_uri (path, NULL, NULL); + result = get_file_size (uri); + g_free (uri); + + return result; +} + + +static time_t +get_file_time_type (const char *uri, + const char *type) +{ + time_t result = 0; + GFile *file; + GFileInfo *info; + GError *err = NULL; + + if ((uri == NULL) || (*uri == '\0')) + return 0; + + file = g_file_new_for_uri (uri); + info = g_file_query_info (file, type, 0, NULL, &err); + if (err == NULL) { + result = (time_t) g_file_info_get_attribute_uint64 (info, type); + } + else { + g_warning ("Failed to get %s: %s", type, err->message); + g_error_free (err); + result = 0; + } + + g_object_unref (info); + g_object_unref (file); + + return result; +} + + +time_t +get_file_mtime (const char *uri) +{ + return get_file_time_type (uri, G_FILE_ATTRIBUTE_TIME_MODIFIED); +} + + +time_t +get_file_mtime_for_path (const char *path) +{ + char *uri; + time_t result; + + uri = g_filename_to_uri (path, NULL, NULL); + result = get_file_mtime (uri); + g_free (uri); + + return result; +} + + +time_t +get_file_ctime (const char *uri) +{ + return get_file_time_type (uri, G_FILE_ATTRIBUTE_TIME_CREATED); +} + + +gboolean +file_is_hidden (const gchar *name) +{ + if (name[0] != '.') return FALSE; + if (name[1] == '\0') return FALSE; + if ((name[1] == '.') && (name[2] == '\0')) return FALSE; + + return TRUE; +} + + +/* like g_path_get_basename but does not warn about NULL and does not + * alloc a new string. */ +const gchar* file_name_from_path(const gchar *file_name) +{ + register char *base; + register gssize last_char; + + if (file_name == NULL) + return NULL; + + if (file_name[0] == '\0') + return ""; + + last_char = strlen (file_name) - 1; + + if (file_name [last_char] == G_DIR_SEPARATOR) + return ""; + + base = g_utf8_strrchr (file_name, -1, G_DIR_SEPARATOR); + if (! base) + return file_name; + + return base + 1; +} + + +char * +dir_name_from_path (const gchar *path) +{ + register gssize base; + register gssize last_char; + + if (path == NULL) + return NULL; + + if (path[0] == '\0') + return g_strdup (""); + + last_char = strlen (path) - 1; + if (path[last_char] == G_DIR_SEPARATOR) + last_char--; + + base = last_char; + while ((base >= 0) && (path[base] != G_DIR_SEPARATOR)) + base--; + + return g_strndup (path + base + 1, last_char - base); +} + + +gchar * +remove_level_from_path (const gchar *path) +{ + int p; + const char *ptr = path; + char *new_path; + + if (path == NULL) + return NULL; + + p = strlen (path) - 1; + if (p < 0) + return NULL; + + while ((p > 0) && (ptr[p] != '/')) + p--; + if ((p == 0) && (ptr[p] == '/')) + p++; + new_path = g_strndup (path, (guint)p); + + return new_path; +} + + +char * +remove_ending_separator (const char *path) +{ + gint len, copy_len; + + if (path == NULL) + return NULL; + + copy_len = len = strlen (path); + if ((len > 1) && (path[len - 1] == '/')) + copy_len--; + + return g_strndup (path, copy_len); +} + + +char * +build_uri (const char *base, ...) +{ + va_list args; + const char *child; + GString *uri; + + uri = g_string_new (base); + + va_start (args, base); + while ((child = va_arg (args, const char *)) != NULL) { + if (! g_str_has_suffix (uri->str, "/") && ! g_str_has_prefix (child, "/")) + g_string_append (uri, "/"); + g_string_append (uri, child); + } + va_end (args); + + return g_string_free (uri, FALSE); +} + + +gchar * +remove_extension_from_path (const gchar *path) +{ + int len; + int p; + const char *ptr = path; + char *new_path; + + if (! path) + return NULL; + + len = strlen (path); + if (len == 1) + return g_strdup (path); + + p = len - 1; + while ((p > 0) && (ptr[p] != '.')) + p--; + if (p == 0) + p = len; + new_path = g_strndup (path, (guint) p); + + return new_path; +} + + +gboolean +make_directory_tree (GFile *dir, + mode_t mode, + GError **error) +{ + gboolean success = TRUE; + GFile *parent; + + if ((dir == NULL) || g_file_query_exists (dir, NULL)) + return TRUE; + + parent = g_file_get_parent (dir); + if (parent != NULL) { + success = make_directory_tree (parent, mode, error); + g_object_unref (parent); + if (! success) + return FALSE; + } + + success = g_file_make_directory (dir, NULL, error); + if ((error != NULL) && (*error != NULL) && g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS)) { + g_clear_error (error); + success = TRUE; + } + + if (success) + g_file_set_attribute_uint32 (dir, + G_FILE_ATTRIBUTE_UNIX_MODE, + mode, + 0, + NULL, + NULL); + + return success; +} + + +gboolean +ensure_dir_exists (const char *uri, + mode_t mode, + GError **error) +{ + GFile *dir; + GError *priv_error = NULL; + + if (uri == NULL) + return FALSE; + + if (error == NULL) + error = &priv_error; + + dir = g_file_new_for_uri (uri); + if (! make_directory_tree (dir, mode, error)) { + g_warning ("could create directory %s: %s", uri, (*error)->message); + if (priv_error != NULL) + g_clear_error (&priv_error); + return FALSE; + } + + return TRUE; +} + + +gboolean +make_directory_tree_from_path (const char *path, + mode_t mode, + GError **error) +{ + char *uri; + gboolean result; + + uri = g_filename_to_uri (path, NULL, NULL); + result = ensure_dir_exists (uri, mode, error); + g_free (uri); + + return result; +} + + +const char * +get_file_extension (const char *filename) +{ + const char *ptr = filename; + int len; + int p; + const char *ext; + + if (filename == NULL) + return NULL; + + len = strlen (filename); + if (len <= 1) + return NULL; + + p = len - 1; + while ((p >= 0) && (ptr[p] != '.')) + p--; + if (p < 0) + return NULL; + + ext = filename + p; + if (ext - 4 > filename) { + const char *test = ext - 4; + if (strncmp (test, ".tar", 4) == 0) + ext = ext - 4; + } + return ext; +} + + +gboolean +file_extension_is (const char *filename, + const char *ext) +{ + int filename_l, ext_l; + + filename_l = strlen (filename); + ext_l = strlen (ext); + + if (filename_l < ext_l) + return FALSE; + return strcasecmp (filename + filename_l - ext_l, ext) == 0; +} + + +gboolean +is_mime_type (const char *mime_type, + const char *pattern) +{ + return (strcasecmp (mime_type, pattern) == 0); +} + + +const char* +get_file_mime_type (const char *uri, + gboolean fast_file_type) +{ + GFile *file; + GFileInfo *info; + GError *err = NULL; + const char *result = NULL; + + file = g_file_new_for_uri (uri); + info = g_file_query_info (file, + fast_file_type ? + G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE : + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, &err); + if (info == NULL) { + g_warning ("could not get content type for %s: %s", uri, err->message); + g_clear_error (&err); + } + else { + result = get_static_string (g_file_info_get_content_type (info)); + g_object_unref (info); + } + + g_object_unref (file); + + return result; +} + + +const char* +get_file_mime_type_for_path (const char *filename, + gboolean fast_file_type) +{ + char *uri; + const char *mime_type; + + uri = g_filename_to_uri (filename, NULL, NULL); + mime_type = get_file_mime_type (uri, fast_file_type); + g_free (uri); + + return mime_type; +} + + +void +path_list_free (GList *path_list) +{ + if (path_list == NULL) + return; + g_list_foreach (path_list, (GFunc) g_free, NULL); + g_list_free (path_list); +} + + +GList * +path_list_dup (GList *path_list) +{ + GList *new_list = NULL; + GList *scan; + + for (scan = path_list; scan; scan = scan->next) + new_list = g_list_prepend (new_list, g_strdup (scan->data)); + + return g_list_reverse (new_list); +} + + +guint64 +get_dest_free_space (const char *path) +{ + guint64 freespace = 0; + GFile *file; + GFileInfo *info; + GError *err = NULL; + + file = g_file_new_for_path (path); + info = g_file_query_filesystem_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &err); + if (info != NULL) { + freespace = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); + g_object_unref (info); + } + else { + g_warning ("Could not get filesystem free space on volume that contains %s: %s", path, err->message); + g_error_free (err); + } + g_object_unref (file); + + return freespace; +} + + +static gboolean +delete_directory_recursive (GFile *dir, + GError **error) +{ + char *uri; + GFileEnumerator *file_enum; + GFileInfo *info; + gboolean error_occurred = FALSE; + + if (error != NULL) + *error = NULL; + + file_enum = g_file_enumerate_children (dir, + G_FILE_ATTRIBUTE_STANDARD_NAME "," + G_FILE_ATTRIBUTE_STANDARD_TYPE, + 0, NULL, error); + + uri = g_file_get_uri (dir); + while (! error_occurred && (info = g_file_enumerator_next_file (file_enum, NULL, error)) != NULL) { + char *child_uri; + GFile *child; + + child_uri = build_uri (uri, g_file_info_get_name (info), NULL); + child = g_file_new_for_uri (child_uri); + + switch (g_file_info_get_file_type (info)) { + case G_FILE_TYPE_DIRECTORY: + if (! delete_directory_recursive (child, error)) + error_occurred = TRUE; + break; + default: + if (! g_file_delete (child, NULL, error)) + error_occurred = TRUE; + break; + } + + g_object_unref (child); + g_free (child_uri); + g_object_unref (info); + } + g_free (uri); + + if (! error_occurred && ! g_file_delete (dir, NULL, error)) + error_occurred = TRUE; + + g_object_unref (file_enum); + + return ! error_occurred; +} + + +gboolean +remove_directory (const char *uri) +{ + GFile *dir; + gboolean result; + GError *error = NULL; + + dir = g_file_new_for_uri (uri); + result = delete_directory_recursive (dir, &error); + if (! result) { + g_warning ("Cannot delete %s: %s", uri, error->message); + g_clear_error (&error); + } + g_object_unref (dir); + + return result; +} + + +gboolean +remove_local_directory (const char *path) +{ + char *uri; + gboolean result; + + if (path == NULL) + return TRUE; + + uri = g_filename_to_uri (path, NULL, NULL); + result = remove_directory (uri); + g_free (uri); + + return result; +} + + +static const char *try_folder[] = { "cache", "~", "tmp", NULL }; + + +static char * +ith_temp_folder_to_try (int n) +{ + const char *folder; + + folder = try_folder[n]; + if (strcmp (folder, "cache") == 0) + folder = g_get_user_cache_dir (); + else if (strcmp (folder, "~") == 0) + folder = g_get_home_dir (); + else if (strcmp (folder, "tmp") == 0) + folder = g_get_tmp_dir (); + + return g_strdup (folder); +} + + +char * +get_temp_work_dir (const char *parent_folder) +{ + guint64 max_size = 0; + char *best_folder = NULL; + int i; + char *template; + char *result = NULL; + + if (parent_folder == NULL) { + /* find the folder with more free space. */ + + for (i = 0; try_folder[i] != NULL; i++) { + char *folder; + guint64 size; + + folder = ith_temp_folder_to_try (i); + size = get_dest_free_space (folder); + if (max_size < size) { + max_size = size; + g_free (best_folder); + best_folder = folder; + } + else + g_free (folder); + } + } + else + best_folder = g_strdup (parent_folder); + + if (best_folder == NULL) + return NULL; + + template = g_strconcat (best_folder, "/.fr-XXXXXX", NULL); + result = mkdtemp (template); + + if ((result == NULL) || (*result == '\0')) { + g_free (template); + result = NULL; + } + + return result; +} + + +gboolean +is_temp_work_dir (const char *dir) +{ + int i; + const char *folder; + + if (strncmp (dir, "file://", 7) == 0) + dir = dir + 7; + else if (dir[0] != '/') + return FALSE; + + for (i = 0; try_folder[i] != NULL; i++) { + + folder = ith_temp_folder_to_try (i); + if (strncmp (dir, folder, strlen (folder)) == 0) + if (strncmp (dir + strlen (folder), "/.fr-", 5) == 0) { + g_free (folder); + return TRUE; + } + } + + g_free (folder); + return FALSE; +} + + +gboolean +is_temp_dir (const char *dir) +{ + if (strncmp (dir, "file://", 7) == 0) + dir = dir + 7; + if (strcmp (g_get_tmp_dir (), dir) == 0) + return TRUE; + if (path_in_path (g_get_tmp_dir (), dir)) + return TRUE; + else + return is_temp_work_dir (dir); +} + + +/* file list utils */ + + +gboolean +file_list__match_pattern (const char *line, + const char *pattern) +{ + const char *l = line, *p = pattern; + + for (; (*p != 0) && (*l != 0); p++, l++) { + if (*p != '%') { + if (*p != *l) + return FALSE; + } + else { + p++; + switch (*p) { + case 'a': + break; + case 'n': + if (!isdigit (*l)) + return FALSE; + break; + case 'c': + if (!isalpha (*l)) + return FALSE; + break; + default: + return FALSE; + } + } + } + + return (*p == 0); +} + + +int +file_list__get_index_from_pattern (const char *line, + const char *pattern) +{ + int line_l, pattern_l; + const char *l; + + line_l = strlen (line); + pattern_l = strlen (pattern); + + if ((pattern_l == 0) || (line_l == 0)) + return -1; + + for (l = line; *l != 0; l++) + if (file_list__match_pattern (l, pattern)) + return (l - line); + + return -1; +} + + +char* +file_list__get_next_field (const char *line, + int start_from, + int field_n) +{ + const char *f_start, *f_end; + + line = line + start_from; + + f_start = line; + while ((*f_start == ' ') && (*f_start != *line)) + f_start++; + f_end = f_start; + + while ((field_n > 0) && (*f_end != 0)) { + if (*f_end == ' ') { + field_n--; + if (field_n != 0) { + while ((*f_end == ' ') && (*f_end != *line)) + f_end++; + f_start = f_end; + } + } else + f_end++; + } + + return g_strndup (f_start, f_end - f_start); +} + + +char* +file_list__get_prev_field (const char *line, + int start_from, + int field_n) +{ + const char *f_start, *f_end; + + f_start = line + start_from - 1; + while ((*f_start == ' ') && (*f_start != *line)) + f_start--; + f_end = f_start; + + while ((field_n > 0) && (*f_start != *line)) { + if (*f_start == ' ') { + field_n--; + if (field_n != 0) { + while ((*f_start == ' ') && (*f_start != *line)) + f_start--; + f_end = f_start; + } + } else + f_start--; + } + + return g_strndup (f_start + 1, f_end - f_start); +} + + +gboolean +check_permissions (const char *uri, + int mode) +{ + GFile *file; + gboolean result; + + file = g_file_new_for_uri (uri); + result = check_file_permissions (file, mode); + + g_object_unref (file); + + return result; +} + + +gboolean +check_file_permissions (GFile *file, + int mode) +{ + gboolean result = TRUE; + GFileInfo *info; + GError *err = NULL; + gboolean default_permission_when_unknown = TRUE; + + info = g_file_query_info (file, "access::*", 0, NULL, &err); + if (err != NULL) { + g_clear_error (&err); + result = FALSE; + } + else { + if ((mode & R_OK) == R_OK) { + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) + result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)); + else + result = (result && default_permission_when_unknown); + } + + if ((mode & W_OK) == W_OK) { + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) + result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)); + else + result = (result && default_permission_when_unknown); + } + + if ((mode & X_OK) == X_OK) { + if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) + result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)); + else + result = (result && default_permission_when_unknown); + } + + g_object_unref (info); + } + + return result; +} + + +gboolean +is_program_in_path (const char *filename) +{ + char *str; + char *value; + int result = FALSE; + + value = g_hash_table_lookup (ProgramsCache, filename); + if (value != NULL) { + result = (strcmp (value, "1") == 0); + return result; + } + + str = g_find_program_in_path (filename); + if (str != NULL) { + g_free (str); + result = TRUE; + } + + g_hash_table_insert (ProgramsCache, + g_strdup (filename), + result ? "1" : "0"); + + return result; +} + + +gboolean +is_program_available (const char *filename, + gboolean check) +{ + return ! check || is_program_in_path (filename); +} + + +const char * +get_home_uri (void) +{ + static char *home_uri = NULL; + if (home_uri == NULL) + home_uri = g_filename_to_uri (g_get_home_dir (), NULL, NULL); + return home_uri; +} + + +char * +get_home_relative_uri (const char *partial_uri) +{ + return g_strconcat (get_home_uri (), + "/", + partial_uri, + NULL); +} + + +GFile * +get_home_relative_file (const char *partial_uri) +{ + GFile *file; + char *uri; + + uri = g_strconcat (get_home_uri (), "/", partial_uri, NULL); + file = g_file_new_for_uri (uri); + g_free (uri); + + return file; +} + + +const char * +remove_host_from_uri (const char *uri) +{ + const char *idx, *sep; + + if (uri == NULL) + return NULL; + + idx = strstr (uri, "://"); + if (idx == NULL) + return uri; + idx += 3; + if (*idx == '\0') + return "/"; + sep = strstr (idx, "/"); + if (sep == NULL) + return idx; + return sep; +} + + +char * +get_uri_host (const char *uri) +{ + const char *idx; + + idx = strstr (uri, "://"); + if (idx == NULL) + return NULL; + idx = strstr (idx + 3, "/"); + if (idx == NULL) + return NULL; + return g_strndup (uri, (idx - uri)); +} + + +char * +get_uri_root (const char *uri) +{ + char *host; + char *root; + + host = get_uri_host (uri); + if (host == NULL) + return NULL; + root = g_strconcat (host, "/", NULL); + g_free (host); + + return root; +} + + +int +uricmp (const char *uri1, + const char *uri2) +{ + return strcmp_null_tolerant (uri1, uri2); +} + + +char * +get_alternative_uri (const char *folder, + const char *name) +{ + char *new_uri = NULL; + int n = 1; + + do { + g_free (new_uri); + if (n == 1) + new_uri = g_strconcat (folder, "/", name, NULL); + else + new_uri = g_strdup_printf ("%s/%s%%20(%d)", folder, name, n); + n++; + } while (uri_exists (new_uri)); + + return new_uri; +} + + +char * +get_alternative_uri_for_uri (const char *uri) +{ + char *base_uri; + char *new_uri; + + base_uri = remove_level_from_path (uri); + new_uri = get_alternative_uri (base_uri, file_name_from_path (uri)); + g_free (base_uri); + + return new_uri; +} + + +GList * +gio_file_list_dup (GList *l) +{ + GList *r = NULL, *scan; + for (scan = l; scan; scan = scan->next) + r = g_list_prepend (r, g_file_dup ((GFile*)scan->data)); + return g_list_reverse (r); +} + + +void +gio_file_list_free (GList *l) +{ + GList *scan; + for (scan = l; scan; scan = scan->next) + g_object_unref (scan->data); + g_list_free (l); +} + + +GList * +gio_file_list_new_from_uri_list (GList *uris) +{ + GList *r = NULL, *scan; + for (scan = uris; scan; scan = scan->next) + r = g_list_prepend (r, g_file_new_for_uri ((char*)scan->data)); + return g_list_reverse (r); +} + + +void +g_key_file_save (GKeyFile *key_file, + GFile *file) +{ + char *file_data; + gsize size; + GError *error = NULL; + + file_data = g_key_file_to_data (key_file, &size, &error); + if (error != NULL) { + g_warning ("Could not save options: %s\n", error->message); + g_clear_error (&error); + } + else { + GFileOutputStream *stream; + + stream = g_file_replace (file, NULL, FALSE, 0, NULL, &error); + if (stream == NULL) { + g_warning ("Could not save options: %s\n", error->message); + g_clear_error (&error); + } + else if (! g_output_stream_write_all (G_OUTPUT_STREAM (stream), file_data, size, NULL, NULL, &error)) { + g_warning ("Could not save options: %s\n", error->message); + g_clear_error (&error); + } + else if (! g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &error)) { + g_warning ("Could not save options: %s\n", error->message); + g_clear_error (&error); + } + + g_object_unref (stream); + } + + g_free (file_data); +} diff --git a/src/file-utils.h b/src/file-utils.h new file mode 100644 index 0000000..d89d5bf --- /dev/null +++ b/src/file-utils.h @@ -0,0 +1,130 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FILE_UTILS_H +#define FILE_UTILS_H + +#include <sys/types.h> +#include <time.h> +#include <unistd.h> +#include <gio/gio.h> + +#define MIME_TYPE_DIRECTORY "folder" +#define MIME_TYPE_ARCHIVE "application/x-archive" + +#define get_home_relative_path(x) \ + g_strconcat (g_get_home_dir (), \ + "/", \ + (x), \ + NULL) + +gboolean uri_exists (const char *uri); +gboolean uri_is_file (const char *uri); +gboolean uri_is_dir (const char *uri); +gboolean path_is_dir (const char *path); +gboolean uri_is_local (const char *uri); +gboolean dir_is_empty (const char *uri); +gboolean dir_contains_one_object (const char *uri); +char * get_dir_content_if_unique (const char *uri); +gboolean path_in_path (const char *path_src, + const char *path_dest); +goffset get_file_size (const char *uri); +goffset get_file_size_for_path (const char *path); +time_t get_file_mtime (const char *uri); +time_t get_file_mtime_for_path (const char *path); +time_t get_file_ctime (const char *uri); +gboolean make_directory_tree (GFile *dir, + mode_t mode, + GError **error); +gboolean ensure_dir_exists (const char *uri, + mode_t mode, + GError **error); +gboolean make_directory_tree_from_path (const char *path, + mode_t mode, + GError **error); +gboolean file_is_hidden (const char *name); +const char* file_name_from_path(const char* path); +char * dir_name_from_path (const char *path); +char * remove_level_from_path (const char *path); +char * remove_ending_separator (const char *path); +char * build_uri (const char *base, ...); +char * remove_extension_from_path (const char *path); +const char * get_file_extension (const char *filename); +gboolean file_extension_is (const char *filename, + const char *ext); +gboolean is_mime_type (const char *type, + const char *pattern); +const char* get_file_mime_type (const char *uri, + gboolean fast_file_type); +const char* get_file_mime_type_for_path (const char *filename, + gboolean fast_file_type); +guint64 get_dest_free_space (const char *path); +gboolean remove_directory (const char *uri); +gboolean remove_local_directory (const char *directory); +char * get_temp_work_dir (const char *parent_folder); +gboolean is_temp_work_dir (const char *dir); +gboolean is_temp_dir (const char *dir); + +/* misc functions used to parse a command output lines. */ + +gboolean file_list__match_pattern (const char *line, + const char *pattern); +int file_list__get_index_from_pattern (const char *line, + const char *pattern); +char* file_list__get_next_field (const char *line, + int start_from, + int field_n); +char* file_list__get_prev_field (const char *line, + int start_from, + int field_n); +gboolean check_permissions (const char *path, + int mode); +gboolean check_file_permissions (GFile *file, + int mode); +gboolean is_program_in_path (const char *filename); +gboolean is_program_available (const char *filename, + gboolean check); + +/* URI utils */ + +const char * get_home_uri (void); +char * get_home_relative_uri (const char *partial_uri); +GFile * get_home_relative_file (const char *partial_uri); +const char * remove_host_from_uri (const char *uri); +char * get_uri_host (const char *uri); +char * get_uri_root (const char *uri); +int uricmp (const char *uri1, + const char *uri2); +char * get_alternative_uri (const char *folder, + const char *name); +char * get_alternative_uri_for_uri (const char *uri); + +void path_list_free (GList *path_list); +GList * path_list_dup (GList *path_list); + +GList * gio_file_list_dup (GList *l); +void gio_file_list_free (GList *l); +GList * gio_file_list_new_from_uri_list (GList *uris); +void g_key_file_save (GKeyFile *key_file, + GFile *file); + +#endif /* FILE_UTILS_H */ diff --git a/src/fr-archive.c b/src/fr-archive.c new file mode 100644 index 0000000..1e79812 --- /dev/null +++ b/src/fr-archive.c @@ -0,0 +1,3354 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2007, 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include "glib-utils.h" +#include "file-utils.h" +#include "gio-utils.h" +#include "file-data.h" +#include "fr-archive.h" +#include "fr-command.h" +#include "fr-error.h" +#include "fr-marshal.h" +#include "fr-process.h" +#include "main.h" + +#ifndef NCARGS +#define NCARGS _POSIX_ARG_MAX +#endif + + +/* -- DroppedItemsData -- */ + + +typedef struct { + FrArchive *archive; + GList *item_list; + char *base_dir; + char *dest_dir; + gboolean update; + char *password; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; +} DroppedItemsData; + + +static DroppedItemsData * +dropped_items_data_new (FrArchive *archive, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) +{ + DroppedItemsData *data; + + data = g_new0 (DroppedItemsData, 1); + data->archive = archive; + data->item_list = path_list_dup (item_list); + if (base_dir != NULL) + data->base_dir = g_strdup (base_dir); + if (dest_dir != NULL) + data->dest_dir = g_strdup (dest_dir); + data->update = update; + if (password != NULL) + data->password = g_strdup (password); + data->encrypt_header = encrypt_header; + data->compression = compression; + data->volume_size = volume_size; + + return data; +} + + +static void +dropped_items_data_free (DroppedItemsData *data) +{ + if (data == NULL) + return; + path_list_free (data->item_list); + g_free (data->base_dir); + g_free (data->dest_dir); + g_free (data->password); + g_free (data); +} + + +struct _FrArchivePrivData { + FakeLoadFunc fake_load_func; /* If returns TRUE, archives are not read when + * fr_archive_load is invoked, used + * in batch mode. */ + gpointer fake_load_data; + FakeLoadFunc add_is_stoppable_func; /* Returns whether the add operation is + * stoppable. */ + gpointer add_is_stoppable_data; + GCancellable *cancellable; + char *temp_dir; + gboolean continue_adding_dropped_items; + DroppedItemsData *dropped_items_data; + + char *temp_extraction_dir; + char *extraction_destination; + gboolean remote_extraction; + gboolean extract_here; +}; + + +typedef struct { + FrArchive *archive; + char *uri; + FrAction action; + GList *file_list; + char *base_uri; + char *dest_dir; + gboolean update; + char *tmp_dir; + guint source_id; + char *password; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; +} XferData; + + +static void +xfer_data_free (XferData *data) +{ + if (data == NULL) + return; + + g_free (data->uri); + g_free (data->password); + path_list_free (data->file_list); + g_free (data->base_uri); + g_free (data->dest_dir); + g_free (data->tmp_dir); + g_free (data); +} + + +#define MAX_CHUNK_LEN (NCARGS * 2 / 3) /* Max command line length */ +#define UNKNOWN_TYPE "application/octet-stream" +#define SAME_FS (FALSE) +#define NO_BACKUP_FILES (TRUE) +#define NO_DOT_FILES (FALSE) +#define IGNORE_CASE (FALSE) +#define LIST_LENGTH_TO_USE_FILE 10 /* FIXME: find a good value */ + + +enum { + START, + DONE, + PROGRESS, + MESSAGE, + STOPPABLE, + WORKING_ARCHIVE, + LAST_SIGNAL +}; + +static GObjectClass *parent_class; +static guint fr_archive_signals[LAST_SIGNAL] = { 0 }; + +static void fr_archive_class_init (FrArchiveClass *class); +static void fr_archive_init (FrArchive *archive); +static void fr_archive_finalize (GObject *object); + + +GType +fr_archive_get_type (void) +{ + static GType type = 0; + + if (! type) { + static const GTypeInfo type_info = { + sizeof (FrArchiveClass), + NULL, + NULL, + (GClassInitFunc) fr_archive_class_init, + NULL, + NULL, + sizeof (FrArchive), + 0, + (GInstanceInitFunc) fr_archive_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "FrArchive", + &type_info, + 0); + } + + return type; +} + + +static void +fr_archive_class_init (FrArchiveClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + gobject_class->finalize = fr_archive_finalize; + + class->start = NULL; + class->done = NULL; + class->progress = NULL; + class->message = NULL; + class->working_archive = NULL; + + /* signals */ + + fr_archive_signals[START] = + g_signal_new ("start", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrArchiveClass, start), + NULL, NULL, + fr_marshal_VOID__INT, + G_TYPE_NONE, + 1, G_TYPE_INT); + fr_archive_signals[DONE] = + g_signal_new ("done", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrArchiveClass, done), + NULL, NULL, + fr_marshal_VOID__INT_POINTER, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_POINTER); + fr_archive_signals[PROGRESS] = + g_signal_new ("progress", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrArchiveClass, progress), + NULL, NULL, + fr_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, + G_TYPE_DOUBLE); + fr_archive_signals[MESSAGE] = + g_signal_new ("message", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrArchiveClass, message), + NULL, NULL, + fr_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + fr_archive_signals[STOPPABLE] = + g_signal_new ("stoppable", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrArchiveClass, stoppable), + NULL, NULL, + fr_marshal_VOID__BOOL, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + fr_archive_signals[WORKING_ARCHIVE] = + g_signal_new ("working_archive", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrArchiveClass, working_archive), + NULL, NULL, + fr_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); +} + + +void +fr_archive_stoppable (FrArchive *archive, + gboolean stoppable) +{ + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[STOPPABLE], + 0, + stoppable); +} + + +void +fr_archive_stop (FrArchive *archive) +{ + if (archive->process != NULL) { + fr_process_stop (archive->process); + return; + } + + if (! g_cancellable_is_cancelled (archive->priv->cancellable)) + g_cancellable_cancel (archive->priv->cancellable); +} + + +void +fr_archive_action_completed (FrArchive *archive, + FrAction action, + FrProcErrorType error_type, + const char *error_details) +{ + archive->error.type = error_type; + archive->error.status = 0; + g_clear_error (&archive->error.gerror); + if (error_details != NULL) + archive->error.gerror = g_error_new_literal (fr_error_quark (), + 0, + error_details); + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[DONE], + 0, + action, + &archive->error); +} + + +static gboolean +fr_archive_add_is_stoppable (FrArchive *archive) +{ + if (archive->priv->add_is_stoppable_func != NULL) + return (*archive->priv->add_is_stoppable_func) (archive, archive->priv->add_is_stoppable_data); + else + return FALSE; +} + + +static gboolean +archive_sticky_only_cb (FrProcess *process, + FrArchive *archive) +{ + fr_archive_stoppable (archive, fr_archive_add_is_stoppable (archive)); + return TRUE; +} + + +static void +fr_archive_init (FrArchive *archive) +{ + archive->file = NULL; + archive->local_copy = NULL; + archive->is_remote = FALSE; + archive->command = NULL; + archive->is_compressed_file = FALSE; + archive->can_create_compressed_file = FALSE; + + archive->priv = g_new0 (FrArchivePrivData, 1); + archive->priv->fake_load_func = NULL; + archive->priv->fake_load_data = NULL; + archive->priv->add_is_stoppable_func = NULL; + archive->priv->add_is_stoppable_data = NULL; + + archive->priv->extraction_destination = NULL; + archive->priv->temp_extraction_dir = NULL; + archive->priv->cancellable = g_cancellable_new (); + + archive->process = fr_process_new (); + g_signal_connect (G_OBJECT (archive->process), + "sticky_only", + G_CALLBACK (archive_sticky_only_cb), + archive); +} + + +FrArchive * +fr_archive_new (void) +{ + return FR_ARCHIVE (g_object_new (FR_TYPE_ARCHIVE, NULL)); +} + + +static GFile * +get_local_copy_for_file (GFile *remote_file) +{ + char *temp_dir; + GFile *local_copy = NULL; + + temp_dir = get_temp_work_dir (NULL); + if (temp_dir != NULL) { + char *archive_name; + char *local_path; + + archive_name = g_file_get_basename (remote_file); + local_path = g_build_filename (temp_dir, archive_name, NULL); + local_copy = g_file_new_for_path (local_path); + + g_free (local_path); + g_free (archive_name); + } + g_free (temp_dir); + + return local_copy; +} + + +static void +fr_archive_set_uri (FrArchive *archive, + const char *uri) +{ + if ((archive->local_copy != NULL) && archive->is_remote) { + GFile *temp_folder; + GError *err = NULL; + + g_file_delete (archive->local_copy, NULL, &err); + if (err != NULL) { + g_warning ("Failed to delete the local copy: %s", err->message); + g_clear_error (&err); + } + + temp_folder = g_file_get_parent (archive->local_copy); + g_file_delete (temp_folder, NULL, &err); + if (err != NULL) { + g_warning ("Failed to delete temp folder: %s", err->message); + g_clear_error (&err); + } + + g_object_unref (temp_folder); + } + + if (archive->file != NULL) { + g_object_unref (archive->file); + archive->file = NULL; + } + if (archive->local_copy != NULL) { + g_object_unref (archive->local_copy); + archive->local_copy = NULL; + } + archive->content_type = NULL; + + if (uri == NULL) + return; + + archive->file = g_file_new_for_uri (uri); + archive->is_remote = ! g_file_has_uri_scheme (archive->file, "file"); + if (archive->is_remote) + archive->local_copy = get_local_copy_for_file (archive->file); + else + archive->local_copy = g_file_dup (archive->file); +} + + +static void +fr_archive_remove_temp_work_dir (FrArchive *archive) +{ + if (archive->priv->temp_dir == NULL) + return; + remove_local_directory (archive->priv->temp_dir); + g_free (archive->priv->temp_dir); + archive->priv->temp_dir = NULL; +} + + +static void +fr_archive_finalize (GObject *object) +{ + FrArchive *archive; + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_ARCHIVE (object)); + + archive = FR_ARCHIVE (object); + + fr_archive_set_uri (archive, NULL); + fr_archive_remove_temp_work_dir (archive); + if (archive->command != NULL) + g_object_unref (archive->command); + g_object_unref (archive->process); + if (archive->priv->dropped_items_data != NULL) { + dropped_items_data_free (archive->priv->dropped_items_data); + archive->priv->dropped_items_data = NULL; + } + g_free (archive->priv->temp_extraction_dir); + g_free (archive->priv->extraction_destination); + g_free (archive->priv); + + /* Chain up */ + + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static const char * +get_mime_type_from_content (GFile *file) +{ + GFileInfo *info; + GError *err = NULL; + const char *content_type = NULL; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + 0, NULL, &err); + if (info == NULL) { + g_warning ("could not get content type: %s", err->message); + g_clear_error (&err); + } + else { + content_type = get_static_string (g_file_info_get_content_type (info)); + g_object_unref (info); + } + + return content_type; +} + + +static const char * +get_mime_type_from_magic_numbers (GFile *file) +{ + static struct { + const char *mime_type; + const char *first_bytes; + int offset; + int len; + } sniffer_data [] = { + /* Magic numbers taken from magic/Magdir/archive from the + * file-4.21 tarball. */ + { "application/x-7z-compressed", "7z\274\257\047\034", 0, 5 }, + { "application/x-ace", "**ACE**", 7, 7 }, + { "application/x-arj", "\x60\xea", 0, 2 }, + { "application/x-bzip2", "BZh", 0, 3 }, + { "application/x-gzip", "\037\213", 0, 2 }, + { "application/x-lzip", "LZIP", 0, 4 }, + { "application/x-lzop", "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", 0, 9 }, + { "application/x-rar", "Rar!", 0, 4 }, + { "application/x-rzip", "RZIP", 0, 4 }, + { "application/x-xz", "\3757zXZ\000", 0, 6 }, + { "application/x-zoo", "\xdc\xa7\xc4\xfd", 20, 4 }, + { "application/zip", "PK\003\004", 0, 4 }, + { "application/zip", "PK00PK\003\004", 0, 8 }, + { "application/x-lrzip", "LRZI", 0, 4 }, + { NULL, NULL, 0 } + }; + char buffer[32]; + int i; + + if (! g_load_file_in_buffer (file, buffer, 32, NULL)) + return NULL; + + for (i = 0; sniffer_data[i].mime_type != NULL; i++) + if (memcmp (sniffer_data[i].first_bytes, + buffer + sniffer_data[i].offset, + sniffer_data[i].len) == 0) + { + return sniffer_data[i].mime_type; + } + + return NULL; +} + + +static const char * +get_mime_type_from_filename (GFile *file) +{ + const char *mime_type = NULL; + char *filename; + + if (file == NULL) + return NULL; + + filename = g_file_get_path (file); + mime_type = get_mime_type_from_extension (get_file_extension (filename)); + g_free (filename); + + return mime_type; +} + + +static gboolean +create_command_from_type (FrArchive *archive, + const char *mime_type, + GType command_type, + FrCommandCaps requested_capabilities) +{ + char *filename; + + if (command_type == 0) + return FALSE; + + filename = g_file_get_path (archive->local_copy); + archive->command = FR_COMMAND (g_object_new (command_type, + "process", archive->process, + "filename", filename, + "mime-type", mime_type, + NULL)); + g_free (filename); + + if (! fr_command_is_capable_of (archive->command, requested_capabilities)) { + g_object_unref (archive->command); + archive->command = NULL; + archive->is_compressed_file = FALSE; + } + else + archive->is_compressed_file = ! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_ARCHIVE_MANY_FILES); + + return (archive->command != NULL); +} + + +static gboolean +create_command_to_load_archive (FrArchive *archive, + const char *mime_type) +{ + FrCommandCaps requested_capabilities = FR_COMMAND_CAN_DO_NOTHING; + GType command_type; + + if (mime_type == NULL) + return FALSE; + + /* try with the WRITE capability even when loading, this way we give + * priority to the commands that can read and write over commands + * that can only read a specific file format. */ + + requested_capabilities |= FR_COMMAND_CAN_READ_WRITE; + command_type = get_command_type_from_mime_type (mime_type, requested_capabilities); + + /* if no command was found, remove the write capability and try again */ + + if (command_type == 0) { + requested_capabilities ^= FR_COMMAND_CAN_WRITE; + command_type = get_command_type_from_mime_type (mime_type, requested_capabilities); + } + + return create_command_from_type (archive, + mime_type, + command_type, + requested_capabilities); +} + + +static gboolean +create_command_to_create_archive (FrArchive *archive, + const char *mime_type) +{ + FrCommandCaps requested_capabilities = FR_COMMAND_CAN_DO_NOTHING; + GType command_type; + + if (mime_type == NULL) + return FALSE; + + requested_capabilities |= FR_COMMAND_CAN_WRITE; + command_type = get_command_type_from_mime_type (mime_type, requested_capabilities); + + return create_command_from_type (archive, + mime_type, + command_type, + requested_capabilities); +} + + +static void +action_started (FrCommand *command, + FrAction action, + FrArchive *archive) +{ +#ifdef DEBUG + debug (DEBUG_INFO, "%s [START] (FR::Archive)\n", action_names[action]); +#endif + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + action); +} + + +/* -- copy_to_remote_location -- */ + + +static void +fr_archive_copy_done (FrArchive *archive, + FrAction action, + GError *error) +{ + FrProcErrorType error_type = FR_PROC_ERROR_NONE; + const char *error_details = NULL; + + if (error != NULL) { + error_type = (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC); + error_details = error->message; + } + fr_archive_action_completed (archive, action, error_type, error_details); +} + + +static void +copy_to_remote_location_done (GError *error, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + fr_archive_copy_done (xfer_data->archive, xfer_data->action, error); + xfer_data_free (xfer_data); +} + + +static void +copy_to_remote_location_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_signal_emit (G_OBJECT (xfer_data->archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_num_bytes / total_num_bytes); +} + + +static void +copy_to_remote_location (FrArchive *archive, + FrAction action) +{ + XferData *xfer_data; + + xfer_data = g_new0 (XferData, 1); + xfer_data->archive = archive; + xfer_data->action = action; + + g_copy_file_async (archive->local_copy, + archive->file, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_to_remote_location_progress, + xfer_data, + copy_to_remote_location_done, + xfer_data); +} + + +/* -- copy_extracted_files_to_destination -- */ + + +static void +move_here (FrArchive *archive) +{ + char *content_uri; + char *parent; + char *parent_parent; + char *new_content_uri; + GFile *source, *destination, *parent_file; + GError *error = NULL; + + content_uri = get_dir_content_if_unique (archive->priv->extraction_destination); + if (content_uri == NULL) + return; + + parent = remove_level_from_path (content_uri); + + if (uricmp (parent, archive->priv->extraction_destination) == 0) { + char *new_uri; + + new_uri = get_alternative_uri_for_uri (archive->priv->extraction_destination); + + source = g_file_new_for_uri (archive->priv->extraction_destination); + destination = g_file_new_for_uri (new_uri); + if (! g_file_move (source, destination, 0, NULL, NULL, NULL, &error)) { + g_warning ("could not rename %s to %s: %s", archive->priv->extraction_destination, new_uri, error->message); + g_clear_error (&error); + } + g_object_unref (source); + g_object_unref (destination); + + g_free (archive->priv->extraction_destination); + archive->priv->extraction_destination = new_uri; + + g_free (parent); + + content_uri = get_dir_content_if_unique (archive->priv->extraction_destination); + if (content_uri == NULL) + return; + + parent = remove_level_from_path (content_uri); + } + + parent_parent = remove_level_from_path (parent); + new_content_uri = get_alternative_uri (parent_parent, file_name_from_path (content_uri)); + + source = g_file_new_for_uri (content_uri); + destination = g_file_new_for_uri (new_content_uri); + if (! g_file_move (source, destination, 0, NULL, NULL, NULL, &error)) { + g_warning ("could not rename %s to %s: %s", content_uri, new_content_uri, error->message); + g_clear_error (&error); + } + + parent_file = g_file_new_for_uri (parent); + if (! g_file_delete (parent_file, NULL, &error)) { + g_warning ("could not remove directory %s: %s", parent, error->message); + g_clear_error (&error); + } + g_object_unref (parent_file); + + g_free (archive->priv->extraction_destination); + archive->priv->extraction_destination = new_content_uri; + + g_free (parent_parent); + g_free (parent); + g_free (content_uri); +} + + +static void +copy_extracted_files_done (GError *error, + gpointer user_data) +{ + FrArchive *archive = user_data; + + remove_local_directory (archive->priv->temp_extraction_dir); + g_free (archive->priv->temp_extraction_dir); + archive->priv->temp_extraction_dir = NULL; + + fr_archive_action_completed (archive, + FR_ACTION_COPYING_FILES_TO_REMOTE, + FR_PROC_ERROR_NONE, + NULL); + + if ((error == NULL) && (archive->priv->extract_here)) + move_here (archive); + + fr_archive_copy_done (archive, FR_ACTION_EXTRACTING_FILES, error); +} + + +static void +copy_extracted_files_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + FrArchive *archive = user_data; + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_file / (total_files + 1)); +} + + +static void +copy_extracted_files_to_destination (FrArchive *archive) +{ + g_directory_copy_async (archive->priv->temp_extraction_dir, + archive->priv->extraction_destination, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_extracted_files_progress, + archive, + copy_extracted_files_done, + archive); +} + + +static void add_dropped_items (DroppedItemsData *data); + + +static void +fr_archive_change_name (FrArchive *archive, + const char *filename) +{ + const char *name; + GFile *parent; + + name = file_name_from_path (filename); + + parent = g_file_get_parent (archive->file); + g_object_unref (archive->file); + archive->file = g_file_get_child (parent, name); + g_object_unref (parent); + + parent = g_file_get_parent (archive->local_copy); + g_object_unref (archive->local_copy); + archive->local_copy = g_file_get_child (parent, name); + g_object_unref (parent); +} + + +static void +action_performed (FrCommand *command, + FrAction action, + FrProcError *error, + FrArchive *archive) +{ +#ifdef DEBUG + debug (DEBUG_INFO, "%s [DONE] (FR::Archive)\n", action_names[action]); +#endif + + switch (action) { + case FR_ACTION_DELETING_FILES: + if (error->type == FR_PROC_ERROR_NONE) { + if (! g_file_has_uri_scheme (archive->file, "file")) { + copy_to_remote_location (archive, action); + return; + } + } + break; + + case FR_ACTION_ADDING_FILES: + if (error->type == FR_PROC_ERROR_NONE) { + fr_archive_remove_temp_work_dir (archive); + if (archive->priv->continue_adding_dropped_items) { + add_dropped_items (archive->priv->dropped_items_data); + return; + } + if (archive->priv->dropped_items_data != NULL) { + dropped_items_data_free (archive->priv->dropped_items_data); + archive->priv->dropped_items_data = NULL; + } + /* the name of the volumes are different from the + * original name */ + if (archive->command->multi_volume) + fr_archive_change_name (archive, archive->command->filename); + if (! g_file_has_uri_scheme (archive->file, "file")) { + copy_to_remote_location (archive, action); + return; + } + } + break; + + case FR_ACTION_EXTRACTING_FILES: + if (error->type == FR_PROC_ERROR_NONE) { + if (archive->priv->remote_extraction) { + copy_extracted_files_to_destination (archive); + return; + } + else if (archive->priv->extract_here) + move_here (archive); + } + else { + /* if an error occurred during extraction remove the + * temp extraction dir, if used. */ + g_print ("action_performed: ERROR!\n"); + + if ((archive->priv->remote_extraction) && (archive->priv->temp_extraction_dir != NULL)) { + remove_local_directory (archive->priv->temp_extraction_dir); + g_free (archive->priv->temp_extraction_dir); + archive->priv->temp_extraction_dir = NULL; + } + + if (archive->priv->extract_here) + remove_directory (archive->priv->extraction_destination); + } + break; + + case FR_ACTION_LISTING_CONTENT: + /* the name of the volumes are different from the + * original name */ + if (archive->command->multi_volume) + fr_archive_change_name (archive, archive->command->filename); + fr_command_update_capabilities (archive->command); + if (! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_WRITE)) + archive->read_only = TRUE; + break; + + default: + /* nothing */ + break; + } + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[DONE], + 0, + action, + error); +} + + +static gboolean +archive_progress_cb (FrCommand *command, + double fraction, + FrArchive *archive) +{ + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[PROGRESS], + 0, + fraction); + return TRUE; +} + + +static gboolean +archive_message_cb (FrCommand *command, + const char *msg, + FrArchive *archive) +{ + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[MESSAGE], + 0, + msg); + return TRUE; +} + + +static gboolean +archive_working_archive_cb (FrCommand *command, + const char *archive_filename, + FrArchive *archive) +{ + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[WORKING_ARCHIVE], + 0, + archive_filename); + return TRUE; +} + + +static void +fr_archive_connect_to_command (FrArchive *archive) +{ + g_signal_connect (G_OBJECT (archive->command), + "start", + G_CALLBACK (action_started), + archive); + g_signal_connect (G_OBJECT (archive->command), + "done", + G_CALLBACK (action_performed), + archive); + g_signal_connect (G_OBJECT (archive->command), + "progress", + G_CALLBACK (archive_progress_cb), + archive); + g_signal_connect (G_OBJECT (archive->command), + "message", + G_CALLBACK (archive_message_cb), + archive); + g_signal_connect (G_OBJECT (archive->command), + "working_archive", + G_CALLBACK (archive_working_archive_cb), + archive); +} + + +gboolean +fr_archive_create (FrArchive *archive, + const char *uri) +{ + FrCommand *tmp_command; + const char *mime_type; + + if (uri == NULL) + return FALSE; + + fr_archive_set_uri (archive, uri); + + tmp_command = archive->command; + + mime_type = get_mime_type_from_filename (archive->local_copy); + if (! create_command_to_create_archive (archive, mime_type)) { + archive->command = tmp_command; + return FALSE; + } + + if (tmp_command != NULL) { + g_signal_handlers_disconnect_by_data (tmp_command, archive); + g_object_unref (G_OBJECT (tmp_command)); + } + + fr_archive_connect_to_command (archive); + archive->read_only = FALSE; + + return TRUE; +} + + +void +fr_archive_set_fake_load_func (FrArchive *archive, + FakeLoadFunc func, + gpointer data) +{ + archive->priv->fake_load_func = func; + archive->priv->fake_load_data = data; +} + + +gboolean +fr_archive_fake_load (FrArchive *archive) +{ + if (archive->priv->fake_load_func != NULL) + return (*archive->priv->fake_load_func) (archive, archive->priv->fake_load_data); + else + return FALSE; +} + + +/* -- fr_archive_load -- */ + + +static void +load_local_archive (FrArchive *archive, + const char *password) +{ + FrCommand *tmp_command; + const char *mime_type; + + if (! g_file_query_exists (archive->file, NULL)) { + fr_archive_action_completed (archive, + FR_ACTION_LOADING_ARCHIVE, + FR_PROC_ERROR_GENERIC, + _("File not found.")); + return; + } + + archive->have_permissions = check_file_permissions (archive->file, W_OK); + archive->read_only = ! archive->have_permissions; + + tmp_command = archive->command; + + mime_type = get_mime_type_from_filename (archive->local_copy); + if (! create_command_to_load_archive (archive, mime_type)) { + mime_type = get_mime_type_from_content (archive->local_copy); + if (! create_command_to_load_archive (archive, mime_type)) { + mime_type = get_mime_type_from_magic_numbers (archive->local_copy); + if (! create_command_to_load_archive (archive, mime_type)) { + archive->command = tmp_command; + archive->content_type = mime_type; + fr_archive_action_completed (archive, + FR_ACTION_LOADING_ARCHIVE, + FR_PROC_ERROR_UNSUPPORTED_FORMAT, + _("Archive type not supported.")); + return; + } + } + } + + if (tmp_command != NULL) { + g_signal_handlers_disconnect_by_data (tmp_command, archive); + g_object_unref (tmp_command); + } + + fr_archive_connect_to_command (archive); + archive->content_type = mime_type; + if (! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_WRITE)) + archive->read_only = TRUE; + fr_archive_stoppable (archive, TRUE); + archive->command->fake_load = fr_archive_fake_load (archive); + + fr_archive_action_completed (archive, + FR_ACTION_LOADING_ARCHIVE, + FR_PROC_ERROR_NONE, + NULL); + + /**/ + + fr_process_clear (archive->process); + g_object_set (archive->command, "password", password, NULL); + fr_command_list (archive->command); + fr_process_start (archive->process); +} + + +static void +copy_remote_file_done (GError *error, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + if (error != NULL) + fr_archive_copy_done (xfer_data->archive, FR_ACTION_LOADING_ARCHIVE, error); + else + load_local_archive (xfer_data->archive, xfer_data->password); + xfer_data_free (xfer_data); +} + + +static void +copy_remote_file_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_signal_emit (G_OBJECT (xfer_data->archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_num_bytes / total_num_bytes); +} + + +static gboolean +copy_remote_file_done_cb (gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_source_remove (xfer_data->source_id); + copy_remote_file_done (NULL, xfer_data); + return FALSE; +} + + +static void +copy_remote_file (FrArchive *archive, + const char *password) +{ + XferData *xfer_data; + + if (! g_file_query_exists (archive->file, NULL)) { + GError *error; + error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("The file doesn't exist")); + fr_archive_copy_done (archive, FR_ACTION_LOADING_ARCHIVE, error); + g_error_free (error); + return; + } + + xfer_data = g_new0 (XferData, 1); + xfer_data->archive = archive; + xfer_data->uri = g_file_get_uri (archive->file); + if (password != NULL) + xfer_data->password = g_strdup (password); + + if (! archive->is_remote) { + xfer_data->source_id = g_idle_add (copy_remote_file_done_cb, xfer_data); + return; + } + + g_copy_file_async (archive->file, + archive->local_copy, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_remote_file_progress, + xfer_data, + copy_remote_file_done, + xfer_data); +} + + +gboolean +fr_archive_load (FrArchive *archive, + const char *uri, + const char *password) +{ + g_return_val_if_fail (archive != NULL, FALSE); + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_LOADING_ARCHIVE); + + fr_archive_set_uri (archive, uri); + copy_remote_file (archive, password); + + return TRUE; +} + + +gboolean +fr_archive_load_local (FrArchive *archive, + const char *uri, + const char *password) +{ + g_return_val_if_fail (archive != NULL, FALSE); + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_LOADING_ARCHIVE); + + fr_archive_set_uri (archive, uri); + copy_remote_file (archive, password); + + return TRUE; +} + + +void +fr_archive_reload (FrArchive *archive, + const char *password) +{ + char *uri; + + g_return_if_fail (archive != NULL); + g_return_if_fail (archive->file != NULL); + + fr_archive_stoppable (archive, TRUE); + archive->command->fake_load = fr_archive_fake_load (archive); + + uri = g_file_get_uri (archive->file); + fr_archive_load (archive, uri, password); + g_free (uri); +} + + +void +fr_archive_rename (FrArchive *archive, + const char *filename) +{ + g_return_if_fail (archive != NULL); + + if (archive->is_compressed_file) { + /* If the archive is a compressed file we have to reload it, + * because in this case the 'content' of the archive changes + * too. */ + fr_archive_load (archive, filename, NULL); + } + else { + if (archive->file != NULL) + g_object_unref (archive->file); + archive->file = g_file_new_for_path (filename); + fr_command_set_filename (archive->command, filename); + } +} + + +/* -- add -- */ + + +static char * +create_tmp_base_dir (const char *base_dir, + const char *dest_path) +{ + char *dest_dir; + char *temp_dir; + char *tmp; + char *parent_dir; + char *dir; + + if ((dest_path == NULL) + || (*dest_path == '\0') + || (strcmp (dest_path, "/") == 0)) + { + return g_strdup (base_dir); + } + + dest_dir = g_strdup (dest_path); + if (dest_dir[strlen (dest_dir) - 1] == G_DIR_SEPARATOR) + dest_dir[strlen (dest_dir) - 1] = 0; + + debug (DEBUG_INFO, "base_dir: %s\n", base_dir); + debug (DEBUG_INFO, "dest_dir: %s\n", dest_dir); + + temp_dir = get_temp_work_dir (NULL); + tmp = remove_level_from_path (dest_dir); + parent_dir = g_build_filename (temp_dir, tmp, NULL); + g_free (tmp); + + debug (DEBUG_INFO, "mkdir %s\n", parent_dir); + make_directory_tree_from_path (parent_dir, 0700, NULL); + g_free (parent_dir); + + dir = g_build_filename (temp_dir, "/", dest_dir, NULL); + debug (DEBUG_INFO, "symlink %s --> %s\n", dir, base_dir); + symlink (base_dir, dir); + + g_free (dir); + g_free (dest_dir); + + return temp_dir; +} + + +static FileData * +find_file_in_archive (FrArchive *archive, + char *path) +{ + int i; + + g_return_val_if_fail (path != NULL, NULL); + + i = find_path_in_file_data_array (archive->command->files, path); + if (i >= 0) + return (FileData *) g_ptr_array_index (archive->command->files, i); + else + return NULL; +} + + +static void delete_from_archive (FrArchive *archive, GList *file_list); + + +static GList * +newer_files_only (FrArchive *archive, + GList *file_list, + const char *base_dir) +{ + GList *newer_files = NULL; + GList *scan; + + for (scan = file_list; scan; scan = scan->next) { + char *filename = scan->data; + char *fullpath; + char *uri; + FileData *fdata; + + fdata = find_file_in_archive (archive, filename); + + if (fdata == NULL) { + newer_files = g_list_prepend (newer_files, g_strdup (scan->data)); + continue; + } + + fullpath = g_strconcat (base_dir, "/", filename, NULL); + uri = g_filename_to_uri (fullpath, NULL, NULL); + + if (fdata->modified >= get_file_mtime (uri)) { + g_free (fullpath); + g_free (uri); + continue; + } + g_free (fullpath); + g_free (uri); + + newer_files = g_list_prepend (newer_files, g_strdup (scan->data)); + } + + return newer_files; +} + + +void +fr_archive_set_add_is_stoppable_func (FrArchive *archive, + FakeLoadFunc func, + gpointer data) +{ + archive->priv->add_is_stoppable_func = func; + archive->priv->add_is_stoppable_data = data; +} + + +static GList * +convert_to_local_file_list (GList *file_list) +{ + GList *local_file_list = NULL; + GList *scan; + + for (scan = file_list; scan; scan = scan->next) { + char *uri = scan->data; + char *local_filename; + + local_filename = g_uri_unescape_string (uri, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH); + if (local_filename != NULL) + local_file_list = g_list_prepend (local_file_list, local_filename); + } + + return local_file_list; +} + + +static gboolean +save_list_to_temp_file (GList *file_list, + char **list_dir, + char **list_filename, + GError **error) +{ + gboolean error_occurred = FALSE; + GFile *list_file; + GFileOutputStream *ostream; + + if (error != NULL) + *error = NULL; + *list_dir = get_temp_work_dir (NULL); + *list_filename = g_build_filename (*list_dir, "file-list", NULL); + list_file = g_file_new_for_path (*list_filename); + ostream = g_file_create (list_file, G_FILE_CREATE_PRIVATE, NULL, error); + + if (ostream != NULL) { + GList *scan; + + for (scan = file_list; scan != NULL; scan = scan->next) { + char *filename = scan->data; + + filename = str_substitute (filename, "\n", "\\n"); + if ((g_output_stream_write (G_OUTPUT_STREAM (ostream), filename, strlen (filename), NULL, error) < 0) + || (g_output_stream_write (G_OUTPUT_STREAM (ostream), "\n", 1, NULL, error) < 0)) + { + error_occurred = TRUE; + } + + g_free (filename); + + if (error_occurred) + break; + } + if (! error_occurred && ! g_output_stream_close (G_OUTPUT_STREAM (ostream), NULL, error)) + error_occurred = TRUE; + g_object_unref (ostream); + } + else + error_occurred = TRUE; + + if (error_occurred) { + remove_local_directory (*list_dir); + g_free (*list_dir); + g_free (*list_filename); + *list_dir = NULL; + *list_filename = NULL; + } + + g_object_unref (list_file); + + return ! error_occurred; +} + + +static GList * +split_in_chunks (GList *file_list) +{ + GList *chunks = NULL; + GList *new_file_list; + GList *scan; + + new_file_list = g_list_copy (file_list); + for (scan = new_file_list; scan != NULL; /* void */) { + GList *prev = scan->prev; + GList *chunk; + int l; + + chunk = scan; + l = 0; + while ((scan != NULL) && (l < MAX_CHUNK_LEN)) { + if (l == 0) + l = strlen (scan->data); + prev = scan; + scan = scan->next; + if (scan != NULL) + l += strlen (scan->data); + } + if (prev != NULL) { + if (prev->next != NULL) + prev->next->prev = NULL; + prev->next = NULL; + } + chunks = g_list_append (chunks, chunk); + } + + return chunks; +} + + +void +fr_archive_add (FrArchive *archive, + GList *file_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + gboolean recursive, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) +{ + GList *new_file_list = NULL; + gboolean base_dir_created = FALSE; + GList *scan; + char *tmp_base_dir = NULL; + gboolean error_occurred = FALSE; + + if (file_list == NULL) + return; + + if (archive->read_only) + return; + + g_object_set (archive->command, + "password", password, + "encrypt_header", encrypt_header, + "compression", compression, + "volume_size", volume_size, + NULL); + + fr_archive_stoppable (archive, fr_archive_add_is_stoppable (archive)); + + file_list = convert_to_local_file_list (file_list); + tmp_base_dir = g_strdup (base_dir); + + if ((dest_dir != NULL) && (*dest_dir != '\0') && (strcmp (dest_dir, "/") != 0)) { + const char *rel_dest_dir = dest_dir; + + tmp_base_dir = create_tmp_base_dir (base_dir, dest_dir); + base_dir_created = TRUE; + + if (dest_dir[0] == G_DIR_SEPARATOR) + rel_dest_dir = dest_dir + 1; + + new_file_list = NULL; + for (scan = file_list; scan != NULL; scan = scan->next) { + char *filename = scan->data; + new_file_list = g_list_prepend (new_file_list, g_build_filename (rel_dest_dir, filename, NULL)); + } + path_list_free (file_list); + } + else + new_file_list = file_list; + + /* if the command cannot update, get the list of files that are + * newer than the ones in the archive. */ + + if (update && ! archive->command->propAddCanUpdate) { + GList *tmp_file_list; + + tmp_file_list = new_file_list; + new_file_list = newer_files_only (archive, tmp_file_list, tmp_base_dir); + path_list_free (tmp_file_list); + } + + if (new_file_list == NULL) { + debug (DEBUG_INFO, "nothing to update.\n"); + + if (base_dir_created) + remove_local_directory (tmp_base_dir); + g_free (tmp_base_dir); + + archive->process->error.type = FR_PROC_ERROR_NONE; + g_signal_emit_by_name (G_OBJECT (archive->process), + "done", + FR_ACTION_ADDING_FILES); + return; + } + + archive->command->creating_archive = ! g_file_test (archive->command->filename, G_FILE_TEST_EXISTS); + + fr_command_uncompress (archive->command); + + /* when files are already present in a tar archive and are added + * again, they are not replaced, so we have to delete them first. */ + + /* if we are adding (== ! update) and 'add' cannot replace or + * if we are updating and 'add' cannot update, + * delete the files first. */ + + if ((! update && ! archive->command->propAddCanReplace) + || (update && ! archive->command->propAddCanUpdate)) + { + GList *del_list = NULL; + + for (scan = new_file_list; scan != NULL; scan = scan->next) { + char *filename = scan->data; + if (find_file_in_archive (archive, filename)) + del_list = g_list_prepend (del_list, filename); + } + + /* delete */ + + if (del_list != NULL) { + delete_from_archive (archive, del_list); + fr_process_set_ignore_error (archive->process, TRUE); + g_list_free (del_list); + } + } + + /* add now. */ + + fr_command_set_n_files (archive->command, g_list_length (new_file_list)); + + if (archive->command->propListFromFile + && (archive->command->n_files > LIST_LENGTH_TO_USE_FILE)) + { + char *list_dir; + char *list_filename; + GError *error = NULL; + + if (! save_list_to_temp_file (new_file_list, &list_dir, &list_filename, &error)) { + archive->process->error.type = FR_PROC_ERROR_GENERIC; + archive->process->error.status = 0; + archive->process->error.gerror = g_error_copy (error); + g_signal_emit_by_name (G_OBJECT (archive->process), + "done", + FR_ACTION_ADDING_FILES); + g_clear_error (&error); + error_occurred = TRUE; + } + else { + fr_command_add (archive->command, + list_filename, + new_file_list, + tmp_base_dir, + update, + recursive); + + /* remove the temp dir */ + + fr_process_begin_command (archive->process, "rm"); + fr_process_set_working_dir (archive->process, g_get_tmp_dir()); + fr_process_set_sticky (archive->process, TRUE); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, list_dir); + fr_process_end_command (archive->process); + } + + g_free (list_filename); + g_free (list_dir); + } + else { + GList *chunks = NULL; + + /* specify the file list on the command line, splitting + * in more commands to avoid to overflow the command line + * length limit. */ + + chunks = split_in_chunks (new_file_list); + for (scan = chunks; scan != NULL; scan = scan->next) { + GList *chunk = scan->data; + + fr_command_add (archive->command, + NULL, + chunk, + tmp_base_dir, + update, + recursive); + g_list_free (chunk); + } + + g_list_free (chunks); + } + + path_list_free (new_file_list); + + if (! error_occurred) { + fr_command_recompress (archive->command); + + if (base_dir_created) { /* remove the temp dir */ + fr_process_begin_command (archive->process, "rm"); + fr_process_set_working_dir (archive->process, g_get_tmp_dir()); + fr_process_set_sticky (archive->process, TRUE); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, tmp_base_dir); + fr_process_end_command (archive->process); + } + } + + g_free (tmp_base_dir); +} + + +static void +fr_archive_add_local_files (FrArchive *archive, + GList *file_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) +{ + fr_archive_stoppable (archive, TRUE); + fr_process_clear (archive->process); + fr_archive_add (archive, + file_list, + base_dir, + dest_dir, + update, + FALSE, + password, + encrypt_header, + compression, + volume_size); + fr_process_start (archive->process); +} + + +static void +copy_remote_files_done (GError *error, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + fr_archive_copy_done (xfer_data->archive, FR_ACTION_COPYING_FILES_FROM_REMOTE, error); + + if (error == NULL) + fr_archive_add_local_files (xfer_data->archive, + xfer_data->file_list, + xfer_data->tmp_dir, + xfer_data->dest_dir, + FALSE, + xfer_data->password, + xfer_data->encrypt_header, + xfer_data->compression, + xfer_data->volume_size); + xfer_data_free (xfer_data); +} + + +static void +copy_remote_files_progress (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + XferData *xfer_data = user_data; + + g_signal_emit (G_OBJECT (xfer_data->archive), + fr_archive_signals[PROGRESS], + 0, + (double) current_file / (total_files + 1)); +} + + +static void +copy_remote_files (FrArchive *archive, + GList *file_list, + const char *base_uri, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size, + const char *tmp_dir) +{ + GList *sources = NULL, *destinations = NULL; + GHashTable *created_folders; + GList *scan; + XferData *xfer_data; + + created_folders = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); + for (scan = file_list; scan; scan = scan->next) { + char *partial_filename = scan->data; + char *local_uri; + char *local_folder_uri; + char *remote_uri; + + local_uri = g_strconcat ("file://", tmp_dir, "/", partial_filename, NULL); + local_folder_uri = remove_level_from_path (local_uri); + if (g_hash_table_lookup (created_folders, local_folder_uri) == NULL) { + GError *error = NULL; + if (! ensure_dir_exists (local_folder_uri, 0755, &error)) { + g_free (local_folder_uri); + g_free (local_uri); + gio_file_list_free (sources); + gio_file_list_free (destinations); + g_hash_table_destroy (created_folders); + + fr_archive_action_completed (archive, + FR_ACTION_COPYING_FILES_FROM_REMOTE, + FR_PROC_ERROR_GENERIC, + error->message); + g_clear_error (&error); + return; + } + + g_hash_table_insert (created_folders, local_folder_uri, GINT_TO_POINTER (1)); + } + else + g_free (local_folder_uri); + + remote_uri = g_strconcat (base_uri, "/", partial_filename, NULL); + sources = g_list_append (sources, g_file_new_for_uri (remote_uri)); + g_free (remote_uri); + + destinations = g_list_append (destinations, g_file_new_for_uri (local_uri)); + g_free (local_uri); + } + g_hash_table_destroy (created_folders); + + xfer_data = g_new0 (XferData, 1); + xfer_data->archive = archive; + xfer_data->file_list = path_list_dup (file_list); + xfer_data->base_uri = g_strdup (base_uri); + xfer_data->dest_dir = g_strdup (dest_dir); + xfer_data->update = update; + xfer_data->dest_dir = g_strdup (dest_dir); + xfer_data->password = g_strdup (password); + xfer_data->encrypt_header = encrypt_header; + xfer_data->compression = compression; + xfer_data->volume_size = volume_size; + xfer_data->tmp_dir = g_strdup (tmp_dir); + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_COPYING_FILES_FROM_REMOTE); + + g_copy_files_async (sources, + destinations, + G_FILE_COPY_OVERWRITE, + G_PRIORITY_DEFAULT, + archive->priv->cancellable, + copy_remote_files_progress, + xfer_data, + copy_remote_files_done, + xfer_data); + + gio_file_list_free (sources); + gio_file_list_free (destinations); +} + + +static char * +fr_archive_get_temp_work_dir (FrArchive *archive) +{ + fr_archive_remove_temp_work_dir (archive); + archive->priv->temp_dir = get_temp_work_dir (NULL); + return archive->priv->temp_dir; +} + + +void +fr_archive_add_files (FrArchive *archive, + GList *file_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) +{ + if (uri_is_local (base_dir)) { + char *local_dir = g_filename_from_uri (base_dir, NULL, NULL); + fr_archive_add_local_files (archive, + file_list, + local_dir, + dest_dir, + update, + password, + encrypt_header, + compression, + volume_size); + g_free (local_dir); + } + else + copy_remote_files (archive, + file_list, + base_dir, + dest_dir, + update, + password, + encrypt_header, + compression, + volume_size, + fr_archive_get_temp_work_dir (archive)); +} + + +/* -- add with wildcard -- */ + + +typedef struct { + FrArchive *archive; + char *source_dir; + char *dest_dir; + gboolean update; + char *password; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; +} AddWithWildcardData; + + +static void +add_with_wildcard_data_free (AddWithWildcardData *aww_data) +{ + g_free (aww_data->source_dir); + g_free (aww_data->dest_dir); + g_free (aww_data->password); + g_free (aww_data); +} + + +static void +add_with_wildcard__step2 (GList *file_list, + GList *dirs_list, + GError *error, + gpointer data) +{ + AddWithWildcardData *aww_data = data; + FrArchive *archive = aww_data->archive; + + if (error != NULL) { + fr_archive_action_completed (archive, + FR_ACTION_GETTING_FILE_LIST, + (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC), + error->message); + return; + } + + fr_archive_action_completed (archive, + FR_ACTION_GETTING_FILE_LIST, + FR_PROC_ERROR_NONE, + NULL); + + if (file_list != NULL) + fr_archive_add_files (aww_data->archive, + file_list, + aww_data->source_dir, + aww_data->dest_dir, + aww_data->update, + aww_data->password, + aww_data->encrypt_header, + aww_data->compression, + aww_data->volume_size); + + path_list_free (file_list); + path_list_free (dirs_list); + add_with_wildcard_data_free (aww_data); +} + + +void +fr_archive_add_with_wildcard (FrArchive *archive, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + const char *source_dir, + const char *dest_dir, + gboolean update, + gboolean follow_links, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) +{ + AddWithWildcardData *aww_data; + + g_return_if_fail (! archive->read_only); + + aww_data = g_new0 (AddWithWildcardData, 1); + aww_data->archive = archive; + aww_data->source_dir = g_strdup (source_dir); + aww_data->dest_dir = g_strdup (dest_dir); + aww_data->update = update; + aww_data->password = g_strdup (password); + aww_data->encrypt_header = encrypt_header; + aww_data->compression = compression; + aww_data->volume_size = volume_size; + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_GETTING_FILE_LIST); + + g_directory_list_async (source_dir, + source_dir, + TRUE, + follow_links, + NO_BACKUP_FILES, + NO_DOT_FILES, + include_files, + exclude_files, + exclude_folders, + IGNORE_CASE, + archive->priv->cancellable, + add_with_wildcard__step2, + aww_data); +} + + +/* -- fr_archive_add_directory -- */ + + +typedef struct { + FrArchive *archive; + char *base_dir; + char *dest_dir; + gboolean update; + char *password; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; +} AddDirectoryData; + + +static void +add_directory_data_free (AddDirectoryData *ad_data) +{ + g_free (ad_data->base_dir); + g_free (ad_data->dest_dir); + g_free (ad_data->password); + g_free (ad_data); +} + + +static void +add_directory__step2 (GList *file_list, + GList *dir_list, + GError *error, + gpointer data) +{ + AddDirectoryData *ad_data = data; + FrArchive *archive = ad_data->archive; + + if (error != NULL) { + fr_archive_action_completed (archive, + FR_ACTION_GETTING_FILE_LIST, + (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC), + error->message); + return; + } + + fr_archive_action_completed (archive, + FR_ACTION_GETTING_FILE_LIST, + FR_PROC_ERROR_NONE, + NULL); + + if (archive->command->propAddCanStoreFolders) + file_list = g_list_concat (file_list, dir_list); + else + path_list_free (dir_list); + + if (file_list != NULL) { + fr_archive_add_files (ad_data->archive, + file_list, + ad_data->base_dir, + ad_data->dest_dir, + ad_data->update, + ad_data->password, + ad_data->encrypt_header, + ad_data->compression, + ad_data->volume_size); + path_list_free (file_list); + } + + add_directory_data_free (ad_data); +} + + +void +fr_archive_add_directory (FrArchive *archive, + const char *directory, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) + +{ + AddDirectoryData *ad_data; + + g_return_if_fail (! archive->read_only); + + ad_data = g_new0 (AddDirectoryData, 1); + ad_data->archive = archive; + ad_data->base_dir = g_strdup (base_dir); + ad_data->dest_dir = g_strdup (dest_dir); + ad_data->update = update; + ad_data->password = g_strdup (password); + ad_data->encrypt_header = encrypt_header; + ad_data->compression = compression; + ad_data->volume_size = volume_size; + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_GETTING_FILE_LIST); + + g_directory_list_all_async (directory, + base_dir, + TRUE, + archive->priv->cancellable, + add_directory__step2, + ad_data); +} + + +void +fr_archive_add_items (FrArchive *archive, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) + +{ + AddDirectoryData *ad_data; + + g_return_if_fail (! archive->read_only); + + ad_data = g_new0 (AddDirectoryData, 1); + ad_data->archive = archive; + ad_data->base_dir = g_strdup (base_dir); + ad_data->dest_dir = g_strdup (dest_dir); + ad_data->update = update; + ad_data->password = g_strdup (password); + ad_data->encrypt_header = encrypt_header; + ad_data->compression = compression; + ad_data->volume_size = volume_size; + + g_signal_emit (G_OBJECT (archive), + fr_archive_signals[START], + 0, + FR_ACTION_GETTING_FILE_LIST); + + g_list_items_async (item_list, + base_dir, + archive->priv->cancellable, + add_directory__step2, + ad_data); +} + + +/* -- fr_archive_add_dropped_items -- */ + + +static gboolean +all_files_in_same_dir (GList *list) +{ + gboolean same_dir = TRUE; + char *first_basedir; + GList *scan; + + if (list == NULL) + return FALSE; + + first_basedir = remove_level_from_path (list->data); + if (first_basedir == NULL) + return TRUE; + + for (scan = list->next; scan; scan = scan->next) { + char *path = scan->data; + char *basedir; + + basedir = remove_level_from_path (path); + if (basedir == NULL) { + same_dir = FALSE; + break; + } + + if (strcmp (first_basedir, basedir) != 0) { + same_dir = FALSE; + g_free (basedir); + break; + } + g_free (basedir); + } + g_free (first_basedir); + + return same_dir; +} + + +static void +add_dropped_items (DroppedItemsData *data) +{ + FrArchive *archive = data->archive; + GList *list = data->item_list; + GList *scan; + + if (list == NULL) { + dropped_items_data_free (archive->priv->dropped_items_data); + archive->priv->dropped_items_data = NULL; + fr_archive_action_completed (archive, + FR_ACTION_ADDING_FILES, + FR_PROC_ERROR_NONE, + NULL); + return; + } + + /* if all files/dirs are in the same directory call fr_archive_add_items... */ + + if (all_files_in_same_dir (list)) { + char *first_base_dir; + + first_base_dir = remove_level_from_path (list->data); + fr_archive_add_items (data->archive, + list, + first_base_dir, + data->dest_dir, + data->update, + data->password, + data->encrypt_header, + data->compression, + data->volume_size); + g_free (first_base_dir); + + dropped_items_data_free (archive->priv->dropped_items_data); + archive->priv->dropped_items_data = NULL; + + return; + } + + /* ...else add a directory at a time. */ + + for (scan = list; scan; scan = scan->next) { + char *path = scan->data; + char *base_dir; + + if (! uri_is_dir (path)) + continue; + + data->item_list = g_list_remove_link (list, scan); + if (data->item_list != NULL) + archive->priv->continue_adding_dropped_items = TRUE; + base_dir = remove_level_from_path (path); + + fr_archive_add_directory (archive, + file_name_from_path (path), + base_dir, + data->dest_dir, + data->update, + data->password, + data->encrypt_header, + data->compression, + data->volume_size); + + g_free (base_dir); + g_free (path); + + return; + } + + /* if all files are in the same directory call fr_archive_add_files. */ + + if (all_files_in_same_dir (list)) { + char *first_basedir; + GList *only_names_list = NULL; + + first_basedir = remove_level_from_path (list->data); + + for (scan = list; scan; scan = scan->next) { + char *name; + + name = g_uri_unescape_string (file_name_from_path (scan->data), NULL); + only_names_list = g_list_prepend (only_names_list, name); + } + + fr_archive_add_files (archive, + only_names_list, + first_basedir, + data->dest_dir, + data->update, + data->password, + data->encrypt_header, + data->compression, + data->volume_size); + + path_list_free (only_names_list); + g_free (first_basedir); + + return; + } + + /* ...else call fr_command_add for each file. This is needed to add + * files without path info. FIXME: doesn't work with remote files. */ + + fr_archive_stoppable (archive, FALSE); + g_object_set (archive->command, + "password", data->password, + "encrypt_header", data->encrypt_header, + "compression", data->compression, + "volume_size", data->volume_size, + NULL); + fr_process_clear (archive->process); + fr_command_uncompress (archive->command); + for (scan = list; scan; scan = scan->next) { + char *fullpath = scan->data; + char *basedir; + GList *singleton; + + basedir = remove_level_from_path (fullpath); + singleton = g_list_prepend (NULL, (char*)file_name_from_path (fullpath)); + fr_command_add (archive->command, + NULL, + singleton, + basedir, + data->update, + FALSE); + g_list_free (singleton); + g_free (basedir); + } + fr_command_recompress (archive->command); + fr_process_start (archive->process); + + path_list_free (data->item_list); + data->item_list = NULL; +} + + +void +fr_archive_add_dropped_items (FrArchive *archive, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size) +{ + GList *scan; + char *archive_uri; + + if (archive->read_only) { + fr_archive_action_completed (archive, + FR_ACTION_ADDING_FILES, + FR_PROC_ERROR_GENERIC, + ! archive->have_permissions ? _("You don't have the right permissions.") : _("This archive type cannot be modified")); + return; + } + + /* FIXME: make this check for all the add actions */ + archive_uri = g_file_get_uri (archive->file); + for (scan = item_list; scan; scan = scan->next) { + if (uricmp (scan->data, archive_uri) == 0) { + g_free (archive_uri); + fr_archive_action_completed (archive, + FR_ACTION_ADDING_FILES, + FR_PROC_ERROR_GENERIC, + _("You can't add an archive to itself.")); + return; + } + } + g_free (archive_uri); + + if (archive->priv->dropped_items_data != NULL) + dropped_items_data_free (archive->priv->dropped_items_data); + archive->priv->dropped_items_data = dropped_items_data_new ( + archive, + item_list, + base_dir, + dest_dir, + update, + password, + encrypt_header, + compression, + volume_size); + add_dropped_items (archive->priv->dropped_items_data); +} + + +/* -- remove -- */ + + +static gboolean +file_is_in_subfolder_of (const char *filename, + GList *folder_list) +{ + GList *scan; + + if (filename == NULL) + return FALSE; + + for (scan = folder_list; scan; scan = scan->next) { + char *folder_in_list = (char*) scan->data; + + if (path_in_path (folder_in_list, filename)) + return TRUE; + } + + return FALSE; +} + +static gboolean +archive_type_has_issues_deleting_non_empty_folders (FrArchive *archive) +{ + return ! archive->command->propCanDeleteNonEmptyFolders; +} + + +static void +delete_from_archive (FrArchive *archive, + GList *file_list) +{ + gboolean file_list_created = FALSE; + GList *tmp_file_list = NULL; + gboolean tmp_file_list_created = FALSE; + GList *scan; + + /* file_list == NULL means delete all the files in the archive. */ + + if (file_list == NULL) { + int i; + + for (i = 0; i < archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (archive->command->files, i); + file_list = g_list_prepend (file_list, fdata->original_path); + } + + file_list_created = TRUE; + } + + if (archive_type_has_issues_deleting_non_empty_folders (archive)) { + GList *folders_to_remove; + + /* remove from the list the files contained in folders to be + * removed. */ + + folders_to_remove = NULL; + for (scan = file_list; scan != NULL; scan = scan->next) { + char *path = scan->data; + + if (path[strlen (path) - 1] == '/') + folders_to_remove = g_list_prepend (folders_to_remove, path); + } + + if (folders_to_remove != NULL) { + tmp_file_list = NULL; + for (scan = file_list; scan != NULL; scan = scan->next) { + char *path = scan->data; + + if (! file_is_in_subfolder_of (path, folders_to_remove)) + tmp_file_list = g_list_prepend (tmp_file_list, path); + } + tmp_file_list_created = TRUE; + g_list_free (folders_to_remove); + } + } + + if (! tmp_file_list_created) + tmp_file_list = g_list_copy (file_list); + + if (file_list_created) + g_list_free (file_list); + + fr_command_set_n_files (archive->command, g_list_length (tmp_file_list)); + + if (archive->command->propListFromFile + && (archive->command->n_files > LIST_LENGTH_TO_USE_FILE)) + { + char *list_dir; + char *list_filename; + + if (save_list_to_temp_file (tmp_file_list, &list_dir, &list_filename, NULL)) { + fr_command_delete (archive->command, + list_filename, + tmp_file_list); + + /* remove the temp dir */ + + fr_process_begin_command (archive->process, "rm"); + fr_process_set_working_dir (archive->process, g_get_tmp_dir()); + fr_process_set_sticky (archive->process, TRUE); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, list_dir); + fr_process_end_command (archive->process); + } + + g_free (list_filename); + g_free (list_dir); + } + else { + for (scan = tmp_file_list; scan != NULL; ) { + GList *prev = scan->prev; + GList *chunk_list; + int l; + + chunk_list = scan; + l = 0; + while ((scan != NULL) && (l < MAX_CHUNK_LEN)) { + if (l == 0) + l = strlen (scan->data); + prev = scan; + scan = scan->next; + if (scan != NULL) + l += strlen (scan->data); + } + + prev->next = NULL; + fr_command_delete (archive->command, NULL, chunk_list); + prev->next = scan; + } + } + + g_list_free (tmp_file_list); +} + + +void +fr_archive_remove (FrArchive *archive, + GList *file_list, + FrCompression compression) +{ + g_return_if_fail (archive != NULL); + + if (archive->read_only) + return; + + fr_archive_stoppable (archive, FALSE); + g_object_set (archive->command, "compression", compression, NULL); + fr_command_uncompress (archive->command); + delete_from_archive (archive, file_list); + fr_command_recompress (archive->command); +} + + +/* -- extract -- */ + + +static void +move_files_to_dir (FrArchive *archive, + GList *file_list, + const char *source_dir, + const char *dest_dir, + gboolean overwrite) +{ + GList *list; + GList *scan; + + /* we prefer mv instead of cp for performance reasons, + * but if the destination folder already exists mv + * doesn't work correctly. (bug #590027) */ + + list = g_list_copy (file_list); + for (scan = list; scan; /* void */) { + GList *next = scan->next; + char *filename = scan->data; + char *basename; + char *destname; + + basename = g_path_get_basename (filename); + destname = g_build_filename (dest_dir, basename, NULL); + if (g_file_test (destname, G_FILE_TEST_IS_DIR)) { + fr_process_begin_command (archive->process, "cp"); + fr_process_add_arg (archive->process, "-R"); + if (overwrite) + fr_process_add_arg (archive->process, "-f"); + else + fr_process_add_arg (archive->process, "-n"); + if (filename[0] == '/') + fr_process_add_arg_concat (archive->process, source_dir, filename, NULL); + else + fr_process_add_arg_concat (archive->process, source_dir, "/", filename, NULL); + fr_process_add_arg (archive->process, dest_dir); + fr_process_end_command (archive->process); + + list = g_list_remove_link (list, scan); + g_list_free (scan); + } + + g_free (destname); + g_free (basename); + + scan = next; + } + + if (list == NULL) + return; + + /* 'list' now contains the files that can be moved without problems */ + + fr_process_begin_command (archive->process, "mv"); + if (overwrite) + fr_process_add_arg (archive->process, "-f"); + else + fr_process_add_arg (archive->process, "-n"); + for (scan = list; scan; scan = scan->next) { + char *filename = scan->data; + + if (filename[0] == '/') + fr_process_add_arg_concat (archive->process, source_dir, filename, NULL); + else + fr_process_add_arg_concat (archive->process, source_dir, "/", filename, NULL); + } + fr_process_add_arg (archive->process, dest_dir); + fr_process_end_command (archive->process); + + g_list_free (list); +} + + +static void +move_files_in_chunks (FrArchive *archive, + GList *file_list, + const char *temp_dir, + const char *dest_dir, + gboolean overwrite) +{ + GList *scan; + int temp_dir_l; + + temp_dir_l = strlen (temp_dir); + + for (scan = file_list; scan != NULL; ) { + GList *prev = scan->prev; + GList *chunk_list; + int l; + + chunk_list = scan; + l = 0; + while ((scan != NULL) && (l < MAX_CHUNK_LEN)) { + if (l == 0) + l = temp_dir_l + 1 + strlen (scan->data); + prev = scan; + scan = scan->next; + if (scan != NULL) + l += temp_dir_l + 1 + strlen (scan->data); + } + + prev->next = NULL; + move_files_to_dir (archive, chunk_list, temp_dir, dest_dir, overwrite); + prev->next = scan; + } +} + + +static void +extract_from_archive (FrArchive *archive, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths, + const char *password) +{ + FrCommand *command = archive->command; + GList *scan; + + g_object_set (command, "password", password, NULL); + + if (file_list == NULL) { + fr_command_extract (command, + NULL, + file_list, + dest_dir, + overwrite, + skip_older, + junk_paths); + return; + } + + if (command->propListFromFile + && (g_list_length (file_list) > LIST_LENGTH_TO_USE_FILE)) + { + char *list_dir; + char *list_filename; + + if (save_list_to_temp_file (file_list, &list_dir, &list_filename, NULL)) { + fr_command_extract (command, + list_filename, + file_list, + dest_dir, + overwrite, + skip_older, + junk_paths); + + /* remove the temp dir */ + + fr_process_begin_command (archive->process, "rm"); + fr_process_set_working_dir (archive->process, g_get_tmp_dir()); + fr_process_set_sticky (archive->process, TRUE); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, list_dir); + fr_process_end_command (archive->process); + } + + g_free (list_filename); + g_free (list_dir); + } + else { + for (scan = file_list; scan != NULL; ) { + GList *prev = scan->prev; + GList *chunk_list; + int l; + + chunk_list = scan; + l = 0; + while ((scan != NULL) && (l < MAX_CHUNK_LEN)) { + if (l == 0) + l = strlen (scan->data); + prev = scan; + scan = scan->next; + if (scan != NULL) + l += strlen (scan->data); + } + + prev->next = NULL; + fr_command_extract (command, + NULL, + chunk_list, + dest_dir, + overwrite, + skip_older, + junk_paths); + prev->next = scan; + } + } +} + + +static char* +compute_base_path (const char *base_dir, + const char *path, + gboolean junk_paths, + gboolean can_junk_paths) +{ + int base_dir_len = strlen (base_dir); + int path_len = strlen (path); + const char *base_path; + char *name_end; + char *new_path; + + if (junk_paths) { + if (can_junk_paths) + new_path = g_strdup (file_name_from_path (path)); + else + new_path = g_strdup (path); + + /*debug (DEBUG_INFO, "%s, %s --> %s\n", base_dir, path, new_path);*/ + + return new_path; + } + + if (path_len <= base_dir_len) + return NULL; + + base_path = path + base_dir_len; + if (path[0] != '/') + base_path -= 1; + name_end = strchr (base_path, '/'); + + if (name_end == NULL) + new_path = g_strdup (path); + else { + int name_len = name_end - path; + new_path = g_strndup (path, name_len); + } + + /*debug (DEBUG_INFO, "%s, %s --> %s\n", base_dir, path, new_path);*/ + + return new_path; +} + + +static GList* +compute_list_base_path (const char *base_dir, + GList *filtered, + gboolean junk_paths, + gboolean can_junk_paths) +{ + GList *scan; + GList *list = NULL, *list_unique = NULL; + GList *last_inserted; + + if (filtered == NULL) + return NULL; + + for (scan = filtered; scan; scan = scan->next) { + const char *path = scan->data; + char *new_path; + + new_path = compute_base_path (base_dir, path, junk_paths, can_junk_paths); + if (new_path != NULL) + list = g_list_prepend (list, new_path); + } + + /* The above operation can create duplicates, we remove them here. */ + list = g_list_sort (list, (GCompareFunc)strcmp); + + last_inserted = NULL; + for (scan = list; scan; scan = scan->next) { + const char *path = scan->data; + + if (last_inserted != NULL) { + const char *last_path = (const char*)last_inserted->data; + if (strcmp (last_path, path) == 0) { + g_free (scan->data); + continue; + } + } + + last_inserted = scan; + list_unique = g_list_prepend (list_unique, scan->data); + } + + g_list_free (list); + + return list_unique; +} + + +static gboolean +archive_type_has_issues_extracting_non_empty_folders (FrArchive *archive) +{ + /*if ((archive->command->files == NULL) || (archive->command->files->len == 0)) + return FALSE; FIXME: test with extract_here */ + return ! archive->command->propCanExtractNonEmptyFolders; +} + + +static gboolean +file_list_contains_files_in_this_dir (GList *file_list, + const char *dirname) +{ + GList *scan; + + for (scan = file_list; scan; scan = scan->next) { + char *filename = scan->data; + + if (path_in_path (dirname, filename)) + return TRUE; + } + + return FALSE; +} + + +static GList* +remove_files_contained_in_this_dir (GList *file_list, + GList *dir_pointer) +{ + char *dirname = dir_pointer->data; + int dirname_l = strlen (dirname); + GList *scan; + + for (scan = dir_pointer->next; scan; /* empty */) { + char *filename = scan->data; + + if (strncmp (dirname, filename, dirname_l) != 0) + break; + + if (path_in_path (dirname, filename)) { + GList *next = scan->next; + + file_list = g_list_remove_link (file_list, scan); + g_list_free (scan); + + scan = next; + } + else + scan = scan->next; + } + + return file_list; +} + + +void +fr_archive_extract_to_local (FrArchive *archive, + GList *file_list, + const char *destination, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + const char *password) +{ + GList *filtered; + GList *scan; + gboolean extract_all; + gboolean use_base_dir; + gboolean all_options_supported; + gboolean move_to_dest_dir; + gboolean file_list_created = FALSE; + + g_return_if_fail (archive != NULL); + + fr_archive_stoppable (archive, TRUE); + + /* if a command supports all the requested options use + * fr_command_extract directly. */ + + use_base_dir = ! ((base_dir == NULL) + || (strcmp (base_dir, "") == 0) + || (strcmp (base_dir, "/") == 0)); + + all_options_supported = (! use_base_dir + && ! (! overwrite && ! archive->command->propExtractCanAvoidOverwrite) + && ! (skip_older && ! archive->command->propExtractCanSkipOlder) + && ! (junk_paths && ! archive->command->propExtractCanJunkPaths)); + + extract_all = (file_list == NULL); + if (extract_all && (! all_options_supported || ! archive->command->propCanExtractAll)) { + int i; + + file_list = NULL; + for (i = 0; i < archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (archive->command->files, i); + file_list = g_list_prepend (file_list, g_strdup (fdata->original_path)); + } + file_list_created = TRUE; + } + + if (extract_all && (file_list == NULL)) + fr_command_set_n_files (archive->command, archive->command->n_regular_files); + else + fr_command_set_n_files (archive->command, g_list_length (file_list)); + + if (all_options_supported) { + gboolean created_filtered_list = FALSE; + + if (! extract_all && archive_type_has_issues_extracting_non_empty_folders (archive)) { + created_filtered_list = TRUE; + filtered = g_list_copy (file_list); + filtered = g_list_sort (filtered, (GCompareFunc) strcmp); + for (scan = filtered; scan; scan = scan->next) + filtered = remove_files_contained_in_this_dir (filtered, scan); + } + else + filtered = file_list; + + if (! (created_filtered_list && (filtered == NULL))) + extract_from_archive (archive, + filtered, + destination, + overwrite, + skip_older, + junk_paths, + password); + + if (created_filtered_list && (filtered != NULL)) + g_list_free (filtered); + + if (file_list_created) + path_list_free (file_list); + + return; + } + + /* .. else we have to implement the unsupported options. */ + + move_to_dest_dir = (use_base_dir + || ((junk_paths + && ! archive->command->propExtractCanJunkPaths))); + + if (extract_all && ! file_list_created) { + int i; + + file_list = NULL; + for (i = 0; i < archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (archive->command->files, i); + file_list = g_list_prepend (file_list, g_strdup (fdata->original_path)); + } + + file_list_created = TRUE; + } + + filtered = NULL; + for (scan = file_list; scan; scan = scan->next) { + FileData *fdata; + char *archive_list_filename = scan->data; + char dest_filename[4096]; + const char *filename; + + fdata = find_file_in_archive (archive, archive_list_filename); + + if (fdata == NULL) + continue; + + if (archive_type_has_issues_extracting_non_empty_folders (archive) + && fdata->dir + && file_list_contains_files_in_this_dir (file_list, archive_list_filename)) + continue; + + /* get the destination file path. */ + + if (! junk_paths) + filename = archive_list_filename; + else + filename = file_name_from_path (archive_list_filename); + + if ((destination[strlen (destination) - 1] == '/') + || (filename[0] == '/')) + sprintf (dest_filename, "%s%s", destination, filename); + else + sprintf (dest_filename, "%s/%s", destination, filename); + + /*debug (DEBUG_INFO, "-> %s\n", dest_filename);*/ + + /**/ + + if (! archive->command->propExtractCanSkipOlder + && skip_older + && g_file_test (dest_filename, G_FILE_TEST_EXISTS) + && (fdata->modified < get_file_mtime_for_path (dest_filename))) + continue; + + if (! archive->command->propExtractCanAvoidOverwrite + && ! overwrite + && g_file_test (dest_filename, G_FILE_TEST_EXISTS)) + continue; + + filtered = g_list_prepend (filtered, fdata->original_path); + } + + if (filtered == NULL) { + /* all files got filtered, do nothing. */ + debug (DEBUG_INFO, "All files got filtered, nothing to do.\n"); + + if (extract_all) + path_list_free (file_list); + return; + } + + if (move_to_dest_dir) { + char *temp_dir; + + temp_dir = get_temp_work_dir (destination); + extract_from_archive (archive, + filtered, + temp_dir, + overwrite, + skip_older, + junk_paths, + password); + + if (use_base_dir) { + GList *tmp_list = compute_list_base_path (base_dir, filtered, junk_paths, archive->command->propExtractCanJunkPaths); + g_list_free (filtered); + filtered = tmp_list; + } + + move_files_in_chunks (archive, + filtered, + temp_dir, + destination, + overwrite); + + /* remove the temp dir. */ + + fr_process_begin_command (archive->process, "rm"); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, temp_dir); + fr_process_end_command (archive->process); + + g_free (temp_dir); + } + else + extract_from_archive (archive, + filtered, + destination, + overwrite, + skip_older, + junk_paths, + password); + + if (filtered != NULL) + g_list_free (filtered); + if (file_list_created) + path_list_free (file_list); +} + + +void +fr_archive_extract (FrArchive *archive, + GList *file_list, + const char *destination, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + const char *password) +{ + g_free (archive->priv->extraction_destination); + archive->priv->extraction_destination = g_strdup (destination); + + g_free (archive->priv->temp_extraction_dir); + archive->priv->temp_extraction_dir = NULL; + + archive->priv->remote_extraction = ! uri_is_local (destination); + if (archive->priv->remote_extraction) { + archive->priv->temp_extraction_dir = get_temp_work_dir (NULL); + fr_archive_extract_to_local (archive, + file_list, + archive->priv->temp_extraction_dir, + base_dir, + skip_older, + overwrite, + junk_paths, + password); + } + else { + char *local_destination; + + local_destination = g_filename_from_uri (destination, NULL, NULL); + fr_archive_extract_to_local (archive, + file_list, + local_destination, + base_dir, + skip_older, + overwrite, + junk_paths, + password); + g_free (local_destination); + } +} + + +static char * +get_desired_destination_for_archive (GFile *file) +{ + GFile *directory; + char *directory_uri; + char *name; + const char *ext; + char *new_name; + char *new_name_escaped; + char *desired_destination = NULL; + + directory = g_file_get_parent (file); + directory_uri = g_file_get_uri (directory); + + name = g_file_get_basename (file); + ext = get_archive_filename_extension (name); + if (ext == NULL) + /* if no extension is present add a suffix to the name... */ + new_name = g_strconcat (name, "_FILES", NULL); + else + /* ...else use the name without the extension */ + new_name = g_strndup (name, strlen (name) - strlen (ext)); + new_name_escaped = g_uri_escape_string (new_name, "", FALSE); + + desired_destination = g_strconcat (directory_uri, "/", new_name_escaped, NULL); + + g_free (new_name_escaped); + g_free (new_name); + g_free (name); + g_free (directory_uri); + g_object_unref (directory); + + return desired_destination; +} + + +static char * +get_extract_here_destination (GFile *file, + GError **error) +{ + char *desired_destination; + char *destination = NULL; + int n = 1; + GFile *directory; + + desired_destination = get_desired_destination_for_archive (file); + do { + *error = NULL; + + g_free (destination); + if (n == 1) + destination = g_strdup (desired_destination); + else + destination = g_strdup_printf ("%s%%20(%d)", desired_destination, n); + + directory = g_file_new_for_uri (destination); + g_file_make_directory (directory, NULL, error); + g_object_unref (directory); + + n++; + } while (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS)); + + g_free (desired_destination); + + if (*error != NULL) { + g_warning ("could not create destination folder: %s\n", (*error)->message); + g_free (destination); + destination = NULL; + } + + return destination; +} + + +gboolean +fr_archive_extract_here (FrArchive *archive, + gboolean skip_older, + gboolean overwrite, + gboolean junk_path, + const char *password) +{ + char *destination; + GError *error = NULL; + + destination = get_extract_here_destination (archive->file, &error); + if (error != NULL) { + fr_archive_action_completed (archive, + FR_ACTION_EXTRACTING_FILES, + FR_PROC_ERROR_GENERIC, + error->message); + g_clear_error (&error); + return FALSE; + } + + archive->priv->extract_here = TRUE; + fr_archive_extract (archive, + NULL, + destination, + NULL, + skip_older, + overwrite, + junk_path, + password); + + g_free (destination); + + return TRUE; +} + + +const char * +fr_archive_get_last_extraction_destination (FrArchive *archive) +{ + return archive->priv->extraction_destination; +} + + +void +fr_archive_test (FrArchive *archive, + const char *password) +{ + fr_archive_stoppable (archive, TRUE); + + g_object_set (archive->command, "password", password, NULL); + fr_process_clear (archive->process); + fr_command_set_n_files (archive->command, 0); + fr_command_test (archive->command); + fr_process_start (archive->process); +} + + +gboolean +uri_is_archive (const char *uri) +{ + GFile *file; + const char *mime_type; + gboolean is_archive = FALSE; + + file = g_file_new_for_uri (uri); + mime_type = get_mime_type_from_magic_numbers (file); + if (mime_type == NULL) + mime_type = get_mime_type_from_content (file); + if (mime_type == NULL) + mime_type = get_mime_type_from_filename (file); + + if (mime_type != NULL) { + int i; + + for (i = 0; mime_type_desc[i].mime_type != NULL; i++) { + if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) { + is_archive = TRUE; + break; + } + } + } + g_object_unref (file); + + return is_archive; +} diff --git a/src/fr-archive.h b/src/fr-archive.h new file mode 100644 index 0000000..530e49e --- /dev/null +++ b/src/fr-archive.h @@ -0,0 +1,219 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef ARCHIVE_H +#define ARCHIVE_H + +#include <glib.h> +#include "fr-process.h" +#include "fr-command.h" + +#define FR_TYPE_ARCHIVE (fr_archive_get_type ()) +#define FR_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_ARCHIVE, FrArchive)) +#define FR_ARCHIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_ARCHIVE, FrArchiveClass)) +#define FR_IS_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_ARCHIVE)) +#define FR_IS_ARCHIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_ARCHIVE)) +#define FR_ARCHIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_ARCHIVE, FrArchiveClass)) + +typedef struct _FrArchive FrArchive; +typedef struct _FrArchiveClass FrArchiveClass; +typedef struct _FrArchivePrivData FrArchivePrivData; + +typedef gboolean (*FakeLoadFunc) (FrArchive *archive, gpointer data); + +struct _FrArchive { + GObject __parent; + + GFile *file; + GFile *local_copy; + gboolean is_remote; + const char *content_type; + FrCommand *command; + FrProcess *process; + FrProcError error; + gboolean can_create_compressed_file; + gboolean is_compressed_file; /* Whether the file is not an + * archive that can contain + * many files but simply a + * compressed file, for + * example foo.txt.gz is a + * compressed file, foo.zip + * is not. */ + gboolean read_only; /* Whether archive is + * read-only for whatever + * reason. */ + gboolean have_permissions; /* true if we have the + * permissions to write the + * file. */ + + FrArchivePrivData *priv; +}; + +struct _FrArchiveClass { + GObjectClass __parent_class; + + /* -- Signals -- */ + + void (*start) (FrArchive *archive, + FrAction action); + void (*done) (FrArchive *archive, + FrAction action, + FrProcError *error); + void (*progress) (FrArchive *archive, + double fraction); + void (*message) (FrArchive *archive, + const char *msg); + void (*stoppable) (FrArchive *archive, + gboolean value); + void (*working_archive) (FrCommand *comm, + const char *filename); +}; + +GType fr_archive_get_type (void); +FrArchive * fr_archive_new (void); +void fr_archive_set_fake_load_func (FrArchive *archive, + FakeLoadFunc func, + gpointer data); +gboolean fr_archive_fake_load (FrArchive *archive); +void fr_archive_set_add_is_stoppable_func (FrArchive *archive, + FakeLoadFunc func, + gpointer data); +void fr_archive_stoppable (FrArchive *archive, + gboolean stoppable); +void fr_archive_stop (FrArchive *archive); +void fr_archive_action_completed (FrArchive *archive, + FrAction action, + FrProcErrorType error_type, + const char *error_details); + +/**/ + +gboolean fr_archive_create (FrArchive *archive, + const char *uri); +gboolean fr_archive_load (FrArchive *archive, + const char *uri, + const char *password); +gboolean fr_archive_load_local (FrArchive *archive, + const char *uri, + const char *password); +void fr_archive_reload (FrArchive *archive, + const char *password); +void fr_archive_rename (FrArchive *archive, + const char *new_uri); + +/**/ + +void fr_archive_add (FrArchive *archive, + GList *file_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + gboolean recursive, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size); +void fr_archive_remove (FrArchive *archive, + GList *file_list, + FrCompression compression); +void fr_archive_extract (FrArchive *archive, + GList *file_list, + const char *dest_uri, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_path, + const char *password); +void fr_archive_extract_to_local (FrArchive *archive, + GList *file_list, + const char *dest_path, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_path, + const char *password); +gboolean fr_archive_extract_here (FrArchive *archive, + gboolean skip_older, + gboolean overwrite, + gboolean junk_path, + const char *password); +const char *fr_archive_get_last_extraction_destination + (FrArchive *archive); + +/**/ + +void fr_archive_add_files (FrArchive *archive, + GList *file_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size); +void fr_archive_add_with_wildcard (FrArchive *archive, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + const char *base_dir, + const char *dest_dir, + gboolean update, + gboolean follow_links, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size); +void fr_archive_add_directory (FrArchive *archive, + const char *directory, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size); +void fr_archive_add_items (FrArchive *archive, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size); +void fr_archive_add_dropped_items (FrArchive *archive, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update, + const char *password, + gboolean encrypt_header, + FrCompression compression, + guint volume_size); +void fr_archive_test (FrArchive *archive, + const char *password); + +/* utilities */ + +gboolean uri_is_archive (const char *uri); + +#endif /* ARCHIVE_H */ diff --git a/src/fr-command-7z.c b/src/fr-command-7z.c new file mode 100644 index 0000000..76fe48d --- /dev/null +++ b/src/fr-command-7z.c @@ -0,0 +1,686 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2004, 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-7z.h" + +static void fr_command_7z_class_init (FrCommand7zClass *class); +static void fr_command_7z_init (FrCommand *afile); +static void fr_command_7z_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + + +static time_t +mktime_from_string (char *date_s, + char *time_s) +{ + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + fields = g_strsplit (date_s, "-", 3); + if (fields[0] != NULL) { + tm.tm_year = atoi (fields[0]) - 1900; + tm.tm_mon = atoi (fields[1]) - 1; + tm.tm_mday = atoi (fields[2]); + } + g_strfreev (fields); + + /* time */ + + fields = g_strsplit (time_s, ":", 3); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_min = atoi (fields[1]); + if (fields[2] != NULL) + tm.tm_sec = atoi (fields[2]); + } + } + g_strfreev (fields); + + return mktime (&tm); +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FrCommand7z *p7z_comm = FR_COMMAND_7Z (comm); + char **fields; + FileData *fdata; + + g_return_if_fail (line != NULL); + + if (! p7z_comm->list_started) { + if (strncmp (line, "p7zip Version ", 14) == 0) { + const char *ver_start; + int ver_len; + char version[256]; + + ver_start = eat_spaces (line + 14); + ver_len = strchr (ver_start, ' ') - ver_start; + strncpy (version, ver_start, ver_len); + version[ver_len] = 0; + + if (strcmp (version, "4.55") < 0) + p7z_comm->old_style = TRUE; + else + p7z_comm->old_style = FALSE; + } + else if (p7z_comm->old_style && (strncmp (line, "Listing archive: ", 17) == 0)) + p7z_comm->list_started = TRUE; + else if (! p7z_comm->old_style && (strcmp (line, "----------") == 0)) + p7z_comm->list_started = TRUE; + else if (strncmp (line, "Multivolume = ", 14) == 0) { + fields = g_strsplit (line, " = ", 2); + comm->multi_volume = (strcmp (fields[1], "+") == 0); + g_strfreev (fields); + } + return; + } + + if (strcmp (line, "") == 0) { + if (p7z_comm->fdata != NULL) { + if (p7z_comm->fdata->original_path == NULL) { + file_data_free (p7z_comm->fdata); + p7z_comm->fdata = NULL; + } + else { + fdata = p7z_comm->fdata; + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + fr_command_add_file (comm, fdata); + p7z_comm->fdata = NULL; + } + } + return; + } + + if (p7z_comm->fdata == NULL) + p7z_comm->fdata = file_data_new (); + + fields = g_strsplit (line, " = ", 2); + + if (g_strv_length (fields) < 2) { + g_strfreev (fields); + return; + } + + fdata = p7z_comm->fdata; + + if (strcmp (fields[0], "Path") == 0) { + fdata->free_original_path = TRUE; + fdata->original_path = g_strdup (fields[1]); + fdata->full_path = g_strconcat ((fdata->original_path[0] != '/') ? "/" : "", + fdata->original_path, + (fdata->dir && (fdata->original_path[strlen (fdata->original_path - 1)] != '/')) ? "/" : "", + NULL); + } + else if (strcmp (fields[0], "Folder") == 0) { + fdata->dir = (strcmp (fields[1], "+") == 0); + } + else if (strcmp (fields[0], "Size") == 0) { + fdata->size = g_ascii_strtoull (fields[1], NULL, 10); + } + else if (strcmp (fields[0], "Modified") == 0) { + char **modified_fields; + + modified_fields = g_strsplit (fields[1], " ", 2); + if (modified_fields[0] != NULL) + fdata->modified = mktime_from_string (modified_fields[0], modified_fields[1]); + g_strfreev (modified_fields); + } + else if (strcmp (fields[0], "Encrypted") == 0) { + if (strcmp (fields[1], "+") == 0) + fdata->encrypted = TRUE; + } + else if (strcmp (fields[0], "Method") == 0) { + if (strstr (fields[1], "AES") != NULL) + fdata->encrypted = TRUE; + } + else if (strcmp (fields[0], "Attributes") == 0) { + if (fields[1][0] == 'D') + fdata->dir = TRUE; + } + g_strfreev (fields); +} + + +static void +fr_command_7z_begin_command (FrCommand *comm) +{ + if (is_program_in_path ("7z")) + fr_process_begin_command (comm->process, "7z"); + else if (is_program_in_path ("7za")) + fr_process_begin_command (comm->process, "7za"); + else if (is_program_in_path ("7zr")) + fr_process_begin_command (comm->process, "7zr"); +} + + +static void +add_password_arg (FrCommand *comm, + const char *password, + gboolean always_specify) +{ + if (always_specify || ((password != NULL) && (*password != 0))) { + char *arg; + + arg = g_strconcat ("-p", password, NULL); + fr_process_add_arg (comm->process, arg); + g_free (arg); + } +} + + +static void +list__begin (gpointer data) +{ + FrCommand7z *p7z_comm = data; + + if (p7z_comm->fdata != NULL) { + file_data_free (p7z_comm->fdata); + p7z_comm->fdata = NULL; + } + p7z_comm->list_started = FALSE; +} + + +static void +fr_command_7z_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, list__process_line, comm); + + fr_command_7z_begin_command (comm); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, "l"); + fr_process_add_arg (comm->process, "-slt"); + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + add_password_arg (comm, comm->password, FALSE); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + + fr_process_start (comm->process); +} + + +static char Progress_Message[4196]; +static char Progress_Filename[4096]; + + +static void +parse_progress_line (FrCommand *comm, + const char *prefix, + const char *message_prefix, + const char *line) +{ + int prefix_len; + + prefix_len = strlen (prefix); + if (strncmp (line, prefix, prefix_len) == 0) { + double fraction; + + strcpy (Progress_Filename, line + prefix_len); + sprintf (Progress_Message, "%s%s", message_prefix, file_name_from_path (Progress_Filename)); + fr_command_message (comm, Progress_Message); + + fraction = (double) ++comm->n_file / (comm->n_files + 1); + fr_command_progress (comm, fraction); + } +} + + +static void +process_line__add (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + if ((comm->volume_size > 0) && (strncmp (line, "Creating archive ", 17) == 0)) { + char *volume_filename; + + volume_filename = g_strconcat (comm->filename, ".001", NULL); + fr_command_set_multi_volume (comm, volume_filename); + g_free (volume_filename); + } + + if (comm->n_files != 0) + parse_progress_line (comm, "Compressing ", _("Adding file: "), line); +} + + +static void +fr_command_7z_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + fr_process_use_standard_locale (comm->process, TRUE); + fr_process_set_out_line_func (comm->process, + process_line__add, + comm); + + fr_command_7z_begin_command (comm); + + if (update) + fr_process_add_arg (comm->process, "u"); + else + fr_process_add_arg (comm->process, "a"); + + if (base_dir != NULL) { + fr_process_set_working_dir (comm->process, base_dir); + fr_process_add_arg_concat (comm->process, "-w", base_dir, NULL); + } + + if (is_mime_type (comm->mime_type, "application/zip") + || is_mime_type (comm->mime_type, "application/x-cbz")) + { + fr_process_add_arg (comm->process, "-tzip"); + fr_process_add_arg (comm->process, "-mem=AES128"); + } + + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-l"); + add_password_arg (comm, comm->password, FALSE); + if ((comm->password != NULL) + && (*comm->password != 0) + && comm->encrypt_header + && fr_command_is_capable_of (comm, FR_COMMAND_CAN_ENCRYPT_HEADER)) + { + fr_process_add_arg (comm->process, "-mhe=on"); + } + + /* fr_process_add_arg (comm->process, "-ms=off"); FIXME: solid mode off? */ + + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-mx=1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-mx=5"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-mx=5"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-mx=7"); break; + } + + if (is_mime_type (comm->mime_type, "application/x-ms-dos-executable")) + fr_process_add_arg (comm->process, "-sfx"); + + if (comm->volume_size > 0) + fr_process_add_arg_printf (comm->process, "-v%ub", comm->volume_size); + + if (from_file != NULL) + fr_process_add_arg_concat (comm->process, "-i@", from_file, NULL); + + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_7z_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + fr_command_7z_begin_command (comm); + fr_process_add_arg (comm->process, "d"); + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + if (is_mime_type (comm->mime_type, "application/x-ms-dos-executable")) + fr_process_add_arg (comm->process, "-sfx"); + + if (from_file != NULL) + fr_process_add_arg_concat (comm->process, "-i@", from_file, NULL); + + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +process_line__extract (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + if (comm->n_files != 0) + parse_progress_line (comm, "Extracting ", _("Extracting file: "), line); +} + + +static void +fr_command_7z_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_use_standard_locale (comm->process, TRUE); + fr_process_set_out_line_func (comm->process, + process_line__extract, + comm); + fr_command_7z_begin_command (comm); + + if (junk_paths) + fr_process_add_arg (comm->process, "e"); + else + fr_process_add_arg (comm->process, "x"); + + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + add_password_arg (comm, comm->password, FALSE); + + if (dest_dir != NULL) + fr_process_add_arg_concat (comm->process, "-o", dest_dir, NULL); + + if (from_file != NULL) + fr_process_add_arg_concat (comm->process, "-i@", from_file, NULL); + + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_7z_test (FrCommand *comm) +{ + fr_command_7z_begin_command (comm); + fr_process_add_arg (comm->process, "t"); + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + add_password_arg (comm, comm->password, FALSE); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +static void +fr_command_7z_handle_error (FrCommand *comm, + FrProcError *error) +{ + if (error->type == FR_PROC_ERROR_NONE) { + FileData *first; + char *basename; + char *testname; + + /* This is a way to fix bug #582712. */ + + if (comm->files->len != 1) + return; + + if (! g_str_has_suffix (comm->filename, ".001")) + return; + + first = g_ptr_array_index (comm->files, 0); + basename = g_path_get_basename (comm->filename); + testname = g_strconcat (first->original_path, ".001", NULL); + + if (strcmp (basename, testname) == 0) + error->type = FR_PROC_ERROR_ASK_PASSWORD; + + g_free (testname); + g_free (basename); + + return; + } + + if (error->status <= 1) { + error->type = FR_PROC_ERROR_NONE; + } + else { + GList *scan; + + for (scan = g_list_last (comm->process->out.raw); scan; scan = scan->prev) { + char *line = scan->data; + + if ((strstr (line, "Wrong password?") != NULL) + || (strstr (line, "Enter password") != NULL)) + { + error->type = FR_PROC_ERROR_ASK_PASSWORD; + break; + } + } + } +} + + +const char *sevenz_mime_types[] = { "application/x-7z-compressed", + "application/x-arj", + "application/vnd.ms-cab-compressed", + "application/x-cd-image", + /*"application/x-cbr",*/ + "application/x-cbz", + "application/x-ms-dos-executable", + "application/x-rar", + "application/zip", + NULL }; + + +const char ** +fr_command_7z_get_mime_types (FrCommand *comm) +{ + return sevenz_mime_types; +} + + +FrCommandCap +fr_command_7z_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (! is_program_available ("7za", check_command) && ! is_program_available ("7zr", check_command) && ! is_program_available ("7z", check_command)) + return capabilities; + + if (is_mime_type (mime_type, "application/x-7z-compressed")) { + capabilities |= FR_COMMAND_CAN_READ_WRITE | FR_COMMAND_CAN_CREATE_VOLUMES; + if (is_program_available ("7z", check_command)) + capabilities |= FR_COMMAND_CAN_ENCRYPT | FR_COMMAND_CAN_ENCRYPT_HEADER; + } + else if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) { + capabilities |= FR_COMMAND_CAN_READ_WRITE; + if (is_program_available ("7z", check_command)) + capabilities |= FR_COMMAND_CAN_ENCRYPT | FR_COMMAND_CAN_ENCRYPT_HEADER; + } + else if (is_program_available ("7z", check_command)) { + if (is_mime_type (mime_type, "application/x-rar") + || is_mime_type (mime_type, "application/x-cbr")) + { + if (! check_command || g_file_test ("/usr/lib/p7zip/Codecs/Rar29.so", G_FILE_TEST_EXISTS)) + capabilities |= FR_COMMAND_CAN_READ; + } + else + capabilities |= FR_COMMAND_CAN_READ; + + if (is_mime_type (mime_type, "application/x-cbz") + || is_mime_type (mime_type, "application/x-ms-dos-executable") + || is_mime_type (mime_type, "application/zip")) + { + capabilities |= FR_COMMAND_CAN_WRITE | FR_COMMAND_CAN_ENCRYPT; + } + } + else if (is_program_available ("7za", check_command)) { + if (is_mime_type (mime_type, "application/vnd.ms-cab-compressed") + || is_mime_type (mime_type, "application/zip")) + { + capabilities |= FR_COMMAND_CAN_READ; + } + + if (is_mime_type (mime_type, "application/zip")) + capabilities |= FR_COMMAND_CAN_WRITE; + } + + /* multi-volumes are read-only */ + if ((comm->files->len > 0) && comm->multi_volume && (capabilities & FR_COMMAND_CAN_WRITE)) + capabilities ^= FR_COMMAND_CAN_WRITE; + + return capabilities; +} + + +static const char * +fr_command_7z_get_packages (FrCommand *comm, + const char *mime_type) +{ + if (is_mime_type (mime_type, "application/x-rar")) + return PACKAGES ("p7zip,p7zip-rar"); + else if (is_mime_type (mime_type, "application/zip") || is_mime_type (mime_type, "application/vnd.ms-cab-compressed")) + return PACKAGES ("p7zip,p7zip-full"); + else + return PACKAGES ("p7zip"); +} + + +static void +fr_command_7z_class_init (FrCommand7zClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_7z_finalize; + + afc->list = fr_command_7z_list; + afc->add = fr_command_7z_add; + afc->delete = fr_command_7z_delete; + afc->extract = fr_command_7z_extract; + afc->test = fr_command_7z_test; + afc->handle_error = fr_command_7z_handle_error; + afc->get_mime_types = fr_command_7z_get_mime_types; + afc->get_capabilities = fr_command_7z_get_capabilities; + afc->get_packages = fr_command_7z_get_packages; +} + + +static void +fr_command_7z_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propAddCanStoreFolders = TRUE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = TRUE; + comm->propPassword = TRUE; + comm->propTest = TRUE; + comm->propListFromFile = TRUE; +} + + +static void +fr_command_7z_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_7Z (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_7z_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommand7zClass), + NULL, + NULL, + (GClassInitFunc) fr_command_7z_class_init, + NULL, + NULL, + sizeof (FrCommand7z), + 0, + (GInstanceInitFunc) fr_command_7z_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommand7z", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-7z.h b/src/fr-command-7z.h new file mode 100644 index 0000000..fc7aa32 --- /dev/null +++ b/src/fr-command-7z.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_7Z_H +#define FR_COMMAND_7Z_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_7Z (fr_command_7z_get_type ()) +#define FR_COMMAND_7Z(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_7Z, FrCommand7z)) +#define FR_COMMAND_7Z_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_7Z, FrCommand7zClass)) +#define FR_IS_COMMAND_7Z(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_7Z)) +#define FR_IS_COMMAND_7Z_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_7Z)) +#define FR_COMMAND_7Z_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_7Z, FrCommand7zClass)) + +typedef struct _FrCommand7z FrCommand7z; +typedef struct _FrCommand7zClass FrCommand7zClass; + +struct _FrCommand7z +{ + FrCommand __parent; + gboolean list_started; + gboolean old_style; + FileData *fdata; +}; + +struct _FrCommand7zClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_7z_get_type (void); + +#endif /* FR_COMMAND_7Z_H */ diff --git a/src/fr-command-ace.c b/src/fr-command-ace.c new file mode 100644 index 0000000..c6e889e --- /dev/null +++ b/src/fr-command-ace.c @@ -0,0 +1,342 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-ace.h" + +static void fr_command_ace_class_init (FrCommandAceClass *class); +static void fr_command_ace_init (FrCommand *afile); +static void fr_command_ace_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + + +static time_t +mktime_from_string (char *date, + char *time) +{ + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + fields = g_strsplit (date, ".", 3); + if (fields[0] != NULL) { + tm.tm_mday = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_mon = atoi (fields[1]) - 1; + if (fields[2] != NULL) { + int y = atoi (fields[2]); + if (y > 75) + tm.tm_year = y; + else + tm.tm_year = 100 + y; + } + } + } + g_strfreev (fields); + + /* time */ + + fields = g_strsplit (time, ":", 2); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) + tm.tm_min = atoi (fields[1]); + } + tm.tm_sec = 0; + g_strfreev (fields); + + return mktime (&tm); +} + + +static void +process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommandAce *ace_comm = FR_COMMAND_ACE (data); + FrCommand *comm = FR_COMMAND (data); + char **fields; + const char *field_name; + + g_return_if_fail (line != NULL); + + if (ace_comm->command_type == FR_ACE_COMMAND_UNKNOWN) { + if (g_str_has_prefix (line, "UNACE")) { + if (strstr (line, "public version") != NULL) + ace_comm->command_type = FR_ACE_COMMAND_PUBLIC; + else + ace_comm->command_type = FR_ACE_COMMAND_NONFREE; + } + return; + } + + if (! ace_comm->list_started) { + if (ace_comm->command_type == FR_ACE_COMMAND_PUBLIC) { + if (g_str_has_prefix (line, "Date")) + ace_comm->list_started = TRUE; + } + else if (ace_comm->command_type == FR_ACE_COMMAND_NONFREE) { + if (g_str_has_prefix (line, " Date")) + ace_comm->list_started = TRUE; + } + return; + } + + fdata = file_data_new (); + + if (ace_comm->command_type == FR_ACE_COMMAND_PUBLIC) + fields = g_strsplit (line, "|", 6); + else if (ace_comm->command_type == FR_ACE_COMMAND_NONFREE) + fields = split_line (line, 5); + + if ((fields == NULL) || (fields[0] == NULL) || (n_fields (fields) < 5)) + return; + + fdata->size = g_ascii_strtoull (fields[3], NULL, 10); + fdata->modified = mktime_from_string (fields[0], fields[1]); + + if (ace_comm->command_type == FR_ACE_COMMAND_PUBLIC) { + field_name = fields[5]; + field_name = field_name + 1; + } + else if (ace_comm->command_type == FR_ACE_COMMAND_NONFREE) + field_name = get_last_field (line, 6); + + if (field_name[0] != '/') { + fdata->full_path = g_strconcat ("/", field_name, NULL); + fdata->original_path = fdata->full_path + 1; + } + else { + fdata->full_path = g_strdup (field_name); + fdata->original_path = fdata->full_path; + } + + g_strfreev (fields); + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +list__begin (gpointer data) +{ + FrCommandAce *comm = data; + + comm->list_started = FALSE; + comm->command_type = FR_ACE_COMMAND_UNKNOWN; +} + + +static void +fr_command_ace_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, process_line, comm); + + fr_process_begin_command (comm->process, "unace"); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, "v"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_ace_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_begin_command (comm->process, "unace"); + + if (dest_dir != NULL) + fr_process_set_working_dir (comm->process, dest_dir); + + if (junk_paths) + fr_process_add_arg (comm->process, "e"); + else + fr_process_add_arg (comm->process, "x"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_ace_test (FrCommand *comm) +{ + fr_process_begin_command (comm->process, "unace"); + fr_process_add_arg (comm->process, "t"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +static void +fr_command_ace_handle_error (FrCommand *comm, + FrProcError *error) +{ + /* FIXME */ +} + + +const char *ace_mime_type[] = { "application/x-ace", NULL }; + + +const char ** +fr_command_ace_get_mime_types (FrCommand *comm) +{ + return ace_mime_type; +} + + +FrCommandCap +fr_command_ace_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("unace", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_ace_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("unace"); +} + + +static void +fr_command_ace_class_init (FrCommandAceClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_ace_finalize; + + afc->list = fr_command_ace_list; + afc->extract = fr_command_ace_extract; + afc->test = fr_command_ace_test; + afc->handle_error = fr_command_ace_handle_error; + afc->get_mime_types = fr_command_ace_get_mime_types; + afc->get_capabilities = fr_command_ace_get_capabilities; + afc->get_packages = fr_command_ace_get_packages; +} + + +static void +fr_command_ace_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = TRUE; + comm->propPassword = FALSE; + comm->propTest = TRUE; +} + + +static void +fr_command_ace_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_ACE (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_ace_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandAceClass), + NULL, + NULL, + (GClassInitFunc) fr_command_ace_class_init, + NULL, + NULL, + sizeof (FrCommandAce), + 0, + (GInstanceInitFunc) fr_command_ace_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandAce", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-ace.h b/src/fr-command-ace.h new file mode 100644 index 0000000..e88586e --- /dev/null +++ b/src/fr-command-ace.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_ACE_H +#define FR_COMMAND_ACE_H + +#include <glib.h> +#include "file-data.h" +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_ACE (fr_command_ace_get_type ()) +#define FR_COMMAND_ACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ACE, FrCommandAce)) +#define FR_COMMAND_ACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ACE, FrCommandAceClass)) +#define FR_IS_COMMAND_ACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ACE)) +#define FR_IS_COMMAND_ACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ACE)) +#define FR_COMMAND_ACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ACE, FrCommandAceClass)) + +typedef enum { + FR_ACE_COMMAND_UNKNOWN = 0, + FR_ACE_COMMAND_PUBLIC, + FR_ACE_COMMAND_NONFREE +} FrAceCommand; + +typedef struct _FrCommandAce FrCommandAce; +typedef struct _FrCommandAceClass FrCommandAceClass; + +struct _FrCommandAce +{ + FrCommand __parent; + + gboolean list_started; + FrAceCommand command_type; +}; + +struct _FrCommandAceClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_ace_get_type (void); + +#endif /* FR_COMMAND_ACE_H */ diff --git a/src/fr-command-alz.c b/src/fr-command-alz.c new file mode 100644 index 0000000..d800e6c --- /dev/null +++ b/src/fr-command-alz.c @@ -0,0 +1,406 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "fr-command.h" +#include "fr-command-alz.h" +#include "glib-utils.h" + +static void fr_command_alz_class_init (FrCommandAlzClass *class); +static void fr_command_alz_init (FrCommand *afile); +static void fr_command_alz_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + + +static time_t +mktime_from_string (char *date_s, + char *time_s) +{ + struct tm tm = {0, }; + char **fields; + + /* date */ + + fields = g_strsplit (date_s, "/", 3); + if (fields[0] != NULL) { + tm.tm_mon = atoi (fields[0]) - 1; + if (fields[1] != NULL) { + tm.tm_mday = atoi (fields[1]); + if (fields[2] != NULL) + tm.tm_year = 100 + atoi (fields[2]); + } + } + g_strfreev (fields); + + /* time */ + + fields = g_strsplit (time_s, ":", 3); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) + tm.tm_min = atoi (fields[1]); + } + g_strfreev (fields); + + return mktime (&tm); +} + + +static void +process_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FrCommandAlz *alz_comm = FR_COMMAND_ALZ (comm); + FileData *fdata; + char **fields; + char *name_field; + char name_last; + gsize name_len; + + g_return_if_fail (line != NULL); + + + if (! alz_comm->list_started) { + if (strncmp (line, "-----", 5 ) == 0 ) + alz_comm->list_started = TRUE; + return; + } + + if (strncmp (line, "-----", 5 ) == 0) { + alz_comm->list_started = FALSE; + return; + + } + + if (! alz_comm->list_started) + return; + + fdata = file_data_new (); + fields = split_line (line, 5); + fdata->modified = mktime_from_string (fields[0], fields[1]); + fdata->size = g_ascii_strtoull (fields[3], NULL, 10); + + name_field = g_strdup (get_last_field (line, 6)); + name_len = strlen (name_field); + + name_last = name_field[name_len - 1]; + fdata->dir = name_last == '\\'; + fdata->encrypted = name_last == '*'; + + if (fdata->dir || fdata->encrypted) + name_field[--name_len] = '\0'; + + if (*name_field == '/') { + fdata->full_path = g_strdup (name_field); + fdata->original_path = fdata->full_path; + } + else { + fdata->full_path = g_strconcat ("/", name_field, NULL); + fdata->original_path = fdata->full_path + 1; + } + + if (fdata->dir) { + char *s; + for (s = fdata->full_path; *s != '\0'; ++s) + if (*s == '\\') *s = '/'; + for (s = fdata->original_path; *s != '\0'; ++s) + if (*s == '\\') *s = '/'; + fdata->name = dir_name_from_path (fdata->full_path); + } + else { + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + } + + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); + + g_free (name_field); + g_strfreev (fields); +} + + +static void +add_codepage_arg (FrCommand *comm) +{ + const char *env_list[] = { "LC_CTYPE", "LC_ALL", "LANG", NULL }; + const char **scan; + const char *arg = "-cp949"; + + for (scan = env_list; *scan != NULL; ++scan) { + char *env = getenv (*scan); + + if (! env) + continue; + + if (strstr (env, "UTF-8") || strstr (env, "utf-8")) + arg = "-utf8"; + else if (strstr (env, "euc") || strstr (env, "EUC")) + arg = "-euc-kr"; + else + continue; + break; + } + + fr_process_add_arg (comm->process, arg); +} + + +static void +add_password_arg (FrCommand *comm, + const char *password, + gboolean disable_query) +{ + if (password != NULL) { + fr_process_add_arg (comm->process, "-pwd"); + fr_process_add_arg (comm->process, password); + } + else if (disable_query) { + fr_process_add_arg (comm->process, "-pwd"); + fr_process_add_arg (comm->process, ""); + } +} + + +static void +list__begin (gpointer data) +{ + FrCommandAlz *comm = data; + + comm->list_started = FALSE; + comm->invalid_password = FALSE; +} + + +static void +fr_command_alz_list (FrCommand *comm) +{ + fr_process_set_out_line_func (FR_COMMAND (comm)->process, process_line, comm); + + fr_process_begin_command (comm->process, "unalz"); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, "-l"); + add_codepage_arg(comm); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_use_standard_locale (comm->process, TRUE); + fr_process_start (comm->process); +} + + +/* -- extract -- */ + +static void +process_extract_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FrCommandAlz *alz_comm = FR_COMMAND_ALZ (comm); + + g_return_if_fail (line != NULL); + + /* - error check - */ + + if (strncmp (line, "err code(28) (invalid password)", 31) == 0) { + alz_comm->invalid_password = TRUE; + fr_process_stop (comm->process); + return; + } + + if (alz_comm->extract_none && (strncmp (line, "unalziiiing :", 13) == 0)) { + alz_comm->extract_none = FALSE; + } + else if ((strncmp (line, "done..", 6) == 0) && alz_comm->extract_none) { + fr_process_stop (comm->process); + return; + } +} + + +static void +fr_command_alz_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + FR_COMMAND_ALZ (comm)->extract_none = TRUE; + + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + process_extract_line, + comm); + + fr_process_begin_command (comm->process, "unalz"); + if (dest_dir != NULL) { + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, dest_dir); + } + add_codepage_arg (comm); + add_password_arg (comm, comm->password, TRUE); + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_alz_handle_error (FrCommand *comm, + FrProcError *error) +{ + if ((error->type == FR_PROC_ERROR_STOPPED)) { + if (FR_COMMAND_ALZ (comm)->extract_none || + FR_COMMAND_ALZ (comm)->invalid_password ) { + error->type = FR_PROC_ERROR_ASK_PASSWORD; + } + } +} + + +const char *alz_mime_type[] = { "application/x-alz", NULL }; + + +const char ** +fr_command_alz_get_mime_types (FrCommand *comm) +{ + return alz_mime_type; +} + + +FrCommandCap +fr_command_alz_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("unalz", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_alz_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("unalz"); +} + + +static void +fr_command_alz_class_init (FrCommandAlzClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_alz_finalize; + + afc->list = fr_command_alz_list; + afc->add = NULL; + afc->delete = NULL; + afc->extract = fr_command_alz_extract; + afc->handle_error = fr_command_alz_handle_error; + afc->get_mime_types = fr_command_alz_get_mime_types; + afc->get_capabilities = fr_command_alz_get_capabilities; + afc->get_packages = fr_command_alz_get_packages; +} + + +static void +fr_command_alz_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = TRUE; + comm->propTest = FALSE; +} + + +static void +fr_command_alz_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_ALZ (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_alz_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandAlzClass), + NULL, + NULL, + (GClassInitFunc) fr_command_alz_class_init, + NULL, + NULL, + sizeof (FrCommandAlz), + 0, + (GInstanceInitFunc) fr_command_alz_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FrCommandAlz", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-alz.h b/src/fr-command-alz.h new file mode 100644 index 0000000..40add85 --- /dev/null +++ b/src/fr-command-alz.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_ALZ_H +#define FR_COMMAND_ALZ_H + +#include <glib.h> +#include "file-data.h" +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_ALZ (fr_command_alz_get_type ()) +#define FR_COMMAND_ALZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ALZ, FrCommandAlz)) +#define FR_COMMAND_ALZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ALZ, FrCommandAlzClass)) +#define FR_IS_COMMAND_ALZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ALZ)) +#define FR_IS_COMMAND_ALZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ALZ)) +#define FR_COMMAND_ALZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ALZ, FrCommandAlzClass)) + +typedef struct _FrCommandAlz FrCommandAlz; +typedef struct _FrCommandAlzClass FrCommandAlzClass; + +struct _FrCommandAlz +{ + FrCommand __parent; + + gboolean extract_none; + gboolean invalid_password; + gboolean list_started; +}; + +struct _FrCommandAlzClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_alz_get_type (void); + +#endif /* FR_COMMAND_ALZ_H */ diff --git a/src/fr-command-ar.c b/src/fr-command-ar.c new file mode 100644 index 0000000..143687e --- /dev/null +++ b/src/fr-command-ar.c @@ -0,0 +1,392 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "fr-command.h" +#include "fr-command-ar.h" + +static void fr_command_ar_class_init (FrCommandArClass *class); +static void fr_command_ar_init (FrCommand *afile); +static void fr_command_ar_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *time_s, + char *day_s, + char *month_s, + char *year_s) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + if (month_s != NULL) { + int i; + for (i = 0; i < 12; i++) + if (strcmp (months[i], month_s) == 0) { + tm.tm_mon = i; + break; + } + } + tm.tm_mday = atoi (day_s); + tm.tm_year = atoi (year_s) - 1900; + + /* time */ + + fields = g_strsplit (time_s, ":", 3); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_min = atoi (fields[1]); + if (fields[2] != NULL) + tm.tm_sec = atoi (fields[2]); + } + } + g_strfreev (fields); + + return mktime (&tm); +} + + +static char* +ar_get_last_field (const char *line, + int start_from, + int field_n) +{ + const char *f_start, *f_end; + + line = line + start_from; + + f_start = line; + while ((*f_start == ' ') && (*f_start != *line)) + f_start++; + f_end = f_start; + + while ((field_n > 0) && (*f_end != 0)) { + if (*f_end == ' ') { + field_n--; + if (field_n != 0) { + while ((*f_end == ' ') && (*f_end != *line)) + f_end++; + f_start = f_end; + } + } else + f_end++; + } + + return g_strdup (f_start); +} + + +static void +process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + int date_idx; + char *field_month, *field_day, *field_time, *field_year; + char *field_size, *field_name; + + g_return_if_fail (line != NULL); + + fdata = file_data_new (); + + date_idx = file_list__get_index_from_pattern (line, "%c%c%c %a%n %n%n:%n%n %n%n%n%n"); + + field_size = file_list__get_prev_field (line, date_idx, 1); + fdata->size = g_ascii_strtoull (field_size, NULL, 10); + g_free (field_size); + + field_month = file_list__get_next_field (line, date_idx, 1); + field_day = file_list__get_next_field (line, date_idx, 2); + field_time = file_list__get_next_field (line, date_idx, 3); + field_year = file_list__get_next_field (line, date_idx, 4); + fdata->modified = mktime_from_string (field_time, field_day, field_month, field_year); + g_free (field_day); + g_free (field_month); + g_free (field_year); + g_free (field_time); + + /* Full path */ + + field_name = ar_get_last_field (line, date_idx, 5); + + fields = g_strsplit (field_name, " -> ", 2); + + if (fields[0] == NULL) { + g_strfreev (fields); + g_free (field_name); + file_data_free (fdata); + return; + } + + if (fields[1] == NULL) { + g_strfreev (fields); + fields = g_strsplit (field_name, " link to ", 2); + } + + if (*(fields[0]) == '/') { + fdata->full_path = g_strdup (fields[0]); + fdata->original_path = fdata->full_path; + } else { + fdata->full_path = g_strconcat ("/", fields[0], NULL); + fdata->original_path = fdata->full_path + 1; + } + + if (fields[1] != NULL) + fdata->link = g_strdup (fields[1]); + g_strfreev (fields); + g_free (field_name); + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_ar_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, process_line, comm); + + fr_process_begin_command (comm->process, "ar"); + fr_process_add_arg (comm->process, "tv"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_ar_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + fr_process_begin_command (comm->process, "ar"); + + if (update) + fr_process_add_arg (comm->process, "ru"); + else + fr_process_add_arg (comm->process, "r"); + + if (base_dir != NULL) + fr_process_set_working_dir (comm->process, base_dir); + + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_ar_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + fr_process_begin_command (comm->process, "ar"); + fr_process_add_arg (comm->process, "d"); + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_ar_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_begin_command (comm->process, "ar"); + + if (dest_dir != NULL) + fr_process_set_working_dir (comm->process, dest_dir); + + fr_process_add_arg (comm->process, "x"); + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_ar_handle_error (FrCommand *comm, + FrProcError *error) +{ + /* FIXME */ +} + + +const char *ar_mime_type[] = { "application/x-ar", + "application/x-deb", + NULL }; + + +const char ** +fr_command_ar_get_mime_types (FrCommand *comm) +{ + return ar_mime_type; +} + + +FrCommandCap +fr_command_ar_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("ar", check_command)) { + if (is_mime_type (mime_type, "application/x-deb")) + capabilities |= FR_COMMAND_CAN_READ; + else if (is_mime_type (mime_type, "application/x-ar")) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + + return capabilities; +} + + +static const char * +fr_command_ar_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("binutils"); +} + + +static void +fr_command_ar_class_init (FrCommandArClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_ar_finalize; + + afc->list = fr_command_ar_list; + afc->add = fr_command_ar_add; + afc->delete = fr_command_ar_delete; + afc->extract = fr_command_ar_extract; + afc->handle_error = fr_command_ar_handle_error; + afc->get_mime_types = fr_command_ar_get_mime_types; + afc->get_capabilities = fr_command_ar_get_capabilities; + afc->get_packages = fr_command_ar_get_packages; +} + + +static void +fr_command_ar_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propAddCanStoreFolders = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +static void +fr_command_ar_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_AR (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_ar_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandArClass), + NULL, + NULL, + (GClassInitFunc) fr_command_ar_class_init, + NULL, + NULL, + sizeof (FrCommandAr), + 0, + (GInstanceInitFunc) fr_command_ar_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandAr", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-ar.h b/src/fr-command-ar.h new file mode 100644 index 0000000..44b724b --- /dev/null +++ b/src/fr-command-ar.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_AR_H +#define FR_COMMAND_AR_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_AR (fr_command_ar_get_type ()) +#define FR_COMMAND_AR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_AR, FrCommandAr)) +#define FR_COMMAND_AR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_AR, FrCommandArClass)) +#define FR_IS_COMMAND_AR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_AR)) +#define FR_IS_COMMAND_AR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_AR)) +#define FR_COMMAND_AR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_AR, FrCommandArClass)) + +typedef struct _FrCommandAr FrCommandAr; +typedef struct _FrCommandArClass FrCommandArClass; + +struct _FrCommandAr +{ + FrCommand __parent; +}; + +struct _FrCommandArClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_ar_get_type (void); + +#endif /* FR_COMMAND_AR_H */ diff --git a/src/fr-command-arj.c b/src/fr-command-arj.c new file mode 100644 index 0000000..ee214c0 --- /dev/null +++ b/src/fr-command-arj.c @@ -0,0 +1,438 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-arj.h" + +static void fr_command_arj_class_init (FrCommandArjClass *class); +static void fr_command_arj_init (FrCommand *afile); +static void fr_command_arj_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *date_s, + char *time_s) +{ + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + fields = g_strsplit (date_s, "-", 3); + if (fields[0] != NULL) { + /* warning : this will work until 2075 ;) */ + int y = atoi (fields[0]); + if (y >= 75) + tm.tm_year = y; + else + tm.tm_year = 100 + y; + + tm.tm_mon = atoi (fields[1]) - 1; + tm.tm_mday = atoi (fields[2]); + } + g_strfreev (fields); + + /* time */ + + fields = g_strsplit (time_s, ":", 3); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_min = atoi (fields[1]); + if (fields[2] != NULL) + tm.tm_sec = atoi (fields[2]); + } + } + g_strfreev (fields); + + return mktime (&tm); +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FrCommandArj *arj_comm = FR_COMMAND_ARJ (comm); + + g_return_if_fail (line != NULL); + + if (! arj_comm->list_started) { + if (strncmp (line, "--------", 8) == 0) { + arj_comm->list_started = TRUE; + arj_comm->line_no = 1; + } + return; + } + + if (strncmp (line, "--------", 8) == 0) { + arj_comm->list_started = FALSE; + return; + } + + if (g_regex_match (arj_comm->filename_line_regex, line, 0, NULL)) { /* Read the filename. */ + FileData *fdata; + const char *name_field; + + arj_comm->line_no = 1; + + arj_comm->fdata = fdata = file_data_new (); + + name_field = get_last_field (line, 2); + + if (*name_field == '/') { + fdata->full_path = g_strdup (name_field); + fdata->original_path = fdata->full_path; + } + else { + fdata->full_path = g_strconcat ("/", name_field, NULL); + fdata->original_path = fdata->full_path + 1; + } + + fdata->link = NULL; + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + } + else if (arj_comm->line_no == 2) { /* Read file size and date. */ + FileData *fdata; + char **fields; + + fdata = arj_comm->fdata; + + /* read file info. */ + + fields = split_line (line, 10); + fdata->size = g_ascii_strtoull (fields[2], NULL, 10); + fdata->modified = mktime_from_string (fields[5], fields[6]); + if ((strcmp (fields[1], "MS-DOS") == 0) || (strcmp (fields[1], "WIN32") == 0)) + fdata->encrypted = (g_ascii_strcasecmp (fields[7], "11") == 0); + else + fdata->encrypted = (g_ascii_strcasecmp (fields[9], "11") == 0); + g_strfreev (fields); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); + arj_comm->fdata = NULL; + } + + arj_comm->line_no++; +} + + +static void +fr_command_arj_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, list__process_line, comm); + + fr_process_begin_command (comm->process, "arj"); + fr_process_add_arg (comm->process, "v"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_arj_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + fr_process_begin_command (comm->process, "arj"); + + fr_process_add_arg (comm->process, "a"); + + if (base_dir != NULL) + fr_process_set_working_dir (comm->process, base_dir); + + if (update) + fr_process_add_arg (comm->process, "-u"); + + if (comm->password != NULL) + fr_process_add_arg_concat (comm->process, "-g/", comm->password, NULL); + + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-m3"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-m2"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-m1"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-m1"); break; + } + + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-"); + + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, (gchar*) scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_arj_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + fr_process_begin_command (comm->process, "arj"); + fr_process_add_arg (comm->process, "d"); + + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-"); + + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_arj_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_begin_command (comm->process, "arj"); + + if (junk_paths) + fr_process_add_arg (comm->process, "e"); + else + fr_process_add_arg (comm->process, "x"); + + if (dest_dir != NULL) + fr_process_add_arg_concat (comm->process, "-ht/", dest_dir, NULL); + + if (! overwrite) + fr_process_add_arg (comm->process, "-n"); + + if (skip_older) + fr_process_add_arg (comm->process, "-u"); + + if (comm->password != NULL) + fr_process_add_arg_concat (comm->process, "-g/", comm->password, NULL); + else + fr_process_add_arg (comm->process, "-g/"); + + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-"); + + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_arj_test (FrCommand *comm) +{ + fr_process_begin_command (comm->process, "arj"); + fr_process_add_arg (comm->process, "t"); + if (comm->password != NULL) + fr_process_add_arg_concat (comm->process, "-g/", comm->password, NULL); + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +static void +fr_command_arj_handle_error (FrCommand *comm, + FrProcError *error) +{ + if (error->type != FR_PROC_ERROR_NONE) { + if (error->status <= 1) + error->type = FR_PROC_ERROR_NONE; + else if (error->status == 3) + error->type = FR_PROC_ERROR_ASK_PASSWORD; + } +} + + +const char *arj_mime_type[] = { "application/x-arj", NULL }; + + +const char ** +fr_command_arj_get_mime_types (FrCommand *comm) +{ + return arj_mime_type; +} + + +FrCommandCap +fr_command_arj_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT; + if (is_program_available ("arj", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + + return capabilities; +} + + +static const char * +fr_command_arj_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("arj"); +} + + +static void +fr_command_arj_class_init (FrCommandArjClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_arj_finalize; + + afc->list = fr_command_arj_list; + afc->add = fr_command_arj_add; + afc->delete = fr_command_arj_delete; + afc->extract = fr_command_arj_extract; + afc->test = fr_command_arj_test; + afc->handle_error = fr_command_arj_handle_error; + afc->get_mime_types = fr_command_arj_get_mime_types; + afc->get_capabilities = fr_command_arj_get_capabilities; + afc->get_packages = fr_command_arj_get_packages; +} + + +static void +fr_command_arj_init (FrCommand *comm) +{ + FrCommandArj *arj_comm; + + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propAddCanStoreFolders = FALSE; + comm->propExtractCanAvoidOverwrite = TRUE; + comm->propExtractCanSkipOlder = TRUE; + comm->propExtractCanJunkPaths = TRUE; + comm->propPassword = TRUE; + comm->propTest = TRUE; + + arj_comm = FR_COMMAND_ARJ (comm); + arj_comm->list_started = FALSE; + arj_comm->fdata = FALSE; + arj_comm->filename_line_regex = g_regex_new ("[0-9]+\\) ", G_REGEX_OPTIMIZE, 0, NULL); +} + + +static void +fr_command_arj_finalize (GObject *object) +{ + FrCommandArj *arj_comm; + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_ARJ (object)); + + arj_comm = FR_COMMAND_ARJ (object); + g_regex_unref (arj_comm->filename_line_regex); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_arj_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandArjClass), + NULL, + NULL, + (GClassInitFunc) fr_command_arj_class_init, + NULL, + NULL, + sizeof (FrCommandArj), + 0, + (GInstanceInitFunc) fr_command_arj_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandArj", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-arj.h b/src/fr-command-arj.h new file mode 100644 index 0000000..b1b860c --- /dev/null +++ b/src/fr-command-arj.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_ARJ_H +#define FR_COMMAND_ARJ_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_ARJ (fr_command_arj_get_type ()) +#define FR_COMMAND_ARJ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ARJ, FrCommandArj)) +#define FR_COMMAND_ARJ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ARJ, FrCommandArjClass)) +#define FR_IS_COMMAND_ARJ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ARJ)) +#define FR_IS_COMMAND_ARJ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ARJ)) +#define FR_COMMAND_ARJ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ARJ, FrCommandArjClass)) + +typedef struct _FrCommandArj FrCommandArj; +typedef struct _FrCommandArjClass FrCommandArjClass; + +struct _FrCommandArj +{ + FrCommand __parent; + + gboolean list_started; + int line_no; + FileData *fdata; + GRegex *filename_line_regex; +}; + +struct _FrCommandArjClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_arj_get_type (void); + +#endif /* FR_COMMAND_ARJ_H */ diff --git a/src/fr-command-cfile.c b/src/fr-command-cfile.c new file mode 100644 index 0000000..d986575 --- /dev/null +++ b/src/fr-command-cfile.c @@ -0,0 +1,619 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-cfile.h" + + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +static char * +get_uncompressed_name_from_archive (FrCommand *comm, + const char *archive) +{ + GFile *file; + GInputStream *stream; + char *filename = NULL; + + if (! is_mime_type (comm->mime_type, "application/x-gzip")) + return NULL; + + file = g_file_new_for_path (archive); + + stream = (GInputStream *) g_file_read (file, NULL, NULL); + if (stream != NULL) { + gboolean filename_present = TRUE; + char buffer[10]; + + if (g_input_stream_read (stream, buffer, 10, NULL, NULL) >= 0) { + /* Check whether the FLG.FNAME is set */ + if (((unsigned char)(buffer[3]) & 0x08) != 0x08) + filename_present = FALSE; + + /* Check whether the FLG.FEXTRA is set */ + if (((unsigned char)(buffer[3]) & 0x04) == 0x04) + filename_present = FALSE; + } + + if (filename_present) { + GString *str = NULL; + + str = g_string_new (""); + while (g_input_stream_read (stream, buffer, 1, NULL, NULL) > 0) { + if (buffer[0] == '\0') { + filename = g_strdup (file_name_from_path (str->str)); +#ifdef DEBUG + g_message ("filename is: %s", filename); +#endif + break; + } + g_string_append_c (str, buffer[0]); + } + g_string_free (str, TRUE); + } + g_object_unref (stream); + } + g_object_unref (file); + + return filename; +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FileData *fdata; + char **fields; + char *filename; + + fdata = file_data_new (); + + fields = split_line (line, 2); + if (strcmp (fields[1], "-1") != 0) + fdata->size = g_ascii_strtoull (fields[1], NULL, 10); + g_strfreev (fields); + + if (fdata->size == 0) + fdata->size = get_file_size (comm->filename); + + filename = get_uncompressed_name_from_archive (comm, comm->filename); + if (filename == NULL) + filename = remove_extension_from_path (comm->filename); + + fdata->full_path = g_strconcat ("/", + file_name_from_path (filename), + NULL); + g_free (filename); + + fdata->original_path = fdata->full_path + 1; + fdata->link = NULL; + fdata->modified = get_file_mtime_for_path (comm->filename); + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_cfile_list (FrCommand *comm) +{ + FrCommandCFile *comm_cfile = FR_COMMAND_CFILE (comm); + + if (is_mime_type (comm->mime_type, "application/x-gzip")) { + /* gzip let us known the uncompressed size */ + + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + list__process_line, + comm); + + fr_process_begin_command (comm->process, "gzip"); + fr_process_add_arg (comm->process, "-l"); + fr_process_add_arg (comm->process, "-q"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); + } + else { + /* ... other compressors do not support this feature so + * simply use the archive size, suboptimal but there is no + * alternative. */ + + FileData *fdata; + char *filename; + + fdata = file_data_new (); + + filename = remove_extension_from_path (comm->filename); + fdata->full_path = g_strconcat ("/", + file_name_from_path (filename), + NULL); + g_free (filename); + + fdata->original_path = fdata->full_path + 1; + fdata->link = NULL; + fdata->size = get_file_size_for_path (comm->filename); + fdata->modified = get_file_mtime_for_path (comm->filename); + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); + + comm_cfile->error.type = FR_PROC_ERROR_NONE; + comm_cfile->error.status = 0; + g_signal_emit_by_name (G_OBJECT (comm), + "done", + comm->action, + &comm_cfile->error); + } +} + + +static void +fr_command_cfile_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + const char *filename; + char *temp_dir; + char *temp_file; + char *compressed_filename; + + if ((file_list == NULL) || (file_list->data == NULL)) + return; + + /* copy file to the temp dir */ + + temp_dir = get_temp_work_dir (NULL); + filename = file_list->data; + temp_file = g_strconcat (temp_dir, "/", filename, NULL); + + fr_process_begin_command (comm->process, "cp"); + fr_process_set_working_dir (comm->process, base_dir); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + + /**/ + + if (is_mime_type (comm->mime_type, "application/x-gzip")) { + fr_process_begin_command (comm->process, "gzip"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".gz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-bzip")) { + fr_process_begin_command (comm->process, "bzip2"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".bz2", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-compress")) { + fr_process_begin_command (comm->process, "compress"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".Z", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lzip")) { + fr_process_begin_command (comm->process, "lzip"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".lz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lzma")) { + fr_process_begin_command (comm->process, "lzma"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".lzma", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-xz")) { + fr_process_begin_command (comm->process, "xz"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".xz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lzop")) { + fr_process_begin_command (comm->process, "lzop"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "-fU"); + fr_process_add_arg (comm->process, "--no-stdin"); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".lzo", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-rzip")) { + fr_process_begin_command (comm->process, "rzip"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + compressed_filename = g_strconcat (filename, ".rz", NULL); + } + + /* copy compressed file to the dest dir */ + + fr_process_begin_command (comm->process, "cp"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, compressed_filename); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + + /* remove the temp dir */ + + fr_process_begin_command (comm->process, "rm"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_add_arg (comm->process, "-rf"); + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, temp_dir); + fr_process_end_command (comm->process); + + g_free (compressed_filename); + g_free (temp_file); + g_free (temp_dir); +} + + +static void +fr_command_cfile_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + /* never called */ +} + + +static void +fr_command_cfile_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + char *temp_dir; + char *dest_file; + char *temp_file; + char *uncompr_file; + char *compr_file; + + /* copy file to the temp dir, remove the already existing file first */ + + temp_dir = get_temp_work_dir (NULL); + temp_file = g_strconcat (temp_dir, + "/", + file_name_from_path (comm->filename), + NULL); + + fr_process_begin_command (comm->process, "cp"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + + /* uncompress the file */ + + if (is_mime_type (comm->mime_type, "application/x-gzip")) { + fr_process_begin_command (comm->process, "gzip"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, "-n"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-bzip")) { + fr_process_begin_command (comm->process, "bzip2"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-compress")) { + if (is_program_in_path ("gzip")) { + fr_process_begin_command (comm->process, "gzip"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, "-n"); + } + else + fr_process_begin_command (comm->process, "uncompress"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lzip")) { + fr_process_begin_command (comm->process, "lzip"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lzma")) { + fr_process_begin_command (comm->process, "lzma"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-xz")) { + fr_process_begin_command (comm->process, "xz"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lzop")) { + fr_process_begin_command (comm->process, "lzop"); + fr_process_set_working_dir (comm->process, temp_dir); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, "-fU"); + fr_process_add_arg (comm->process, "--no-stdin"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-rzip")) { + fr_process_begin_command (comm->process, "rzip"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, temp_file); + fr_process_end_command (comm->process); + } + + /* copy uncompress file to the dest dir */ + + uncompr_file = remove_extension_from_path (temp_file); + + compr_file = get_uncompressed_name_from_archive (comm, comm->filename); + if (compr_file == NULL) + compr_file = remove_extension_from_path (file_name_from_path (comm->filename)); + dest_file = g_strconcat (dest_dir, + "/", + compr_file, + NULL); + + fr_process_begin_command (comm->process, "cp"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, uncompr_file); + fr_process_add_arg (comm->process, dest_file); + fr_process_end_command (comm->process); + + /* remove the temp dir */ + + fr_process_begin_command (comm->process, "rm"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_add_arg (comm->process, "-rf"); + fr_process_add_arg (comm->process, temp_dir); + fr_process_end_command (comm->process); + + g_free (dest_file); + g_free (compr_file); + g_free (uncompr_file); + g_free (temp_file); + g_free (temp_dir); +} + + +const char *cfile_mime_type[] = { "application/x-gzip", + "application/x-bzip", + "application/x-compress", + "application/x-lzip", + "application/x-lzma", + "application/x-lzop", + "application/x-rzip", + "application/x-xz", + NULL }; + + +const char ** +fr_command_cfile_get_mime_types (FrCommand *comm) +{ + return cfile_mime_type; +} + + +FrCommandCap +fr_command_cfile_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_DO_NOTHING; + if (is_mime_type (mime_type, "application/x-gzip")) { + if (is_program_available ("gzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-bzip")) { + if (is_program_available ("bzip2", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-compress")) { + if (is_program_available ("compress", check_command)) + capabilities |= FR_COMMAND_CAN_WRITE; + if (is_program_available ("uncompress", check_command) || is_program_available ("gzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + } + else if (is_mime_type (mime_type, "application/x-lzip")) { + if (is_program_available ("lzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-lzma")) { + if (is_program_available ("lzma", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-xz")) { + if (is_program_available ("xz", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-lzop")) { + if (is_program_available ("lzop", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-rzip")) { + if (is_program_available ("rzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + + return capabilities; +} + + +static void +fr_command_cfile_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_CFILE (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static const char * +fr_command_cfile_get_packages (FrCommand *comm, + const char *mime_type) +{ + if (is_mime_type (mime_type, "application/x-gzip")) + return PACKAGES ("gzip"); + else if (is_mime_type (mime_type, "application/x-bzip")) + return PACKAGES ("bzip2"); + else if (is_mime_type (mime_type, "application/x-compress")) + return PACKAGES ("ncompress"); + else if (is_mime_type (mime_type, "application/x-lzip")) + return PACKAGES ("lzip"); + else if (is_mime_type (mime_type, "application/x-lzma")) + return PACKAGES ("lzma"); + else if (is_mime_type (mime_type, "application/x-xz")) + return PACKAGES ("xz"); + else if (is_mime_type (mime_type, "application/x-lzop")) + return PACKAGES ("lzop"); + else if (is_mime_type (mime_type, "application/x-rzip")) + return PACKAGES ("rzip"); + + return NULL; +} + + +static void +fr_command_cfile_class_init (FrCommandCFileClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_cfile_finalize; + + afc->list = fr_command_cfile_list; + afc->add = fr_command_cfile_add; + afc->delete = fr_command_cfile_delete; + afc->extract = fr_command_cfile_extract; + afc->get_mime_types = fr_command_cfile_get_mime_types; + afc->get_capabilities = fr_command_cfile_get_capabilities; + afc->get_packages = fr_command_cfile_get_packages; +} + + +static void +fr_command_cfile_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +GType +fr_command_cfile_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandCFileClass), + NULL, + NULL, + (GClassInitFunc) fr_command_cfile_class_init, + NULL, + NULL, + sizeof (FrCommandCFile), + 0, + (GInstanceInitFunc) fr_command_cfile_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandCFile", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-cfile.h b/src/fr-command-cfile.h new file mode 100644 index 0000000..d4e251c --- /dev/null +++ b/src/fr-command-cfile.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_CFILE_H +#define FR_COMMAND_CFILE_H + +#include <gtk/gtk.h> +#include "fr-command.h" +#include "typedefs.h" + +#define FR_TYPE_COMMAND_CFILE (fr_command_cfile_get_type ()) +#define FR_COMMAND_CFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_CFILE, FrCommandCFile)) +#define FR_COMMAND_CFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_CFILE, FrCommandCFileClass)) +#define FR_IS_COMMAND_CFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_CFILE)) +#define FR_IS_COMMAND_CFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_CFILE)) +#define FR_COMMAND_CFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_CFILE, FrCommandCFileClass)) + +typedef struct _FrCommandCFile FrCommandCFile; +typedef struct _FrCommandCFileClass FrCommandCFileClass; + +struct _FrCommandCFile +{ + FrCommand __parent; + + /*<private>*/ + + FrProcError error; +}; + +struct _FrCommandCFileClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_cfile_get_type (void); + +#endif /* FR_COMMAND_CFILE_H */ diff --git a/src/fr-command-cpio.c b/src/fr-command-cpio.c new file mode 100644 index 0000000..feeaad7 --- /dev/null +++ b/src/fr-command-cpio.c @@ -0,0 +1,329 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-cpio.h" + +static void fr_command_cpio_class_init (FrCommandCpioClass *class); +static void fr_command_cpio_init (FrCommand *afile); +static void fr_command_cpio_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *month, + char *mday, + char *year) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + struct tm tm = {0, }; + + tm.tm_isdst = -1; + + if (month != NULL) { + int i; + for (i = 0; i < 12; i++) + if (strcmp (months[i], month) == 0) { + tm.tm_mon = i; + break; + } + } + tm.tm_mday = atoi (mday); + if (strchr (year, ':') != NULL) { + char **fields = g_strsplit (year, ":", 2); + if (n_fields (fields) == 2) { + time_t now; + struct tm *now_tm; + + tm.tm_hour = atoi (fields[0]); + tm.tm_min = atoi (fields[1]); + + now = time(NULL); + now_tm = localtime (&now); + tm.tm_year = now_tm->tm_year; + } + } else + tm.tm_year = atoi (year) - 1900; + + return mktime (&tm); +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + const char *name_field; + char *name; + int ofs = 0; + + g_return_if_fail (line != NULL); + + fdata = file_data_new (); + +#ifdef __sun + fields = split_line (line, 9); + fdata->size = g_ascii_strtoull (fields[4], NULL, 10); + fdata->modified = mktime_from_string (fields[5], fields[6], fields[8]); + g_strfreev (fields); + + name_field = get_last_field (line, 10); +#else /* !__sun */ + /* Handle char and block device files */ + if ((line[0] == 'c') || (line[0] == 'b')) { + fields = split_line (line, 9); + ofs = 1; + fdata->size = 0; + /* FIXME: We should also specify the content type */ + } + else { + fields = split_line (line, 8); + fdata->size = g_ascii_strtoull (fields[4], NULL, 10); + } + fdata->modified = mktime_from_string (fields[5+ofs], fields[6+ofs], fields[7+ofs]); + g_strfreev (fields); + + name_field = get_last_field (line, 9+ofs); +#endif /* !__sun */ + + fields = g_strsplit (name_field, " -> ", 2); + + if (fields[1] == NULL) { + g_strfreev (fields); + fields = g_strsplit (name_field, " link to ", 2); + } + + fdata->dir = line[0] == 'd'; + + name = g_strcompress (fields[0]); + if (*(fields[0]) == '/') { + fdata->full_path = g_strdup (name); + fdata->original_path = fdata->full_path; + } + else { + fdata->full_path = g_strconcat ("/", name, NULL); + fdata->original_path = fdata->full_path + 1; + } + + if (fdata->dir && (name[strlen (name) - 1] != '/')) { + char *old_full_path = fdata->full_path; + fdata->full_path = g_strconcat (old_full_path, "/", NULL); + g_free (old_full_path); + fdata->original_path = g_strdup (name); + fdata->free_original_path = TRUE; + } + g_free (name); + + if (fields[1] != NULL) + fdata->link = g_strcompress (fields[1]); + g_strfreev (fields); + + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_cpio_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, list__process_line, comm); + + fr_process_begin_command (comm->process, "sh"); + fr_process_add_arg (comm->process, "-c"); + fr_process_add_arg_concat (comm->process, "cpio -itv < ", comm->e_filename, NULL); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_cpio_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + GString *cmd; + + fr_process_begin_command (comm->process, "sh"); + if (dest_dir != NULL) + fr_process_set_working_dir (comm->process, dest_dir); + fr_process_add_arg (comm->process, "-c"); + + cmd = g_string_new ("cpio -idu --no-absolute-filenames "); + for (scan = file_list; scan; scan = scan->next) { + char *filepath = scan->data; + char *filename; + + if (filepath[0] == '/') + filename = g_shell_quote (filepath + 1); + else + filename = g_shell_quote (filepath); + g_string_append (cmd, filename); + g_string_append (cmd, " "); + + g_free (filename); + } + g_string_append (cmd, " < "); + g_string_append (cmd, comm->e_filename); + fr_process_add_arg (comm->process, cmd->str); + g_string_free (cmd, TRUE); + + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +const char *cpio_mime_type[] = { "application/x-cpio", NULL }; + + +const char ** +fr_command_cpio_get_mime_types (FrCommand *comm) +{ + return cpio_mime_type; +} + + +FrCommandCap +fr_command_cpio_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("cpio", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_cpio_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("cpio"); +} + + +static void +fr_command_cpio_class_init (FrCommandCpioClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_cpio_finalize; + + afc->list = fr_command_cpio_list; + afc->extract = fr_command_cpio_extract; + afc->get_mime_types = fr_command_cpio_get_mime_types; + afc->get_capabilities = fr_command_cpio_get_capabilities; + afc->get_packages = fr_command_cpio_get_packages; +} + + +static void +fr_command_cpio_init (FrCommand *comm) +{ + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propAddCanStoreFolders = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +static void +fr_command_cpio_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_CPIO (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_cpio_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandCpioClass), + NULL, + NULL, + (GClassInitFunc) fr_command_cpio_class_init, + NULL, + NULL, + sizeof (FrCommandCpio), + 0, + (GInstanceInitFunc) fr_command_cpio_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandCpio", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-cpio.h b/src/fr-command-cpio.h new file mode 100644 index 0000000..f94159d --- /dev/null +++ b/src/fr-command-cpio.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_CPIO_H +#define FR_COMMAND_CPIO_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_CPIO (fr_command_cpio_get_type ()) +#define FR_COMMAND_CPIO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_CPIO, FrCommandCpio)) +#define FR_COMMAND_CPIO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_CPIO, FrCommandCpioClass)) +#define FR_IS_COMMAND_CPIO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_CPIO)) +#define FR_IS_COMMAND_CPIO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_CPIO)) +#define FR_COMMAND_CPIO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_CPIO, FrCommandCpioClass)) + +typedef struct _FrCommandCpio FrCommandCpio; +typedef struct _FrCommandCpioClass FrCommandCpioClass; + +struct _FrCommandCpio +{ + FrCommand __parent; + gboolean is_empty; +}; + +struct _FrCommandCpioClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_cpio_get_type (void); + +#endif /* FR_COMMAND_CPIO_H */ diff --git a/src/fr-command-dpkg.c b/src/fr-command-dpkg.c new file mode 100644 index 0000000..8c2c012 --- /dev/null +++ b/src/fr-command-dpkg.c @@ -0,0 +1,314 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-dpkg.h" + +static void fr_command_dpkg_class_init (FrCommandDpkgClass *class); +static void fr_command_dpkg_init (FrCommand *afile); +static void fr_command_dpkg_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + +static void +process_metadata_line (char *line, + FrCommand *comm) +{ + FileData *fdata; + char **fields; + char *name; + + g_return_if_fail (line != NULL); + + fields = split_line (line, 6); + if (!fields[1] || !g_str_equal (fields[1], "bytes,")) { + g_strfreev (fields); + return; + } + + fdata = file_data_new (); + fdata->size = g_ascii_strtoull (fields[0], NULL, 10); + + if (fields[5] && g_str_equal (fields[4],"*")) { + name = g_strdup (fields[5]); + } else { + name = g_strdup (get_last_field (line, 5)); + } + g_strstrip (name); + + fdata->full_path = g_strconcat ("/DEBIAN/", name, NULL); + fdata->original_path = fdata->full_path + 1; + + g_strfreev (fields); + g_free (name); + + fdata->name = g_strdup (name); + fdata->path = g_strdup ("DEBIAN"); + fr_command_add_file (comm, fdata); +} + +static void +process_data_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + char **tmfields; + struct tm tm = {0, }; + const char *name; + + g_return_if_fail (line != NULL); + + if (line[0] == ' ') { + /* This is the output of dpkg-deb -I */ + process_metadata_line (line, comm); + return; + } + + fdata = file_data_new (); + + fields = split_line (line, 5); + fdata->size = g_ascii_strtoull (fields[2], NULL, 10); + tmfields = g_strsplit(fields[3], "-", 3); + if (tmfields[2]) { + tm.tm_year = atoi (tmfields[0]) - 1900; + tm.tm_mon = atoi (tmfields[1]); + tm.tm_mday = atoi (tmfields[2]); + } + g_strfreev (tmfields); + tmfields = g_strsplit (fields[4], ":", 2); + if (tmfields[1]) { + tm.tm_hour = atoi (tmfields[0]); + tm.tm_min = atoi (tmfields[1]); + } + g_strfreev (tmfields); + fdata->modified = mktime (&tm); + g_strfreev (fields); + + name = get_last_field (line, 6); + fields = g_strsplit (name, " -> ", 2); + + fdata->dir = line[0] == 'd'; + name = fields[0]; + if (g_str_has_prefix (name, "./")) { /* Should generally be the case */ + fdata->full_path = g_strdup (name + 1); + fdata->original_path = fdata->full_path + 1; + } else if (name[0] == '/') { + fdata->full_path = g_strdup (name); + fdata->original_path = fdata->full_path; + } else { + fdata->full_path = g_strconcat ("/", name, NULL); + fdata->original_path = fdata->full_path + 1; + } + if (fdata->dir && (name[strlen (name) - 1] != '/')) { + char *old_full_path = fdata->full_path; + fdata->full_path = g_strconcat (old_full_path, "/", NULL); + g_free (old_full_path); + fdata->original_path = g_strdup (name); + fdata->free_original_path = TRUE; + } + + if (fields[1] != NULL) + fdata->link = g_strdup (fields[1]); + g_strfreev (fields); + + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_dpkg_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, process_data_line, comm); + + fr_process_begin_command (comm->process, "dpkg-deb"); + fr_process_add_arg (comm->process, "-I"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); + + fr_process_begin_command (comm->process, "dpkg-deb"); + fr_process_add_arg (comm->process, "-c"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_dpkg_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + fr_process_begin_command (comm->process, "dpkg-deb"); + fr_process_add_arg (comm->process, "-x"); + fr_process_add_arg (comm->process, comm->filename); + if (dest_dir != NULL) { + fr_process_add_arg (comm->process, dest_dir); + } else { + fr_process_add_arg (comm->process, "."); + } + /* FIXME it is not possible to unpack only some files */ + fr_process_end_command (comm->process); + + /* Also extract metadata in DEBIAN/ */ + fr_process_begin_command (comm->process, "dpkg-deb"); + if (dest_dir != NULL) { + fr_process_set_working_dir (comm->process, dest_dir); + } + fr_process_add_arg (comm->process, "-e"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + + fr_process_start (comm->process); +} + + +const char *dpkg_mime_type[] = { "application/x-deb", NULL }; + + +const char ** +fr_command_dpkg_get_mime_types (FrCommand *comm) +{ + return dpkg_mime_type; +} + + +FrCommandCap +fr_command_dpkg_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("dpkg-deb", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_dpkg_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("dpkg"); +} + + +static void +fr_command_dpkg_class_init (FrCommandDpkgClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_dpkg_finalize; + + afc->list = fr_command_dpkg_list; + afc->extract = fr_command_dpkg_extract; + afc->get_mime_types = fr_command_dpkg_get_mime_types; + afc->get_capabilities = fr_command_dpkg_get_capabilities; + afc->get_packages = fr_command_dpkg_get_packages; +} + + +static void +fr_command_dpkg_init (FrCommand *comm) +{ + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +static void +fr_command_dpkg_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_DPKG (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_dpkg_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandDpkgClass), + NULL, + NULL, + (GClassInitFunc) fr_command_dpkg_class_init, + NULL, + NULL, + sizeof (FrCommandDpkg), + 0, + (GInstanceInitFunc) fr_command_dpkg_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandDpkg", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-dpkg.h b/src/fr-command-dpkg.h new file mode 100644 index 0000000..802eb54 --- /dev/null +++ b/src/fr-command-dpkg.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_DPKG_H +#define FR_COMMAND_DPKG_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_DPKG (fr_command_dpkg_get_type ()) +#define FR_COMMAND_DPKG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_DPKG, FrCommandDpkg)) +#define FR_COMMAND_DPKG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_DPKG, FrCommandDpkgClass)) +#define FR_IS_COMMAND_DPKG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_DPKG)) +#define FR_IS_COMMAND_DPKG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_DPKG)) +#define FR_COMMAND_DPKG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_DPKG, FrCommandDpkgClass)) + +typedef struct _FrCommandDpkg FrCommandDpkg; +typedef struct _FrCommandDpkgClass FrCommandDpkgClass; + +struct _FrCommandDpkg +{ + FrCommand __parent; + gboolean is_empty; +}; + +struct _FrCommandDpkgClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_dpkg_get_type (void); + +#endif /* FR_COMMAND_DPKG_H */ diff --git a/src/fr-command-iso.c b/src/fr-command-iso.c new file mode 100644 index 0000000..011c734 --- /dev/null +++ b/src/fr-command-iso.c @@ -0,0 +1,319 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2004 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-iso.h" + +static void fr_command_iso_class_init (FrCommandIsoClass *class); +static void fr_command_iso_init (FrCommand *afile); +static void fr_command_iso_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +static time_t +mktime_from_string (char *month, + char *mday, + char *year) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + struct tm tm = {0, }; + + tm.tm_isdst = -1; + + if (month != NULL) { + int i; + for (i = 0; i < 12; i++) + if (strcmp (months[i], month) == 0) { + tm.tm_mon = i; + break; + } + } + tm.tm_mday = atoi (mday); + tm.tm_year = atoi (year) - 1900; + + return mktime (&tm); +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + FrCommandIso *comm_iso = FR_COMMAND_ISO (comm); + char **fields; + const char *name_field; + + g_return_if_fail (line != NULL); + + if (line[0] == 'd') /* Ignore directories. */ + return; + + if (line[0] == 'D') { + g_free (comm_iso->cur_path); + comm_iso->cur_path = g_strdup (get_last_field (line, 4)); + + } else if (line[0] == '-') { /* Is file */ + const char *last_field, *first_bracket; + + fdata = file_data_new (); + + fields = split_line (line, 8); + fdata->size = g_ascii_strtoull (fields[4], NULL, 10); + fdata->modified = mktime_from_string (fields[5], fields[6], fields[7]); + g_strfreev (fields); + + /* Full path */ + + last_field = get_last_field (line, 9); + first_bracket = strchr (last_field, ']'); + if (first_bracket == NULL) { + file_data_free (fdata); + return; + } + + name_field = eat_spaces (first_bracket + 1); + if ((name_field == NULL) + || (strcmp (name_field, ".") == 0) + || (strcmp (name_field, "..") == 0)) { + file_data_free (fdata); + return; + } + + if (comm_iso->cur_path[0] != '/') + fdata->full_path = g_strstrip (g_strconcat ("/", comm_iso->cur_path, name_field, NULL)); + else + fdata->full_path = g_strstrip (g_strconcat (comm_iso->cur_path, name_field, NULL)); + fdata->original_path = fdata->full_path; + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + fr_command_add_file (comm, fdata); + } +} + + +static void +list__begin (gpointer data) +{ + FrCommandIso *comm = data; + + g_free (comm->cur_path); + comm->cur_path = NULL; +} + + +static void +fr_command_iso_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, list__process_line, comm); + + fr_process_begin_command (comm->process, "sh"); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, SHDIR "isoinfo.sh"); + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_add_arg (comm->process, "-l"); + fr_process_end_command (comm->process); + + fr_process_start (comm->process); +} + + +static void +fr_command_iso_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + for (scan = file_list; scan; scan = scan->next) { + char *path = scan->data; + const char *filename; + char *file_dir; + char *temp_dest_dir = NULL; + + filename = file_name_from_path (path); + file_dir = remove_level_from_path (path); + if ((file_dir != NULL) && (strcmp (file_dir, "/") != 0)) + temp_dest_dir = g_build_filename (dest_dir, file_dir, NULL); + else + temp_dest_dir = g_strdup (dest_dir); + g_free (file_dir); + + if (temp_dest_dir == NULL) + continue; + + make_directory_tree_from_path (temp_dest_dir, 0700, NULL); + + fr_process_begin_command (comm->process, "sh"); + fr_process_set_working_dir (comm->process, temp_dest_dir); + fr_process_add_arg (comm->process, SHDIR "isoinfo.sh"); + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_add_arg (comm->process, "-x"); + fr_process_add_arg (comm->process, path); + fr_process_add_arg (comm->process, filename); + fr_process_end_command (comm->process); + + g_free (temp_dest_dir); + } + + fr_process_start (comm->process); +} + + +const char *iso_mime_type[] = { "application/x-cd-image", NULL }; + + +const char ** +fr_command_iso_get_mime_types (FrCommand *comm) +{ + return iso_mime_type; +} + + +FrCommandCap +fr_command_iso_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("isoinfo", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_iso_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("genisoimage"); +} + + +static void +fr_command_iso_class_init (FrCommandIsoClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_iso_finalize; + + afc->list = fr_command_iso_list; + afc->extract = fr_command_iso_extract; + afc->get_mime_types = fr_command_iso_get_mime_types; + afc->get_capabilities = fr_command_iso_get_capabilities; + afc->get_packages = fr_command_iso_get_packages; +} + + +static void +fr_command_iso_init (FrCommand *comm) +{ + FrCommandIso *comm_iso = FR_COMMAND_ISO (comm); + + comm_iso->cur_path = NULL; + comm_iso->joliet = TRUE; + + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; + comm->propCanExtractAll = FALSE; +} + + +static void +fr_command_iso_finalize (GObject *object) +{ + FrCommandIso *comm_iso; + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_ISO (object)); + + comm_iso = FR_COMMAND_ISO (object); + + g_free (comm_iso->cur_path); + comm_iso->cur_path = NULL; + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_iso_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandIsoClass), + NULL, + NULL, + (GClassInitFunc) fr_command_iso_class_init, + NULL, + NULL, + sizeof (FrCommandIso), + 0, + (GInstanceInitFunc) fr_command_iso_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandIso", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-iso.h b/src/fr-command-iso.h new file mode 100644 index 0000000..aba1814 --- /dev/null +++ b/src/fr-command-iso.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_ISO_H +#define FR_COMMAND_ISO_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_ISO (fr_command_iso_get_type ()) +#define FR_COMMAND_ISO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ISO, FrCommandIso)) +#define FR_COMMAND_ISO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ISO, FrCommandIsoClass)) +#define FR_IS_COMMAND_ISO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ISO)) +#define FR_IS_COMMAND_ISO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ISO)) +#define FR_COMMAND_ISO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ISO, FrCommandIsoClass)) + +typedef struct _FrCommandIso FrCommandIso; +typedef struct _FrCommandIsoClass FrCommandIsoClass; + +struct _FrCommandIso +{ + FrCommand __parent; + char *cur_path; + gboolean joliet; +}; + +struct _FrCommandIsoClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_iso_get_type (void); + +#endif /* FR_COMMAND_ISO_H */ diff --git a/src/fr-command-jar.c b/src/fr-command-jar.c new file mode 100644 index 0000000..3edb2e9 --- /dev/null +++ b/src/fr-command-jar.c @@ -0,0 +1,243 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "file-utils.h" +#include "fr-command.h" +#include "fr-command-zip.h" +#include "fr-command-jar.h" +#include "java-utils.h" + + +typedef struct { + char *filename; + char *rel_path; + char *package_minus_one_level; + char *link_name; /* package dir = package_minus_one_level + '/' + link_name */ +} JarData; + + +static void fr_command_jar_class_init (FrCommandJarClass *class); +static void fr_command_jar_init (FrCommand *afile); +static void fr_command_jar_finalize (GObject *object); + + +static FrCommandClass *parent_class = NULL; + + +static void +fr_command_jar_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + FrProcess *proc = comm->process; + GList *zip_list = NULL, *jardata_list = NULL, *jar_list = NULL; + GList *scan; + char *tmp_dir; + + for (scan = file_list; scan; scan = scan->next) { + char *filename = scan->data; + char *path = build_uri (base_dir, filename, NULL); + char *package = NULL; + + if (file_extension_is (filename, ".java")) + package = get_package_name_from_java_file (path); + else if (file_extension_is (filename, ".class")) + package = get_package_name_from_class_file (path); + + if ((package == NULL) || (strlen (package) == 0)) + zip_list = g_list_append (zip_list, g_strdup (filename)); + else { + JarData *newdata = g_new0 (JarData, 1); + + newdata->package_minus_one_level = remove_level_from_path (package); + newdata->link_name = g_strdup (file_name_from_path (package)); + newdata->rel_path = remove_level_from_path (filename); + newdata->filename = g_strdup (file_name_from_path (filename)); + jardata_list = g_list_append (jardata_list, newdata); + } + + g_free (package); + g_free (path); + } + + tmp_dir = get_temp_work_dir (NULL); + for (scan = jardata_list; scan ; scan = scan->next) { + JarData *jdata = scan->data; + char *pack_path; + char *old_link; + char *link_name; + int retval; + + pack_path = build_uri (tmp_dir, jdata->package_minus_one_level, NULL); + if (! make_directory_tree_from_path (pack_path, 0755, NULL)) { + g_free (pack_path); + continue; + } + + old_link = build_uri (base_dir, jdata->rel_path, NULL); + link_name = g_build_filename (pack_path, jdata->link_name, NULL); + + retval = symlink (old_link, link_name); + if ((retval != -1) || (errno == EEXIST)) + jar_list = g_list_append (jar_list, + g_build_filename (jdata->package_minus_one_level, + jdata->link_name, + jdata->filename, + NULL)); + + g_free (link_name); + g_free (old_link); + g_free (pack_path); + } + + if (zip_list != NULL) + parent_class->add (comm, NULL, zip_list, base_dir, update, FALSE); + + if (jar_list != NULL) + parent_class->add (comm, NULL, jar_list, tmp_dir, update, FALSE); + + fr_process_begin_command (proc, "rm"); + fr_process_set_working_dir (proc, "/"); + fr_process_add_arg (proc, "-r"); + fr_process_add_arg (proc, "-f"); + fr_process_add_arg (proc, tmp_dir); + fr_process_end_command (proc); + fr_process_set_sticky (proc, TRUE); + + for (scan = jardata_list; scan ; scan = scan->next) { + JarData *jdata = scan->data; + g_free (jdata->filename); + g_free (jdata->package_minus_one_level); + g_free (jdata->link_name); + g_free (jdata->rel_path); + } + + path_list_free (jardata_list); + path_list_free (jar_list); + path_list_free (zip_list); + g_free (tmp_dir); +} + + +const char *jar_mime_type[] = { "application/x-java-archive", + NULL }; + + +const char ** +fr_command_jar_get_mime_types (FrCommand *comm) +{ + return jar_mime_type; +} + + +FrCommandCap +fr_command_jar_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("zip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + + return capabilities; +} + + +static const char * +fr_command_jar_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("zip,unzip"); +} + + +static void +fr_command_jar_class_init (FrCommandJarClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(class); + FrCommandClass *afc = FR_COMMAND_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + gobject_class->finalize = fr_command_jar_finalize; + + afc->add = fr_command_jar_add; + afc->get_mime_types = fr_command_jar_get_mime_types; + afc->get_capabilities = fr_command_jar_get_capabilities; + afc->get_packages = fr_command_jar_get_packages; +} + + +static void +fr_command_jar_init (FrCommand *comm) +{ +} + + +static void +fr_command_jar_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_JAR (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_jar_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandJarClass), + NULL, + NULL, + (GClassInitFunc) fr_command_jar_class_init, + NULL, + NULL, + sizeof (FrCommandJar), + 0, + (GInstanceInitFunc) fr_command_jar_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND_ZIP, + "FRCommandJar", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-jar.h b/src/fr-command-jar.h new file mode 100644 index 0000000..cf108b1 --- /dev/null +++ b/src/fr-command-jar.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_JAR_H +#define FR_COMMAND_JAR_H + +#include <glib.h> +#include "fr-command-zip.h" + +#define FR_TYPE_COMMAND_JAR (fr_command_jar_get_type ()) +#define FR_COMMAND_JAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_JAR, FrCommandJar)) +#define FR_COMMAND_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_JAR, FrCommandJarClass)) +#define FR_IS_COMMAND_JAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_JAR)) +#define FR_IS_COMMAND_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_JAR)) +#define FR_COMMAND_JAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_JAR, FrCommandJarClass)) + +typedef struct _FrCommandJar FrCommandJar; +typedef struct _FrCommandJarClass FrCommandJarClass; + +struct _FrCommandJar +{ + FrCommandZip __parent; +}; + +struct _FrCommandJarClass +{ + FrCommandZipClass __parent_class; +}; + +GType fr_command_jar_get_type (void); + +#endif /* FR_COMMAND_JAR_H */ diff --git a/src/fr-command-lha.c b/src/fr-command-lha.c new file mode 100644 index 0000000..8fce58c --- /dev/null +++ b/src/fr-command-lha.c @@ -0,0 +1,409 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-lha.h" + +static void fr_command_lha_class_init (FrCommandLhaClass *class); +static void fr_command_lha_init (FrCommand *afile); +static void fr_command_lha_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *month, + char *mday, + char *time_or_year) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + if (month != NULL) { + int i; + for (i = 0; i < 12; i++) + if (strcmp (months[i], month) == 0) { + tm.tm_mon = i; + break; + } + } + tm.tm_mday = atoi (mday); + if (strchr (time_or_year, ':') == NULL) + tm.tm_year = atoi (time_or_year) - 1900; + else { + time_t now; + struct tm *tm_now; + + now = time (NULL); + tm_now = localtime (&now); + if (tm_now != NULL) + tm.tm_year = tm_now->tm_year; + + /* time */ + + fields = g_strsplit (time_or_year, ":", 2); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) + tm.tm_min = atoi (fields[1]); + } + g_strfreev (fields); + } + + return mktime (&tm); +} + + +static char ** +split_line_lha (char *line) +{ + char **fields; + int n_fields = 7; + const char *scan, *field_end; + int i; + + fields = g_new0 (char *, n_fields + 1); + fields[n_fields] = NULL; + + i = 0; + + if (strncmp (line, "[MS-DOS]", 8) == 0) { + fields[i++] = g_strdup (""); + fields[i++] = g_strdup (""); + line += strlen ("[MS-DOS]"); + } + else if (strncmp (line, "[generic]", 9) == 0) { + fields[i++] = g_strdup (""); + fields[i++] = g_strdup (""); + line += strlen ("[generic]"); + } + else if (strncmp (line, "[unknown]", 9) == 0) { + fields[i++] = g_strdup (""); + fields[i++] = g_strdup (""); + line += strlen ("[unknown]"); + } + + scan = eat_spaces (line); + for (; i < n_fields; i++) { + field_end = strchr (scan, ' '); + if (field_end != NULL) { + fields[i] = g_strndup (scan, field_end - scan); + scan = eat_spaces (field_end); + } + } + + return fields; +} + + +static const char * +get_last_field_lha (char *line) +{ + int i; + const char *field; + int n = 7; + + if (strncmp (line, "[MS-DOS]", 8) == 0) + n--; + + if (strncmp (line, "[generic]", 9) == 0) + n--; + + if (strncmp (line, "[unknown]", 9) == 0) + n--; + + field = eat_spaces (line); + for (i = 0; i < n; i++) { + field = strchr (field, ' '); + field = eat_spaces (field); + } + + return field; +} + + +static void +process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + const char *name_field; + + g_return_if_fail (line != NULL); + + fdata = file_data_new (); + + fields = split_line_lha (line); + fdata->size = g_ascii_strtoull (fields[2], NULL, 10); + fdata->modified = mktime_from_string (fields[4], + fields[5], + fields[6]); + g_strfreev (fields); + + /* Full path */ + + name_field = get_last_field_lha (line); + + if (name_field && *name_field == '/') { + fdata->full_path = g_strdup (name_field); + fdata->original_path = fdata->full_path; + } else { + fdata->full_path = g_strconcat ("/", name_field, NULL); + fdata->original_path = fdata->full_path + 1; + } + + fdata->link = NULL; + + fdata->dir = line[0] == 'd'; + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_lha_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, process_line, comm); + + fr_process_begin_command (comm->process, "lha"); + fr_process_add_arg (comm->process, "lq"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_lha_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + fr_process_begin_command (comm->process, "lha"); + if (base_dir != NULL) + fr_process_set_working_dir (comm->process, base_dir); + if (update) + fr_process_add_arg (comm->process, "u"); + else + fr_process_add_arg (comm->process, "a"); + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_lha_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + fr_process_begin_command (comm->process, "lha"); + fr_process_add_arg (comm->process, "d"); + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_lha_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + char options[5]; + int i = 0; + + fr_process_begin_command (comm->process, "lha"); + + if (dest_dir != NULL) + fr_process_set_working_dir (comm->process, dest_dir); + + options[i++] = 'x'; + options[i++] = 'f'; /* Always overwrite. + * The overwrite option is handled in + * fr_archive_extract, + * this is because lha asks the user whether he + * wants to overwrite a file. */ + + if (junk_paths) + options[i++] = 'i'; + + options[i++] = 0; + fr_process_add_arg (comm->process, options); + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +const char *lha_mime_type[] = { "application/x-lha", NULL }; + + +const char ** +fr_command_lha_get_mime_types (FrCommand *comm) +{ + return lha_mime_type; +} + + +FrCommandCap +fr_command_lha_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("lha", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + + return capabilities; +} + + +static const char * +fr_command_lha_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("lha"); +} + + +static void +fr_command_lha_class_init (FrCommandLhaClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_lha_finalize; + + afc->list = fr_command_lha_list; + afc->add = fr_command_lha_add; + afc->delete = fr_command_lha_delete; + afc->extract = fr_command_lha_extract; + afc->get_mime_types = fr_command_lha_get_mime_types; + afc->get_capabilities = fr_command_lha_get_capabilities; + afc->get_packages = fr_command_lha_get_packages; +} + + +static void +fr_command_lha_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propAddCanStoreFolders = TRUE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = TRUE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +static void +fr_command_lha_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_LHA (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_lha_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandLhaClass), + NULL, + NULL, + (GClassInitFunc) fr_command_lha_class_init, + NULL, + NULL, + sizeof (FrCommandLha), + 0, + (GInstanceInitFunc) fr_command_lha_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandLha", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-lha.h b/src/fr-command-lha.h new file mode 100644 index 0000000..507f189 --- /dev/null +++ b/src/fr-command-lha.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_LHA_H +#define FR_COMMAND_LHA_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_LHA (fr_command_lha_get_type ()) +#define FR_COMMAND_LHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_LHA, FrCommandLha)) +#define FR_COMMAND_LHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_LHA, FrCommandLhaClass)) +#define FR_IS_COMMAND_LHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_LHA)) +#define FR_IS_COMMAND_LHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_LHA)) +#define FR_COMMAND_LHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_LHA, FrCommandLhaClass)) + +typedef struct _FrCommandLha FrCommandLha; +typedef struct _FrCommandLhaClass FrCommandLhaClass; + +struct _FrCommandLha +{ + FrCommand __parent; +}; + +struct _FrCommandLhaClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_lha_get_type (void); + +#endif /* FR_COMMAND_LHA_H */ diff --git a/src/fr-command-lrzip.c b/src/fr-command-lrzip.c new file mode 100644 index 0000000..6a2833c --- /dev/null +++ b/src/fr-command-lrzip.c @@ -0,0 +1,271 @@ +/* + * fr-command-lrzip.c + * + * Created on: 10.04.2010 + * Author: Alexander Saprykin + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-lrzip.h" + +static void fr_command_lrzip_class_init (FrCommandLrzipClass *class); +static void fr_command_lrzip_init (FrCommand *afile); +static void fr_command_lrzip_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + + +static void +list__process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + + g_return_if_fail (line != NULL); + + if (strlen (line) == 0) + return; + + if (! g_str_has_prefix (line, "Decompressed file size:")) + return; + + fdata = file_data_new (); + fdata->size = g_ascii_strtoull (get_last_field (line, 4), NULL, 10); + + struct stat st; + time_t tt; + if (stat (comm->filename, &st) == 0) + fdata->modified = st.st_mtim.tv_sec; + else + time(&(fdata->modified)); + fdata->modified; + fdata->encrypted = FALSE; + + char *new_fname = g_strdup (file_name_from_path (comm->filename)); + if (g_str_has_suffix (new_fname, ".lrz")) + new_fname[strlen (new_fname) - 4] = '\0'; + + if (*new_fname == '/') { + fdata->full_path = g_strdup (new_fname); + fdata->original_path = fdata->full_path; + } + else { + fdata->full_path = g_strconcat ("/", new_fname, NULL); + fdata->original_path = fdata->full_path + 1; + } + fdata->path = remove_level_from_path (fdata->full_path); + fdata->name = new_fname; + fdata->dir = FALSE; + fdata->link = NULL; + + if (fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_lrzip_list (FrCommand *comm) +{ + fr_process_set_err_line_func (comm->process, list__process_line, comm); + + fr_process_begin_command (comm->process, "lrzip"); + fr_process_add_arg (comm->process, "-i"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_lrzip_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + fr_process_begin_command (comm->process, "lrzip"); + + if (base_dir != NULL) + fr_process_set_working_dir (comm->process, base_dir); + + /* preserve links. */ + + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-l"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-g"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-b"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-z"); break; + } + + fr_process_add_arg (comm->process, "-o"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_add_arg (comm->process, (char *) file_list->data); + + fr_process_end_command (comm->process); +} + +static void +fr_command_lrzip_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + fr_process_begin_command (comm->process, "lrzip"); + fr_process_add_arg (comm->process, "-d"); + + if (dest_dir != NULL) { + fr_process_add_arg (comm->process, "-O"); + fr_process_add_arg (comm->process, dest_dir); + } + if (overwrite) + fr_process_add_arg (comm->process, "-f"); + + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +/* +static void +fr_command_lrzip_test (FrCommand *comm) +{ + fr_process_begin_command (comm->process, "lrzip"); + fr_process_add_arg (comm->process, "-t"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} +*/ + + +const char *lrzip_mime_type[] = { "application/x-lrzip", NULL }; + + +const char ** +fr_command_lrzip_get_mime_types (FrCommand *comm) +{ + return lrzip_mime_type; +} + + +FrCommandCap +fr_command_lrzip_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities = FR_COMMAND_CAN_DO_NOTHING; + + if (is_program_available ("lrzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + + return capabilities; +} + + +static const char * +fr_command_lrzip_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("lrzip"); +} + + +static void +fr_command_lrzip_class_init (FrCommandLrzipClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_lrzip_finalize; + + afc->list = fr_command_lrzip_list; + afc->add = fr_command_lrzip_add; + afc->extract = fr_command_lrzip_extract; + afc->get_mime_types = fr_command_lrzip_get_mime_types; + afc->get_capabilities = fr_command_lrzip_get_capabilities; + afc->get_packages = fr_command_lrzip_get_packages; +} + + +static void +fr_command_lrzip_init (FrCommand *comm) +{ + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propAddCanStoreFolders = FALSE; + comm->propExtractCanAvoidOverwrite = TRUE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +static void +fr_command_lrzip_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_LRZIP (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_lrzip_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandLrzipClass), + NULL, + NULL, + (GClassInitFunc) fr_command_lrzip_class_init, + NULL, + NULL, + sizeof (FrCommandLrzip), + 0, + (GInstanceInitFunc) fr_command_lrzip_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandLrzip", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-lrzip.h b/src/fr-command-lrzip.h new file mode 100644 index 0000000..910a696 --- /dev/null +++ b/src/fr-command-lrzip.h @@ -0,0 +1,37 @@ +/* + * fr-command-lrzip.h + * + * Created on: 10.04.2010 + * Author: Alexander Saprykin + */ + +#ifndef FRCOMMANDLRZIP_H_ +#define FRCOMMANDLRZIP_H_ + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_LRZIP (fr_command_lrzip_get_type ()) +#define FR_COMMAND_LRZIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_LRZIP, FrCommandLrzip)) +#define FR_COMMAND_LRZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_LRZIP, FrCommandLrzipClass)) +#define FR_IS_COMMAND_LRZIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_LRZIP)) +#define FR_IS_COMMAND_LRZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_LRZIP)) +#define FR_COMMAND_LRZIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_LRZIP, FrCommandLrzipClass)) + +typedef struct _FrCommandLrzip FrCommandLrzip; +typedef struct _FrCommandLrzipClass FrCommandLrzipClass; + +struct _FrCommandLrzip +{ + FrCommand __parent; +}; + +struct _FrCommandLrzipClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_lrzip_get_type (void); + +#endif /* FRCOMMANDLRZIP_H_ */ diff --git a/src/fr-command-rar.c b/src/fr-command-rar.c new file mode 100644 index 0000000..2c42ba4 --- /dev/null +++ b/src/fr-command-rar.c @@ -0,0 +1,813 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "file-data.h" +#include "file-utils.h" +#include "gio-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-rar.h" +#include "fr-error.h" + +static void fr_command_rar_class_init (FrCommandRarClass *class); +static void fr_command_rar_init (FrCommand *afile); +static void fr_command_rar_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +static gboolean +have_rar (void) +{ + return is_program_in_path ("rar"); +} + + +/* -- list -- */ + + +static time_t +mktime_from_string (char *date_s, + char *time_s) +{ + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + fields = g_strsplit (date_s, "-", 3); + if (fields[0] != NULL) { + tm.tm_mday = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_mon = atoi (fields[1]) - 1; + if (fields[2] != NULL) + tm.tm_year = 100 + atoi (fields[2]); + } + } + g_strfreev (fields); + + /* time */ + + fields = g_strsplit (time_s, ":", 2); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) + tm.tm_min = atoi (fields[1]); + } + g_strfreev (fields); + + return mktime (&tm); +} + + +static void +process_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FrCommandRar *rar_comm = FR_COMMAND_RAR (comm); + char **fields; + const char *name_field; + + g_return_if_fail (line != NULL); + + if (! rar_comm->list_started) { + if (strncmp (line, "--------", 8) == 0) { + rar_comm->list_started = TRUE; + rar_comm->odd_line = TRUE; + } + else if (strncmp (line, "Volume ", 7) == 0) + comm->multi_volume = TRUE; + return; + } + + if (strncmp (line, "--------", 8) == 0) { + rar_comm->list_started = FALSE; + return; + } + + if (! rar_comm->odd_line) { + FileData *fdata; + + fdata = rar_comm->fdata; + + /* read file info. */ + + fields = split_line (line, 6); + if (g_strv_length (fields) < 6) { + /* wrong line format, treat this line as a filename line */ + g_strfreev (fields); + file_data_free (rar_comm->fdata); + rar_comm->fdata = NULL; + rar_comm->odd_line = TRUE; + } + else { + if ((strcmp (fields[2], "<->") == 0) + || (strcmp (fields[2], "<--") == 0)) + { + /* ignore files that span more volumes */ + + file_data_free (rar_comm->fdata); + rar_comm->fdata = NULL; + } + else { + fdata->size = g_ascii_strtoull (fields[0], NULL, 10); + fdata->modified = mktime_from_string (fields[3], fields[4]); + + if ((fields[5][1] == 'D') || (fields[5][0] == 'd')) { + char *tmp; + + tmp = fdata->full_path; + fdata->full_path = g_strconcat (fdata->full_path, "/", NULL); + + fdata->original_path = g_strdup (fdata->original_path); + fdata->free_original_path = TRUE; + + g_free (tmp); + + fdata->name = dir_name_from_path (fdata->full_path); + fdata->dir = TRUE; + } + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + + fr_command_add_file (comm, fdata); + rar_comm->fdata = NULL; + } + + g_strfreev (fields); + } + } + + if (rar_comm->odd_line) { + FileData *fdata; + + rar_comm->fdata = fdata = file_data_new (); + + /* read file name. */ + + fdata->encrypted = (line[0] == '*') ? TRUE : FALSE; + + name_field = line + 1; + + if (*name_field == '/') { + fdata->full_path = g_strdup (name_field); + fdata->original_path = fdata->full_path; + } + else { + fdata->full_path = g_strconcat ("/", name_field, NULL); + fdata->original_path = fdata->full_path + 1; + } + + fdata->link = NULL; + fdata->path = remove_level_from_path (fdata->full_path); + } + else { + + } + + rar_comm->odd_line = ! rar_comm->odd_line; +} + + +static void +add_password_arg (FrCommand *comm, + const char *password, + gboolean disable_query) +{ + if ((password != NULL) && (password[0] != '\0')) { + if (comm->encrypt_header) + fr_process_add_arg_concat (comm->process, "-hp", password, NULL); + else + fr_process_add_arg_concat (comm->process, "-p", password, NULL); + } + else if (disable_query) + fr_process_add_arg (comm->process, "-p-"); +} + + +typedef enum { + FIRST_VOLUME_IS_000, + FIRST_VOLUME_IS_001, + FIRST_VOLUME_IS_RAR +} FirstVolumeExtension; + + +static char * +get_first_volume_name (const char *name, + const char *pattern, + FirstVolumeExtension extension_type) +{ + char *volume_name = NULL; + GRegex *re; + + re = g_regex_new (pattern, G_REGEX_CASELESS, 0, NULL); + if (g_regex_match (re, name, 0, NULL)) { + char **parts; + int l, i; + + parts = g_regex_split (re, name, 0); + l = strlen (parts[2]); + switch (extension_type) { + case FIRST_VOLUME_IS_000: + for (i = 0; i < l; i++) + parts[2][i] = '0'; + break; + + case FIRST_VOLUME_IS_001: + for (i = 0; i < l; i++) + parts[2][i] = (i < l - 1) ? '0' : '1'; + break; + + case FIRST_VOLUME_IS_RAR: + if (g_str_has_suffix (parts[1], "r")) { + parts[2][0] = 'a'; + parts[2][1] = 'r'; + } + else { + parts[2][0] = 'A'; + parts[2][1] = 'R'; + } + break; + } + + volume_name = g_strjoinv ("", parts); + + g_strfreev (parts); + } + g_regex_unref (re); + + if (volume_name != NULL) { + char *tmp; + + tmp = volume_name; + volume_name = g_filename_from_utf8 (tmp, -1, NULL, NULL, NULL); + g_free (tmp); + } + + return volume_name; +} + + +static void +check_multi_vomule (FrCommand *comm) +{ + GFile *file; + char buffer[11]; + + file = g_file_new_for_path (comm->filename); + if (! g_load_file_in_buffer (file, buffer, 11, NULL)) { + g_object_unref (file); + return; + } + + if ((buffer[10] & 0x01) == 0x01) { + char *volume_name = NULL; + char *name; + + name = g_filename_to_utf8 (file_name_from_path (comm->filename), -1, NULL, NULL, NULL); + + volume_name = get_first_volume_name (name, "^(.*\\.part)([0-9]+)(\\.rar)$", FIRST_VOLUME_IS_001); + if (volume_name == NULL) + volume_name = get_first_volume_name (name, "^(.*\\.r)([0-9]+)$", FIRST_VOLUME_IS_RAR); + if (volume_name == NULL) + volume_name = get_first_volume_name (name, "^(.*\\.)([0-9]+)$", FIRST_VOLUME_IS_001); + + if (volume_name != NULL) { + GFile *parent; + GFile *child; + char *volume_filename; + + parent = g_file_get_parent (file); + child = g_file_get_child (parent, volume_name); + volume_filename = g_file_get_path (child); + fr_command_set_multi_volume (comm, volume_filename); + + g_free (volume_filename); + g_object_unref (child); + g_object_unref (parent); + } + + g_free (name); + } + + g_object_unref (file); +} + + +static void +list__begin (gpointer data) +{ + FrCommandRar *comm = data; + + comm->list_started = FALSE; +} + + +static void +fr_command_rar_list (FrCommand *comm) +{ + check_multi_vomule (comm); + + fr_process_set_out_line_func (comm->process, process_line, comm); + + if (have_rar ()) + fr_process_begin_command (comm->process, "rar"); + else + fr_process_begin_command (comm->process, "unrar"); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, "v"); + fr_process_add_arg (comm->process, "-c-"); + fr_process_add_arg (comm->process, "-v"); + + add_password_arg (comm, comm->password, TRUE); + + /* stop switches scanning */ + fr_process_add_arg (comm->process, "--"); + + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + + fr_process_start (comm->process); +} + + +static char Progress_Message[4196]; +static char Progress_Filename[4096]; + + +static void +parse_progress_line (FrCommand *comm, + const char *prefix, + const char *message_prefix, + const char *line) +{ + int prefix_len; + + prefix_len = strlen (prefix); + if (strncmp (line, prefix, prefix_len) == 0) { + double fraction; + int len; + char *b_idx; + + strcpy (Progress_Filename, line + prefix_len); + + /* when a new volume is created a sequence of backspaces is + * issued, remove the backspaces from the filename */ + b_idx = strchr (Progress_Filename, '\x08'); + if (b_idx != NULL) + *b_idx = 0; + + /* remove the OK at the end of the filename */ + len = strlen (Progress_Filename); + if ((len > 5) && (strncmp (Progress_Filename + len - 5, " OK ", 5) == 0)) + Progress_Filename[len - 5] = 0; + + sprintf (Progress_Message, "%s%s", message_prefix, file_name_from_path (Progress_Filename)); + fr_command_message (comm, Progress_Message); + + fraction = (double) ++comm->n_file / (comm->n_files + 1); + fr_command_progress (comm, fraction); + } +} + + +static void +process_line__add (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + if (strncmp (line, "Creating archive ", 17) == 0) { + const char *archive_filename = line + 17; + char *uri; + + uri = g_filename_to_uri (archive_filename, NULL, NULL); + if ((comm->volume_size > 0) + && g_regex_match_simple ("^.*\\.part(0)*2\\.rar$", uri, G_REGEX_CASELESS, 0)) + { + char *volume_filename; + + volume_filename = g_strdup (archive_filename); + volume_filename[strlen (volume_filename) - 5] = '1'; + fr_command_set_multi_volume (comm, volume_filename); + g_free (volume_filename); + } + fr_command_working_archive (comm, uri); + + g_free (uri); + return; + } + + if (comm->n_files != 0) + parse_progress_line (comm, "Adding ", _("Adding file: "), line); +} + + +static void +fr_command_rar_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + fr_process_use_standard_locale (comm->process, TRUE); + fr_process_set_out_line_func (comm->process, + process_line__add, + comm); + + fr_process_begin_command (comm->process, "rar"); + + if (base_dir != NULL) + fr_process_set_working_dir (comm->process, base_dir); + + if (update) + fr_process_add_arg (comm->process, "u"); + else + fr_process_add_arg (comm->process, "a"); + + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-m1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-m2"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-m3"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-m5"); break; + } + + add_password_arg (comm, comm->password, FALSE); + + if (comm->volume_size > 0) + fr_process_add_arg_printf (comm->process, "-v%ub", comm->volume_size); + + /* disable percentage indicator */ + fr_process_add_arg (comm->process, "-Idp"); + + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + else + fr_process_add_arg_concat (comm->process, "@", from_file, NULL); + + fr_process_end_command (comm->process); +} + + +static void +process_line__delete (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + if (strncmp (line, "Deleting from ", 14) == 0) { + char *uri; + + uri = g_filename_to_uri (line + 14, NULL, NULL); + fr_command_working_archive (comm, uri); + g_free (uri); + + return; + } + + if (comm->n_files != 0) + parse_progress_line (comm, "Deleting ", _("Removing file: "), line); +} + + +static void +fr_command_rar_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + fr_process_use_standard_locale (comm->process, TRUE); + fr_process_set_out_line_func (comm->process, + process_line__delete, + comm); + + fr_process_begin_command (comm->process, "rar"); + fr_process_add_arg (comm->process, "d"); + + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + else + fr_process_add_arg_concat (comm->process, "@", from_file, NULL); + + fr_process_end_command (comm->process); +} + + +static void +process_line__extract (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + if (strncmp (line, "Extracting from ", 16) == 0) { + char *uri; + + uri = g_filename_to_uri (line + 16, NULL, NULL); + fr_command_working_archive (comm, uri); + g_free (uri); + + return; + } + + if (comm->n_files != 0) + parse_progress_line (comm, "Extracting ", _("Extracting file: "), line); +} + + +static void +fr_command_rar_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_use_standard_locale (comm->process, TRUE); + fr_process_set_out_line_func (comm->process, + process_line__extract, + comm); + + if (have_rar ()) + fr_process_begin_command (comm->process, "rar"); + else + fr_process_begin_command (comm->process, "unrar"); + + fr_process_add_arg (comm->process, "x"); + + /* keep broken extracted files */ + fr_process_add_arg (comm->process, "-kb"); + + if (overwrite) + fr_process_add_arg (comm->process, "-o+"); + else + fr_process_add_arg (comm->process, "-o-"); + + if (skip_older) + fr_process_add_arg (comm->process, "-u"); + + if (junk_paths) + fr_process_add_arg (comm->process, "-ep"); + + add_password_arg (comm, comm->password, TRUE); + + /* disable percentage indicator */ + fr_process_add_arg (comm->process, "-Idp"); + + fr_process_add_arg (comm->process, "--"); + fr_process_add_arg (comm->process, comm->filename); + + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + else + fr_process_add_arg_concat (comm->process, "@", from_file, NULL); + + if (dest_dir != NULL) + fr_process_add_arg (comm->process, dest_dir); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_rar_test (FrCommand *comm) +{ + if (have_rar ()) + fr_process_begin_command (comm->process, "rar"); + else + fr_process_begin_command (comm->process, "unrar"); + + fr_process_add_arg (comm->process, "t"); + + add_password_arg (comm, comm->password, TRUE); + + /* disable percentage indicator */ + fr_process_add_arg (comm->process, "-Idp"); + + /* stop switches scanning */ + fr_process_add_arg (comm->process, "--"); + + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +static void +fr_command_rar_handle_error (FrCommand *comm, + FrProcError *error) +{ + GList *scan; + +#if 0 + { + GList *scan; + + for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev) + g_print ("%s\n", (char*)scan->data); + } +#endif + + if (error->type == FR_PROC_ERROR_NONE) + return; + + /*if (error->status == 3) + error->type = FR_PROC_ERROR_ASK_PASSWORD; + else */ + if (error->status <= 1) + error->type = FR_PROC_ERROR_NONE; + + for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev) { + char *line = scan->data; + + if (strstr (line, "password incorrect") != NULL) { + error->type = FR_PROC_ERROR_ASK_PASSWORD; + break; + } + + if (strncmp (line, "Unexpected end of archive", 25) == 0) { + /* FIXME: handle this type of errors at a higher level when the freeze is over. */ + } + + if (strncmp (line, "Cannot find volume", 18) == 0) { + char *volume_filename; + + g_clear_error (&error->gerror); + + error->type = FR_PROC_ERROR_MISSING_VOLUME; + volume_filename = g_path_get_basename (line + strlen ("Cannot find volume ")); + error->gerror = g_error_new (FR_ERROR, error->status, _("Could not find the volume: %s"), volume_filename); + g_free (volume_filename); + break; + } + } +} + + +const char *rar_mime_type[] = { "application/x-cbr", + "application/x-rar", + NULL }; + + +const char ** +fr_command_rar_get_mime_types (FrCommand *comm) +{ + return rar_mime_type; +} + + +FrCommandCap +fr_command_rar_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT | FR_COMMAND_CAN_ENCRYPT_HEADER; + if (is_program_available ("rar", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE | FR_COMMAND_CAN_CREATE_VOLUMES; + else if (is_program_available ("unrar", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + /* multi-volumes are read-only */ + if ((comm->files->len > 0) && comm->multi_volume && (capabilities & FR_COMMAND_CAN_WRITE)) + capabilities ^= FR_COMMAND_CAN_WRITE; + + return capabilities; +} + + +static const char * +fr_command_rar_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("rar,unrar"); +} + + +static void +fr_command_rar_class_init (FrCommandRarClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_rar_finalize; + + afc->list = fr_command_rar_list; + afc->add = fr_command_rar_add; + afc->delete = fr_command_rar_delete; + afc->extract = fr_command_rar_extract; + afc->test = fr_command_rar_test; + afc->handle_error = fr_command_rar_handle_error; + afc->get_mime_types = fr_command_rar_get_mime_types; + afc->get_capabilities = fr_command_rar_get_capabilities; + afc->get_packages = fr_command_rar_get_packages; +} + + +static void +fr_command_rar_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propAddCanStoreFolders = TRUE; + comm->propExtractCanAvoidOverwrite = TRUE; + comm->propExtractCanSkipOlder = TRUE; + comm->propExtractCanJunkPaths = TRUE; + comm->propPassword = TRUE; + comm->propTest = TRUE; + comm->propListFromFile = TRUE; +} + + +static void +fr_command_rar_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_RAR (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_rar_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandRarClass), + NULL, + NULL, + (GClassInitFunc) fr_command_rar_class_init, + NULL, + NULL, + sizeof (FrCommandRar), + 0, + (GInstanceInitFunc) fr_command_rar_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandRar", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-rar.h b/src/fr-command-rar.h new file mode 100644 index 0000000..b9fffdc --- /dev/null +++ b/src/fr-command-rar.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_RAR_H +#define FR_COMMAND_RAR_H + +#include <glib.h> +#include "file-data.h" +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_RAR (fr_command_rar_get_type ()) +#define FR_COMMAND_RAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_RAR, FrCommandRar)) +#define FR_COMMAND_RAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_RAR, FrCommandRarClass)) +#define FR_IS_COMMAND_RAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_RAR)) +#define FR_IS_COMMAND_RAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_RAR)) +#define FR_COMMAND_RAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_RAR, FrCommandRarClass)) + +typedef struct _FrCommandRar FrCommandRar; +typedef struct _FrCommandRarClass FrCommandRarClass; + +struct _FrCommandRar +{ + FrCommand __parent; + + gboolean list_started; + gboolean odd_line; + FileData *fdata; +}; + +struct _FrCommandRarClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_rar_get_type (void); + +#endif /* FR_COMMAND_RAR_H */ diff --git a/src/fr-command-rpm.c b/src/fr-command-rpm.c new file mode 100644 index 0000000..73f5e39 --- /dev/null +++ b/src/fr-command-rpm.c @@ -0,0 +1,320 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-rpm.h" + +static void fr_command_rpm_class_init (FrCommandRpmClass *class); +static void fr_command_rpm_init (FrCommand *afile); +static void fr_command_rpm_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *month, + char *mday, + char *year) +{ + static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + struct tm tm = {0, }; + + tm.tm_isdst = -1; + + if (month != NULL) { + int i; + for (i = 0; i < 12; i++) + if (strcmp (months[i], month) == 0) { + tm.tm_mon = i; + break; + } + } + tm.tm_mday = atoi (mday); + if (strchr (year, ':') != NULL) { + char **fields = g_strsplit (year, ":", 2); + if (n_fields (fields) == 2) { + time_t now; + struct tm *now_tm; + + tm.tm_hour = atoi (fields[0]); + tm.tm_min = atoi (fields[1]); + + now = time(NULL); + now_tm = localtime (&now); + tm.tm_year = now_tm->tm_year; + } + } else + tm.tm_year = atoi (year) - 1900; + + return mktime (&tm); +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + const char *name_field; + char *name; + int ofs = 0; + + g_return_if_fail (line != NULL); + + fdata = file_data_new (); + +#ifdef __sun + fields = split_line (line, 9); + fdata->size = g_ascii_strtoull (fields[4], NULL, 10); + fdata->modified = mktime_from_string (fields[5], fields[6], fields[8]); + g_strfreev (fields); + + name_field = get_last_field (line, 10); +#else /* !__sun */ + /* Handle char and block device files */ + if ((line[0] == 'c') || (line[0] == 'b')) { + fields = split_line (line, 9); + ofs = 1; + fdata->size = 0; + /* TODO We should also specify the content type */ + } + else { + fields = split_line (line, 8); + fdata->size = g_ascii_strtoull (fields[4], NULL, 10); + } + fdata->modified = mktime_from_string (fields[5+ofs], fields[6+ofs], fields[7+ofs]); + g_strfreev (fields); + + name_field = get_last_field (line, 9+ofs); +#endif /* !__sun */ + + fields = g_strsplit (name_field, " -> ", 2); + + if (fields[1] == NULL) { + g_strfreev (fields); + fields = g_strsplit (name_field, " link to ", 2); + } + + fdata->dir = line[0] == 'd'; + + name = g_strcompress (fields[0]); + if (*(fields[0]) == '/') { + fdata->full_path = g_strdup (name); + fdata->original_path = fdata->full_path; + } + else { + fdata->full_path = g_strconcat ("/", name, NULL); + fdata->original_path = fdata->full_path + 1; + } + if (fdata->dir && (name[strlen (name) - 1] != '/')) { + char *old_full_path = fdata->full_path; + fdata->full_path = g_strconcat (old_full_path, "/", NULL); + g_free (old_full_path); + fdata->original_path = g_strdup (name); + fdata->free_original_path = TRUE; + } + g_free (name); + + if (fields[1] != NULL) + fdata->link = g_strcompress (fields[1]); + g_strfreev (fields); + + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +fr_command_rpm_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, list__process_line, comm); + + fr_process_begin_command (comm->process, "sh"); + fr_process_add_arg (comm->process, "-c"); + fr_process_add_arg_concat (comm->process, PRIVEXECDIR "rpm2cpio ", comm->e_filename, " -itv", NULL); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_rpm_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + GString *cmd; + + fr_process_begin_command (comm->process, "sh"); + if (dest_dir != NULL) + fr_process_set_working_dir (comm->process, dest_dir); + fr_process_add_arg (comm->process, "-c"); + + cmd = g_string_new (PRIVEXECDIR "rpm2cpio "); + g_string_append (cmd, comm->e_filename); + g_string_append (cmd, " -idu "); + for (scan = file_list; scan; scan = scan->next) { + char *filename = g_shell_quote (scan->data); + g_string_append (cmd, filename); + g_free (filename); + g_string_append (cmd, " "); + } + fr_process_add_arg (comm->process, cmd->str); + g_string_free (cmd, TRUE); + + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +const char *rpm_mime_type[] = { "application/x-rpm", NULL }; + + +const char ** +fr_command_rpm_get_mime_types (FrCommand *comm) +{ + return rpm_mime_type; +} + + +FrCommandCap +fr_command_rpm_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("cpio", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_rpm_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("cpio,rpm"); +} + + +static void +fr_command_rpm_class_init (FrCommandRpmClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_rpm_finalize; + + afc->list = fr_command_rpm_list; + afc->extract = fr_command_rpm_extract; + afc->get_mime_types = fr_command_rpm_get_mime_types; + afc->get_capabilities = fr_command_rpm_get_capabilities; + afc->get_packages = fr_command_rpm_get_packages; +} + + +static void +fr_command_rpm_init (FrCommand *comm) +{ + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; +} + + +static void +fr_command_rpm_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_RPM (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_rpm_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandRpmClass), + NULL, + NULL, + (GClassInitFunc) fr_command_rpm_class_init, + NULL, + NULL, + sizeof (FrCommandRpm), + 0, + (GInstanceInitFunc) fr_command_rpm_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandRpm", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-rpm.h b/src/fr-command-rpm.h new file mode 100644 index 0000000..9b30ed3 --- /dev/null +++ b/src/fr-command-rpm.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_RPM_H +#define FR_COMMAND_RPM_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_RPM (fr_command_rpm_get_type ()) +#define FR_COMMAND_RPM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_RPM, FrCommandRpm)) +#define FR_COMMAND_RPM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_RPM, FrCommandRpmClass)) +#define FR_IS_COMMAND_RPM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_RPM)) +#define FR_IS_COMMAND_RPM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_RPM)) +#define FR_COMMAND_RPM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_RPM, FrCommandRpmClass)) + +typedef struct _FrCommandRpm FrCommandRpm; +typedef struct _FrCommandRpmClass FrCommandRpmClass; + +struct _FrCommandRpm +{ + FrCommand __parent; + gboolean is_empty; +}; + +struct _FrCommandRpmClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_rpm_get_type (void); + +#endif /* FR_COMMAND_RPM_H */ diff --git a/src/fr-command-tar.c b/src/fr-command-tar.c new file mode 100644 index 0000000..4c214f4 --- /dev/null +++ b/src/fr-command-tar.c @@ -0,0 +1,1233 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include <glib.h> +#include <glib/gi18n.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-tar.h" + +#define ACTIVITY_DELAY 20 + +static void fr_command_tar_class_init (FrCommandTarClass *class); +static void fr_command_tar_init (FrCommand *afile); +static void fr_command_tar_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *date_s, + char *time_s) +{ + struct tm tm = {0, }; + char **fields; + + tm.tm_isdst = -1; + + /* date */ + + fields = g_strsplit (date_s, "-", 3); + if (fields[0] != NULL) { + tm.tm_year = atoi (fields[0]) - 1900; + if (fields[1] != NULL) { + tm.tm_mon = atoi (fields[1]) - 1; + if (fields[2] != NULL) + tm.tm_mday = atoi (fields[2]); + } + } + g_strfreev (fields); + + /* time */ + + fields = g_strsplit (time_s, ":", 3); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_min = atoi (fields[1]); + if (fields[2] != NULL) + tm.tm_sec = atoi (fields[2]); + } + } + g_strfreev (fields); + + return mktime (&tm); +} + + +static char* +tar_get_last_field (const char *line, + int start_from, + int field_n) +{ + const char *f_start, *f_end; + + line = line + start_from; + + f_start = line; + while ((*f_start == ' ') && (*f_start != *line)) + f_start++; + f_end = f_start; + + while ((field_n > 0) && (*f_end != 0)) { + if (*f_end == ' ') { + field_n--; + if (field_n != 0) { + while ((*f_end == ' ') && (*f_end != *line)) + f_end++; + f_start = f_end; + } + } else + f_end++; + } + + return g_strdup (f_start); +} + + +static void +process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + int date_idx; + char *field_date, *field_time, *field_size, *field_name; + char *name; + + g_return_if_fail (line != NULL); + + date_idx = file_list__get_index_from_pattern (line, "%n%n%n%n-%n%n-%n%n %n%n:%n%n"); + if (date_idx < 0) + return; + + fdata = file_data_new (); + + field_size = file_list__get_prev_field (line, date_idx, 1); + fdata->size = g_ascii_strtoull (field_size, NULL, 10); + g_free (field_size); + + field_date = file_list__get_next_field (line, date_idx, 1); + field_time = file_list__get_next_field (line, date_idx, 2); + fdata->modified = mktime_from_string (field_date, field_time); + g_free (field_date); + g_free (field_time); + + /* Full path */ + + field_name = tar_get_last_field (line, date_idx, 3); + fields = g_strsplit (field_name, " -> ", 2); + + if (fields[1] == NULL) { + g_strfreev (fields); + fields = g_strsplit (field_name, " link to ", 2); + } + + name = g_strcompress (fields[0]); + if (*name == '/') { + fdata->full_path = g_strdup (name); + fdata->original_path = fdata->full_path; + } else { + fdata->full_path = g_strconcat ("/", name, NULL); + fdata->original_path = fdata->full_path + 1; + } + g_free (name); + name = g_filename_from_utf8 (fdata->original_path, -1, NULL, NULL, NULL); + if (name) + fdata->original_path = name; + + if (fields[1] != NULL) + fdata->link = g_strdup (fields[1]); + g_strfreev (fields); + g_free (field_name); + + fdata->dir = line[0] == 'd'; + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +add_compress_arg (FrCommand *comm) +{ + if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) + fr_process_add_arg (comm->process, "-z"); + + else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) + fr_process_add_arg (comm->process, "--use-compress-program=bzip2"); + + else if (is_mime_type (comm->mime_type, "application/x-tarz")) { + if (is_program_in_path ("gzip")) + fr_process_add_arg (comm->process, "-z"); + else + fr_process_add_arg (comm->process, "-Z"); + } + else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) + fr_process_add_arg (comm->process, "--use-compress-program=lrzip"); + + else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) + fr_process_add_arg (comm->process, "--use-compress-program=lzip"); + + else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) + fr_process_add_arg (comm->process, "--use-compress-program=lzma"); + + else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) + fr_process_add_arg (comm->process, "--use-compress-program=xz"); + + else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) + fr_process_add_arg (comm->process, "--use-compress-program=lzop"); + + else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) { + FrCommandTar *comm_tar = (FrCommandTar*) comm; + char *option; + + option = g_strdup_printf ("--use-compress-program=%s", comm_tar->compress_command); + fr_process_add_arg (comm->process, option); + g_free (option); + } +} + + +static void +begin_tar_command (FrCommand *comm) +{ + char *command = NULL; + + /* In solaris gtar is present under /usr/sfw/bin */ + + command = g_find_program_in_path ("gtar"); +#if defined (__SVR4) && defined (__sun) + if (g_file_test ("/usr/sfw/bin/gtar", G_FILE_TEST_IS_EXECUTABLE)) + command = g_strdup ("/usr/sfw/bin/gtar"); +#endif + if (command != NULL) + fr_process_begin_command (comm->process, command); + else + fr_process_begin_command (comm->process, "tar"); + g_free (command); +} + + +static void +fr_command_tar_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, process_line, comm); + + begin_tar_command (comm); + fr_process_add_arg (comm->process, "--force-local"); + fr_process_add_arg (comm->process, "--no-wildcards"); + fr_process_add_arg (comm->process, "-tvf"); + fr_process_add_arg (comm->process, comm->filename); + add_compress_arg (comm); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static gboolean +can_create_a_compressed_archive (FrCommand *comm) +{ + return comm->creating_archive && ! is_mime_type (comm->mime_type, "application/x-7z-compressed-tar"); +} + + +static void +process_line__generic (char *line, + gpointer data, + char *action_msg) +{ + FrCommand *comm = FR_COMMAND (data); + char *msg; + + if (line == NULL) + return; + + if (line[strlen (line) - 1] == '/') /* ignore directories */ + return; + + msg = g_strconcat (action_msg, file_name_from_path (line), NULL); + fr_command_message (comm, msg); + g_free (msg); + + if (comm->n_files != 0) { + double fraction = (double) ++comm->n_file / (comm->n_files + 1); + fr_command_progress (comm, fraction); + } +} + + +static void +process_line__add (char *line, + gpointer data) +{ + /* Translators: after the colon there is a filename. */ + process_line__generic (line, data, _("Adding file: ")); +} + + +static void +fr_command_tar_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + FrCommandTar *c_tar = FR_COMMAND_TAR (comm); + GList *scan; + + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + process_line__add, + comm); + + begin_tar_command (comm); + fr_process_add_arg (comm->process, "--force-local"); + if (! recursive) + fr_process_add_arg (comm->process, "--no-recursion"); + fr_process_add_arg (comm->process, "--no-wildcards"); + fr_process_add_arg (comm->process, "-v"); + fr_process_add_arg (comm->process, "-p"); + + if (base_dir != NULL) { + fr_process_add_arg (comm->process, "-C"); + fr_process_add_arg (comm->process, base_dir); + } + + if (can_create_a_compressed_archive (comm)) { + fr_process_add_arg (comm->process, "-cf"); + fr_process_add_arg (comm->process, comm->filename); + add_compress_arg (comm); + } + else { + if (comm->creating_archive) + fr_process_add_arg (comm->process, "-cf"); + else + fr_process_add_arg (comm->process, "-rf"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + } + + if (from_file != NULL) { + fr_process_add_arg (comm->process, "-T"); + fr_process_add_arg (comm->process, from_file); + } + + fr_process_add_arg (comm->process, "--"); + + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +process_line__delete (char *line, + gpointer data) +{ + /* Translators: after the colon there is a filename. */ + process_line__generic (line, data, _("Removing file: ")); +} + + +static void +begin_func__delete (gpointer data) +{ + FrCommand *comm = data; + fr_command_progress (comm, -1.0); + fr_command_message (comm, _("Deleting files from archive")); +} + + +static void +fr_command_tar_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + FrCommandTar *c_tar = FR_COMMAND_TAR (comm); + GList *scan; + + fr_process_set_out_line_func (comm->process, + process_line__delete, + comm); + + begin_tar_command (comm); + fr_process_set_begin_func (comm->process, begin_func__delete, comm); + fr_process_add_arg (comm->process, "--force-local"); + fr_process_add_arg (comm->process, "--no-wildcards"); + fr_process_add_arg (comm->process, "-v"); + fr_process_add_arg (comm->process, "--delete"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + + if (from_file != NULL) { + fr_process_add_arg (comm->process, "-T"); + fr_process_add_arg (comm->process, from_file); + } + + fr_process_add_arg (comm->process, "--"); + + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +process_line__extract (char *line, + gpointer data) +{ + /* Translators: after the colon there is a filename. */ + process_line__generic (line, data, _("Extracting file: ")); +} + + +static void +fr_command_tar_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_set_out_line_func (comm->process, + process_line__extract, + comm); + + begin_tar_command (comm); + fr_process_add_arg (comm->process, "--force-local"); + fr_process_add_arg (comm->process, "--no-wildcards"); + fr_process_add_arg (comm->process, "-v"); + fr_process_add_arg (comm->process, "-p"); + + if (! overwrite) + fr_process_add_arg (comm->process, "-k"); + if (skip_older) + fr_process_add_arg (comm->process, "--keep-newer-files"); + + fr_process_add_arg (comm->process, "-xf"); + fr_process_add_arg (comm->process, comm->filename); + add_compress_arg (comm); + + if (dest_dir != NULL) { + fr_process_add_arg (comm->process, "-C"); + fr_process_add_arg (comm->process, dest_dir); + } + + if (from_file != NULL) { + fr_process_add_arg (comm->process, "-T"); + fr_process_add_arg (comm->process, from_file); + } + + fr_process_add_arg (comm->process, "--"); + + if (from_file == NULL) + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +begin_func__recompress (gpointer data) +{ + FrCommand *comm = data; + fr_command_progress (comm, -1.0); + fr_command_message (comm, _("Recompressing archive")); +} + + +static gboolean +gzip_continue_func (gpointer user_data) +{ + FrCommand *comm = user_data; + + /* ignore gzip warnings */ + + if (comm->process->error.status == 2) { + comm->process->error.type = FR_PROC_ERROR_NONE; + comm->process->error.status = 0; + g_clear_error (&comm->process->error.gerror); + } + + return comm->process->error.status == 0; +} + + +static void +fr_command_tar_recompress (FrCommand *comm) +{ + FrCommandTar *c_tar = FR_COMMAND_TAR (comm); + char *new_name = NULL; + + if (can_create_a_compressed_archive (comm)) + return; + + if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) { + fr_process_begin_command (comm->process, "gzip"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + fr_process_set_continue_func (comm->process, gzip_continue_func, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".gz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) { + fr_process_begin_command (comm->process, "bzip2"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".bz2", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-tarz")) { + fr_process_begin_command (comm->process, "compress"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".Z", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) { + fr_process_begin_command (comm->process, "lrzip"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-l"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-g"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-b"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-z"); break; + } + fr_process_add_arg (comm->process, "-o"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".lrz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) { + fr_process_begin_command (comm->process, "lzip"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".lz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) { + fr_process_begin_command (comm->process, "lzma"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".lzma", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) { + fr_process_begin_command (comm->process, "xz"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".xz", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) { + fr_process_begin_command (comm->process, "lzop"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + fr_process_add_arg (comm->process, "-fU"); + fr_process_add_arg (comm->process, "--no-stdin"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + new_name = g_strconcat (c_tar->uncomp_filename, ".lzo", NULL); + } + else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) { + FrCommandTar *comm_tar = (FrCommandTar*) comm; + + fr_process_begin_command (comm->process, comm_tar->compress_command); + fr_process_set_sticky (comm->process, TRUE); + fr_process_set_begin_func (comm->process, begin_func__recompress, comm); + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-mx=1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-mx=5"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-mx=5"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-mx=7"); break; + } + fr_process_add_arg (comm->process, "a"); + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, "-l"); + + new_name = g_strconcat (c_tar->uncomp_filename, ".7z", NULL); + fr_process_add_arg_concat (comm->process, new_name, NULL); + + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + + /* remove the uncompressed tar */ + + fr_process_begin_command (comm->process, "rm"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, c_tar->uncomp_filename); + fr_process_end_command (comm->process); + } + + if (c_tar->name_modified) { + char *tmp_dir; + + /* Restore original name. */ + + fr_process_begin_command (comm->process, "mv"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, new_name); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + + tmp_dir = remove_level_from_path (new_name); + + fr_process_begin_command (comm->process, "rm"); + fr_process_set_sticky (comm->process, TRUE); + fr_process_add_arg (comm->process, "-fr"); + fr_process_add_arg (comm->process, tmp_dir); + fr_process_end_command (comm->process); + + g_free (tmp_dir); + } + + g_free (new_name); + g_free (c_tar->uncomp_filename); + c_tar->uncomp_filename = NULL; +} + + +static void +begin_func__uncompress (gpointer data) +{ + FrCommand *comm = data; + fr_command_progress (comm, -1.0); + fr_command_message (comm, _("Decompressing archive")); +} + + +static char * +get_uncompressed_name (FrCommandTar *c_tar, + const char *e_filename) +{ + FrCommand *comm = FR_COMMAND (c_tar); + char *new_name = g_strdup (e_filename); + int l = strlen (new_name); + + if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) { + /* X.tgz --> X.tar + * X.tar.gz --> X.tar */ + if (file_extension_is (e_filename, ".tgz")) { + new_name[l - 2] = 'a'; + new_name[l - 1] = 'r'; + } + else if (file_extension_is (e_filename, ".tar.gz")) + new_name[l - 3] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) { + /* X.tbz2 --> X.tar + * X.tar.bz2 --> X.tar */ + if (file_extension_is (e_filename, ".tbz2")) { + new_name[l - 3] = 'a'; + new_name[l - 2] = 'r'; + new_name[l - 1] = 0; + } + else if (file_extension_is (e_filename, ".tar.bz2")) + new_name[l - 4] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-tarz")) { + /* X.taz --> X.tar + * X.tar.Z --> X.tar */ + if (file_extension_is (e_filename, ".taz")) + new_name[l - 1] = 'r'; + else if (file_extension_is (e_filename, ".tar.Z")) + new_name[l - 2] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) { + /* X.tlrz --> X.tar + * X.tar.lrz --> X.tar */ + if (file_extension_is (e_filename, ".tlrz")) { + new_name[l - 3] = 'a'; + new_name[l - 2] = 'r'; + new_name[l - 1] = 0; + } + else if (file_extension_is (e_filename, ".tar.lrz")) + new_name[l - 4] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) { + /* X.tlz --> X.tar + * X.tar.lz --> X.tar */ + if (file_extension_is (e_filename, ".tlz")) { + new_name[l - 2] = 'a'; + new_name[l - 1] = 'r'; + } + else if (file_extension_is (e_filename, ".tar.lz")) + new_name[l - 3] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) { + /* X.tar.lzma --> X.tar + * (There doesn't seem to be a shorthand suffix) */ + if (file_extension_is (e_filename, ".tar.lzma")) + new_name[l - 5] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) { + /* X.tar.xz --> X.tar + * (There doesn't seem to be a shorthand suffix) */ + if (file_extension_is (e_filename, ".tar.xz")) + new_name[l - 3] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) { + /* X.tzo --> X.tar + * X.tar.lzo --> X.tar */ + if (file_extension_is (e_filename, ".tzo")) { + new_name[l - 2] = 'a'; + new_name[l - 1] = 'r'; + } + else if (file_extension_is (e_filename, ".tar.lzo")) + new_name[l - 4] = 0; + } + else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) { + /* X.tar.7z --> X.tar */ + if (file_extension_is (e_filename, ".tar.7z")) + new_name[l - 3] = 0; + } + + return new_name; +} + + +#define MAX_TRIES 50 + + +static char * +get_temp_name (FrCommandTar *c_tar, + const char *filepath) +{ + char *dirname = remove_level_from_path (filepath); + char *template; + char *result = NULL; + char *temp_name = NULL; + + template = g_strconcat (dirname, "/.fr-XXXXXX", NULL); + result = mkdtemp (template); + temp_name = g_build_filename (result, file_name_from_path (filepath), NULL); + g_free (template); + + return temp_name; +} + + +static void +fr_command_tar_uncompress (FrCommand *comm) +{ + FrCommandTar *c_tar = FR_COMMAND_TAR (comm); + char *tmp_name; + gboolean archive_exists; + + if (can_create_a_compressed_archive (comm)) + return; + + if (c_tar->uncomp_filename != NULL) { + g_free (c_tar->uncomp_filename); + c_tar->uncomp_filename = NULL; + } + + { + char *uri = g_filename_to_uri (comm->filename, NULL, NULL); + archive_exists = uri_exists (uri); + g_free (uri); + } + + c_tar->name_modified = ! is_mime_type (comm->mime_type, "application/x-tar"); + if (c_tar->name_modified) { + tmp_name = get_temp_name (c_tar, comm->filename); + if (archive_exists) { + fr_process_begin_command (comm->process, "mv"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + } + else + tmp_name = g_strdup (comm->filename); + + if (archive_exists) { + if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) { + fr_process_begin_command (comm->process, "gzip"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_set_continue_func (comm->process, gzip_continue_func, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) { + fr_process_begin_command (comm->process, "bzip2"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-tarz")) { + if (is_program_in_path ("gzip")) { + fr_process_begin_command (comm->process, "gzip"); + fr_process_set_continue_func (comm->process, gzip_continue_func, comm); + } + else + fr_process_begin_command (comm->process, "uncompress"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) { + fr_process_begin_command (comm->process, "lrzip"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) { + fr_process_begin_command (comm->process, "lzip"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) { + fr_process_begin_command (comm->process, "lzma"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) { + fr_process_begin_command (comm->process, "xz"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) { + fr_process_begin_command (comm->process, "lzop"); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "-dfU"); + fr_process_add_arg (comm->process, "--no-stdin"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) { + FrCommandTar *comm_tar = (FrCommandTar*) comm; + + fr_process_begin_command (comm->process, comm_tar->compress_command); + fr_process_set_begin_func (comm->process, begin_func__uncompress, comm); + fr_process_add_arg (comm->process, "e"); + fr_process_add_arg (comm->process, "-bd"); + fr_process_add_arg (comm->process, "-y"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + + /* remove the compressed tar */ + + fr_process_begin_command (comm->process, "rm"); + fr_process_add_arg (comm->process, "-f"); + fr_process_add_arg (comm->process, tmp_name); + fr_process_end_command (comm->process); + } + } + + c_tar->uncomp_filename = get_uncompressed_name (c_tar, tmp_name); + g_free (tmp_name); +} + + +static void +fr_command_tar_handle_error (FrCommand *comm, + FrProcError *error) +{ + if (error->type != FR_PROC_ERROR_NONE) { + if (error->status <= 1) + error->type = FR_PROC_ERROR_NONE; + } +} + + +const char *tar_mime_types[] = { "application/x-compressed-tar", + "application/x-bzip-compressed-tar", + "application/x-tar", + "application/x-7z-compressed-tar", + "application/x-lrzip-compressed-tar", + "application/x-lzip-compressed-tar", + "application/x-lzma-compressed-tar", + "application/x-lzop-compressed-tar", + "application/x-tarz", + "application/x-xz-compressed-tar", + NULL }; + + +const char ** +fr_command_tar_get_mime_types (FrCommand *comm) +{ + return tar_mime_types; +} + + +FrCommandCap +fr_command_tar_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + + /* In solaris gtar is present under /usr/sfw/bin */ + if (! is_program_available ("tar", check_command) && ! is_program_available ("/usr/sfw/bin/gtar", check_command)) + return capabilities; + + if (is_mime_type (mime_type, "application/x-tar")) { + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-compressed-tar")) { + if (is_program_available ("gzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-bzip-compressed-tar")) { + if (is_program_available ("bzip2", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-tarz")) { + if (is_program_available ("compress", check_command) && is_program_available ("uncompress", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + else if (is_program_available ("gzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + } + else if (is_mime_type (mime_type, "application/x-lrzip-compressed-tar")) { + if (is_program_available ("lrzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-lzip-compressed-tar")) { + if (is_program_available ("lzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-lzma-compressed-tar")) { + if (is_program_available ("lzma", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-xz-compressed-tar")) { + if (is_program_available ("xz", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-lzop-compressed-tar")) { + if (is_program_available ("lzop", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) { + char *try_command[3] = { "7za", "7zr", "7z" }; + int i; + + for (i = 0; i < G_N_ELEMENTS (try_command); i++) { + if (is_program_available (try_command[i], check_command)) { + capabilities |= FR_COMMAND_CAN_WRITE; + break; + } + } + } + + return capabilities; +} + + +static void +fr_command_tar_set_mime_type (FrCommand *comm, + const char *mime_type) +{ + FrCommandTar *comm_tar = FR_COMMAND_TAR (comm); + + FR_COMMAND_CLASS (parent_class)->set_mime_type (comm, mime_type); + + if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) { + char *try_command[3] = { "7za", "7zr", "7z" }; + int i; + + for (i = 0; i < G_N_ELEMENTS (try_command); i++) { + if (is_program_in_path (try_command[i])) { + comm_tar->compress_command = g_strdup (try_command[i]); + break; + } + } + } +} + + +static const char * +fr_command_tar_get_packages (FrCommand *comm, + const char *mime_type) +{ + if (is_mime_type (mime_type, "application/x-tar")) + return PACKAGES ("tar"); + else if (is_mime_type (mime_type, "application/x-compressed-tar")) + return PACKAGES ("tar,gzip"); + else if (is_mime_type (mime_type, "application/x-bzip-compressed-tar")) + return PACKAGES ("tar,bzip2"); + else if (is_mime_type (mime_type, "application/x-tarz")) + return PACKAGES ("tar,gzip,ncompress"); + else if (is_mime_type (mime_type, "application/x-lrzip-compressed-tar")) + return PACKAGES ("tar,lrzip"); + else if (is_mime_type (mime_type, "application/x-lzip-compressed-tar")) + return PACKAGES ("tar,lzip"); + else if (is_mime_type (mime_type, "application/x-lzma-compressed-tar")) + return PACKAGES ("tar,lzma"); + else if (is_mime_type (mime_type, "application/x-xz-compressed-tar")) + return PACKAGES ("tar,xz"); + else if (is_mime_type (mime_type, "application/x-lzop-compressed-tar")) + return PACKAGES ("tar,lzop"); + else if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) + return PACKAGES ("tar,p7zip"); + + return NULL; +} + + +static void +fr_command_tar_class_init (FrCommandTarClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_tar_finalize; + + afc->list = fr_command_tar_list; + afc->add = fr_command_tar_add; + afc->delete = fr_command_tar_delete; + afc->extract = fr_command_tar_extract; + afc->handle_error = fr_command_tar_handle_error; + afc->get_mime_types = fr_command_tar_get_mime_types; + afc->get_capabilities = fr_command_tar_get_capabilities; + afc->set_mime_type = fr_command_tar_set_mime_type; + afc->recompress = fr_command_tar_recompress; + afc->uncompress = fr_command_tar_uncompress; + afc->get_packages = fr_command_tar_get_packages; +} + + +static void +fr_command_tar_init (FrCommand *comm) +{ + FrCommandTar *comm_tar = (FrCommandTar*) comm; + + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propAddCanStoreFolders = TRUE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = TRUE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; + comm->propCanDeleteNonEmptyFolders = FALSE; + comm->propCanExtractNonEmptyFolders = FALSE; + comm->propListFromFile = TRUE; + + comm_tar->msg = NULL; + comm_tar->uncomp_filename = NULL; +} + + +static void +fr_command_tar_finalize (GObject *object) +{ + FrCommandTar *comm_tar; + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_TAR (object)); + + comm_tar = FR_COMMAND_TAR (object); + + if (comm_tar->uncomp_filename != NULL) { + g_free (comm_tar->uncomp_filename); + comm_tar->uncomp_filename = NULL; + } + + if (comm_tar->msg != NULL) { + g_free (comm_tar->msg); + comm_tar->msg = NULL; + } + + if (comm_tar->compress_command != NULL) { + g_free (comm_tar->compress_command); + comm_tar->compress_command = NULL; + } + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_tar_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandTarClass), + NULL, + NULL, + (GClassInitFunc) fr_command_tar_class_init, + NULL, + NULL, + sizeof (FrCommandTar), + 0, + (GInstanceInitFunc) fr_command_tar_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandTar", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-tar.h b/src/fr-command-tar.h new file mode 100644 index 0000000..a214353 --- /dev/null +++ b/src/fr-command-tar.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_TAR_H +#define FR_COMMAND_TAR_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" +#include "typedefs.h" + +#define FR_TYPE_COMMAND_TAR (fr_command_tar_get_type ()) +#define FR_COMMAND_TAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_TAR, FrCommandTar)) +#define FR_COMMAND_TAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_TAR, FrCommandTarClass)) +#define FR_IS_COMMAND_TAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_TAR)) +#define FR_IS_COMMAND_TAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_TAR)) +#define FR_COMMAND_TAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_TAR, FrCommandTarClass)) + +typedef struct _FrCommandTar FrCommandTar; +typedef struct _FrCommandTarClass FrCommandTarClass; + +struct _FrCommandTar +{ + FrCommand __parent; + + /*<private>*/ + + char *uncomp_filename; + gboolean name_modified; + char *compress_command; + + char *msg; +}; + +struct _FrCommandTarClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_tar_get_type (void); + +#endif /* FR_COMMAND_TAR_H */ diff --git a/src/fr-command-unstuff.c b/src/fr-command-unstuff.c new file mode 100644 index 0000000..5bd5e36 --- /dev/null +++ b/src/fr-command-unstuff.c @@ -0,0 +1,390 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "fr-command.h" +#include "fr-command-unstuff.h" + +static void fr_command_unstuff_class_init (FrCommandUnstuffClass *class); +static void fr_command_unstuff_init (FrCommand *afile); +static void fr_command_unstuff_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + +/* recursive rmdir to remove the left-overs from unstuff */ +static void +recursive_rmdir (const char *path) +{ + GDir *dir; + const char *dirname; + + dir = g_dir_open (path, 0, NULL); + if (dir == NULL) + return; + + dirname = g_dir_read_name (dir); + while (dirname != NULL) + { + char *full_path; + + if (strcmp (dirname, ".") == 0 || strcmp (dirname, "..") == 0) + continue; + + full_path = g_build_filename (path, dirname, NULL); + recursive_rmdir (full_path); + g_free (full_path); + + dirname = g_dir_read_name (dir); + } + + rmdir (path); + + g_dir_close (dir); +} + + +/* unstuff doesn't like file paths starting with /, that's so shite */ +static char * +unstuff_is_shit_with_filenames (const char *orig) +{ + int i, num_slashes; + char *current_dir, *filename; + + g_return_val_if_fail (orig != NULL, NULL); + + current_dir = g_get_current_dir (); + i = num_slashes = 0; + while (current_dir[i] != '\0') { + if (current_dir[i] == '/') + num_slashes++; + i++; + } + g_free (current_dir); + + /* 3 characters for each ../ plus filename length plus \0 */ + filename = g_malloc (3 * i + strlen (orig) + 1); + i = 0; + for ( ; num_slashes > 0 ; num_slashes--) { + memcpy (filename + i, "../", 3); + i+=3; + } + memcpy (filename + i, orig, strlen (orig) + 1); + + return filename; +} + + +static void +process_line (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + FrCommandUnstuff *unstuff_comm = FR_COMMAND_UNSTUFF (comm); + const char *str_start; + char *filename, *real_filename; + int i; + FileData *fdata; + + g_return_if_fail (line != NULL); + + if (strstr (line, "progressEvent - ")) { + const char *ssize; + guint size; + + ssize = strstr (line, "progressEvent - ") + + strlen ("progressEvent - "); + if (ssize[0] == '\0') + size = 0; + else + size = g_ascii_strtoull (ssize, NULL, 10); + + if (unstuff_comm->fdata != NULL) + unstuff_comm->fdata->size = size; + + return; + } + + if (strstr (line, "fileEvent") == NULL) + return; + if (strstr (line, unstuff_comm->target_dir + 1) == NULL) + return; + + /* Look for the filename, ends with a comma */ + str_start = strstr (line, unstuff_comm->target_dir + 1); + str_start = str_start + strlen (unstuff_comm->target_dir) - 1; + if (str_start[0] != '/') + str_start--; + if (str_start[0] == '.') + str_start--; + i = 0; + while (str_start[i] != '\0' && str_start[i] != ',') { + i++; + } + /* This is not supposed to happen */ + g_return_if_fail (str_start[i] != '\0'); + filename = g_strndup (str_start, i); + + /* Same thing with the real filename */ + str_start = strstr (line, unstuff_comm->target_dir); + i = 0; + while (str_start[i] != '\0' && str_start[i] != ',') { + i++; + } + real_filename = g_strndup (str_start, i); + + fdata = file_data_new (); + fdata->full_path = filename; + fdata->original_path = filename; + fdata->link = NULL; + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + fdata->size = 0; + fdata->modified = time (NULL); + + unstuff_comm->fdata = fdata; + fr_command_add_file (comm, fdata); + + unlink (real_filename); + g_free (real_filename); +} + + +static void +list__begin (gpointer data) +{ + FrCommandUnstuff *comm = data; + + comm->fdata = NULL; +} + + +static void +fr_command_unstuff_list (FrCommand *comm) +{ + char *arg, *path; + char *filename; + char *path_dots; + + fr_process_set_out_line_func (comm->process, process_line, comm); + + fr_process_begin_command (comm->process, "unstuff"); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, "--trace"); + + /* Actually unpack everything in a temporary directory */ + path = get_temp_work_dir (NULL); + path_dots = unstuff_is_shit_with_filenames (path); + g_free (path); + + arg = g_strdup_printf ("-d=%s", path_dots); + FR_COMMAND_UNSTUFF (comm)->target_dir = path_dots; + fr_process_add_arg (comm->process, arg); + g_free (arg); + + filename = unstuff_is_shit_with_filenames (comm->filename); + fr_process_add_arg (comm->process, filename); + g_free (filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +fr_command_unstuff_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ +#if 0 + GList *scan; +#endif + char *filename; + + fr_process_begin_command (comm->process, "unstuff"); + + if (dest_dir != NULL) { + char *dest_dir_dots; + char *arg; + + dest_dir_dots = unstuff_is_shit_with_filenames (dest_dir); + arg = g_strdup_printf ("-d=%s", dest_dir_dots); + fr_process_add_arg (comm->process, arg); + FR_COMMAND_UNSTUFF (comm)->target_dir = NULL; + g_free (arg); + g_free (dest_dir_dots); + } + + fr_process_add_arg (comm->process, "--trace"); + + /* unstuff doesn't like file paths starting with /, that's so shite */ + filename = unstuff_is_shit_with_filenames (comm->filename); + fr_process_add_arg (comm->process, filename); + g_free (filename); + + /* FIXME it is not possible to unpack only some files */ +#if 0 + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); +#endif + + fr_process_end_command (comm->process); +} + + +static void +fr_command_unstuff_handle_error (FrCommand *comm, + FrProcError *error) +{ + if ((error->type != FR_PROC_ERROR_NONE) + && (error->status <= 1)) + { + error->type = FR_PROC_ERROR_NONE; + } +} + + +const char *unstuff_mime_type[] = { "application/x-stuffit", NULL }; + + +const char ** +fr_command_unstuff_get_mime_types (FrCommand *comm) +{ + return unstuff_mime_type; +} + + +FrCommandCap +fr_command_unstuff_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("unstuff", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_unstaff_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("unstaff"); +} + + +static void +fr_command_unstuff_class_init (FrCommandUnstuffClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_unstuff_finalize; + + afc->list = fr_command_unstuff_list; + afc->add = NULL; + afc->delete = NULL; + afc->extract = fr_command_unstuff_extract; + afc->handle_error = fr_command_unstuff_handle_error; + afc->get_mime_types = fr_command_unstuff_get_mime_types; + afc->get_capabilities = fr_command_unstuff_get_capabilities; + afc->get_packages = fr_command_unstaff_get_packages; +} + + +static void +fr_command_unstuff_init (FrCommand *comm) +{ + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = TRUE; + comm->propTest = FALSE; +} + + +static void +fr_command_unstuff_finalize (GObject *object) +{ + FrCommandUnstuff *unstuff_comm = FR_COMMAND_UNSTUFF (object); + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_UNSTUFF (object)); + + if (unstuff_comm->target_dir != NULL) { + recursive_rmdir (unstuff_comm->target_dir); + g_free (unstuff_comm->target_dir); + } + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_unstuff_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandUnstuffClass), + NULL, + NULL, + (GClassInitFunc) fr_command_unstuff_class_init, + NULL, + NULL, + sizeof (FrCommandUnstuff), + 0, + (GInstanceInitFunc) fr_command_unstuff_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandUnstuff", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-unstuff.h b/src/fr-command-unstuff.h new file mode 100644 index 0000000..e0fb7fb --- /dev/null +++ b/src/fr-command-unstuff.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_UNSTUFF_H +#define FR_COMMAND_UNSTUFF_H + +#include <glib.h> +#include "file-data.h" +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_UNSTUFF (fr_command_unstuff_get_type ()) +#define FR_COMMAND_UNSTUFF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_UNSTUFF, FrCommandUnstuff)) +#define FR_COMMAND_UNSTUFF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_UNSTUFF, FrCommandUnstuffClass)) +#define FR_IS_COMMAND_UNSTUFF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_UNSTUFF)) +#define FR_IS_COMMAND_UNSTUFF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_UNSTUFF)) +#define FR_COMMAND_UNSTUFF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_UNSTUFF, FrCommandUnstuffClass)) + +typedef struct _FrCommandUnstuff FrCommandUnstuff; +typedef struct _FrCommandUnstuffClass FrCommandUnstuffClass; + +struct _FrCommandUnstuff +{ + FrCommand __parent; + + char *target_dir; + FileData *fdata; +}; + +struct _FrCommandUnstuffClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_unstuff_get_type (void); + +#endif /* FR_COMMAND_UNSTUFF_H */ diff --git a/src/fr-command-zip.c b/src/fr-command-zip.c new file mode 100644 index 0000000..3e651f7 --- /dev/null +++ b/src/fr-command-zip.c @@ -0,0 +1,494 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-zip.h" + +#define EMPTY_ARCHIVE_WARNING "Empty zipfile." +#define ZIP_SPECIAL_CHARACTERS "[]*?!^-\\" + +static void fr_command_zip_class_init (FrCommandZipClass *class); +static void fr_command_zip_init (FrCommand *afile); +static void fr_command_zip_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string (char *datetime_s) +{ + struct tm tm = {0, }; + char *date; + char *time; + char *year; + char *month; + char *day; + char *hour; + char *min; + char *sec; + + tm.tm_isdst = -1; + + /* date */ + + date = datetime_s; + year = g_strndup (date, 4); + month = g_strndup (date + 4, 2); + day = g_strndup (date + 6, 2); + tm.tm_year = atoi (year) - 1900; + tm.tm_mon = atoi (month) - 1; + tm.tm_mday = atoi (day); + g_free (year); + g_free (month); + g_free (day); + + /* time */ + + time = datetime_s + 9; + hour = g_strndup (time, 2); + min = g_strndup (time + 2, 2); + sec = g_strndup (time + 4, 2); + tm.tm_hour = atoi (hour); + tm.tm_min = atoi (min); + tm.tm_sec = atoi (sec); + g_free(hour); + g_free(min); + g_free(sec); + + return mktime (&tm); +} + + +static void +list__process_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *comm = FR_COMMAND (data); + char **fields; + const char *name_field; + gint line_l; + + g_return_if_fail (line != NULL); + + /* check whether unzip gave the empty archive warning. */ + + if (FR_COMMAND_ZIP (comm)->is_empty) + return; + + line_l = strlen (line); + + if (line_l == 0) + return; + + if (strcmp (line, EMPTY_ARCHIVE_WARNING) == 0) { + FR_COMMAND_ZIP (comm)->is_empty = TRUE; + return; + } + + /* ignore lines that do not describe a file or a + * directory. */ + if ((line[0] != '?') && (line[0] != 'd') && (line[0] != '-')) + return; + + /**/ + + fdata = file_data_new (); + + fields = split_line (line, 7); + fdata->size = g_ascii_strtoull (fields[3], NULL, 10); + fdata->modified = mktime_from_string (fields[6]); + fdata->encrypted = (*fields[4] == 'B') || (*fields[4] == 'T'); + g_strfreev (fields); + + /* Full path */ + + name_field = get_last_field (line, 8); + + if (*name_field == '/') { + fdata->full_path = g_strdup (name_field); + fdata->original_path = fdata->full_path; + } else { + fdata->full_path = g_strconcat ("/", name_field, NULL); + fdata->original_path = fdata->full_path + 1; + } + + fdata->link = NULL; + + fdata->dir = line[0] == 'd'; + if (fdata->dir) + fdata->name = dir_name_from_path (fdata->full_path); + else + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (comm, fdata); +} + + +static void +add_password_arg (FrCommand *comm, + const char *password) +{ + if ((password != NULL) && (password[0] != '\0')) { + fr_process_add_arg (comm->process, "-P"); + fr_process_add_arg (comm->process, password); + } +} + + +static void +list__begin (gpointer data) +{ + FrCommandZip *comm = data; + + comm->is_empty = FALSE; +} + + +static void +fr_command_zip_list (FrCommand *comm) +{ + fr_process_set_out_line_func (comm->process, list__process_line, comm); + + fr_process_begin_command (comm->process, "unzip"); + fr_process_set_begin_func (comm->process, list__begin, comm); + fr_process_add_arg (comm->process, "-ZTs"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); + fr_process_start (comm->process); +} + + +static void +process_line__common (char *line, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + if (line == NULL) + return; + + fr_command_message (comm, line); + + if (comm->n_files != 0) { + double fraction = (double) ++comm->n_file / (comm->n_files + 1); + fr_command_progress (comm, fraction); + } +} + + +static void +fr_command_zip_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + process_line__common, + comm); + + fr_process_begin_command (comm->process, "zip"); + + if (base_dir != NULL) + fr_process_set_working_dir (comm->process, base_dir); + + /* preserve links. */ + fr_process_add_arg (comm->process, "-y"); + + if (update) + fr_process_add_arg (comm->process, "-u"); + + add_password_arg (comm, comm->password); + + switch (comm->compression) { + case FR_COMPRESSION_VERY_FAST: + fr_process_add_arg (comm->process, "-1"); break; + case FR_COMPRESSION_FAST: + fr_process_add_arg (comm->process, "-3"); break; + case FR_COMPRESSION_NORMAL: + fr_process_add_arg (comm->process, "-6"); break; + case FR_COMPRESSION_MAXIMUM: + fr_process_add_arg (comm->process, "-9"); break; + } + + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_zip_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + process_line__common, + comm); + + fr_process_begin_command (comm->process, "zip"); + fr_process_add_arg (comm->process, "-d"); + + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) { + char *escaped; + + escaped = escape_str (scan->data, ZIP_SPECIAL_CHARACTERS); + fr_process_add_arg (comm->process, escaped); + g_free (escaped); + } + + fr_process_end_command (comm->process); +} + + +static void +fr_command_zip_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_set_out_line_func (FR_COMMAND (comm)->process, + process_line__common, + comm); + + fr_process_begin_command (comm->process, "unzip"); + + if (dest_dir != NULL) { + fr_process_add_arg (comm->process, "-d"); + fr_process_add_arg (comm->process, dest_dir); + } + if (overwrite) + fr_process_add_arg (comm->process, "-o"); + else + fr_process_add_arg (comm->process, "-n"); + if (skip_older) + fr_process_add_arg (comm->process, "-u"); + if (junk_paths) + fr_process_add_arg (comm->process, "-j"); + add_password_arg (comm, comm->password); + + fr_process_add_arg (comm->process, comm->filename); + for (scan = file_list; scan; scan = scan->next) { + char *escaped; + + escaped = escape_str (scan->data, ZIP_SPECIAL_CHARACTERS); + fr_process_add_arg (comm->process, escaped); + g_free (escaped); + } + + fr_process_end_command (comm->process); +} + + +static void +fr_command_zip_test (FrCommand *comm) +{ + fr_process_begin_command (comm->process, "unzip"); + fr_process_add_arg (comm->process, "-t"); + add_password_arg (comm, comm->password); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +static void +fr_command_zip_handle_error (FrCommand *comm, + FrProcError *error) +{ + if (error->type != FR_PROC_ERROR_NONE) { + if (error->status <= 1) + error->type = FR_PROC_ERROR_NONE; + else if ((error->status == 82) || (error->status == 5)) + error->type = FR_PROC_ERROR_ASK_PASSWORD; + else { + GList *output; + GList *scan; + + if (comm->action == FR_ACTION_TESTING_ARCHIVE) + output = comm->process->out.raw; + else + output = comm->process->err.raw; + + for (scan = g_list_last (output); scan; scan = scan->prev) { + char *line = scan->data; + + if (strstr (line, "incorrect password") != NULL) { + error->type = FR_PROC_ERROR_ASK_PASSWORD; + break; + } + } + } + } +} + + +const char *zip_mime_type[] = { "application/x-cbz", + "application/x-ms-dos-executable", + "application/zip", + NULL }; + + +const char ** +fr_command_zip_get_mime_types (FrCommand *comm) +{ + return zip_mime_type; +} + + +FrCommandCap +fr_command_zip_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT; + if (is_program_available ("zip", check_command)) { + if (strcmp (mime_type, "application/x-ms-dos-executable") == 0) + capabilities |= FR_COMMAND_CAN_READ; + else + capabilities |= FR_COMMAND_CAN_READ_WRITE; + } + else if (is_program_available ("unzip", check_command)) + capabilities |= FR_COMMAND_CAN_READ; + + return capabilities; +} + + +static const char * +fr_command_zip_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("zip,unzip"); +} + + +static void +fr_command_zip_class_init (FrCommandZipClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_zip_finalize; + + afc->list = fr_command_zip_list; + afc->add = fr_command_zip_add; + afc->delete = fr_command_zip_delete; + afc->extract = fr_command_zip_extract; + afc->test = fr_command_zip_test; + afc->handle_error = fr_command_zip_handle_error; + afc->get_mime_types = fr_command_zip_get_mime_types; + afc->get_capabilities = fr_command_zip_get_capabilities; + afc->get_packages = fr_command_zip_get_packages; +} + + +static void +fr_command_zip_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = TRUE; + comm->propAddCanStoreFolders = TRUE; + comm->propExtractCanAvoidOverwrite = TRUE; + comm->propExtractCanSkipOlder = TRUE; + comm->propExtractCanJunkPaths = TRUE; + comm->propPassword = TRUE; + comm->propTest = TRUE; + + FR_COMMAND_ZIP (comm)->is_empty = FALSE; +} + + +static void +fr_command_zip_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_ZIP (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_zip_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandZipClass), + NULL, + NULL, + (GClassInitFunc) fr_command_zip_class_init, + NULL, + NULL, + sizeof (FrCommandZip), + 0, + (GInstanceInitFunc) fr_command_zip_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandZip", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-zip.h b/src/fr-command-zip.h new file mode 100644 index 0000000..969985d --- /dev/null +++ b/src/fr-command-zip.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_ZIP_H +#define FR_COMMAND_ZIP_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" + +#define FR_TYPE_COMMAND_ZIP (fr_command_zip_get_type ()) +#define FR_COMMAND_ZIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ZIP, FrCommandZip)) +#define FR_COMMAND_ZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ZIP, FrCommandZipClass)) +#define FR_IS_COMMAND_ZIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ZIP)) +#define FR_IS_COMMAND_ZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ZIP)) +#define FR_COMMAND_ZIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ZIP, FrCommandZipClass)) + +typedef struct _FrCommandZip FrCommandZip; +typedef struct _FrCommandZipClass FrCommandZipClass; + +struct _FrCommandZip +{ + FrCommand __parent; + gboolean is_empty; +}; + +struct _FrCommandZipClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_zip_get_type (void); + +#endif /* FR_COMMAND_ZIP_H */ diff --git a/src/fr-command-zoo.c b/src/fr-command-zoo.c new file mode 100644 index 0000000..00b79de --- /dev/null +++ b/src/fr-command-zoo.c @@ -0,0 +1,428 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <glib.h> + +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-zoo.h" + +static void fr_command_zoo_class_init (FrCommandZooClass *class); +static void fr_command_zoo_init (FrCommand *afile); +static void fr_command_zoo_finalize (GObject *object); + +/* Parent Class */ + +static FrCommandClass *parent_class = NULL; + + +/* -- list -- */ + +static time_t +mktime_from_string_zoo (char *mday_s, + char *month_s, + char *year_s, + char *time_s) +{ + struct tm tm = {0, }; + char **fields; + int year; + + tm.tm_isdst = -1; + + /* This will break in 2075 */ + year = atoi (year_s); + if (year >= 75) { + tm.tm_year = year; + } else { + tm.tm_year = 100 + year; + } + + if (g_ascii_strncasecmp(month_s, "Jan", 3) == 0) { + tm.tm_mon = 0; + } else if (g_ascii_strncasecmp(month_s, "Feb", 3) == 0) { + tm.tm_mon = 1; + } else if (g_ascii_strncasecmp(month_s, "Mar", 3) == 0) { + tm.tm_mon = 2; + } else if (g_ascii_strncasecmp(month_s, "Apr", 3) == 0) { + tm.tm_mon = 3; + } else if (g_ascii_strncasecmp(month_s, "May", 3) == 0) { + tm.tm_mon = 4; + } else if (g_ascii_strncasecmp(month_s, "Jun", 3) == 0) { + tm.tm_mon = 5; + } else if (g_ascii_strncasecmp(month_s, "Jul", 3) == 0) { + tm.tm_mon = 6; + } else if (g_ascii_strncasecmp(month_s, "Aug", 3) == 0) { + tm.tm_mon = 7; + } else if (g_ascii_strncasecmp(month_s, "Sep", 3) == 0) { + tm.tm_mon = 8; + } else if (g_ascii_strncasecmp(month_s, "Oct", 3) == 0) { + tm.tm_mon = 9; + } else if (g_ascii_strncasecmp(month_s, "Nov", 3) == 0) { + tm.tm_mon = 10; + } else if (g_ascii_strncasecmp(month_s, "Dec", 3) == 0) { + tm.tm_mon = 11; + } + + tm.tm_mday = atoi (mday_s); + + fields = g_strsplit (time_s, ":", 3); + if (fields[0] != NULL) { + tm.tm_hour = atoi (fields[0]); + if (fields[1] != NULL) { + tm.tm_min = atoi (fields[1]); + if (fields[2] != NULL) + tm.tm_sec = atoi (fields[2]); + } + } + + g_strfreev (fields); + + return mktime (&tm); +} + + +static char ** +split_line_zoo (char *line) +{ + char **fields; + const char *scan, *field_end; + int i; + + if (line[0] == '-') { + return NULL; + } + + fields = g_new0 (char *, 6); + fields[5] = NULL; + + /* Get Length */ + scan = eat_spaces (line); + field_end = strchr (scan, ' '); + fields[0] = g_strndup (scan, field_end - scan); + scan = eat_spaces (field_end); + + /* Toss CF, Size Now */ + for (i = 0; i < 2; i++) { + field_end = strchr (scan, ' '); + scan = eat_spaces (field_end); + } + + /* Get Day, Month, Year, Time */ + for (i = 1; i < 5; i++) { + if (i == 2 && g_ascii_strncasecmp (scan, "file", 4) == 0) { + g_strfreev(fields); + return NULL; + } + field_end = strchr (scan, ' '); + fields[i] = g_strndup (scan, field_end - scan); + scan = eat_spaces (field_end); + } + + return fields; +} + + +static const char * +get_last_field_zoo (char *line) +{ + const char *field; + int i; + int n = 6; + + field = eat_spaces (line); + for (i = 0; i < n; i++) { + field = strchr (field, ' '); + field = eat_spaces (field); + } + field = strchr (field, ' '); + if (g_ascii_strncasecmp (field, " C ", 3) == 0) { + field = eat_spaces (field); + field = strchr (field, ' '); + field = eat_spaces (field); + } else + field = eat_spaces (field); + + return field; +} + + +static void +process_zoo_line (char *line, + gpointer data) +{ + FileData *fdata; + FrCommand *zoo_comm = FR_COMMAND (data); + char **fields; + const char *name_field; + + g_return_if_fail (line != NULL); + if (line[0] == '-') + return; + + fields = split_line_zoo (line); + if (fields == NULL) + return; + + fdata = file_data_new (); + + fdata->size = g_ascii_strtoull (fields[0], NULL, 10); + fdata->modified = mktime_from_string_zoo (fields[1], + fields[2], + fields[3], + fields[4]); + g_strfreev (fields); + + /* Full path */ + + name_field = get_last_field_zoo (line); + if (*(name_field) == '/') { + fdata->full_path = g_strdup (name_field); + fdata->original_path = fdata->full_path; + } else { + fdata->full_path = g_strconcat ("/", name_field, NULL); + fdata->original_path = fdata->full_path + 1; + } + + fdata->name = g_strdup (file_name_from_path (fdata->full_path)); + fdata->path = remove_level_from_path (fdata->full_path); + + if (*fdata->name == 0) + file_data_free (fdata); + else + fr_command_add_file (zoo_comm, fdata); +} + + +static void +fr_command_zoo_list (FrCommand *zoo_comm) +{ + fr_process_set_out_line_func (zoo_comm->process, process_zoo_line, zoo_comm); + + fr_process_begin_command (zoo_comm->process, "zoo"); + fr_process_add_arg (zoo_comm->process, "lq"); + fr_process_add_arg (zoo_comm->process, zoo_comm->filename); + fr_process_end_command (zoo_comm->process); + fr_process_start (zoo_comm->process); +} + + +static void +fr_command_zoo_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + GList *scan; + + /* Add files. */ + + fr_process_begin_command (comm->process, "zoo"); + + fr_process_set_working_dir (comm->process, base_dir); + + if (update) + fr_process_add_arg (comm->process, "auP"); + else + fr_process_add_arg (comm->process, "aP"); + + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_zoo_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + GList *scan; + + /* Delete files. */ + + fr_process_begin_command (comm->process, "zoo"); + fr_process_add_arg (comm->process, "DP"); + fr_process_add_arg (comm->process, comm->filename); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + fr_process_end_command (comm->process); +} + + +static void +fr_command_zoo_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + GList *scan; + + fr_process_begin_command (comm->process, "zoo"); + + if (overwrite) + fr_process_add_arg (comm->process, "xO"); + else + fr_process_add_arg (comm->process, "x"); + + fr_process_add_arg (comm->process, comm->filename); + + if (dest_dir != NULL) + fr_process_set_working_dir (comm->process, dest_dir); + + for (scan = file_list; scan; scan = scan->next) + fr_process_add_arg (comm->process, scan->data); + + fr_process_end_command (comm->process); +} + + +static void +fr_command_zoo_test (FrCommand *comm) +{ + fr_process_begin_command (comm->process, "zoo"); + fr_process_add_arg (comm->process, "-test"); + fr_process_add_arg (comm->process, comm->filename); + fr_process_end_command (comm->process); +} + + +const char *zoo_mime_type[] = { "application/x-zoo", NULL }; + + +const char ** +fr_command_zoo_get_mime_types (FrCommand *comm) +{ + return zoo_mime_type; +} + + +FrCommandCap +fr_command_zoo_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + FrCommandCap capabilities; + + capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES; + if (is_program_available ("zoo", check_command)) + capabilities |= FR_COMMAND_CAN_READ_WRITE; + + return capabilities; +} + + +static const char * +fr_command_zoo_get_packages (FrCommand *comm, + const char *mime_type) +{ + return PACKAGES ("zoo"); +} + + +static void +fr_command_zoo_class_init (FrCommandZooClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + FrCommandClass *afc; + + parent_class = g_type_class_peek_parent (class); + afc = (FrCommandClass*) class; + + gobject_class->finalize = fr_command_zoo_finalize; + + afc->list = fr_command_zoo_list; + afc->add = fr_command_zoo_add; + afc->delete = fr_command_zoo_delete; + afc->extract = fr_command_zoo_extract; + afc->test = fr_command_zoo_test; + afc->get_mime_types = fr_command_zoo_get_mime_types; + afc->get_capabilities = fr_command_zoo_get_capabilities; + afc->get_packages = fr_command_zoo_get_packages; +} + + +static void +fr_command_zoo_init (FrCommand *comm) +{ + comm->propAddCanUpdate = TRUE; + comm->propAddCanReplace = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = TRUE; +} + + +static void +fr_command_zoo_finalize (GObject *object) +{ + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND_ZOO (object)); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +GType +fr_command_zoo_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandZooClass), + NULL, + NULL, + (GClassInitFunc) fr_command_zoo_class_init, + NULL, + NULL, + sizeof (FrCommandZoo), + 0, + (GInstanceInitFunc) fr_command_zoo_init + }; + + type = g_type_register_static (FR_TYPE_COMMAND, + "FRCommandZoo", + &type_info, + 0); + } + + return type; +} diff --git a/src/fr-command-zoo.h b/src/fr-command-zoo.h new file mode 100644 index 0000000..4b889c3 --- /dev/null +++ b/src/fr-command-zoo.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2003 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_ZOO_H +#define FR_COMMAND_ZOO_H + +#include <glib.h> +#include "fr-command.h" +#include "fr-process.h" +#include "typedefs.h" + +#define FR_TYPE_COMMAND_ZOO (fr_command_zoo_get_type ()) +#define FR_COMMAND_ZOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ZOO, FrCommandZoo)) +#define FR_COMMAND_ZOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ZOO, FrCommandZooClass)) +#define FR_IS_COMMAND_ZOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ZOO)) +#define FR_IS_COMMAND_ZOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ZOO)) +#define FR_COMMAND_ZOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ZOO, FrCommandZooClass)) + +typedef struct _FrCommandZoo FrCommandZoo; +typedef struct _FrCommandZooClass FrCommandZooClass; + +struct _FrCommandZoo +{ + FrCommand __parent; +}; + +struct _FrCommandZooClass +{ + FrCommandClass __parent_class; +}; + +GType fr_command_zoo_get_type (void); + +#endif /* FR_COMMAND_ZOO_H */ diff --git a/src/fr-command.c b/src/fr-command.c new file mode 100644 index 0000000..873c02d --- /dev/null +++ b/src/fr-command.c @@ -0,0 +1,826 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <glib.h> +#include "file-data.h" +#include "file-utils.h" +#include "fr-command.h" +#include "fr-enum-types.h" +#include "fr-marshal.h" +#include "fr-process.h" +#include "glib-utils.h" + +#define INITIAL_SIZE 256 + + +/* Signals */ +enum { + START, + DONE, + PROGRESS, + MESSAGE, + WORKING_ARCHIVE, + LAST_SIGNAL +}; + +/* Properties */ +enum { + PROP_0, + PROP_FILENAME, + PROP_MIME_TYPE, + PROP_PROCESS, + PROP_PASSWORD, + PROP_ENCRYPT_HEADER, + PROP_COMPRESSION, + PROP_VOLUME_SIZE +}; + +static GObjectClass *parent_class = NULL; +static guint fr_command_signals[LAST_SIGNAL] = { 0 }; + +static void fr_command_class_init (FrCommandClass *class); +static void fr_command_init (FrCommand *afile); +static void fr_command_finalize (GObject *object); + +char *action_names[] = { "NONE", + "CREATING_NEW_ARCHIVE", + "LOADING_ARCHIVE", + "LISTING_CONTENT", + "DELETING_FILES", + "TESTING_ARCHIVE", + "GETTING_FILE_LIST", + "COPYING_FILES_FROM_REMOTE", + "ADDING_FILES", + "EXTRACTING_FILES", + "COPYING_FILES_TO_REMOTE", + "CREATING_ARCHIVE", + "SAVING_REMOTE_ARCHIVE" }; + +GType +fr_command_get_type () +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrCommandClass), + NULL, + NULL, + (GClassInitFunc) fr_command_class_init, + NULL, + NULL, + sizeof (FrCommand), + 0, + (GInstanceInitFunc) fr_command_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "FRCommand", + &type_info, + 0); + } + + return type; +} + + +static void +base_fr_command_list (FrCommand *comm) +{ +} + + +static void +base_fr_command_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ +} + + +static void +base_fr_command_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ +} + + +static void +base_fr_command_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ +} + + +static void +base_fr_command_test (FrCommand *comm) +{ +} + + +static void +base_fr_command_uncompress (FrCommand *comm) +{ +} + + +static void +base_fr_command_recompress (FrCommand *comm) +{ +} + + +static void +base_fr_command_handle_error (FrCommand *comm, + FrProcError *error) +{ +} + + +const char **void_mime_types = { NULL }; + + +const char ** +base_fr_command_get_mime_types (FrCommand *comm) +{ + return void_mime_types; +} + + +FrCommandCap +base_fr_command_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + return FR_COMMAND_CAN_DO_NOTHING; +} + + +static void +base_fr_command_set_mime_type (FrCommand *comm, + const char *mime_type) +{ + comm->mime_type = get_static_string (mime_type); + fr_command_update_capabilities (comm); +} + + +static const char * +base_fr_command_get_packages (FrCommand *comm, + const char *mime_type) +{ + return NULL; +} + + +static void +fr_command_start (FrProcess *process, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + g_signal_emit (G_OBJECT (comm), + fr_command_signals[START], + 0, + comm->action); +} + + +static void +fr_command_done (FrProcess *process, + gpointer data) +{ + FrCommand *comm = FR_COMMAND (data); + + comm->process->restart = FALSE; + fr_command_handle_error (comm, &process->error); + + if (comm->process->restart) { + fr_process_start (comm->process); + return; + } + + if (comm->action == FR_ACTION_LISTING_CONTENT) { + /* order the list by name to speed up search */ + g_ptr_array_sort (comm->files, file_data_compare_by_path); + } + + g_signal_emit (G_OBJECT (comm), + fr_command_signals[DONE], + 0, + comm->action, + &process->error); +} + + +static void +fr_command_set_process (FrCommand *comm, + FrProcess *process) +{ + if (comm->process != NULL) { + g_signal_handlers_disconnect_matched (G_OBJECT (comm->process), + G_SIGNAL_MATCH_DATA, + 0, + 0, NULL, + 0, + comm); + g_object_unref (G_OBJECT (comm->process)); + comm->process = NULL; + } + + if (process == NULL) + return; + + g_object_ref (G_OBJECT (process)); + comm->process = process; + g_signal_connect (G_OBJECT (comm->process), + "start", + G_CALLBACK (fr_command_start), + comm); + g_signal_connect (G_OBJECT (comm->process), + "done", + G_CALLBACK (fr_command_done), + comm); +} + + +static void +fr_command_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + FrCommand *comm; + + comm = FR_COMMAND (object); + + switch (prop_id) { + case PROP_PROCESS: + fr_command_set_process (comm, g_value_get_object (value)); + break; + case PROP_FILENAME: + fr_command_set_filename (comm, g_value_get_string (value)); + break; + case PROP_MIME_TYPE: + fr_command_set_mime_type (comm, g_value_get_string (value)); + break; + case PROP_PASSWORD: + g_free (comm->password); + comm->password = g_strdup (g_value_get_string (value)); + break; + case PROP_ENCRYPT_HEADER: + comm->encrypt_header = g_value_get_boolean (value); + break; + case PROP_COMPRESSION: + comm->compression = g_value_get_enum (value); + break; + case PROP_VOLUME_SIZE: + comm->volume_size = g_value_get_uint (value); + break; + default: + break; + } +} + + +static void +fr_command_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + FrCommand *comm; + + comm = FR_COMMAND (object); + + switch (prop_id) { + case PROP_PROCESS: + g_value_set_object (value, comm->process); + break; + case PROP_FILENAME: + g_value_set_string (value, comm->filename); + break; + case PROP_MIME_TYPE: + g_value_set_static_string (value, comm->mime_type); + break; + case PROP_PASSWORD: + g_value_set_string (value, comm->password); + break; + case PROP_ENCRYPT_HEADER: + g_value_set_boolean (value, comm->encrypt_header); + break; + case PROP_COMPRESSION: + g_value_set_enum (value, comm->compression); + break; + case PROP_VOLUME_SIZE: + g_value_set_uint (value, comm->volume_size); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +fr_command_class_init (FrCommandClass *class) +{ + GObjectClass *gobject_class; + + parent_class = g_type_class_peek_parent (class); + + gobject_class = G_OBJECT_CLASS (class); + + /* virtual functions */ + + gobject_class->finalize = fr_command_finalize; + gobject_class->set_property = fr_command_set_property; + gobject_class->get_property = fr_command_get_property; + + class->list = base_fr_command_list; + class->add = base_fr_command_add; + class->delete = base_fr_command_delete; + class->extract = base_fr_command_extract; + class->test = base_fr_command_test; + class->uncompress = base_fr_command_uncompress; + class->recompress = base_fr_command_recompress; + class->handle_error = base_fr_command_handle_error; + class->get_mime_types = base_fr_command_get_mime_types; + class->get_capabilities = base_fr_command_get_capabilities; + class->set_mime_type = base_fr_command_set_mime_type; + class->get_packages = base_fr_command_get_packages; + class->start = NULL; + class->done = NULL; + class->progress = NULL; + class->message = NULL; + + /* signals */ + + fr_command_signals[START] = + g_signal_new ("start", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrCommandClass, start), + NULL, NULL, + fr_marshal_VOID__INT, + G_TYPE_NONE, + 1, G_TYPE_INT); + fr_command_signals[DONE] = + g_signal_new ("done", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrCommandClass, done), + NULL, NULL, + fr_marshal_VOID__INT_POINTER, + G_TYPE_NONE, 2, + G_TYPE_INT, + G_TYPE_POINTER); + fr_command_signals[PROGRESS] = + g_signal_new ("progress", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrCommandClass, progress), + NULL, NULL, + fr_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, + G_TYPE_DOUBLE); + fr_command_signals[MESSAGE] = + g_signal_new ("message", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrCommandClass, message), + NULL, NULL, + fr_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + fr_command_signals[WORKING_ARCHIVE] = + g_signal_new ("working_archive", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrCommandClass, working_archive), + NULL, NULL, + fr_marshal_VOID__STRING, + G_TYPE_NONE, 1, + G_TYPE_STRING); + + /* properties */ + + g_object_class_install_property (gobject_class, + PROP_PROCESS, + g_param_spec_object ("process", + "Process", + "The process object used by the command", + FR_TYPE_PROCESS, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_FILENAME, + g_param_spec_string ("filename", + "Filename", + "The archive filename", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_MIME_TYPE, + g_param_spec_string ("mime-type", + "Mime type", + "The file mime-type", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_PASSWORD, + g_param_spec_string ("password", + "Password", + "The archive password", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_ENCRYPT_HEADER, + g_param_spec_boolean ("encrypt-header", + "Encrypt header", + "Whether to encrypt the archive header when creating the archive", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_COMPRESSION, + g_param_spec_enum ("compression", + "Compression type", + "The compression type to use when creating the archive", + FR_TYPE_COMPRESSION, + FR_COMPRESSION_NORMAL, + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, + PROP_VOLUME_SIZE, + g_param_spec_uint ("volume-size", + "Volume size", + "The size of each volume or 0 to not use volumes", + 0L, + G_MAXUINT, + 0, + G_PARAM_READWRITE)); +} + + +static void +fr_command_init (FrCommand *comm) +{ + comm->files = g_ptr_array_sized_new (INITIAL_SIZE); + + comm->password = NULL; + comm->encrypt_header = FALSE; + comm->compression = FR_COMPRESSION_NORMAL; + comm->volume_size = 0; + comm->filename = NULL; + comm->e_filename = NULL; + comm->fake_load = FALSE; + + comm->propAddCanUpdate = FALSE; + comm->propAddCanReplace = FALSE; + comm->propAddCanStoreFolders = FALSE; + comm->propExtractCanAvoidOverwrite = FALSE; + comm->propExtractCanSkipOlder = FALSE; + comm->propExtractCanJunkPaths = FALSE; + comm->propPassword = FALSE; + comm->propTest = FALSE; + comm->propCanExtractAll = TRUE; + comm->propCanDeleteNonEmptyFolders = TRUE; + comm->propCanExtractNonEmptyFolders = TRUE; + comm->propListFromFile = FALSE; +} + + +static void +fr_command_finalize (GObject *object) +{ + FrCommand* comm; + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_COMMAND (object)); + + comm = FR_COMMAND (object); + + g_free (comm->filename); + g_free (comm->e_filename); + g_free (comm->password); + if (comm->files != NULL) + g_ptr_array_free_full (comm->files, (GFunc) file_data_free, NULL); + fr_command_set_process (comm, NULL); + + /* Chain up */ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +void +fr_command_set_filename (FrCommand *comm, + const char *filename) +{ + g_return_if_fail (FR_IS_COMMAND (comm)); + + if (comm->filename != NULL) { + g_free (comm->filename); + comm->filename = NULL; + } + + if (comm->e_filename != NULL) { + g_free (comm->e_filename); + comm->e_filename = NULL; + } + + if (filename != NULL) { + if (! g_path_is_absolute (filename)) { + char *current_dir; + + current_dir = g_get_current_dir (); + comm->filename = g_strconcat (current_dir, + "/", + filename, + NULL); + g_free (current_dir); + } + else + comm->filename = g_strdup (filename); + + comm->e_filename = g_shell_quote (comm->filename); + + debug (DEBUG_INFO, "filename : %s\n", comm->filename); + debug (DEBUG_INFO, "e_filename : %s\n", comm->e_filename); + } + + fr_command_working_archive (comm, comm->filename); +} + + +void +fr_command_set_multi_volume (FrCommand *comm, + const char *filename) +{ + comm->multi_volume = TRUE; + fr_command_set_filename (comm, filename); +} + + +void +fr_command_list (FrCommand *comm) +{ + g_return_if_fail (FR_IS_COMMAND (comm)); + + fr_command_progress (comm, -1.0); + + if (comm->files != NULL) { + g_ptr_array_free_full (comm->files, (GFunc) file_data_free, NULL); + comm->files = g_ptr_array_sized_new (INITIAL_SIZE); + } + + comm->action = FR_ACTION_LISTING_CONTENT; + fr_process_set_out_line_func (comm->process, NULL, NULL); + fr_process_set_err_line_func (comm->process, NULL, NULL); + fr_process_use_standard_locale (comm->process, TRUE); + comm->multi_volume = FALSE; + + if (! comm->fake_load) + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->list (comm); + else + g_signal_emit (G_OBJECT (comm), + fr_command_signals[DONE], + 0, + comm->action, + &comm->process->error); +} + + +void +fr_command_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive) +{ + fr_command_progress (comm, -1.0); + + comm->action = FR_ACTION_ADDING_FILES; + fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL); + fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL); + + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->add (comm, + from_file, + file_list, + base_dir, + update, + recursive); +} + + +void +fr_command_delete (FrCommand *comm, + const char *from_file, + GList *file_list) +{ + fr_command_progress (comm, -1.0); + + comm->action = FR_ACTION_DELETING_FILES; + fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL); + fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL); + + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->delete (comm, from_file, file_list); +} + + +void +fr_command_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths) +{ + fr_command_progress (comm, -1.0); + + comm->action = FR_ACTION_EXTRACTING_FILES; + fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL); + fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL); + + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->extract (comm, + from_file, + file_list, + dest_dir, + overwrite, + skip_older, + junk_paths); +} + + +void +fr_command_test (FrCommand *comm) +{ + fr_command_progress (comm, -1.0); + + comm->action = FR_ACTION_TESTING_ARCHIVE; + fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL); + fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL); + + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->test (comm); +} + + +void +fr_command_uncompress (FrCommand *comm) +{ + fr_command_progress (comm, -1.0); + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->uncompress (comm); +} + + +void +fr_command_recompress (FrCommand *comm) +{ + fr_command_progress (comm, -1.0); + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->recompress (comm); +} + + +const char ** +fr_command_get_mime_types (FrCommand *comm) +{ + return FR_COMMAND_GET_CLASS (G_OBJECT (comm))->get_mime_types (comm); +} + + +void +fr_command_update_capabilities (FrCommand *comm) +{ + comm->capabilities = fr_command_get_capabilities (comm, comm->mime_type, TRUE); +} + + +FrCommandCap +fr_command_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command) +{ + return FR_COMMAND_GET_CLASS (G_OBJECT (comm))->get_capabilities (comm, mime_type, check_command); +} + + +gboolean +fr_command_is_capable_of (FrCommand *comm, + FrCommandCaps requested_capabilities) +{ + return (((comm->capabilities ^ requested_capabilities) & requested_capabilities) == 0); +} + + +const char * +fr_command_get_packages (FrCommand *comm, + const char *mime_type) +{ + return FR_COMMAND_GET_CLASS (G_OBJECT (comm))->get_packages (comm, mime_type); +} + + +/* fraction == -1 means : I don't known how much time the current operation + * will take, the dialog will display this info pulsing + * the progress bar. + * fraction in [0.0, 1.0] means the amount of work, in percentage, + * accomplished. + */ +void +fr_command_progress (FrCommand *comm, + double fraction) +{ + g_signal_emit (G_OBJECT (comm), + fr_command_signals[PROGRESS], + 0, + fraction); +} + + +void +fr_command_message (FrCommand *comm, + const char *msg) +{ + g_signal_emit (G_OBJECT (comm), + fr_command_signals[MESSAGE], + 0, + msg); +} + + +void +fr_command_working_archive (FrCommand *comm, + const char *archive_name) +{ + g_signal_emit (G_OBJECT (comm), + fr_command_signals[WORKING_ARCHIVE], + 0, + archive_name); +} + + +void +fr_command_set_n_files (FrCommand *comm, + int n_files) +{ + comm->n_files = n_files; + comm->n_file = 0; +} + + +void +fr_command_add_file (FrCommand *comm, + FileData *fdata) +{ + file_data_update_content_type (fdata); + g_ptr_array_add (comm->files, fdata); + if (! fdata->dir) + comm->n_regular_files++; +} + + +void +fr_command_set_mime_type (FrCommand *comm, + const char *mime_type) +{ + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->set_mime_type (comm, mime_type); +} + + +void +fr_command_handle_error (FrCommand *comm, + FrProcError *error) +{ + FR_COMMAND_GET_CLASS (G_OBJECT (comm))->handle_error (comm, error); +} diff --git a/src/fr-command.h b/src/fr-command.h new file mode 100644 index 0000000..39b18de --- /dev/null +++ b/src/fr-command.h @@ -0,0 +1,228 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_COMMAND_H +#define FR_COMMAND_H + +#include <glib.h> +#include "file-data.h" +#include "fr-process.h" + +#define PACKAGES(x) (x) + +#define FR_TYPE_COMMAND (fr_command_get_type ()) +#define FR_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND, FrCommand)) +#define FR_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND, FrCommandClass)) +#define FR_IS_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND)) +#define FR_IS_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND)) +#define FR_COMMAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND, FrCommandClass)) + +typedef struct _FrCommand FrCommand; +typedef struct _FrCommandClass FrCommandClass; + +typedef enum { + FR_ACTION_NONE, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_ACTION_LOADING_ARCHIVE, /* loading the archive from a remote location */ + FR_ACTION_LISTING_CONTENT, /* listing the content of the archive */ + FR_ACTION_DELETING_FILES, /* deleting files from the archive */ + FR_ACTION_TESTING_ARCHIVE, /* testing the archive integrity */ + FR_ACTION_GETTING_FILE_LIST, /* getting the file list (when fr_archive_add_with_wildcard or + fr_archive_add_directory are used, we need to scan a directory + and collect the files to add to the archive, this + may require some time to complete, so the operation + is asynchronous) */ + FR_ACTION_COPYING_FILES_FROM_REMOTE, /* copying files to be added to the archive from a remote location */ + FR_ACTION_ADDING_FILES, /* adding files to an archive */ + FR_ACTION_EXTRACTING_FILES, /* extracting files */ + FR_ACTION_COPYING_FILES_TO_REMOTE, /* copying extracted files to a remote location */ + FR_ACTION_CREATING_ARCHIVE, /* creating a local archive */ + FR_ACTION_SAVING_REMOTE_ARCHIVE /* copying the archive to a remote location */ +} FrAction; + +#ifdef DEBUG +extern char *action_names[]; +#endif + +struct _FrCommand +{ + GObject __parent; + + /*<public, read only>*/ + + GPtrArray *files; /* Array of FileData* */ + int n_regular_files; + FrProcess *process; /* the process object used to execute + * commands. */ + char *filename; /* archive file path. */ + char *e_filename; /* escaped archive filename. */ + const char *mime_type; + gboolean multi_volume; + + /*<protected>*/ + + /* options */ + + char *password; + gboolean encrypt_header : 1; + FrCompression compression; + guint volume_size; + gboolean creating_archive; + + /* features. */ + + guint propAddCanUpdate : 1; + guint propAddCanReplace : 1; + guint propAddCanStoreFolders : 1; + guint propExtractCanAvoidOverwrite : 1; + guint propExtractCanSkipOlder : 1; + guint propExtractCanJunkPaths : 1; + guint propPassword : 1; + guint propTest : 1; + guint propCanExtractAll : 1; + guint propCanDeleteNonEmptyFolders : 1; + guint propCanExtractNonEmptyFolders : 1; + guint propListFromFile : 1; + + /*<private>*/ + + FrCommandCaps capabilities; + FrAction action; /* current action. */ + gboolean fake_load; /* if TRUE does nothing when the list + * operation is invoked. */ + + /* progress data */ + + int n_file; + int n_files; +}; + +struct _FrCommandClass +{ + GObjectClass __parent_class; + + /*<virtual functions>*/ + + void (*list) (FrCommand *comm); + void (*add) (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive); + void (*delete) (FrCommand *comm, + const char *from_file, + GList *file_list); + void (*extract) (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths); + void (*test) (FrCommand *comm); + void (*uncompress) (FrCommand *comm); + void (*recompress) (FrCommand *comm); + void (*handle_error) (FrCommand *comm, + FrProcError *error); + const char ** (*get_mime_types) (FrCommand *comm); + FrCommandCap (*get_capabilities) (FrCommand *comm, + const char *mime_type, + gboolean check_command); + void (*set_mime_type) (FrCommand *comm, + const char *mime_type); + const char * (*get_packages) (FrCommand *comm, + const char *mime_type); + + /*<signals>*/ + + void (*start) (FrCommand *comm, + FrAction action); + void (*done) (FrCommand *comm, + FrAction action, + FrProcError *error); + void (*progress) (FrCommand *comm, + double fraction); + void (*message) (FrCommand *comm, + const char *msg); + void (*working_archive) (FrCommand *comm, + const char *filename); +}; + +GType fr_command_get_type (void); +void fr_command_set_filename (FrCommand *comm, + const char *filename); +void fr_command_set_multi_volume (FrCommand *comm, + const char *filename); +void fr_command_list (FrCommand *comm); +void fr_command_add (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *base_dir, + gboolean update, + gboolean recursive); +void fr_command_delete (FrCommand *comm, + const char *from_file, + GList *file_list); +void fr_command_extract (FrCommand *comm, + const char *from_file, + GList *file_list, + const char *dest_dir, + gboolean overwrite, + gboolean skip_older, + gboolean junk_paths); +void fr_command_test (FrCommand *comm); +void fr_command_uncompress (FrCommand *comm); +void fr_command_recompress (FrCommand *comm); +gboolean fr_command_is_capable_of (FrCommand *comm, + FrCommandCaps capabilities); +const char ** fr_command_get_mime_types (FrCommand *comm); +void fr_command_update_capabilities (FrCommand *comm); +FrCommandCap fr_command_get_capabilities (FrCommand *comm, + const char *mime_type, + gboolean check_command); +void fr_command_set_mime_type (FrCommand *comm, + const char *mime_type); +gboolean fr_command_is_capable_of (FrCommand *comm, + FrCommandCaps capabilities); +const char * fr_command_get_packages (FrCommand *comm, + const char *mime_type); + +/* protected functions */ + +void fr_command_progress (FrCommand *comm, + double fraction); +void fr_command_message (FrCommand *comm, + const char *msg); +void fr_command_working_archive (FrCommand *comm, + const char *archive_name); +void fr_command_set_n_files (FrCommand *comm, + int n_files); +void fr_command_add_file (FrCommand *comm, + FileData *fdata); + +/* private functions */ + +void fr_command_handle_error (FrCommand *comm, + FrProcError *error); + +#endif /* FR_COMMAND_H */ diff --git a/src/fr-error.c b/src/fr-error.c new file mode 100644 index 0000000..ab48661 --- /dev/null +++ b/src/fr-error.c @@ -0,0 +1,36 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + + +#include "fr-error.h" + + +GQuark +fr_error_quark (void) +{ + static GQuark quark; + + if (!quark) + quark = g_quark_from_static_string ("file_roller_error"); + + return quark; +} diff --git a/src/fr-error.h b/src/fr-error.h new file mode 100644 index 0000000..59399e8 --- /dev/null +++ b/src/fr-error.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FR_ERROR_H__ +#define __FR_ERROR_H__ + +#include <glib.h> + +#define FR_ERROR fr_error_quark () +GQuark fr_error_quark (void); + + +#endif /* __FR_ERROR_H__ */ diff --git a/src/fr-list-model.c b/src/fr-list-model.c new file mode 100644 index 0000000..61b71d1 --- /dev/null +++ b/src/fr-list-model.c @@ -0,0 +1,188 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include "eggtreemultidnd.h" +#include "fr-list-model.h" +#include "fr-window.h" + + +static GtkListStoreClass *parent_class; + + +static gboolean +fr_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source, + GList *path_list) +{ + FrWindow *window; + GtkTreeModel *model; + GList *scan; + + window = g_object_get_data (G_OBJECT (drag_source), "FrWindow"); + g_return_val_if_fail (window != NULL, FALSE); + + model = fr_window_get_list_store (window); + + for (scan = path_list; scan; scan = scan->next) { + GtkTreeRowReference *reference = scan->data; + GtkTreePath *path; + GtkTreeIter iter; + FileData *fdata; + + path = gtk_tree_row_reference_get_path (reference); + if (path == NULL) + continue; + + if (! gtk_tree_model_get_iter (model, &iter, path)) + continue; + + gtk_tree_model_get (model, &iter, + COLUMN_FILE_DATA, &fdata, + -1); + + if (fdata != NULL) + return TRUE; + } + + return FALSE; +} + + +static gboolean +fr_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list) +{ + FrWindow *window; + + window = g_object_get_data (G_OBJECT (drag_source), "FrWindow"); + g_return_val_if_fail (window != NULL, FALSE); + + return fr_window_file_list_drag_data_get (window, + context, + selection_data, + path_list); +} + + +static gboolean +fr_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source, + GList *path_list) +{ + return TRUE; +} + + +static void +fr_list_model_finalize (GObject *object) +{ + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static void +fr_list_model_init (FRListModel *model) +{ +} + + +static void +fr_list_model_class_init (FRListModelClass *klass) +{ + GObjectClass *object_class; + + object_class = (GObjectClass *)klass; + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = fr_list_model_finalize; +} + + + +static void +fr_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface) +{ + iface->row_draggable = fr_list_model_multi_row_draggable; + iface->drag_data_get = fr_list_model_multi_drag_data_get; + iface->drag_data_delete = fr_list_model_multi_drag_data_delete; +} + + +GType +fr_list_model_get_type (void) +{ + static GType object_type = 0; + + if (object_type == 0) { + static const GTypeInfo object_info = { + sizeof (FRListModelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) fr_list_model_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (FRListModel), + 0, + (GInstanceInitFunc) fr_list_model_init, + }; + + static const GInterfaceInfo multi_drag_source_info = { + (GInterfaceInitFunc) fr_list_model_multi_drag_source_init, + NULL, + NULL + }; + + object_type = g_type_register_static (GTK_TYPE_LIST_STORE, "FRListModel", &object_info, 0); + g_type_add_interface_static (object_type, + EGG_TYPE_TREE_MULTI_DRAG_SOURCE, + &multi_drag_source_info); + } + + return object_type; +} + + +GtkListStore * +fr_list_model_new (int n_columns, ...) +{ + GtkListStore *retval; + GType *types; + va_list args; + int i; + + g_return_val_if_fail (n_columns > 0, NULL); + + retval = g_object_new (FR_TYPE_LIST_MODEL, NULL); + + va_start (args, n_columns); + types = g_new0 (GType, n_columns); + for (i = 0; i < n_columns; i++) + types[i] = va_arg (args, GType); + va_end (args); + + gtk_list_store_set_column_types (retval, n_columns, types); + g_free (types); + + return retval; +} diff --git a/src/fr-list-model.h b/src/fr-list-model.h new file mode 100644 index 0000000..c59f9dd --- /dev/null +++ b/src/fr-list-model.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_LIST_MODEL_H +#define FR_LIST_MODEL_H + +#include <gtk/gtk.h> + +#define FR_TYPE_LIST_MODEL (fr_list_model_get_type ()) +#define FR_LIST_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_LIST_MODEL, FRListModel)) +#define FR_LIST_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_LIST_MODEL, FRListModelClass)) +#define FR_IS_LIST_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_LIST_MODEL)) +#define FR_IS_LIST_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_LIST_MODEL)) +#define FR_LIST_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_LIST_MODEL, FRListModelClass)) + +typedef struct FRListModel { + GtkListStore __parent; +} FRListModel; + +typedef struct FRListModelClass { + GtkListStoreClass __parent_class; +} FRListModelClass; + +GType fr_list_model_get_type (void); +GtkListStore *fr_list_model_new (int n_columns, ...); + +#endif /* FR_LIST_MODEL_H */ diff --git a/src/fr-marshal.list b/src/fr-marshal.list new file mode 100644 index 0000000..d3fcc1a --- /dev/null +++ b/src/fr-marshal.list @@ -0,0 +1,8 @@ +VOID:INT +VOID:INT,INT +VOID:INT,POINTER +VOID:POINTER +VOID:VOID +VOID:DOUBLE +VOID:STRING +VOID:BOOL diff --git a/src/fr-process.c b/src/fr-process.c new file mode 100644 index 0000000..4750f59 --- /dev/null +++ b/src/fr-process.c @@ -0,0 +1,1028 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <glib.h> +#include "fr-process.h" +#include "fr-marshal.h" +#include "glib-utils.h" + +#define REFRESH_RATE 20 +#define BUFFER_SIZE 16384 + +enum { + START, + DONE, + STICKY_ONLY, + LAST_SIGNAL +}; + +static GObjectClass *parent_class; +static guint fr_process_signals[LAST_SIGNAL] = { 0 }; + +static void fr_process_class_init (FrProcessClass *class); +static void fr_process_init (FrProcess *process); +static void fr_process_finalize (GObject *object); + + +typedef struct { + GList *args; /* command to execute */ + char *dir; /* working directory */ + guint sticky : 1; /* whether the command must be + * executed even if a previous + * command has failed. */ + guint ignore_error : 1; /* whether to continue to execute + * other commands if this command + * fails. */ + ContinueFunc continue_func; + gpointer continue_data; + ProcFunc begin_func; + gpointer begin_data; + ProcFunc end_func; + gpointer end_data; +} FrCommandInfo; + + +static FrCommandInfo * +fr_command_info_new (void) +{ + FrCommandInfo *info; + + info = g_new0 (FrCommandInfo, 1); + info->args = NULL; + info->dir = NULL; + info->sticky = FALSE; + info->ignore_error = FALSE; + + return info; +} + + +static void +fr_command_info_free (FrCommandInfo *info) +{ + if (info == NULL) + return; + + if (info->args != NULL) { + g_list_foreach (info->args, (GFunc) g_free, NULL); + g_list_free (info->args); + info->args = NULL; + } + + if (info->dir != NULL) { + g_free (info->dir); + info->dir = NULL; + } + + g_free (info); +} + + +static void +fr_channel_data_init (FrChannelData *channel) +{ + channel->source = NULL; + channel->raw = NULL; + channel->status = G_IO_STATUS_NORMAL; + channel->error = NULL; +} + + +static void +fr_channel_data_close_source (FrChannelData *channel) +{ + if (channel->source != NULL) { + g_io_channel_shutdown (channel->source, FALSE, NULL); + g_io_channel_unref (channel->source); + channel->source = NULL; + } +} + + +static GIOStatus +fr_channel_data_read (FrChannelData *channel) +{ + char *line; + gsize length; + gsize terminator_pos; + + channel->status = G_IO_STATUS_NORMAL; + g_clear_error (&channel->error); + + while ((channel->status = g_io_channel_read_line (channel->source, + &line, + &length, + &terminator_pos, + &channel->error)) == G_IO_STATUS_NORMAL) + { + line[terminator_pos] = 0; + channel->raw = g_list_prepend (channel->raw, line); + if (channel->line_func != NULL) + (*channel->line_func) (line, channel->line_data); + } + + return channel->status; +} + + +static GIOStatus +fr_channel_data_flush (FrChannelData *channel) +{ + GIOStatus status; + + while (((status = fr_channel_data_read (channel)) != G_IO_STATUS_ERROR) && (status != G_IO_STATUS_EOF)) + /* void */; + fr_channel_data_close_source (channel); + + return status; +} + + +static void +fr_channel_data_reset (FrChannelData *channel) +{ + fr_channel_data_close_source (channel); + + if (channel->raw != NULL) { + g_list_foreach (channel->raw, (GFunc) g_free, NULL); + g_list_free (channel->raw); + channel->raw = NULL; + } +} + + +static void +fr_channel_data_free (FrChannelData *channel) +{ + fr_channel_data_reset (channel); +} + + +static void +fr_channel_data_set_fd (FrChannelData *channel, + int fd, + const char *charset) +{ + fr_channel_data_reset (channel); + + channel->source = g_io_channel_unix_new (fd); + g_io_channel_set_flags (channel->source, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_buffer_size (channel->source, BUFFER_SIZE); + if (charset != NULL) + g_io_channel_set_encoding (channel->source, charset, NULL); +} + + +const char *try_charsets[] = { "UTF-8", "ISO-8859-1", "WINDOW-1252" }; +int n_charsets = G_N_ELEMENTS (try_charsets); + + +struct _FrProcessPrivate { + GPtrArray *comm; /* FrCommandInfo elements. */ + gint n_comm; /* total number of commands */ + gint current_comm; /* currenlty editing command. */ + + GPid command_pid; + guint check_timeout; + + FrProcError first_error; + + gboolean running; + gboolean stopping; + gint current_command; + gint error_command; /* command that coused an error. */ + + gboolean use_standard_locale; + gboolean sticky_only; /* whether to execute only sticky + * commands. */ + int current_charset; +}; + + +GType +fr_process_get_type (void) +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrProcessClass), + NULL, + NULL, + (GClassInitFunc) fr_process_class_init, + NULL, + NULL, + sizeof (FrProcess), + 0, + (GInstanceInitFunc) fr_process_init + }; + + type = g_type_register_static (G_TYPE_OBJECT, + "FRProcess", + &type_info, + 0); + } + + return type; +} + + +static void +fr_process_class_init (FrProcessClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + parent_class = g_type_class_peek_parent (class); + + fr_process_signals[START] = + g_signal_new ("start", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrProcessClass, start), + NULL, NULL, + fr_marshal_VOID__VOID, + G_TYPE_NONE, 0); + fr_process_signals[DONE] = + g_signal_new ("done", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrProcessClass, done), + NULL, NULL, + fr_marshal_VOID__VOID, + G_TYPE_NONE, 0); + fr_process_signals[STICKY_ONLY] = + g_signal_new ("sticky_only", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrProcessClass, sticky_only), + NULL, NULL, + fr_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gobject_class->finalize = fr_process_finalize; + + class->start = NULL; + class->done = NULL; +} + + +static void +fr_process_init (FrProcess *process) +{ + process->priv = g_new0 (FrProcessPrivate, 1); + + process->term_on_stop = TRUE; + + process->priv->comm = g_ptr_array_new (); + process->priv->n_comm = -1; + process->priv->current_comm = -1; + + process->priv->command_pid = 0; + fr_channel_data_init (&process->out); + fr_channel_data_init (&process->err); + + process->error.gerror = NULL; + process->priv->first_error.gerror = NULL; + + process->priv->check_timeout = 0; + process->priv->running = FALSE; + process->priv->stopping = FALSE; + process->restart = FALSE; + + process->priv->current_charset = -1; + + process->priv->use_standard_locale = FALSE; +} + + +FrProcess * +fr_process_new (void) +{ + return FR_PROCESS (g_object_new (FR_TYPE_PROCESS, NULL)); +} + + +static void fr_process_stop_priv (FrProcess *process, gboolean emit_signal); + + +static void +fr_process_finalize (GObject *object) +{ + FrProcess *process; + + g_return_if_fail (object != NULL); + g_return_if_fail (FR_IS_PROCESS (object)); + + process = FR_PROCESS (object); + + fr_process_stop_priv (process, FALSE); + fr_process_clear (process); + + g_ptr_array_free (process->priv->comm, FALSE); + + fr_channel_data_free (&process->out); + fr_channel_data_free (&process->err); + + g_clear_error (&process->error.gerror); + g_clear_error (&process->priv->first_error.gerror); + + g_free (process->priv); + + /* Chain up */ + + if (G_OBJECT_CLASS (parent_class)->finalize) + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +void +fr_process_begin_command (FrProcess *process, + const char *arg) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + + info = fr_command_info_new (); + info->args = g_list_prepend (NULL, g_strdup (arg)); + + g_ptr_array_add (process->priv->comm, info); + + process->priv->n_comm++; + process->priv->current_comm = process->priv->n_comm; +} + + +void +fr_process_begin_command_at (FrProcess *process, + const char *arg, + int index) +{ + FrCommandInfo *info, *old_c_info; + + g_return_if_fail (process != NULL); + g_return_if_fail (index >= 0 && index <= process->priv->n_comm); + + process->priv->current_comm = index; + + old_c_info = g_ptr_array_index (process->priv->comm, index); + + if (old_c_info != NULL) + fr_command_info_free (old_c_info); + + info = fr_command_info_new (); + info->args = g_list_prepend (NULL, g_strdup (arg)); + + g_ptr_array_index (process->priv->comm, index) = info; +} + + +void +fr_process_set_working_dir (FrProcess *process, + const char *dir) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + g_return_if_fail (process->priv->current_comm >= 0); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + if (info->dir != NULL) + g_free (info->dir); + info->dir = g_strdup (dir); +} + + +void +fr_process_set_sticky (FrProcess *process, + gboolean sticky) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + g_return_if_fail (process->priv->current_comm >= 0); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->sticky = sticky; +} + + +void +fr_process_set_ignore_error (FrProcess *process, + gboolean ignore_error) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + g_return_if_fail (process->priv->current_comm >= 0); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->ignore_error = ignore_error; +} + + +void +fr_process_add_arg (FrProcess *process, + const char *arg) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + g_return_if_fail (process->priv->current_comm >= 0); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->args = g_list_prepend (info->args, g_strdup (arg)); +} + + +void +fr_process_add_arg_concat (FrProcess *process, + const char *arg1, + ...) +{ + GString *arg; + va_list args; + char *s; + + arg = g_string_new (arg1); + + va_start (args, arg1); + while ((s = va_arg (args, char*)) != NULL) + g_string_append (arg, s); + va_end (args); + + fr_process_add_arg (process, arg->str); + g_string_free (arg, TRUE); +} + + +void +fr_process_add_arg_printf (FrProcess *fr_proc, + const char *format, + ...) +{ + va_list args; + char *arg; + + va_start (args, format); + arg = g_strdup_vprintf (format, args); + va_end (args); + + fr_process_add_arg (fr_proc, arg); + + g_free (arg); +} + + +void +fr_process_set_arg_at (FrProcess *process, + int n_comm, + int n_arg, + const char *arg_value) +{ + FrCommandInfo *info; + GList *arg; + + g_return_if_fail (process != NULL); + + info = g_ptr_array_index (process->priv->comm, n_comm); + arg = g_list_nth (info->args, n_arg); + g_return_if_fail (arg != NULL); + + g_free (arg->data); + arg->data = g_strdup (arg_value); +} + + +void +fr_process_set_begin_func (FrProcess *process, + ProcFunc func, + gpointer func_data) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->begin_func = func; + info->begin_data = func_data; +} + + +void +fr_process_set_end_func (FrProcess *process, + ProcFunc func, + gpointer func_data) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->end_func = func; + info->end_data = func_data; +} + + +void +fr_process_set_continue_func (FrProcess *process, + ContinueFunc func, + gpointer func_data) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + + if (process->priv->current_comm < 0) + return; + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->continue_func = func; + info->continue_data = func_data; +} + + +void +fr_process_end_command (FrProcess *process) +{ + FrCommandInfo *info; + + g_return_if_fail (process != NULL); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_comm); + info->args = g_list_reverse (info->args); +} + + +void +fr_process_clear (FrProcess *process) +{ + gint i; + + g_return_if_fail (process != NULL); + + for (i = 0; i <= process->priv->n_comm; i++) { + FrCommandInfo *info; + + info = g_ptr_array_index (process->priv->comm, i); + fr_command_info_free (info); + g_ptr_array_index (process->priv->comm, i) = NULL; + } + + for (i = 0; i <= process->priv->n_comm; i++) + g_ptr_array_remove_index_fast (process->priv->comm, 0); + + process->priv->n_comm = -1; + process->priv->current_comm = -1; +} + + +void +fr_process_set_out_line_func (FrProcess *process, + LineFunc func, + gpointer data) +{ + g_return_if_fail (process != NULL); + + process->out.line_func = func; + process->out.line_data = data; +} + + +void +fr_process_set_err_line_func (FrProcess *process, + LineFunc func, + gpointer data) +{ + g_return_if_fail (process != NULL); + + process->err.line_func = func; + process->err.line_data = data; +} + + +static gboolean check_child (gpointer data); + + +static void +child_setup (gpointer user_data) +{ + FrProcess *process = user_data; + + if (process->priv->use_standard_locale) + putenv ("LC_MESSAGES=C"); + + /* detach from the tty */ + + setsid (); +} + + +static const char * +fr_process_get_charset (FrProcess *process) +{ + const char *charset = NULL; + + if (process->priv->current_charset >= 0) + charset = try_charsets[process->priv->current_charset]; + else if (g_get_charset (&charset)) + charset = NULL; + + return charset; +} + + +static void +start_current_command (FrProcess *process) +{ + FrCommandInfo *info; + GList *scan; + char **argv; + int out_fd, err_fd; + int i = 0; + + debug (DEBUG_INFO, "%d/%d) ", process->priv->current_command, process->priv->n_comm); + + info = g_ptr_array_index (process->priv->comm, process->priv->current_command); + + argv = g_new (char *, g_list_length (info->args) + 1); + for (scan = info->args; scan; scan = scan->next) + argv[i++] = scan->data; + argv[i] = NULL; + +#ifdef DEBUG + { + int j; + + if (process->priv->use_standard_locale) + g_print ("\tLC_MESSAGES=C\n"); + + if (info->dir != NULL) + g_print ("\tcd %s\n", info->dir); + + g_print ("\t"); + for (j = 0; j < i; j++) + g_print ("%s ", argv[j]); + g_print ("\n"); + } +#endif + + if (info->begin_func != NULL) + (*info->begin_func) (info->begin_data); + + if (! g_spawn_async_with_pipes (info->dir, + argv, + NULL, + (G_SPAWN_LEAVE_DESCRIPTORS_OPEN + | G_SPAWN_SEARCH_PATH + | G_SPAWN_DO_NOT_REAP_CHILD), + child_setup, + process, + &process->priv->command_pid, + NULL, + &out_fd, + &err_fd, + &process->error.gerror)) + { + process->error.type = FR_PROC_ERROR_SPAWN; + g_signal_emit (G_OBJECT (process), + fr_process_signals[DONE], + 0); + g_free (argv); + return; + } + + g_free (argv); + + fr_channel_data_set_fd (&process->out, out_fd, fr_process_get_charset (process)); + fr_channel_data_set_fd (&process->err, err_fd, fr_process_get_charset (process)); + + process->priv->check_timeout = g_timeout_add (REFRESH_RATE, + check_child, + process); +} + + +static gboolean +command_is_sticky (FrProcess *process, + int i) +{ + FrCommandInfo *info; + + info = g_ptr_array_index (process->priv->comm, i); + return info->sticky; +} + + +static void +allow_sticky_processes_only (FrProcess *process, + gboolean emit_signal) +{ + if (! process->priv->sticky_only) { + /* Remember the first error. */ + process->priv->error_command = process->priv->current_command; + process->priv->first_error.type = process->error.type; + process->priv->first_error.status = process->error.status; + g_clear_error (&process->priv->first_error.gerror); + if (process->error.gerror != NULL) + process->priv->first_error.gerror = g_error_copy (process->error.gerror); + } + + process->priv->sticky_only = TRUE; + if (emit_signal) + g_signal_emit (G_OBJECT (process), + fr_process_signals[STICKY_ONLY], + 0); +} + + +static void +fr_process_set_error (FrProcess *process, + FrProcErrorType type, + int status, + GError *gerror) +{ + process->error.type = type; + process->error.status = status; + if (gerror != process->error.gerror) { + g_clear_error (&process->error.gerror); + if (gerror != NULL) + process->error.gerror = g_error_copy (gerror); + } +} + + +static gint +check_child (gpointer data) +{ + FrProcess *process = data; + FrCommandInfo *info; + pid_t pid; + int status; + gboolean continue_process; + gboolean channel_error = FALSE; + + info = g_ptr_array_index (process->priv->comm, process->priv->current_command); + + /* Remove check. */ + + g_source_remove (process->priv->check_timeout); + process->priv->check_timeout = 0; + + if (fr_channel_data_read (&process->out) == G_IO_STATUS_ERROR) { + fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->out.error); + channel_error = TRUE; + } + else if (fr_channel_data_read (&process->err) == G_IO_STATUS_ERROR) { + fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->err.error); + channel_error = TRUE; + } + else { + pid = waitpid (process->priv->command_pid, &status, WNOHANG); + if (pid != process->priv->command_pid) { + /* Add check again. */ + process->priv->check_timeout = g_timeout_add (REFRESH_RATE, + check_child, + process); + return FALSE; + } + } + + if (info->ignore_error) { + process->error.type = FR_PROC_ERROR_NONE; + debug (DEBUG_INFO, "[ignore error]\n"); + } + else if (! channel_error && (process->error.type != FR_PROC_ERROR_STOPPED)) { + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) == 0) + process->error.type = FR_PROC_ERROR_NONE; + else if (WEXITSTATUS (status) == 255) + process->error.type = FR_PROC_ERROR_COMMAND_NOT_FOUND; + else { + process->error.type = FR_PROC_ERROR_COMMAND_ERROR; + process->error.status = WEXITSTATUS (status); + } + } + else { + process->error.type = FR_PROC_ERROR_EXITED_ABNORMALLY; + process->error.status = 255; + } + } + + process->priv->command_pid = 0; + + if (fr_channel_data_flush (&process->out) == G_IO_STATUS_ERROR) { + fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->out.error); + channel_error = TRUE; + } + else if (fr_channel_data_flush (&process->err) == G_IO_STATUS_ERROR) { + fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->err.error); + channel_error = TRUE; + } + + if (info->end_func != NULL) + (*info->end_func) (info->end_data); + + /**/ + + if (channel_error + && (process->error.type == FR_PROC_ERROR_IO_CHANNEL) + && g_error_matches (process->error.gerror, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE)) + { + if (process->priv->current_charset < n_charsets - 1) { + /* try with another charset */ + process->priv->current_charset++; + process->priv->running = FALSE; + process->restart = TRUE; + fr_process_start (process); + return FALSE; + } + /*fr_process_set_error (process, FR_PROC_ERROR_NONE, 0, NULL);*/ + fr_process_set_error (process, FR_PROC_ERROR_BAD_CHARSET, 0, process->error.gerror); + } + + /* Check whether to continue or stop the process */ + + continue_process = TRUE; + if (info->continue_func != NULL) + continue_process = (*info->continue_func) (info->continue_data); + + /* Execute next command. */ + if (continue_process) { + if (process->error.type != FR_PROC_ERROR_NONE) { + allow_sticky_processes_only (process, TRUE); +#ifdef DEBUG + { + GList *scan; + + g_print ("** ERROR **\n"); + for (scan = process->err.raw; scan; scan = scan->next) + g_print ("%s\n", (char *)scan->data); + } +#endif + } + + if (process->priv->sticky_only) { + do { + process->priv->current_command++; + } while ((process->priv->current_command <= process->priv->n_comm) + && ! command_is_sticky (process, process->priv->current_command)); + } + else + process->priv->current_command++; + + if (process->priv->current_command <= process->priv->n_comm) { + start_current_command (process); + return FALSE; + } + } + + /* Done */ + + process->priv->current_command = -1; + process->priv->use_standard_locale = FALSE; + + if (process->out.raw != NULL) + process->out.raw = g_list_reverse (process->out.raw); + if (process->err.raw != NULL) + process->err.raw = g_list_reverse (process->err.raw); + + process->priv->running = FALSE; + process->priv->stopping = FALSE; + + if (process->priv->sticky_only) { + /* Restore the first error. */ + fr_process_set_error (process, + process->priv->first_error.type, + process->priv->first_error.status, + process->priv->first_error.gerror); + } + + g_signal_emit (G_OBJECT (process), + fr_process_signals[DONE], + 0); + + return FALSE; +} + + +void +fr_process_use_standard_locale (FrProcess *process, + gboolean use_stand_locale) +{ + g_return_if_fail (process != NULL); + process->priv->use_standard_locale = use_stand_locale; +} + + +void +fr_process_start (FrProcess *process) +{ + g_return_if_fail (process != NULL); + + if (process->priv->running) + return; + + fr_channel_data_reset (&process->out); + fr_channel_data_reset (&process->err); + + process->priv->sticky_only = FALSE; + process->priv->current_command = 0; + fr_process_set_error (process, FR_PROC_ERROR_NONE, 0, NULL); + + if (! process->restart) { + process->priv->current_charset = -1; + g_signal_emit (G_OBJECT (process), + fr_process_signals[START], + 0); + } + + process->priv->stopping = FALSE; + + if (process->priv->n_comm == -1) { + process->priv->running = FALSE; + g_signal_emit (G_OBJECT (process), + fr_process_signals[DONE], + 0); + } + else { + process->priv->running = TRUE; + start_current_command (process); + } +} + + +static void +fr_process_stop_priv (FrProcess *process, + gboolean emit_signal) +{ + g_return_if_fail (process != NULL); + + if (! process->priv->running) + return; + + if (process->priv->stopping) + return; + + process->priv->stopping = TRUE; + process->error.type = FR_PROC_ERROR_STOPPED; + + if (command_is_sticky (process, process->priv->current_command)) + allow_sticky_processes_only (process, emit_signal); + + else if (process->term_on_stop && (process->priv->command_pid > 0)) + kill (process->priv->command_pid, SIGTERM); + + else { + if (process->priv->check_timeout != 0) { + g_source_remove (process->priv->check_timeout); + process->priv->check_timeout = 0; + } + + process->priv->command_pid = 0; + fr_channel_data_close_source (&process->out); + fr_channel_data_close_source (&process->err); + + process->priv->running = FALSE; + + if (emit_signal) + g_signal_emit (G_OBJECT (process), + fr_process_signals[DONE], + 0); + } +} + + +void +fr_process_stop (FrProcess *process) +{ + fr_process_stop_priv (process, TRUE); +} diff --git a/src/fr-process.h b/src/fr-process.h new file mode 100644 index 0000000..d519b03 --- /dev/null +++ b/src/fr-process.h @@ -0,0 +1,135 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003, 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_PROCESS_H +#define FR_PROCESS_H + +#include <glib.h> +#include <gtk/gtk.h> +#include <sys/types.h> +#include "typedefs.h" + +#define FR_TYPE_PROCESS (fr_process_get_type ()) +#define FR_PROCESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_PROCESS, FrProcess)) +#define FR_PROCESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_PROCESS, FrProcessClass)) +#define FR_IS_PROCESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_PROCESS)) +#define FR_IS_PROCESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_PROCESS)) +#define FR_PROCESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_PROCESS, FrProcessClass)) + +typedef struct _FrProcess FrProcess; +typedef struct _FrProcessClass FrProcessClass; +typedef struct _FrProcessPrivate FrProcessPrivate; + +typedef void (*ProcFunc) (gpointer data); +typedef gboolean (*ContinueFunc) (gpointer data); +typedef void (*LineFunc) (char *line, gpointer data); + +typedef struct { + GIOChannel *source; + GList *raw; + LineFunc line_func; + gpointer line_data; + GIOStatus status; + GError *error; +} FrChannelData; + +struct _FrProcess { + GObject __parent; + + /*< public >*/ + + gboolean term_on_stop; /* whether we must terminate the + * command when calling + * fr_process_stop. */ + + /*< public read-only >*/ + + FrChannelData out; + FrChannelData err; + FrProcError error; + + /*< protected >*/ + + gboolean restart; /* whether to restart the process + * after an error. */ + + FrProcessPrivate *priv; +}; + +struct _FrProcessClass { + GObjectClass __parent_class; + + /* -- Signals -- */ + + void (* start) (FrProcess *fr_proc); + void (* done) (FrProcess *fr_proc); + void (* sticky_only) (FrProcess *fr_proc); +}; + +GType fr_process_get_type (void); +FrProcess * fr_process_new (void); +void fr_process_clear (FrProcess *fr_proc); +void fr_process_begin_command (FrProcess *fr_proc, + const char *arg); +void fr_process_begin_command_at (FrProcess *fr_proc, + const char *arg, + int index); +void fr_process_add_arg (FrProcess *fr_proc, + const char *arg); +void fr_process_add_arg_concat (FrProcess *fr_proc, + const char *arg, + ...) G_GNUC_NULL_TERMINATED; +void fr_process_add_arg_printf (FrProcess *fr_proc, + const char *format, + ...) G_GNUC_PRINTF (2, 3); +void fr_process_set_arg_at (FrProcess *fr_proc, + int n_comm, + int n_arg, + const char *arg); +void fr_process_set_begin_func (FrProcess *fr_proc, + ProcFunc func, + gpointer func_data); +void fr_process_set_end_func (FrProcess *fr_proc, + ProcFunc func, + gpointer func_data); +void fr_process_set_continue_func (FrProcess *fr_proc, + ContinueFunc func, + gpointer func_data); +void fr_process_end_command (FrProcess *fr_proc); +void fr_process_set_working_dir (FrProcess *fr_proc, + const char *arg); +void fr_process_set_sticky (FrProcess *fr_proc, + gboolean sticky); +void fr_process_set_ignore_error (FrProcess *fr_proc, + gboolean ignore_error); +void fr_process_use_standard_locale (FrProcess *fr_proc, + gboolean use_stand_locale); +void fr_process_set_out_line_func (FrProcess *fr_proc, + LineFunc func, + gpointer func_data); +void fr_process_set_err_line_func (FrProcess *fr_proc, + LineFunc func, + gpointer func_data); +void fr_process_start (FrProcess *fr_proc); +void fr_process_stop (FrProcess *fr_proc); + +#endif /* FR_PROCESS_H */ diff --git a/src/fr-stock.c b/src/fr-stock.c new file mode 100644 index 0000000..eecd5d7 --- /dev/null +++ b/src/fr-stock.c @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File Roller + * + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include "fr-stock.h" + + +static gboolean stock_initialized = FALSE; + +static const struct { + char *stock_id; + char *icon; +} stock_icons [] = { + { FR_STOCK_CREATE_ARCHIVE, "add-files-to-archive" }, + { FR_STOCK_ADD_FILES, "add-files-to-archive" }, + { FR_STOCK_ADD_FOLDER, "add-folder-to-archive" }, + { FR_STOCK_EXTRACT, "extract-archive" } +}; + +static const GtkStockItem stock_items [] = { + { FR_STOCK_CREATE_ARCHIVE, N_("C_reate"), 0, 0, GETTEXT_PACKAGE }, + { FR_STOCK_ADD_FILES, N_("_Add"), 0, 0, GETTEXT_PACKAGE }, + { FR_STOCK_ADD_FOLDER, N_("_Add"), 0, 0, GETTEXT_PACKAGE }, + { FR_STOCK_EXTRACT, N_("_Extract"), 0, 0, GETTEXT_PACKAGE } +}; + +void +fr_stock_init (void) +{ + GtkIconFactory *factory; + GtkIconSource *source; + int i; + + if (stock_initialized) + return; + stock_initialized = TRUE; + + gtk_stock_add_static (stock_items, G_N_ELEMENTS (stock_items)); + + factory = gtk_icon_factory_new (); + gtk_icon_factory_add_default (factory); + + source = gtk_icon_source_new (); + + for (i = 0; i < G_N_ELEMENTS (stock_icons); i++) { + GtkIconSet *set; + + gtk_icon_source_set_icon_name (source, stock_icons [i].icon); + + set = gtk_icon_set_new (); + gtk_icon_set_add_source (set, source); + + gtk_icon_factory_add (factory, stock_icons [i].stock_id, set); + gtk_icon_set_unref (set); + } + + gtk_icon_source_free (source); + + g_object_unref (factory); +} diff --git a/src/fr-stock.h b/src/fr-stock.h new file mode 100644 index 0000000..8d43bcd --- /dev/null +++ b/src/fr-stock.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File Roller + * + * Copyright (C) 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_STOCK_H +#define FR_STOCK_H + +#define FR_STOCK_CREATE_ARCHIVE "create-archive" +#define FR_STOCK_ADD_FILES "add-files-to-archive" +#define FR_STOCK_ADD_FOLDER "add-folder-to-archive" +#define FR_STOCK_EXTRACT "extract-archive" + +void fr_stock_init (void); + +#endif /* FR_STOCK_H */ diff --git a/src/fr-window.c b/src/fr-window.c new file mode 100644 index 0000000..41407c6 --- /dev/null +++ b/src/fr-window.c @@ -0,0 +1,8855 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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. + * + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <math.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "actions.h" +#include "dlg-batch-add.h" +#include "dlg-delete.h" +#include "dlg-extract.h" +#include "dlg-open-with.h" +#include "dlg-ask-password.h" +#include "dlg-package-installer.h" +#include "dlg-update.h" +#include "eggtreemultidnd.h" +#include "fr-marshal.h" +#include "fr-list-model.h" +#include "fr-archive.h" +#include "fr-error.h" +#include "fr-stock.h" +#include "fr-window.h" +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "main.h" +#include "gtk-utils.h" +#include "mateconf-utils.h" +#include "open-file.h" +#include "typedefs.h" +#include "ui.h" + + +#define LAST_OUTPUT_DIALOG_NAME "last-output" +#define MAX_HISTORY_LEN 5 +#define ACTIVITY_DELAY 100 +#define ACTIVITY_PULSE_STEP (0.033) +#define MAX_MESSAGE_LENGTH 50 + +#define PROGRESS_DIALOG_DEFAULT_WIDTH 400 +#define PROGRESS_TIMEOUT_MSECS 5000 +#define PROGRESS_BAR_HEIGHT 10 +#undef LOG_PROGRESS + +#define HIDE_PROGRESS_TIMEOUT_MSECS 500 +#define DEFAULT_NAME_COLUMN_WIDTH 250 +#define OTHER_COLUMNS_WIDTH 100 +#define RECENT_ITEM_MAX_WIDTH 25 + +#define DEF_WIN_WIDTH 600 +#define DEF_WIN_HEIGHT 480 +#define DEF_SIDEBAR_WIDTH 200 + +#define FILE_LIST_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR +#define DIR_TREE_ICON_SIZE GTK_ICON_SIZE_MENU + +#define BAD_CHARS "/\\*" + +static GHashTable *pixbuf_hash = NULL; +static GHashTable *tree_pixbuf_hash = NULL; +static GtkIconTheme *icon_theme = NULL; +static int file_list_icon_size = 0; +static int dir_tree_icon_size = 0; + +#define XDS_FILENAME "xds.txt" +#define MAX_XDS_ATOM_VAL_LEN 4096 +#define XDS_ATOM gdk_atom_intern ("XdndDirectSave0", FALSE) +#define TEXT_ATOM gdk_atom_intern ("text/plain", FALSE) +#define OCTET_ATOM gdk_atom_intern ("application/octet-stream", FALSE) +#define XFR_ATOM gdk_atom_intern ("XdndFileRoller0", FALSE) + +#define FR_CLIPBOARD (gdk_atom_intern_static_string ("_FILE_ROLLER_SPECIAL_CLIPBOARD")) +#define FR_SPECIAL_URI_LIST (gdk_atom_intern_static_string ("application/file-roller-uri-list")) + +static GtkTargetEntry clipboard_targets[] = { + { "application/file-roller-uri-list", 0, 1 } +}; + +static GtkTargetEntry target_table[] = { + { "XdndFileRoller0", 0, 0 }, + { "text/uri-list", 0, 1 }, +}; + +static GtkTargetEntry folder_tree_targets[] = { + { "XdndFileRoller0", 0, 0 }, + { "XdndDirectSave0", 0, 2 } +}; + + +typedef struct { + FrBatchActionType type; + void * data; + GFreeFunc free_func; +} FRBatchAction; + + +typedef struct { + guint converting : 1; + char *temp_dir; + FrArchive *new_archive; + char *password; + gboolean encrypt_header; + guint volume_size; + char *new_file; +} FRConvertData; + + +typedef enum { + FR_CLIPBOARD_OP_CUT, + FR_CLIPBOARD_OP_COPY +} FRClipboardOp; + + +typedef struct { + GList *file_list; + char *extract_to_dir; + char *base_dir; + gboolean skip_older; + gboolean overwrite; + gboolean junk_paths; + char *password; + gboolean extract_here; +} ExtractData; + + +typedef enum { + FR_WINDOW_AREA_MENUBAR, + FR_WINDOW_AREA_TOOLBAR, + FR_WINDOW_AREA_LOCATIONBAR, + FR_WINDOW_AREA_CONTENTS, + FR_WINDOW_AREA_FILTERBAR, + FR_WINDOW_AREA_STATUSBAR, +} FrWindowArea; + + +typedef enum { + DIALOG_RESPONSE_NONE = 1, + DIALOG_RESPONSE_OPEN_ARCHIVE, + DIALOG_RESPONSE_OPEN_DESTINATION_FOLDER, + DIALOG_RESPONSE_QUIT +} DialogResponse; + + +/* -- FrClipboardData -- */ + + +typedef struct { + int refs; + char *archive_filename; + char *archive_password; + FRClipboardOp op; + char *base_dir; + GList *files; + char *tmp_dir; + char *current_dir; +} FrClipboardData; + + +static FrClipboardData* +fr_clipboard_data_new (void) +{ + FrClipboardData *data; + + data = g_new0 (FrClipboardData, 1); + data->refs = 1; + + return data; +} + + +static FrClipboardData * +fr_clipboard_data_ref (FrClipboardData *clipboard_data) +{ + clipboard_data->refs++; + return clipboard_data; +} + + +static void +fr_clipboard_data_unref (FrClipboardData *clipboard_data) +{ + if (clipboard_data == NULL) + return; + if (--clipboard_data->refs > 0) + return; + + g_free (clipboard_data->archive_filename); + g_free (clipboard_data->archive_password); + g_free (clipboard_data->base_dir); + g_free (clipboard_data->tmp_dir); + g_free (clipboard_data->current_dir); + g_list_foreach (clipboard_data->files, (GFunc) g_free, NULL); + g_list_free (clipboard_data->files); + g_free (clipboard_data); +} + + +static void +fr_clipboard_data_set_password (FrClipboardData *clipboard_data, + const char *password) +{ + if (clipboard_data->archive_password != password) + g_free (clipboard_data->archive_password); + if (password != NULL) + clipboard_data->archive_password = g_strdup (password); +} + + +/**/ + +enum { + ARCHIVE_LOADED, + LAST_SIGNAL +}; + +static GtkWindowClass *parent_class = NULL; +static guint fr_window_signals[LAST_SIGNAL] = { 0 }; + +struct _FrWindowPrivateData { + GtkWidget *layout; + GtkWidget *contents; + GtkWidget *list_view; + GtkListStore *list_store; + GtkWidget *tree_view; + GtkTreeStore *tree_store; + GtkWidget *toolbar; + GtkWidget *statusbar; + GtkWidget *progress_bar; + GtkWidget *location_bar; + GtkWidget *location_entry; + GtkWidget *location_label; + GtkWidget *filter_bar; + GtkWidget *filter_entry; + GtkWidget *paned; + GtkWidget *sidepane; + GtkTreePath *tree_hover_path; + GtkTreePath *list_hover_path; + GtkTreeViewColumn *filename_column; + + gboolean filter_mode; + gint current_view_length; + + guint help_message_cid; + guint list_info_cid; + guint progress_cid; + char * last_status_message; + + GtkWidget * up_arrows[5]; + GtkWidget * down_arrows[5]; + + FrAction action; + gboolean archive_present; + gboolean archive_new; /* A new archive has been created + * but it doesn't contain any + * file yet. The real file will + * be created only when the user + * adds some file to the + * archive.*/ + + char * archive_uri; + char * open_default_dir; /* default directory to be used + * in the Open dialog. */ + char * add_default_dir; /* default directory to be used + * in the Add dialog. */ + char * extract_default_dir; /* default directory to be used + * in the Extract dialog. */ + gboolean freeze_default_dir; + gboolean asked_for_password; + gboolean ask_to_open_destination_after_extraction; + gboolean destroy_with_error_dialog; + + FRBatchAction current_batch_action; + + gboolean give_focus_to_the_list; + gboolean single_click; + GtkTreePath *path_clicked; + + FrWindowSortMethod sort_method; + GtkSortType sort_type; + + char * last_location; + + gboolean view_folders; + FrWindowListMode list_mode; + FrWindowListMode last_list_mode; + GList * history; + GList * history_current; + char * password; + char * password_for_paste; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; + + guint activity_timeout_handle; /* activity timeout + * handle. */ + gint activity_ref; /* when > 0 some activity + * is present. */ + + guint update_timeout_handle; /* update file list + * timeout handle. */ + + FRConvertData convert_data; + + gboolean stoppable; + gboolean closing; + + FrClipboardData *clipboard_data; + FrClipboardData *copy_data; + + FrArchive *copy_from_archive; + + GtkActionGroup *actions; + + GtkRecentManager *recent_manager; + GtkWidget *recent_chooser_menu; + GtkWidget *recent_chooser_toolbar; + + GtkWidget *file_popup_menu; + GtkWidget *folder_popup_menu; + GtkWidget *sidebar_folder_popup_menu; + GtkWidget *mitem_recents_menu; + GtkWidget *recent_toolbar_menu; + GtkAction *open_action; + + /* dragged files data */ + + char *drag_destination_folder; + char *drag_base_dir; + GError *drag_error; + GList *drag_file_list; /* the list of files we are + * dragging*/ + + /* progress dialog data */ + + GtkWidget *progress_dialog; + GtkWidget *pd_action; + GtkWidget *pd_archive; + GtkWidget *pd_message; + GtkWidget *pd_progress_bar; + GtkWidget *pd_cancel_button; + GtkWidget *pd_close_button; + GtkWidget *pd_open_archive_button; + GtkWidget *pd_open_destination_button; + GtkWidget *pd_quit_button; + gboolean progress_pulse; + guint progress_timeout; /* Timeout to display the progress dialog. */ + guint hide_progress_timeout; /* Timeout to hide the progress dialog. */ + FrAction pd_last_action; + char *pd_last_archive; + char *working_archive; + + /* update dialog data */ + + gpointer update_dialog; + GList *open_files; + + /* batch mode data */ + + gboolean batch_mode; /* whether we are in a non interactive + * mode. */ + GList *batch_action_list; /* FRBatchAction * elements */ + GList *batch_action; /* current action. */ + + /* misc */ + + guint cnxn_id[MATECONF_NOTIFICATIONS]; + + gulong theme_changed_handler_id; + gboolean non_interactive; + char *extract_here_dir; + gboolean extract_interact_use_default_dir; + gboolean update_dropped_files; + gboolean batch_adding_one_file; + + GtkWindow *load_error_parent_window; + gboolean showing_error_dialog; + GtkWindow *error_dialog_parent; +}; + + +/* -- fr_window_free_private_data -- */ + + +static void +fr_window_remove_notifications (FrWindow *window) +{ + int i; + + for (i = 0; i < MATECONF_NOTIFICATIONS; i++) + if (window->priv->cnxn_id[i] != -1) + eel_mateconf_notification_remove (window->priv->cnxn_id[i]); +} + + +static void +fr_window_free_batch_data (FrWindow *window) +{ + GList *scan; + + for (scan = window->priv->batch_action_list; scan; scan = scan->next) { + FRBatchAction *adata = scan->data; + + if ((adata->data != NULL) && (adata->free_func != NULL)) + (*adata->free_func) (adata->data); + g_free (adata); + } + + g_list_free (window->priv->batch_action_list); + window->priv->batch_action_list = NULL; + window->priv->batch_action = NULL; +} + + +static void +gh_unref_pixbuf (gpointer key, + gpointer value, + gpointer user_data) +{ + g_object_unref (value); +} + + +static void +fr_window_clipboard_remove_file_list (FrWindow *window, + GList *file_list) +{ + GList *scan1; + + if (window->priv->copy_data == NULL) + return; + + if (file_list == NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + return; + } + + for (scan1 = file_list; scan1; scan1 = scan1->next) { + const char *name1 = scan1->data; + GList *scan2; + + for (scan2 = window->priv->copy_data->files; scan2;) { + const char *name2 = scan2->data; + + if (strcmp (name1, name2) == 0) { + GList *tmp = scan2->next; + window->priv->copy_data->files = g_list_remove_link (window->priv->copy_data->files, scan2); + g_free (scan2->data); + g_list_free (scan2); + scan2 = tmp; + } + else + scan2 = scan2->next; + } + } + + if (window->priv->copy_data->files == NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + } +} + + +static void +fr_window_history_clear (FrWindow *window) +{ + if (window->priv->history != NULL) + path_list_free (window->priv->history); + window->priv->history = NULL; + window->priv->history_current = NULL; + g_free (window->priv->last_location); + window->priv->last_location = NULL; +} + + +static void +fr_window_free_open_files (FrWindow *window) +{ + GList *scan; + + for (scan = window->priv->open_files; scan; scan = scan->next) { + OpenFile *file = scan->data; + + if (file->monitor != NULL) + g_file_monitor_cancel (file->monitor); + open_file_free (file); + } + g_list_free (window->priv->open_files); + window->priv->open_files = NULL; +} + + +static void +fr_window_convert_data_free (FrWindow *window, + gboolean all) +{ + if (all) { + g_free (window->priv->convert_data.new_file); + window->priv->convert_data.new_file = NULL; + } + + window->priv->convert_data.converting = FALSE; + + if (window->priv->convert_data.temp_dir != NULL) { + g_free (window->priv->convert_data.temp_dir); + window->priv->convert_data.temp_dir = NULL; + } + + if (window->priv->convert_data.new_archive != NULL) { + g_object_unref (window->priv->convert_data.new_archive); + window->priv->convert_data.new_archive = NULL; + } + + if (window->priv->convert_data.password != NULL) { + g_free (window->priv->convert_data.password); + window->priv->convert_data.password = NULL; + } +} + + +static void +fr_window_free_private_data (FrWindow *window) +{ + FrWindowPrivateData *priv = window->priv; + + if (priv->update_timeout_handle != 0) { + g_source_remove (priv->update_timeout_handle); + priv->update_timeout_handle = 0; + } + + fr_window_remove_notifications (window); + + if (priv->open_action != NULL) { + g_object_unref (priv->open_action); + priv->open_action = NULL; + } + + if (priv->recent_toolbar_menu != NULL) { + gtk_widget_destroy (priv->recent_toolbar_menu); + priv->recent_toolbar_menu = NULL; + } + + while (priv->activity_ref > 0) + fr_window_stop_activity_mode (window); + + if (priv->progress_timeout != 0) { + g_source_remove (priv->progress_timeout); + priv->progress_timeout = 0; + } + + if (priv->hide_progress_timeout != 0) { + g_source_remove (priv->hide_progress_timeout); + priv->hide_progress_timeout = 0; + } + + if (priv->theme_changed_handler_id != 0) + g_signal_handler_disconnect (icon_theme, priv->theme_changed_handler_id); + + fr_window_history_clear (window); + + g_free (priv->open_default_dir); + g_free (priv->add_default_dir); + g_free (priv->extract_default_dir); + g_free (priv->archive_uri); + + g_free (priv->password); + g_free (priv->password_for_paste); + + g_object_unref (priv->list_store); + + if (window->priv->clipboard_data != NULL) { + fr_clipboard_data_unref (window->priv->clipboard_data); + window->priv->clipboard_data = NULL; + } + if (window->priv->copy_data != NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + } + if (priv->copy_from_archive != NULL) { + g_object_unref (priv->copy_from_archive); + priv->copy_from_archive = NULL; + } + + fr_window_free_open_files (window); + + fr_window_convert_data_free (window, TRUE); + + g_clear_error (&priv->drag_error); + path_list_free (priv->drag_file_list); + priv->drag_file_list = NULL; + + if (priv->file_popup_menu != NULL) { + gtk_widget_destroy (priv->file_popup_menu); + priv->file_popup_menu = NULL; + } + + if (priv->folder_popup_menu != NULL) { + gtk_widget_destroy (priv->folder_popup_menu); + priv->folder_popup_menu = NULL; + } + + if (priv->sidebar_folder_popup_menu != NULL) { + gtk_widget_destroy (priv->sidebar_folder_popup_menu); + priv->sidebar_folder_popup_menu = NULL; + } + + g_free (window->priv->last_location); + + fr_window_free_batch_data (window); + fr_window_reset_current_batch_action (window); + + g_free (priv->pd_last_archive); + g_free (priv->extract_here_dir); + g_free (priv->last_status_message); + + preferences_set_sort_method (priv->sort_method); + preferences_set_sort_type (priv->sort_type); + preferences_set_list_mode (priv->last_list_mode); +} + + +static void +fr_window_finalize (GObject *object) +{ + FrWindow *window = FR_WINDOW (object); + + fr_window_free_open_files (window); + + if (window->archive != NULL) { + g_object_unref (window->archive); + window->archive = NULL; + } + + if (window->priv != NULL) { + fr_window_free_private_data (window); + g_free (window->priv); + window->priv = NULL; + } + + WindowList = g_list_remove (WindowList, window); + + G_OBJECT_CLASS (parent_class)->finalize (object); + + if (WindowList == NULL) { + if (pixbuf_hash != NULL) { + g_hash_table_foreach (pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (pixbuf_hash); + } + if (tree_pixbuf_hash != NULL) { + g_hash_table_foreach (tree_pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (tree_pixbuf_hash); + } + + gtk_main_quit (); + } +} + + +static gboolean +close__step2 (gpointer data) +{ + gtk_widget_destroy (GTK_WIDGET (data)); + return FALSE; +} + + +void +fr_window_close (FrWindow *window) +{ + if (window->priv->activity_ref > 0) + return; + + window->priv->closing = TRUE; + + if (gtk_widget_get_realized (GTK_WIDGET (window))) { + int width, height; + + #if GTK_CHECK_VERSION(3, 0, 0) + width = gdk_window_get_width(gtk_widget_get_window(GTK_WIDGET(window))); + height = gdk_window_get_height(gtk_widget_get_window(GTK_WIDGET(window))); + #else + gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(window)), &width, &height); + #endif + + eel_mateconf_set_integer (PREF_UI_WINDOW_WIDTH, width); + eel_mateconf_set_integer (PREF_UI_WINDOW_HEIGHT, height); + + width = gtk_paned_get_position (GTK_PANED (window->priv->paned)); + if (width > 0) + eel_mateconf_set_integer (PREF_UI_SIDEBAR_WIDTH, width); + + width = gtk_tree_view_column_get_width (window->priv->filename_column); + if (width > 0) + eel_mateconf_set_integer (PREF_NAME_COLUMN_WIDTH, width); + } + + g_idle_add (close__step2, window); +} + + +static void +fr_window_class_init (FrWindowClass *class) +{ + GObjectClass *gobject_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (class); + + fr_window_signals[ARCHIVE_LOADED] = + g_signal_new ("archive-loaded", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrWindowClass, archive_loaded), + NULL, NULL, + fr_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + gobject_class = (GObjectClass*) class; + gobject_class->finalize = fr_window_finalize; + + widget_class = (GtkWidgetClass*) class; +} + + +static void fr_window_update_paste_command_sensitivity (FrWindow *, GtkClipboard *); + + +static void +clipboard_owner_change_cb (GtkClipboard *clipboard, + GdkEvent *event, + gpointer user_data) +{ + fr_window_update_paste_command_sensitivity ((FrWindow *) user_data, clipboard); +} + + +static void +fr_window_realized (GtkWidget *window, + gpointer *data) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (window, FR_CLIPBOARD); + g_signal_connect (clipboard, + "owner_change", + G_CALLBACK (clipboard_owner_change_cb), + window); +} + + +static void +fr_window_unrealized (GtkWidget *window, + gpointer *data) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (window, FR_CLIPBOARD); + g_signal_handlers_disconnect_by_func (clipboard, + G_CALLBACK (clipboard_owner_change_cb), + window); +} + + +static void +fr_window_init (FrWindow *window) +{ + window->priv = g_new0 (FrWindowPrivateData, 1); + window->priv->update_dropped_files = FALSE; + window->priv->filter_mode = FALSE; + + g_signal_connect (window, + "realize", + G_CALLBACK (fr_window_realized), + NULL); + g_signal_connect (window, + "unrealize", + G_CALLBACK (fr_window_unrealized), + NULL); + + WindowList = g_list_prepend (WindowList, window); +} + + +GType +fr_window_get_type (void) +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrWindowClass), + NULL, + NULL, + (GClassInitFunc) fr_window_class_init, + NULL, + NULL, + sizeof (FrWindow), + 0, + (GInstanceInitFunc) fr_window_init + }; + + type = g_type_register_static (GTK_TYPE_WINDOW, + "FrWindow", + &type_info, + 0); + } + + return type; +} + + +/* -- window history -- */ + + +#if 0 +static void +fr_window_history_print (FrWindow *window) +{ + GList *list; + + debug (DEBUG_INFO, "history:\n"); + for (list = window->priv->history; list; list = list->next) + g_print ("\t%s %s\n", + (char*) list->data, + (list == window->priv->history_current)? "<-": ""); + g_print ("\n"); +} +#endif + + +static void +fr_window_history_add (FrWindow *window, + const char *path) +{ + if ((window->priv->history != NULL) && (window->priv->history_current != NULL)) { + if (strcmp (window->priv->history_current->data, path) == 0) + return; + + /* Add locations visited using the back button to the history + * list. */ + if (window->priv->history != window->priv->history_current) { + GList *scan = window->priv->history->next; + while (scan != window->priv->history_current->next) { + window->priv->history = g_list_prepend (window->priv->history, g_strdup (scan->data)); + scan = scan->next; + } + } + } + + window->priv->history = g_list_prepend (window->priv->history, g_strdup (path)); + window->priv->history_current = window->priv->history; +} + + +static void +fr_window_history_pop (FrWindow *window) +{ + GList *first; + + if (window->priv->history == NULL) + return; + + first = window->priv->history; + window->priv->history = g_list_remove_link (window->priv->history, first); + if (window->priv->history_current == first) + window->priv->history_current = window->priv->history; + g_free (first->data); + g_list_free (first); +} + + +/* -- window_update_file_list -- */ + + +static GPtrArray * +fr_window_get_current_dir_list (FrWindow *window) +{ + GPtrArray *files; + int i; + + files = g_ptr_array_sized_new (128); + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + + if (fdata->list_name == NULL) + continue; + g_ptr_array_add (files, fdata); + } + + return files; +} + + +static gint +sort_by_name (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + + return strcasecmp (fdata1->list_name, fdata2->list_name); +} + + +static gint +sort_by_size (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) { + if (fdata1->dir_size > fdata2->dir_size) + return 1; + else + return -1; + } + + if (fdata1->size == fdata2->size) + return sort_by_name (ptr1, ptr2); + else if (fdata1->size > fdata2->size) + return 1; + else + return -1; +} + + +static gint +sort_by_type (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + int result; + const char *desc1, *desc2; + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) + return sort_by_name (ptr1, ptr2); + + desc1 = g_content_type_get_description (fdata1->content_type); + desc2 = g_content_type_get_description (fdata2->content_type); + + result = strcasecmp (desc1, desc2); + if (result == 0) + return sort_by_name (ptr1, ptr2); + else + return result; +} + + +static gint +sort_by_time (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) + return sort_by_name (ptr1, ptr2); + + if (fdata1->modified == fdata2->modified) + return sort_by_name (ptr1, ptr2); + else if (fdata1->modified > fdata2->modified) + return 1; + else + return -1; +} + + +static gint +sort_by_path (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + int result; + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) + return sort_by_name (ptr1, ptr2); + + /* 2 files */ + + result = strcasecmp (fdata1->path, fdata2->path); + if (result == 0) + return sort_by_name (ptr1, ptr2); + else + return result; +} + + +static guint64 +get_dir_size (FrWindow *window, + const char *current_dir, + const char *name) +{ + guint64 size; + char *dirname; + int dirname_l; + int i; + + dirname = g_strconcat (current_dir, name, "/", NULL); + dirname_l = strlen (dirname); + + size = 0; + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + + if (strncmp (dirname, fd->full_path, dirname_l) == 0) + size += fd->size; + } + + g_free (dirname); + + return size; +} + + +static gboolean +file_data_respects_filter (FrWindow *window, + FileData *fdata) +{ + const char *filter; + + filter = gtk_entry_get_text (GTK_ENTRY (window->priv->filter_entry)); + if ((fdata == NULL) || (filter == NULL) || (*filter == '\0')) + return TRUE; + + if (fdata->dir || (fdata->name == NULL)) + return FALSE; + + return strncasecmp (fdata->name, filter, strlen (filter)) == 0; +} + + +static gboolean +compute_file_list_name (FrWindow *window, + FileData *fdata, + const char *current_dir, + int current_dir_len, + GHashTable *names_hash, + gboolean *different_name) +{ + register char *scan, *end; + + *different_name = FALSE; + + if (! file_data_respects_filter (window, fdata)) + return FALSE; + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + fdata->list_name = g_strdup (fdata->name); + if (fdata->dir) + fdata->dir_size = 0; + return FALSE; + } + + if (strncmp (fdata->full_path, current_dir, current_dir_len) != 0) { + *different_name = TRUE; + return FALSE; + } + + if (strlen (fdata->full_path) == current_dir_len) + return FALSE; + + scan = fdata->full_path + current_dir_len; + end = strchr (scan, '/'); + if ((end == NULL) && ! fdata->dir) { /* file */ + fdata->list_name = g_strdup (scan); + } + else { /* folder */ + char *dir_name; + + if (end != NULL) + dir_name = g_strndup (scan, end - scan); + else + dir_name = g_strdup (scan); + + /* avoid to insert duplicated folders */ + if (g_hash_table_lookup (names_hash, dir_name) != NULL) { + g_free (dir_name); + return FALSE; + } + g_hash_table_insert (names_hash, dir_name, GINT_TO_POINTER (1)); + + if ((end != NULL) && (*(end + 1) != '\0')) + fdata->list_dir = TRUE; + fdata->list_name = dir_name; + fdata->dir_size = get_dir_size (window, current_dir, dir_name); + } + + return TRUE; +} + + +static void +fr_window_compute_list_names (FrWindow *window, + GPtrArray *files) +{ + const char *current_dir; + int current_dir_len; + GHashTable *names_hash; + int i; + gboolean visible_list_started = FALSE; + gboolean visible_list_completed = FALSE; + gboolean different_name; + + current_dir = fr_window_get_current_location (window); + current_dir_len = strlen (current_dir); + names_hash = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < files->len; i++) { + FileData *fdata = g_ptr_array_index (files, i); + + g_free (fdata->list_name); + fdata->list_name = NULL; + fdata->list_dir = FALSE; + + /* the files array is sorted by path, when the visible list + * is started and we find a path that doesn't match the + * current_dir path, the following files can't match + * the current_dir path. */ + + if (visible_list_completed) + continue; + + if (compute_file_list_name (window, fdata, current_dir, current_dir_len, names_hash, &different_name)) { + visible_list_started = TRUE; + } + else if (visible_list_started && different_name) + visible_list_completed = TRUE; + } + + g_hash_table_destroy (names_hash); +} + + +static gboolean +fr_window_dir_exists_in_archive (FrWindow *window, + const char *dir_name) +{ + int dir_name_len; + int i; + + if (dir_name == NULL) + return FALSE; + + dir_name_len = strlen (dir_name); + if (dir_name_len == 0) + return TRUE; + + if (strcmp (dir_name, "/") == 0) + return TRUE; + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + + if (strncmp (dir_name, fdata->full_path, dir_name_len) == 0) { + return TRUE; + } + else if (fdata->dir + && (fdata->full_path[strlen (fdata->full_path)] != '/') + && (strncmp (dir_name, fdata->full_path, dir_name_len - 1) == 0)) + { + return TRUE; + } + } + + return FALSE; +} + + +static char * +get_parent_dir (const char *current_dir) +{ + char *dir; + char *new_dir; + char *retval; + + if (current_dir == NULL) + return NULL; + if (strcmp (current_dir, "/") == 0) + return g_strdup ("/"); + + dir = g_strdup (current_dir); + dir[strlen (dir) - 1] = 0; + new_dir = remove_level_from_path (dir); + g_free (dir); + + if (new_dir[strlen (new_dir) - 1] == '/') + retval = new_dir; + else { + retval = g_strconcat (new_dir, "/", NULL); + g_free (new_dir); + } + + return retval; +} + + +static void fr_window_update_statusbar_list_info (FrWindow *window); + + +static GdkPixbuf * +get_mime_type_icon (const char *mime_type) +{ + GdkPixbuf *pixbuf = NULL; + + pixbuf = g_hash_table_lookup (tree_pixbuf_hash, mime_type); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + pixbuf = get_mime_type_pixbuf (mime_type, file_list_icon_size, icon_theme); + if (pixbuf == NULL) + return NULL; + + pixbuf = gdk_pixbuf_copy (pixbuf); + g_hash_table_insert (tree_pixbuf_hash, (gpointer) mime_type, pixbuf); + g_object_ref (G_OBJECT (pixbuf)); + + return pixbuf; +} + + +static GdkPixbuf * +get_icon (GtkWidget *widget, + FileData *fdata) +{ + GdkPixbuf *pixbuf = NULL; + const char *content_type; + GIcon *icon; + + if (file_data_is_dir (fdata)) + content_type = MIME_TYPE_DIRECTORY; + else + content_type = fdata->content_type; + + /* look in the hash table. */ + + pixbuf = g_hash_table_lookup (pixbuf_hash, content_type); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + icon = g_content_type_get_icon (content_type); + pixbuf = get_icon_pixbuf (icon, file_list_icon_size, icon_theme); + g_object_unref (icon); + + if (pixbuf == NULL) + return NULL; + + pixbuf = gdk_pixbuf_copy (pixbuf); + g_hash_table_insert (pixbuf_hash, (gpointer) content_type, pixbuf); + g_object_ref (G_OBJECT (pixbuf)); + + return pixbuf; +} + + +static GdkPixbuf * +get_emblem (GtkWidget *widget, + FileData *fdata) +{ + GdkPixbuf *pixbuf = NULL; + + if (! fdata->encrypted) + return NULL; + + /* encrypted */ + + pixbuf = g_hash_table_lookup (pixbuf_hash, "emblem-nowrite"); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + pixbuf = gtk_icon_theme_load_icon (icon_theme, + "emblem-nowrite", + file_list_icon_size, + 0, + NULL); + if (pixbuf == NULL) + return NULL; + + pixbuf = gdk_pixbuf_copy (pixbuf); + g_hash_table_insert (pixbuf_hash, (gpointer) "emblem-nowrite", pixbuf); + g_object_ref (G_OBJECT (pixbuf)); + + return pixbuf; +} + + +static int +get_column_from_sort_method (FrWindowSortMethod sort_method) +{ + switch (sort_method) { + case FR_WINDOW_SORT_BY_NAME: return COLUMN_NAME; + case FR_WINDOW_SORT_BY_SIZE: return COLUMN_SIZE; + case FR_WINDOW_SORT_BY_TYPE: return COLUMN_TYPE; + case FR_WINDOW_SORT_BY_TIME: return COLUMN_TIME; + case FR_WINDOW_SORT_BY_PATH: return COLUMN_PATH; + default: + break; + } + + return COLUMN_NAME; +} + + +static int +get_sort_method_from_column (int column_id) +{ + switch (column_id) { + case COLUMN_NAME: return FR_WINDOW_SORT_BY_NAME; + case COLUMN_SIZE: return FR_WINDOW_SORT_BY_SIZE; + case COLUMN_TYPE: return FR_WINDOW_SORT_BY_TYPE; + case COLUMN_TIME: return FR_WINDOW_SORT_BY_TIME; + case COLUMN_PATH: return FR_WINDOW_SORT_BY_PATH; + default: + break; + } + + return FR_WINDOW_SORT_BY_NAME; +} + + +static void +add_selected_from_list_view (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list = data; + FileData *fdata; + + gtk_tree_model_get (model, iter, + COLUMN_FILE_DATA, &fdata, + -1); + *list = g_list_prepend (*list, fdata); +} + + +static void +add_selected_from_tree_view (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list = data; + char *dir_path; + + gtk_tree_model_get (model, iter, + TREE_COLUMN_PATH, &dir_path, + -1); + *list = g_list_prepend (*list, dir_path); +} + + +static void +add_selected_fd (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list = data; + FileData *fdata; + + gtk_tree_model_get (model, iter, + COLUMN_FILE_DATA, &fdata, + -1); + if (! fdata->list_dir) + *list = g_list_prepend (*list, fdata); +} + + +static GList * +get_selection_as_fd (FrWindow *window) +{ + GtkTreeSelection *selection; + GList *list = NULL; + + if (! gtk_widget_get_realized (window->priv->list_view)) + return NULL; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return NULL; + gtk_tree_selection_selected_foreach (selection, add_selected_fd, &list); + + return list; +} + + +static void +fr_window_update_statusbar_list_info (FrWindow *window) +{ + char *info, *archive_info, *selected_info; + char *size_txt, *sel_size_txt; + int tot_n, sel_n; + goffset tot_size, sel_size; + GList *scan; + + if (window == NULL) + return; + + if ((window->archive == NULL) || (window->archive->command == NULL)) { + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->list_info_cid); + return; + } + + tot_n = 0; + tot_size = 0; + + if (window->priv->archive_present) { + GPtrArray *files = fr_window_get_current_dir_list (window); + int i; + + for (i = 0; i < files->len; i++) { + FileData *fd = g_ptr_array_index (files, i); + + tot_n++; + if (! file_data_is_dir (fd)) + tot_size += fd->size; + else + tot_size += fd->dir_size; + } + g_ptr_array_free (files, TRUE); + } + + sel_n = 0; + sel_size = 0; + + if (window->priv->archive_present) { + GList *selection = get_selection_as_fd (window); + + for (scan = selection; scan; scan = scan->next) { + FileData *fd = scan->data; + + sel_n++; + if (! file_data_is_dir (fd)) + sel_size += fd->size; + } + g_list_free (selection); + } + + size_txt = g_format_size_for_display (tot_size); + sel_size_txt = g_format_size_for_display (sel_size); + + if (tot_n == 0) + archive_info = g_strdup (""); + else + archive_info = g_strdup_printf (ngettext ("%d object (%s)", "%d objects (%s)", tot_n), tot_n, size_txt); + + if (sel_n == 0) + selected_info = g_strdup (""); + else + selected_info = g_strdup_printf (ngettext ("%d object selected (%s)", "%d objects selected (%s)", sel_n), sel_n, sel_size_txt); + + info = g_strconcat (archive_info, + ((sel_n == 0) ? NULL : ", "), + selected_info, + NULL); + + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), window->priv->list_info_cid, info); + + g_free (size_txt); + g_free (sel_size_txt); + g_free (archive_info); + g_free (selected_info); + g_free (info); +} + + +static void +fr_window_populate_file_list (FrWindow *window, + GPtrArray *files) +{ + int i; + + gtk_list_store_clear (window->priv->list_store); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + for (i = 0; i < files->len; i++) { + FileData *fdata = g_ptr_array_index (files, i); + GtkTreeIter iter; + GdkPixbuf *icon, *emblem; + char *utf8_name; + + if (fdata->list_name == NULL) + continue; + + gtk_list_store_append (window->priv->list_store, &iter); + + icon = get_icon (GTK_WIDGET (window), fdata); + utf8_name = g_filename_display_name (fdata->list_name); + emblem = get_emblem (GTK_WIDGET (window), fdata); + + if (file_data_is_dir (fdata)) { + char *utf8_path; + char *tmp; + char *s_size; + char *s_time; + + if (fdata->list_dir) + tmp = remove_ending_separator (fr_window_get_current_location (window)); + + else + tmp = remove_level_from_path (fdata->path); + utf8_path = g_filename_display_name (tmp); + g_free (tmp); + + s_size = g_format_size_for_display (fdata->dir_size); + + if (fdata->list_dir) + s_time = g_strdup (""); + else + s_time = get_time_string (fdata->modified); + + gtk_list_store_set (window->priv->list_store, &iter, + COLUMN_FILE_DATA, fdata, + COLUMN_ICON, icon, + COLUMN_NAME, utf8_name, + COLUMN_EMBLEM, emblem, + COLUMN_TYPE, _("Folder"), + COLUMN_SIZE, s_size, + COLUMN_TIME, s_time, + COLUMN_PATH, utf8_path, + -1); + g_free (utf8_path); + g_free (s_size); + g_free (s_time); + } + else { + char *utf8_path; + char *s_size; + char *s_time; + const char *desc; + + utf8_path = g_filename_display_name (fdata->path); + + s_size = g_format_size_for_display (fdata->size); + s_time = get_time_string (fdata->modified); + desc = g_content_type_get_description (fdata->content_type); + + gtk_list_store_set (window->priv->list_store, &iter, + COLUMN_FILE_DATA, fdata, + COLUMN_ICON, icon, + COLUMN_NAME, utf8_name, + COLUMN_EMBLEM, emblem, + COLUMN_TYPE, desc, + COLUMN_SIZE, s_size, + COLUMN_TIME, s_time, + COLUMN_PATH, utf8_path, + -1); + g_free (utf8_path); + g_free (s_size); + g_free (s_time); + } + g_free (utf8_name); + if (icon != NULL) + g_object_unref (icon); + if (emblem != NULL) + g_object_unref (emblem); + } + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), + get_column_from_sort_method (window->priv->sort_method), + window->priv->sort_type); + + fr_window_update_statusbar_list_info (window); + fr_window_stop_activity_mode (window); +} + + +static int +path_compare (gconstpointer a, + gconstpointer b) +{ + char *path_a = *((char**) a); + char *path_b = *((char**) b); + + return strcmp (path_a, path_b); +} + + +static gboolean +get_tree_iter_from_path (FrWindow *window, + const char *path, + GtkTreeIter *parent, + GtkTreeIter *iter) +{ + gboolean result = FALSE; + + if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (window->priv->tree_store), iter, parent)) + return FALSE; + + do { + GtkTreeIter tmp; + char *iter_path; + + if (get_tree_iter_from_path (window, path, iter, &tmp)) { + *iter = tmp; + return TRUE; + } + + gtk_tree_model_get (GTK_TREE_MODEL (window->priv->tree_store), + iter, + TREE_COLUMN_PATH, &iter_path, + -1); + + if ((iter_path != NULL) && (strcmp (path, iter_path) == 0)) { + result = TRUE; + g_free (iter_path); + break; + } + g_free (iter_path); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (window->priv->tree_store), iter)); + + return result; +} + + +static void +set_sensitive (FrWindow *window, + const char *action_name, + gboolean sensitive) +{ + GtkAction *action; + + action = gtk_action_group_get_action (window->priv->actions, action_name); + g_object_set (action, "sensitive", sensitive, NULL); +} + + +static void +fr_window_update_current_location (FrWindow *window) +{ + const char *current_dir = fr_window_get_current_location (window); + char *path; + GtkTreeIter iter; + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + gtk_widget_hide (window->priv->location_bar); + return; + } + + gtk_widget_show (window->priv->location_bar); + + gtk_entry_set_text (GTK_ENTRY (window->priv->location_entry), window->priv->archive_present? current_dir: ""); + + set_sensitive (window, "GoBack", window->priv->archive_present && (current_dir != NULL) && (window->priv->history_current != NULL) && (window->priv->history_current->next != NULL)); + set_sensitive (window, "GoForward", window->priv->archive_present && (current_dir != NULL) && (window->priv->history_current != NULL) && (window->priv->history_current->prev != NULL)); + set_sensitive (window, "GoUp", window->priv->archive_present && (current_dir != NULL) && (strcmp (current_dir, "/") != 0)); + set_sensitive (window, "GoHome", window->priv->archive_present); + gtk_widget_set_sensitive (window->priv->location_entry, window->priv->archive_present); + gtk_widget_set_sensitive (window->priv->location_label, window->priv->archive_present); + gtk_widget_set_sensitive (window->priv->filter_entry, window->priv->archive_present); + +#if 0 + fr_window_history_print (window); +#endif + + path = remove_ending_separator (current_dir); + if (get_tree_iter_from_path (window, path, NULL, &iter)) { + GtkTreeSelection *selection; + GtkTreePath *t_path; + + t_path = gtk_tree_model_get_path (GTK_TREE_MODEL (window->priv->tree_store), &iter); + gtk_tree_view_expand_to_path (GTK_TREE_VIEW (window->priv->tree_view), t_path); + gtk_tree_path_free (t_path); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + gtk_tree_selection_select_iter (selection, &iter); + } + g_free (path); +} + + +void +fr_window_update_dir_tree (FrWindow *window) +{ + GPtrArray *dirs; + GHashTable *dir_cache; + int i; + GdkPixbuf *icon; + + gtk_tree_store_clear (window->priv->tree_store); + + if (! window->priv->view_folders + || ! window->priv->archive_present + || (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT)) + { + gtk_widget_set_sensitive (window->priv->tree_view, FALSE); + gtk_widget_hide (window->priv->sidepane); + return; + } + else { + gtk_widget_set_sensitive (window->priv->tree_view, TRUE); + if (! gtk_widget_get_visible (window->priv->sidepane)) + gtk_widget_show_all (window->priv->sidepane); + } + + if (gtk_widget_get_realized (window->priv->tree_view)) + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->tree_view), 0, 0); + + /**/ + + dirs = g_ptr_array_sized_new (128); + + dir_cache = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + char *dir; + + if (gtk_entry_get_text (GTK_ENTRY (window->priv->filter_entry)) != NULL) { + if (! file_data_respects_filter (window, fdata)) + continue; + } + + if (fdata->dir) + dir = remove_ending_separator (fdata->full_path); + else + dir = remove_level_from_path (fdata->full_path); + + while ((dir != NULL) && (strcmp (dir, "/") != 0)) { + char *new_dir; + + if (g_hash_table_lookup (dir_cache, dir) != NULL) + break; + + new_dir = dir; + g_ptr_array_add (dirs, new_dir); + g_hash_table_replace (dir_cache, new_dir, "1"); + + dir = remove_level_from_path (new_dir); + } + + g_free (dir); + } + g_hash_table_destroy (dir_cache); + + g_ptr_array_sort (dirs, path_compare); + dir_cache = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) gtk_tree_path_free); + + /**/ + + icon = get_mime_type_icon (MIME_TYPE_ARCHIVE); + { + GtkTreeIter node; + char *uri; + char *name; + + uri = g_file_get_uri (window->archive->file); + name = g_uri_display_basename (uri); + + gtk_tree_store_append (window->priv->tree_store, &node, NULL); + gtk_tree_store_set (window->priv->tree_store, &node, + TREE_COLUMN_ICON, icon, + TREE_COLUMN_NAME, name, + TREE_COLUMN_PATH, "/", + TREE_COLUMN_WEIGHT, PANGO_WEIGHT_BOLD, + -1); + g_hash_table_replace (dir_cache, "/", gtk_tree_model_get_path (GTK_TREE_MODEL (window->priv->tree_store), &node)); + + g_free (name); + g_free (uri); + } + g_object_unref (icon); + + /**/ + + icon = get_mime_type_icon (MIME_TYPE_DIRECTORY); + for (i = 0; i < dirs->len; i++) { + char *dir = g_ptr_array_index (dirs, i); + char *parent_dir; + GtkTreePath *parent_path; + GtkTreeIter parent; + GtkTreeIter node; + + parent_dir = remove_level_from_path (dir); + if (parent_dir == NULL) + continue; + + parent_path = g_hash_table_lookup (dir_cache, parent_dir); + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->tree_store), + &parent, + parent_path); + gtk_tree_store_append (window->priv->tree_store, &node, &parent); + gtk_tree_store_set (window->priv->tree_store, &node, + TREE_COLUMN_ICON, icon, + TREE_COLUMN_NAME, file_name_from_path (dir), + TREE_COLUMN_PATH, dir, + TREE_COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL, + -1); + g_hash_table_replace (dir_cache, dir, gtk_tree_model_get_path (GTK_TREE_MODEL (window->priv->tree_store), &node)); + + g_free (parent_dir); + } + g_hash_table_destroy (dir_cache); + if (icon != NULL) + g_object_unref (icon); + + g_ptr_array_free (dirs, TRUE); + + fr_window_update_current_location (window); +} + + +static void +fr_window_update_filter_bar_visibility (FrWindow *window) +{ + const char *filter; + + filter = gtk_entry_get_text (GTK_ENTRY (window->priv->filter_entry)); + if ((filter == NULL) || (*filter == '\0')) + gtk_widget_hide (window->priv->filter_bar); + else + gtk_widget_show (window->priv->filter_bar); +} + + +static void +fr_window_update_file_list (FrWindow *window, + gboolean update_view) +{ + GPtrArray *files; + gboolean free_files = FALSE; + + if (gtk_widget_get_realized (window->priv->list_view)) + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->list_view), 0, 0); + + if (! window->priv->archive_present || window->priv->archive_new) { + if (update_view) + gtk_list_store_clear (window->priv->list_store); + + window->priv->current_view_length = 0; + + if (window->priv->archive_new) { + gtk_widget_set_sensitive (window->priv->list_view, TRUE); + gtk_widget_show_all (gtk_widget_get_parent (window->priv->list_view)); + } + else { + gtk_widget_set_sensitive (window->priv->list_view, FALSE); + gtk_widget_hide_all (gtk_widget_get_parent (window->priv->list_view)); + } + + return; + } + else { + gtk_widget_set_sensitive (window->priv->list_view, TRUE); + if (! gtk_widget_get_visible (window->priv->list_view)) + gtk_widget_show_all (gtk_widget_get_parent (window->priv->list_view)); + } + + if (window->priv->give_focus_to_the_list) { + gtk_widget_grab_focus (window->priv->list_view); + window->priv->give_focus_to_the_list = FALSE; + } + + /**/ + + fr_window_start_activity_mode (window); + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + fr_window_compute_list_names (window, window->archive->command->files); + files = window->archive->command->files; + free_files = FALSE; + } + else { + char *current_dir = g_strdup (fr_window_get_current_location (window)); + + while (! fr_window_dir_exists_in_archive (window, current_dir)) { + char *tmp; + + fr_window_history_pop (window); + + tmp = get_parent_dir (current_dir); + g_free (current_dir); + current_dir = tmp; + + fr_window_history_add (window, current_dir); + } + g_free (current_dir); + + fr_window_compute_list_names (window, window->archive->command->files); + files = fr_window_get_current_dir_list (window); + free_files = TRUE; + } + + if (files != NULL) + window->priv->current_view_length = files->len; + else + window->priv->current_view_length = 0; + + if (update_view) + fr_window_populate_file_list (window, files); + + if (free_files) + g_ptr_array_free (files, TRUE); +} + + +void +fr_window_update_list_order (FrWindow *window) +{ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), get_column_from_sort_method (window->priv->sort_method), window->priv->sort_type); +} + + +static void +fr_window_update_title (FrWindow *window) +{ + if (! window->priv->archive_present) + gtk_window_set_title (GTK_WINDOW (window), _("Archive Manager")); + else { + char *title; + char *name; + + name = g_uri_display_basename (fr_window_get_archive_uri (window)); + title = g_strdup_printf ("%s %s", + name, + window->archive->read_only ? _("[read only]") : ""); + + gtk_window_set_title (GTK_WINDOW (window), title); + g_free (title); + g_free (name); + } +} + + +static void +check_whether_has_a_dir (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + gboolean *has_a_dir = data; + FileData *fdata; + + gtk_tree_model_get (model, iter, + COLUMN_FILE_DATA, &fdata, + -1); + if (file_data_is_dir (fdata)) + *has_a_dir = TRUE; +} + + +static gboolean +selection_has_a_dir (FrWindow *window) +{ + GtkTreeSelection *selection; + gboolean has_a_dir = FALSE; + + if (! gtk_widget_get_realized (window->priv->list_view)) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + gtk_tree_selection_selected_foreach (selection, + check_whether_has_a_dir, + &has_a_dir); + + return has_a_dir; +} + + +static void +set_active (FrWindow *window, + const char *action_name, + gboolean is_active) +{ + GtkAction *action; + + action = gtk_action_group_get_action (window->priv->actions, action_name); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), is_active); +} + + +static void +fr_window_update_paste_command_sensitivity (FrWindow *window, + GtkClipboard *clipboard) +{ + gboolean running; + gboolean no_archive; + gboolean ro; + gboolean compr_file; + + if (window->priv->closing) + return; + + if (clipboard == NULL) + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), FR_CLIPBOARD); + running = window->priv->activity_ref > 0; + no_archive = (window->archive == NULL) || ! window->priv->archive_present; + ro = ! no_archive && window->archive->read_only; + compr_file = ! no_archive && window->archive->is_compressed_file; + + set_sensitive (window, "Paste", ! no_archive && ! ro && ! running && ! compr_file && (window->priv->list_mode != FR_WINDOW_LIST_MODE_FLAT) && gtk_clipboard_wait_is_target_available (clipboard, FR_SPECIAL_URI_LIST)); +} + + +static void +fr_window_update_sensitivity (FrWindow *window) +{ + gboolean no_archive; + gboolean ro; + gboolean file_op; + gboolean running; + gboolean compr_file; + gboolean sel_not_null; + gboolean one_file_selected; + gboolean dir_selected; + int n_selected; + + if (window->priv->batch_mode) + return; + + running = window->priv->activity_ref > 0; + no_archive = (window->archive == NULL) || ! window->priv->archive_present; + ro = ! no_archive && window->archive->read_only; + file_op = ! no_archive && ! window->priv->archive_new && ! running; + compr_file = ! no_archive && window->archive->is_compressed_file; + n_selected = fr_window_get_n_selected_files (window); + sel_not_null = n_selected > 0; + one_file_selected = n_selected == 1; + dir_selected = selection_has_a_dir (window); + + set_sensitive (window, "AddFiles", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "AddFiles_Toolbar", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "AddFolder", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "AddFolder_Toolbar", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "Copy", ! no_archive && ! ro && ! running && ! compr_file && sel_not_null && (window->priv->list_mode != FR_WINDOW_LIST_MODE_FLAT)); + set_sensitive (window, "Cut", ! no_archive && ! ro && ! running && ! compr_file && sel_not_null && (window->priv->list_mode != FR_WINDOW_LIST_MODE_FLAT)); + set_sensitive (window, "Delete", ! no_archive && ! ro && ! window->priv->archive_new && ! running && ! compr_file); + set_sensitive (window, "DeselectAll", ! no_archive && sel_not_null); + set_sensitive (window, "Extract", file_op); + set_sensitive (window, "Extract_Toolbar", file_op); + set_sensitive (window, "Find", ! no_archive); + set_sensitive (window, "LastOutput", ((window->archive != NULL) + && (window->archive->process != NULL) + && (window->archive->process->out.raw != NULL))); + set_sensitive (window, "New", ! running); + set_sensitive (window, "Open", ! running); + set_sensitive (window, "Open_Toolbar", ! running); + set_sensitive (window, "OpenSelection", file_op && sel_not_null && ! dir_selected); + set_sensitive (window, "OpenFolder", file_op && one_file_selected && dir_selected); + set_sensitive (window, "Password", ! running && (window->priv->asked_for_password || (! no_archive && window->archive->command->propPassword))); + set_sensitive (window, "Properties", file_op); + set_sensitive (window, "Close", !running || window->priv->stoppable); + set_sensitive (window, "Reload", ! (no_archive || running)); + set_sensitive (window, "Rename", ! no_archive && ! ro && ! running && ! compr_file && one_file_selected); + set_sensitive (window, "SaveAs", ! no_archive && ! compr_file && ! running); + set_sensitive (window, "SelectAll", ! no_archive); + set_sensitive (window, "Stop", running && window->priv->stoppable); + set_sensitive (window, "TestArchive", ! no_archive && ! running && window->archive->command->propTest); + set_sensitive (window, "ViewSelection", file_op && one_file_selected && ! dir_selected); + set_sensitive (window, "ViewSelection_Toolbar", file_op && one_file_selected && ! dir_selected); + + if (window->priv->progress_dialog != NULL) + gtk_dialog_set_response_sensitive (GTK_DIALOG (window->priv->progress_dialog), + GTK_RESPONSE_OK, + running && window->priv->stoppable); + + fr_window_update_paste_command_sensitivity (window, NULL); + + set_sensitive (window, "SelectAll", (window->priv->current_view_length > 0) && (window->priv->current_view_length != n_selected)); + set_sensitive (window, "DeselectAll", n_selected > 0); + set_sensitive (window, "OpenRecentMenu", ! running); + + set_sensitive (window, "ViewFolders", (window->priv->list_mode == FR_WINDOW_LIST_MODE_AS_DIR)); + + set_sensitive (window, "ViewAllFiles", ! window->priv->filter_mode); + set_sensitive (window, "ViewAsFolder", ! window->priv->filter_mode); +} + + +static gboolean +location_entry_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + FrWindow *window) +{ + if ((event->keyval == GDK_Return) + || (event->keyval == GDK_KP_Enter) + || (event->keyval == GDK_ISO_Enter)) + { + fr_window_go_to_location (window, gtk_entry_get_text (GTK_ENTRY (window->priv->location_entry)), FALSE); + } + + return FALSE; +} + + +static gboolean +real_close_progress_dialog (gpointer data) +{ + FrWindow *window = data; + + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + + if (window->priv->progress_dialog != NULL) + gtk_widget_hide (window->priv->progress_dialog); + + return FALSE; +} + + +static void +close_progress_dialog (FrWindow *window, + gboolean close_now) +{ + if (window->priv->progress_timeout != 0) { + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + if (! window->priv->batch_mode && gtk_widget_get_mapped (GTK_WIDGET (window))) + gtk_widget_hide (window->priv->progress_bar); + + if (window->priv->progress_dialog == NULL) + return; + + if (close_now) { + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + real_close_progress_dialog (window); + } + else { + if (window->priv->hide_progress_timeout != 0) + return; + window->priv->hide_progress_timeout = g_timeout_add (HIDE_PROGRESS_TIMEOUT_MSECS, + real_close_progress_dialog, + window); + } +} + + +static gboolean +progress_dialog_delete_event (GtkWidget *caller, + GdkEvent *event, + FrWindow *window) +{ + if (window->priv->stoppable) { + activate_action_stop (NULL, window); + close_progress_dialog (window, TRUE); + } + + return TRUE; +} + + +static void +open_folder (GtkWindow *parent, + const char *folder) +{ + GError *error = NULL; + + if (folder == NULL) + return; + + if (! show_uri (gtk_window_get_screen (parent), folder, GDK_CURRENT_TIME, &error)) { + GtkWidget *d; + char *utf8_name; + char *message; + + utf8_name = g_filename_display_name (folder); + message = g_strdup_printf (_("Could not display the folder \"%s\""), utf8_name); + g_free (utf8_name); + + d = _gtk_error_dialog_new (parent, + GTK_DIALOG_MODAL, + NULL, + message, + "%s", + error->message); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + + g_free (message); + g_clear_error (&error); + } +} + + +static void +fr_window_view_extraction_destination_folder (FrWindow *window) +{ + open_folder (GTK_WINDOW (window), fr_archive_get_last_extraction_destination (window->archive)); +} + + +static void +progress_dialog_response (GtkDialog *dialog, + int response_id, + FrWindow *window) +{ + GtkWidget *new_window; + + switch (response_id) { + case GTK_RESPONSE_CANCEL: + if (window->priv->stoppable) { + activate_action_stop (NULL, window); + close_progress_dialog (window, TRUE); + } + break; + case GTK_RESPONSE_CLOSE: + close_progress_dialog (window, TRUE); + break; + case DIALOG_RESPONSE_OPEN_ARCHIVE: + new_window = fr_window_new (); + gtk_widget_show (new_window); + fr_window_archive_open (FR_WINDOW (new_window), window->priv->convert_data.new_file, GTK_WINDOW (new_window)); + close_progress_dialog (window, TRUE); + break; + case DIALOG_RESPONSE_OPEN_DESTINATION_FOLDER: + fr_window_view_extraction_destination_folder (window); + close_progress_dialog (window, TRUE); + break; + case DIALOG_RESPONSE_QUIT: + fr_window_close (window); + break; + default: + break; + } +} + + +static const char* +get_message_from_action (FrAction action) +{ + char *message = ""; + + switch (action) { + case FR_ACTION_CREATING_NEW_ARCHIVE: + message = _("Creating archive"); + break; + case FR_ACTION_LOADING_ARCHIVE: + message = _("Loading archive"); + break; + case FR_ACTION_LISTING_CONTENT: + message = _("Reading archive"); + break; + case FR_ACTION_DELETING_FILES: + message = _("Deleting files from archive"); + break; + case FR_ACTION_TESTING_ARCHIVE: + message = _("Testing archive"); + break; + case FR_ACTION_GETTING_FILE_LIST: + message = _("Getting the file list"); + break; + case FR_ACTION_COPYING_FILES_FROM_REMOTE: + message = _("Copying the file list"); + break; + case FR_ACTION_ADDING_FILES: + message = _("Adding files to archive"); + break; + case FR_ACTION_EXTRACTING_FILES: + message = _("Extracting files from archive"); + break; + case FR_ACTION_COPYING_FILES_TO_REMOTE: + message = _("Copying the file list"); + break; + case FR_ACTION_CREATING_ARCHIVE: + message = _("Creating archive"); + break; + case FR_ACTION_SAVING_REMOTE_ARCHIVE: + message = _("Saving archive"); + break; + default: + message = ""; + break; + } + + return message; +} + + +static void +progress_dialog__set_last_action (FrWindow *window, + FrAction action) +{ + const char *title; + char *markup; + + window->priv->pd_last_action = action; + title = get_message_from_action (window->priv->pd_last_action); + gtk_window_set_title (GTK_WINDOW (window->priv->progress_dialog), title); + markup = g_markup_printf_escaped ("<span weight=\"bold\" size=\"larger\">%s</span>", title); + gtk_label_set_markup (GTK_LABEL (window->priv->pd_action), markup); + g_free (markup); +} + + +static void +pd_update_archive_name (FrWindow *window) +{ + char *current_archive; + + if (window->priv->convert_data.converting) + current_archive = window->priv->convert_data.new_file; + else if (window->priv->working_archive != NULL) + current_archive = window->priv->working_archive; + else + current_archive = window->priv->archive_uri; + + if (strcmp_null_tolerant (window->priv->pd_last_archive, current_archive) != 0) { + g_free (window->priv->pd_last_archive); + if (current_archive == NULL) { + window->priv->pd_last_archive = NULL; + gtk_label_set_text (GTK_LABEL (window->priv->pd_archive), ""); +#ifdef LOG_PROGRESS + g_print ("archive name > (none)\n"); +#endif + } + else { + char *name; + + window->priv->pd_last_archive = g_strdup (current_archive); + + name = g_uri_display_basename (window->priv->pd_last_archive); + gtk_label_set_text (GTK_LABEL (window->priv->pd_archive), name); +#ifdef LOG_PROGRESS + g_print ("archive name > %s\n", name); +#endif + g_free (name); + } + } +} + + +static gboolean +fr_window_working_archive_cb (FrCommand *command, + const char *archive_filename, + FrWindow *window) +{ + g_free (window->priv->working_archive); + window->priv->working_archive = NULL; + if (archive_filename != NULL) + window->priv->working_archive = g_strdup (archive_filename); + pd_update_archive_name (window); + + return TRUE; +} + + +static gboolean +fr_window_message_cb (FrCommand *command, + const char *msg, + FrWindow *window) +{ + if (window->priv->progress_dialog == NULL) + return TRUE; + + if (msg != NULL) { + while (*msg == ' ') + msg++; + if (*msg == 0) + msg = NULL; + } + + if (msg == NULL) { + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), ""); + } + else { + char *utf8_msg; + + if (! g_utf8_validate (msg, -1, NULL)) + utf8_msg = g_locale_to_utf8 (msg, -1 , 0, 0, 0); + else + utf8_msg = g_strdup (msg); + if (utf8_msg == NULL) + return TRUE; + + if (g_utf8_validate (utf8_msg, -1, NULL)) + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), utf8_msg); +#ifdef LOG_PROGRESS + g_print ("message > %s\n", utf8_msg); +#endif + g_free (utf8_msg); + } + + if (window->priv->convert_data.converting) { + if (window->priv->pd_last_action != FR_ACTION_CREATING_ARCHIVE) + progress_dialog__set_last_action (window, FR_ACTION_CREATING_ARCHIVE); + } + else if (window->priv->pd_last_action != window->priv->action) + progress_dialog__set_last_action (window, window->priv->action); + + pd_update_archive_name (window); + + return TRUE; +} + + +static void +create_the_progress_dialog (FrWindow *window) +{ + GtkWindow *parent; + GtkDialog *d; + GtkWidget *vbox; + GtkWidget *align; + GtkWidget *progress_vbox; + GtkWidget *lbl; + const char *title; + char *markup; + PangoAttrList *attr_list; + + if (window->priv->progress_dialog != NULL) + return; + + if (window->priv->batch_mode) + parent = NULL; + else + parent = GTK_WINDOW (window); + + window->priv->pd_last_action = window->priv->action; + title = get_message_from_action (window->priv->pd_last_action); + window->priv->progress_dialog = gtk_dialog_new_with_buttons (title, + parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL); + + window->priv->pd_quit_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), GTK_STOCK_QUIT, DIALOG_RESPONSE_QUIT); + window->priv->pd_open_archive_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), _("_Open the Archive"), DIALOG_RESPONSE_OPEN_ARCHIVE); + window->priv->pd_open_destination_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), _("_Show the Files"), DIALOG_RESPONSE_OPEN_DESTINATION_FOLDER); + window->priv->pd_close_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); + window->priv->pd_cancel_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + d = GTK_DIALOG (window->priv->progress_dialog); + gtk_dialog_set_has_separator (d, FALSE); + gtk_window_set_resizable (GTK_WINDOW (d), TRUE); + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK); + gtk_window_set_default_size (GTK_WINDOW (d), PROGRESS_DIALOG_DEFAULT_WIDTH, -1); + + /* Main */ + + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (d)), vbox, FALSE, FALSE, 10); + + /* action label */ + + lbl = window->priv->pd_action = gtk_label_new (""); + + markup = g_markup_printf_escaped ("<span weight=\"bold\" size=\"larger\">%s</span>", title); + gtk_label_set_markup (GTK_LABEL (lbl), markup); + g_free (markup); + + align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 12, 0, 0); + + gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5); + gtk_misc_set_padding (GTK_MISC (lbl), 0, 0); + gtk_label_set_ellipsize (GTK_LABEL (lbl), PANGO_ELLIPSIZE_END); + + gtk_container_add (GTK_CONTAINER (align), lbl); + gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); + + /* archive name */ + + g_free (window->priv->pd_last_archive); + window->priv->pd_last_archive = NULL; + if (window->priv->archive_uri != NULL) { + GtkWidget *hbox; + char *name; + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + lbl = gtk_label_new (""); + markup = g_markup_printf_escaped ("<b>%s</b>", _("Archive:")); + gtk_label_set_markup (GTK_LABEL (lbl), markup); + g_free (markup); + gtk_box_pack_start (GTK_BOX (hbox), lbl, FALSE, FALSE, 0); + + window->priv->pd_last_archive = g_strdup (window->priv->archive_uri); + name = g_uri_display_basename (window->priv->pd_last_archive); + lbl = window->priv->pd_archive = gtk_label_new (name); + g_free (name); + + gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5); + gtk_label_set_ellipsize (GTK_LABEL (lbl), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (hbox), lbl, TRUE, TRUE, 0); + } + + /* progress and details */ + + align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 6, 0, 0); + + progress_vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (align), progress_vbox); + gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); + + /* progress bar */ + + window->priv->pd_progress_bar = gtk_progress_bar_new (); + gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (window->priv->pd_progress_bar), ACTIVITY_PULSE_STEP); + gtk_box_pack_start (GTK_BOX (progress_vbox), window->priv->pd_progress_bar, TRUE, TRUE, 0); + + /* details label */ + + lbl = window->priv->pd_message = gtk_label_new (""); + + attr_list = pango_attr_list_new (); + pango_attr_list_insert (attr_list, pango_attr_style_new (PANGO_STYLE_ITALIC)); + gtk_label_set_attributes (GTK_LABEL (lbl), attr_list); + pango_attr_list_unref (attr_list); + + gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5); + gtk_label_set_ellipsize (GTK_LABEL (lbl), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (progress_vbox), lbl, TRUE, TRUE, 0); + + gtk_widget_show_all (vbox); + + /* signals */ + + g_signal_connect (G_OBJECT (window->priv->progress_dialog), + "response", + G_CALLBACK (progress_dialog_response), + window); + g_signal_connect (G_OBJECT (window->priv->progress_dialog), + "delete_event", + G_CALLBACK (progress_dialog_delete_event), + window); +} + + +static gboolean +display_progress_dialog (gpointer data) +{ + FrWindow *window = data; + + if (window->priv->progress_timeout != 0) + g_source_remove (window->priv->progress_timeout); + + if (window->priv->progress_dialog != NULL) { + gtk_dialog_set_response_sensitive (GTK_DIALOG (window->priv->progress_dialog), + GTK_RESPONSE_OK, + window->priv->stoppable); + if (! window->priv->non_interactive) + gtk_widget_show (GTK_WIDGET (window)); + gtk_widget_hide (window->priv->progress_bar); + gtk_widget_show (window->priv->progress_dialog); + fr_window_message_cb (NULL, window->priv->last_status_message, window); + } + + window->priv->progress_timeout = 0; + + return FALSE; +} + + +static void +open_progress_dialog (FrWindow *window, + gboolean open_now) +{ + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + + if (open_now) { + if (window->priv->progress_timeout != 0) + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + if ((window->priv->progress_timeout != 0) + || ((window->priv->progress_dialog != NULL) && gtk_widget_get_visible (window->priv->progress_dialog))) + return; + + if (! window->priv->batch_mode && ! open_now) + gtk_widget_show (window->priv->progress_bar); + + create_the_progress_dialog (window); + gtk_widget_show (window->priv->pd_cancel_button); + gtk_widget_hide (window->priv->pd_open_archive_button); + gtk_widget_hide (window->priv->pd_open_destination_button); + gtk_widget_hide (window->priv->pd_quit_button); + gtk_widget_hide (window->priv->pd_close_button); + + if (open_now) + display_progress_dialog (window); + else + window->priv->progress_timeout = g_timeout_add (PROGRESS_TIMEOUT_MSECS, + display_progress_dialog, + window); +} + + +static gboolean +fr_window_progress_cb (FrCommand *command, + double fraction, + FrWindow *window) +{ + window->priv->progress_pulse = (fraction < 0.0); + if (! window->priv->progress_pulse) { + fraction = CLAMP (fraction, 0.0, 1.0); + if (window->priv->progress_dialog != NULL) + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->pd_progress_bar), fraction); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->progress_bar), fraction); +#ifdef LOG_PROGRESS + g_print ("progress > %2.2f\n", fraction); +#endif + } + return TRUE; +} + + +static void +open_progress_dialog_with_open_destination (FrWindow *window) +{ + window->priv->ask_to_open_destination_after_extraction = FALSE; + + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + if (window->priv->progress_timeout != 0) { + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + create_the_progress_dialog (window); + gtk_widget_hide (window->priv->pd_cancel_button); + gtk_widget_hide (window->priv->pd_open_archive_button); + gtk_widget_show (window->priv->pd_open_destination_button); + gtk_widget_show (window->priv->pd_quit_button); + gtk_widget_show (window->priv->pd_close_button); + display_progress_dialog (window); + fr_window_progress_cb (NULL, 1.0, window); + fr_window_message_cb (NULL, _("Extraction completed successfully"), window); +} + + +static void +open_progress_dialog_with_open_archive (FrWindow *window) +{ + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + if (window->priv->progress_timeout != 0) { + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + create_the_progress_dialog (window); + gtk_widget_hide (window->priv->pd_cancel_button); + gtk_widget_hide (window->priv->pd_open_destination_button); + gtk_widget_show (window->priv->pd_open_archive_button); + gtk_widget_show (window->priv->pd_close_button); + display_progress_dialog (window); + fr_window_progress_cb (NULL, 1.0, window); + fr_window_message_cb (NULL, _("Archive created successfully"), window); +} + + +void +fr_window_push_message (FrWindow *window, + const char *msg) +{ + if (! gtk_widget_get_mapped (GTK_WIDGET (window))) + return; + + g_free (window->priv->last_status_message); + window->priv->last_status_message = g_strdup (msg); + + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), window->priv->progress_cid, window->priv->last_status_message); + if (window->priv->progress_dialog != NULL) + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), window->priv->last_status_message); +} + + +void +fr_window_pop_message (FrWindow *window) +{ + if (! gtk_widget_get_mapped (GTK_WIDGET (window))) + return; + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->progress_cid); + if (window->priv->progress_dialog != NULL) + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), ""); +} + + +static void +action_started (FrArchive *archive, + FrAction action, + gpointer data) +{ + FrWindow *window = data; + const char *message; + char *full_msg; + + window->priv->action = action; + fr_window_start_activity_mode (window); + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [START] (FR::Window)\n", action_names[action]); +#endif + + message = get_message_from_action (action); + full_msg = g_strdup_printf ("%s, %s", message, _("please wait...")); + fr_window_push_message (window, full_msg); + + switch (action) { + case FR_ACTION_EXTRACTING_FILES: + open_progress_dialog (window, window->priv->ask_to_open_destination_after_extraction || window->priv->convert_data.converting || window->priv->batch_mode); + break; + default: + open_progress_dialog (window, window->priv->batch_mode); + break; + } + + if (archive->command != NULL) { + fr_command_progress (archive->command, -1.0); + fr_command_message (archive->command, message); + } + + g_free (full_msg); +} + + +static void +fr_window_add_to_recent_list (FrWindow *window, + char *uri) +{ + if (window->priv->batch_mode) + return; + + if (is_temp_dir (uri)) + return; + + if (window->archive->content_type != NULL) { + GtkRecentData *recent_data; + + recent_data = g_new0 (GtkRecentData, 1); + recent_data->mime_type = g_content_type_get_mime_type (window->archive->content_type); + recent_data->app_name = "File Roller"; + recent_data->app_exec = "file-roller"; + gtk_recent_manager_add_full (window->priv->recent_manager, uri, recent_data); + + g_free (recent_data); + } + else + gtk_recent_manager_add_item (window->priv->recent_manager, uri); +} + + +static void +fr_window_remove_from_recent_list (FrWindow *window, + char *filename) +{ + if (filename != NULL) + gtk_recent_manager_remove_item (window->priv->recent_manager, filename, NULL); +} + + +static void +error_dialog_response_cb (GtkDialog *dialog, + gint arg1, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkWindow *dialog_parent = window->priv->error_dialog_parent; + + window->priv->showing_error_dialog = FALSE; + window->priv->error_dialog_parent = NULL; + + if ((dialog_parent != NULL) && (gtk_widget_get_toplevel (GTK_WIDGET (dialog_parent)) != (GtkWidget*) dialog_parent)) + gtk_window_set_modal (dialog_parent, TRUE); + gtk_widget_destroy (GTK_WIDGET (dialog)); + if (window->priv->destroy_with_error_dialog) + gtk_widget_destroy (GTK_WIDGET (window)); +} + + +static void +fr_window_show_error_dialog (FrWindow *window, + GtkWidget *dialog, + GtkWindow *dialog_parent) +{ + close_progress_dialog (window, TRUE); + + if (dialog_parent != NULL) + gtk_window_set_modal (dialog_parent, FALSE); + g_signal_connect (dialog, + "response", + (window->priv->batch_mode) ? G_CALLBACK (gtk_main_quit) : G_CALLBACK (error_dialog_response_cb), + window); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_widget_show (dialog); + + window->priv->showing_error_dialog = TRUE; + window->priv->error_dialog_parent = dialog_parent; +} + + +void +fr_window_destroy_with_error_dialog (FrWindow *window) +{ + window->priv->destroy_with_error_dialog = TRUE; +} + + +static gboolean +handle_errors (FrWindow *window, + FrArchive *archive, + FrAction action, + FrProcError *error) +{ + if (error->type == FR_PROC_ERROR_ASK_PASSWORD) { + close_progress_dialog (window, TRUE); + dlg_ask_password (window); + return FALSE; + } + else if (error->type == FR_PROC_ERROR_UNSUPPORTED_FORMAT) { + close_progress_dialog (window, TRUE); + dlg_package_installer (window, archive, action); + return FALSE; + } +#if 0 + else if (error->type == FR_PROC_ERROR_BAD_CHARSET) { + close_progress_dialog (window, TRUE); + /* dlg_ask_archive_charset (window); FIXME: implement after feature freeze */ + return FALSE; + } +#endif + else if (error->type == FR_PROC_ERROR_STOPPED) { + /* nothing */ + } + else if (error->type != FR_PROC_ERROR_NONE) { + char *msg = NULL; + char *utf8_name; + char *details = NULL; + GtkWindow *dialog_parent; + GtkWidget *dialog; + FrProcess *process = archive->process; + GList *output = NULL; + + if (window->priv->batch_mode) { + dialog_parent = NULL; + window->priv->load_error_parent_window = NULL; + } + else { + dialog_parent = (GtkWindow *) window; + if (window->priv->load_error_parent_window == NULL) + window->priv->load_error_parent_window = (GtkWindow *) window; + } + + if ((action == FR_ACTION_LISTING_CONTENT) || (action == FR_ACTION_LOADING_ARCHIVE)) + fr_window_archive_close (window); + + switch (action) { + case FR_ACTION_CREATING_NEW_ARCHIVE: + dialog_parent = window->priv->load_error_parent_window; + msg = _("Could not create the archive"); + break; + + case FR_ACTION_EXTRACTING_FILES: + case FR_ACTION_COPYING_FILES_TO_REMOTE: + msg = _("An error occurred while extracting files."); + break; + + case FR_ACTION_LOADING_ARCHIVE: + dialog_parent = window->priv->load_error_parent_window; + utf8_name = g_uri_display_basename (window->priv->archive_uri); + msg = g_strdup_printf (_("Could not open \"%s\""), utf8_name); + g_free (utf8_name); + break; + + case FR_ACTION_LISTING_CONTENT: + msg = _("An error occurred while loading the archive."); + break; + + case FR_ACTION_DELETING_FILES: + msg = _("An error occurred while deleting files from the archive."); + break; + + case FR_ACTION_ADDING_FILES: + case FR_ACTION_GETTING_FILE_LIST: + case FR_ACTION_COPYING_FILES_FROM_REMOTE: + msg = _("An error occurred while adding files to the archive."); + break; + + case FR_ACTION_TESTING_ARCHIVE: + msg = _("An error occurred while testing archive."); + break; + + case FR_ACTION_SAVING_REMOTE_ARCHIVE: + msg = _("An error occurred while saving the archive."); + break; + + default: + msg = _("An error occurred."); + break; + } + + switch (error->type) { + case FR_PROC_ERROR_COMMAND_NOT_FOUND: + details = _("Command not found."); + break; + case FR_PROC_ERROR_EXITED_ABNORMALLY: + details = _("Command exited abnormally."); + break; + case FR_PROC_ERROR_SPAWN: + details = error->gerror->message; + break; + default: + if (error->gerror != NULL) + details = error->gerror->message; + else + details = NULL; + break; + } + + if (error->type != FR_PROC_ERROR_GENERIC) + output = (process->err.raw != NULL) ? process->err.raw : process->out.raw; + + dialog = _gtk_error_dialog_new (dialog_parent, + 0, + output, + msg, + ((details != NULL) ? "%s" : NULL), + details); + fr_window_show_error_dialog (window, dialog, dialog_parent); + + return FALSE; + } + + return TRUE; +} + + +static void +convert__action_performed (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer data) +{ + FrWindow *window = data; + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [CONVERT::DONE] (FR::Window)\n", action_names[action]); +#endif + + if ((action == FR_ACTION_GETTING_FILE_LIST) || (action == FR_ACTION_ADDING_FILES)) { + fr_window_stop_activity_mode (window); + fr_window_pop_message (window); + close_progress_dialog (window, FALSE); + } + + if (action != FR_ACTION_ADDING_FILES) + return; + + handle_errors (window, archive, action, error); + + if (error->type == FR_PROC_ERROR_NONE) + open_progress_dialog_with_open_archive (window); + + remove_local_directory (window->priv->convert_data.temp_dir); + fr_window_convert_data_free (window, FALSE); + + fr_window_update_sensitivity (window); + fr_window_update_statusbar_list_info (window); +} + + +static void fr_window_exec_next_batch_action (FrWindow *window); + + +static void +action_performed (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer data) +{ + FrWindow *window = data; + gboolean continue_batch = FALSE; + char *archive_dir; + gboolean temp_dir; + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [DONE] (FR::Window)\n", action_names[action]); +#endif + + fr_window_stop_activity_mode (window); + fr_window_pop_message (window); + + continue_batch = handle_errors (window, archive, action, error); + + if ((error->type == FR_PROC_ERROR_ASK_PASSWORD) + || (error->type == FR_PROC_ERROR_UNSUPPORTED_FORMAT) + /*|| (error->type == FR_PROC_ERROR_BAD_CHARSET)*/) + { + return; + } + + switch (action) { + case FR_ACTION_CREATING_NEW_ARCHIVE: + case FR_ACTION_CREATING_ARCHIVE: + close_progress_dialog (window, FALSE); + if (error->type != FR_PROC_ERROR_STOPPED) { + fr_window_history_clear (window); + fr_window_go_to_location (window, "/", TRUE); + fr_window_update_dir_tree (window); + fr_window_update_title (window); + fr_window_update_sensitivity (window); + } + break; + + case FR_ACTION_LOADING_ARCHIVE: + close_progress_dialog (window, FALSE); + if (error->type != FR_PROC_ERROR_NONE) { + fr_window_remove_from_recent_list (window, window->priv->archive_uri); + if (window->priv->non_interactive) { + fr_window_archive_close (window); + fr_window_stop_batch (window); + } + } + else { + fr_window_add_to_recent_list (window, window->priv->archive_uri); + if (! window->priv->non_interactive) + gtk_window_present (GTK_WINDOW (window)); + } + continue_batch = FALSE; + g_signal_emit (window, + fr_window_signals[ARCHIVE_LOADED], + 0, + error->type == FR_PROC_ERROR_NONE); + break; + + case FR_ACTION_LISTING_CONTENT: + /* update the uri because multi-volume archives can have + * a different name after loading. */ + g_free (window->priv->archive_uri); + window->priv->archive_uri = g_file_get_uri (window->archive->file); + + close_progress_dialog (window, FALSE); + if (error->type != FR_PROC_ERROR_NONE) { + fr_window_remove_from_recent_list (window, window->priv->archive_uri); + fr_window_archive_close (window); + fr_window_set_password (window, NULL); + break; + } + + archive_dir = remove_level_from_path (window->priv->archive_uri); + temp_dir = is_temp_dir (archive_dir); + if (! window->priv->archive_present) { + window->priv->archive_present = TRUE; + + fr_window_history_clear (window); + fr_window_history_add (window, "/"); + + if (! temp_dir) { + fr_window_set_open_default_dir (window, archive_dir); + fr_window_set_add_default_dir (window, archive_dir); + if (! window->priv->freeze_default_dir) + fr_window_set_extract_default_dir (window, archive_dir, FALSE); + } + + window->priv->archive_new = FALSE; + } + g_free (archive_dir); + + if (! temp_dir) + fr_window_add_to_recent_list (window, window->priv->archive_uri); + + fr_window_update_title (window); + fr_window_go_to_location (window, fr_window_get_current_location (window), TRUE); + fr_window_update_dir_tree (window); + if (! window->priv->batch_mode && window->priv->non_interactive) + gtk_window_present (GTK_WINDOW (window)); + break; + + case FR_ACTION_DELETING_FILES: + close_progress_dialog (window, FALSE); + fr_window_archive_reload (window); + return; + + case FR_ACTION_ADDING_FILES: + close_progress_dialog (window, FALSE); + + /* update the uri because multi-volume archives can have + * a different name after creation. */ + g_free (window->priv->archive_uri); + window->priv->archive_uri = g_file_get_uri (window->archive->file); + + if (error->type == FR_PROC_ERROR_NONE) { + if (window->priv->archive_new) + window->priv->archive_new = FALSE; + fr_window_add_to_recent_list (window, window->priv->archive_uri); + } + if (! window->priv->batch_mode) { + fr_window_archive_reload (window); + return; + } + break; + + case FR_ACTION_TESTING_ARCHIVE: + close_progress_dialog (window, FALSE); + if (error->type == FR_PROC_ERROR_NONE) + fr_window_view_last_output (window, _("Test Result")); + return; + + case FR_ACTION_EXTRACTING_FILES: + if (error->type != FR_PROC_ERROR_NONE) { + if (window->priv->convert_data.converting) { + remove_local_directory (window->priv->convert_data.temp_dir); + fr_window_convert_data_free (window, TRUE); + } + break; + } + if (window->priv->convert_data.converting) { + char *source_dir; + + source_dir = g_filename_to_uri (window->priv->convert_data.temp_dir, NULL, NULL); + fr_archive_add_with_wildcard ( + window->priv->convert_data.new_archive, + "*", + NULL, + NULL, + source_dir, + NULL, + FALSE, + TRUE, + window->priv->convert_data.password, + window->priv->convert_data.encrypt_header, + window->priv->compression, + window->priv->convert_data.volume_size); + g_free (source_dir); + } + else { + if (window->priv->ask_to_open_destination_after_extraction) + open_progress_dialog_with_open_destination (window); + else + close_progress_dialog (window, FALSE); + } + break; + + default: + close_progress_dialog (window, FALSE); + continue_batch = FALSE; + break; + } + + if (window->priv->batch_action == NULL) { + fr_window_update_sensitivity (window); + fr_window_update_statusbar_list_info (window); + } + + if (continue_batch) { + if (error->type != FR_PROC_ERROR_NONE) + fr_window_stop_batch (window); + else + fr_window_exec_next_batch_action (window); + } +} + + +/* -- selections -- */ + + +static GList * +get_dir_list_from_path (FrWindow *window, + char *path) +{ + char *dirname; + int dirname_l; + GList *list = NULL; + int i; + + if (path[strlen (path) - 1] != '/') + dirname = g_strconcat (path, "/", NULL); + else + dirname = g_strdup (path); + dirname_l = strlen (dirname); + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + + if (strncmp (dirname, fd->full_path, dirname_l) == 0) + list = g_list_prepend (list, g_strdup (fd->original_path)); + } + g_free (dirname); + + return g_list_reverse (list); +} + + +static GList * +get_dir_list_from_file_data (FrWindow *window, + FileData *fdata) +{ + char *dirname; + GList *list; + + dirname = g_strconcat (fr_window_get_current_location (window), + fdata->list_name, + NULL); + list = get_dir_list_from_path (window, dirname); + g_free (dirname); + + return list; +} + + +GList * +fr_window_get_file_list_selection (FrWindow *window, + gboolean recursive, + gboolean *has_dirs) +{ + GtkTreeSelection *selection; + GList *selections = NULL, *list, *scan; + + g_return_val_if_fail (window != NULL, NULL); + + if (has_dirs != NULL) + *has_dirs = FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return NULL; + gtk_tree_selection_selected_foreach (selection, add_selected_from_list_view, &selections); + + list = NULL; + for (scan = selections; scan; scan = scan->next) { + FileData *fd = scan->data; + + if (!fd) + continue; + + if (file_data_is_dir (fd)) { + if (has_dirs != NULL) + *has_dirs = TRUE; + + if (recursive) + list = g_list_concat (list, get_dir_list_from_file_data (window, fd)); + } + else + list = g_list_prepend (list, g_strdup (fd->original_path)); + } + if (selections) + g_list_free (selections); + + return g_list_reverse (list); +} + + +GList * +fr_window_get_folder_tree_selection (FrWindow *window, + gboolean recursive, + gboolean *has_dirs) +{ + GtkTreeSelection *tree_selection; + GList *selections, *list, *scan; + + g_return_val_if_fail (window != NULL, NULL); + + if (has_dirs != NULL) + *has_dirs = FALSE; + + tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + if (tree_selection == NULL) + return NULL; + + selections = NULL; + gtk_tree_selection_selected_foreach (tree_selection, add_selected_from_tree_view, &selections); + if (selections == NULL) + return NULL; + + if (has_dirs != NULL) + *has_dirs = TRUE; + + list = NULL; + for (scan = selections; scan; scan = scan->next) { + char *path = scan->data; + + if (recursive) + list = g_list_concat (list, get_dir_list_from_path (window, path)); + } + path_list_free (selections); + + return g_list_reverse (list); +} + + +GList * +fr_window_get_file_list_from_path_list (FrWindow *window, + GList *path_list, + gboolean *has_dirs) +{ + GtkTreeModel *model; + GList *selections, *list, *scan; + + g_return_val_if_fail (window != NULL, NULL); + + model = GTK_TREE_MODEL (window->priv->list_store); + selections = NULL; + + if (has_dirs != NULL) + *has_dirs = FALSE; + + for (scan = path_list; scan; scan = scan->next) { + GtkTreeRowReference *reference = scan->data; + GtkTreePath *path; + GtkTreeIter iter; + FileData *fdata; + + path = gtk_tree_row_reference_get_path (reference); + if (path == NULL) + continue; + + if (! gtk_tree_model_get_iter (model, &iter, path)) + continue; + + gtk_tree_model_get (model, &iter, + COLUMN_FILE_DATA, &fdata, + -1); + + selections = g_list_prepend (selections, fdata); + } + + list = NULL; + for (scan = selections; scan; scan = scan->next) { + FileData *fd = scan->data; + + if (!fd) + continue; + + if (file_data_is_dir (fd)) { + if (has_dirs != NULL) + *has_dirs = TRUE; + list = g_list_concat (list, get_dir_list_from_file_data (window, fd)); + } + else + list = g_list_prepend (list, g_strdup (fd->original_path)); + } + + if (selections != NULL) + g_list_free (selections); + + return g_list_reverse (list); +} + + +GList * +fr_window_get_file_list_pattern (FrWindow *window, + const char *pattern) +{ + GRegex **regexps; + GList *list; + int i; + + g_return_val_if_fail (window != NULL, NULL); + + regexps = search_util_get_regexps (pattern, G_REGEX_CASELESS); + list = NULL; + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + char *utf8_name; + + /* FIXME: only files in the current location ? */ + + if (fd == NULL) + continue; + + utf8_name = g_filename_to_utf8 (fd->name, -1, NULL, NULL, NULL); + if (match_regexps (regexps, utf8_name, 0)) + list = g_list_prepend (list, g_strdup (fd->original_path)); + g_free (utf8_name); + } + free_regexps (regexps); + + return g_list_reverse (list); +} + + +int +fr_window_get_n_selected_files (FrWindow *window) +{ + return _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view))); +} + + +/**/ + + +static int +dir_tree_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + FrWindow *window = data; + GtkTreeSelection *selection; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->tree_view))) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + if (selection == NULL) + return FALSE; + + if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) { + GtkTreePath *path; + GtkTreeIter iter; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->tree_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->tree_store), &iter, path)) { + gtk_tree_path_free (path); + return FALSE; + } + gtk_tree_path_free (path); + + if (! gtk_tree_selection_iter_is_selected (selection, &iter)) { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_iter (selection, &iter); + } + + gtk_menu_popup (GTK_MENU (window->priv->sidebar_folder_popup_menu), + NULL, NULL, NULL, + window, + event->button, + event->time); + } + else + gtk_tree_selection_unselect_all (selection); + + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 8)) { + fr_window_go_back (window); + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 9)) { + fr_window_go_forward (window); + return TRUE; + } + + return FALSE; +} + + +static FileData * +fr_window_get_selected_item_from_file_list (FrWindow *window) +{ + GtkTreeSelection *tree_selection; + GList *selection; + FileData *fdata = NULL; + + g_return_val_if_fail (window != NULL, NULL); + + tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (tree_selection == NULL) + return NULL; + + selection = NULL; + gtk_tree_selection_selected_foreach (tree_selection, add_selected_from_list_view, &selection); + if ((selection == NULL) || (selection->next != NULL)) { + /* return NULL if the selection contains more than one entry. */ + g_list_free (selection); + return NULL; + } + + fdata = file_data_copy (selection->data); + g_list_free (selection); + + return fdata; +} + + +static char * +fr_window_get_selected_folder_in_tree_view (FrWindow *window) +{ + GtkTreeSelection *tree_selection; + GList *selections; + char *path = NULL; + + g_return_val_if_fail (window != NULL, NULL); + + tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + if (tree_selection == NULL) + return NULL; + + selections = NULL; + gtk_tree_selection_selected_foreach (tree_selection, add_selected_from_tree_view, &selections); + + if (selections != NULL) { + path = selections->data; + g_list_free (selections); + } + + return path; +} + + +void +fr_window_current_folder_activated (FrWindow *window, + gboolean from_sidebar) +{ + char *dir_path; + + if (! from_sidebar) { + FileData *fdata; + char *dir_name; + + fdata = fr_window_get_selected_item_from_file_list (window); + if ((fdata == NULL) || ! file_data_is_dir (fdata)) { + file_data_free (fdata); + return; + } + dir_name = g_strdup (fdata->list_name); + dir_path = g_strconcat (fr_window_get_current_location (window), + dir_name, + "/", + NULL); + g_free (dir_name); + file_data_free (fdata); + } + else + dir_path = fr_window_get_selected_folder_in_tree_view (window); + + fr_window_go_to_location (window, dir_path, FALSE); + + g_free (dir_path); +} + + +static gboolean +row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + FrWindow *window = data; + FileData *fdata; + GtkTreeIter iter; + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, + path)) + return FALSE; + + gtk_tree_model_get (GTK_TREE_MODEL (window->priv->list_store), &iter, + COLUMN_FILE_DATA, &fdata, + -1); + + if (! file_data_is_dir (fdata)) { + GList *list = g_list_prepend (NULL, fdata->original_path); + fr_window_open_files (window, list, FALSE); + g_list_free (list); + } + else if (window->priv->list_mode == FR_WINDOW_LIST_MODE_AS_DIR) { + char *new_dir; + new_dir = g_strconcat (fr_window_get_current_location (window), + fdata->list_name, + "/", + NULL); + fr_window_go_to_location (window, new_dir, FALSE); + g_free (new_dir); + } + + return FALSE; +} + + +static int +file_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + FrWindow *window = data; + GtkTreeSelection *selection; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view))) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) { + GtkTreePath *path; + GtkTreeIter iter; + int n_selected; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), &iter, path)) { + gtk_tree_path_free (path); + return FALSE; + } + gtk_tree_path_free (path); + + if (! gtk_tree_selection_iter_is_selected (selection, &iter)) { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_iter (selection, &iter); + } + } + else + gtk_tree_selection_unselect_all (selection); + + n_selected = fr_window_get_n_selected_files (window); + if ((n_selected == 1) && selection_has_a_dir (window)) + gtk_menu_popup (GTK_MENU (window->priv->folder_popup_menu), + NULL, NULL, NULL, + window, + event->button, + event->time); + else + gtk_menu_popup (GTK_MENU (window->priv->file_popup_menu), + NULL, NULL, NULL, + window, + event->button, + event->time); + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) { + GtkTreePath *path = NULL; + + if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + gtk_tree_selection_unselect_all (selection); + } + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + if (path != NULL) { + window->priv->path_clicked = gtk_tree_path_copy (path); + gtk_tree_path_free (path); + } + + return FALSE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 8)) { + // go back + fr_window_go_back (window); + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 9)) { + // go forward + fr_window_go_forward (window); + return TRUE; + } + + return FALSE; +} + + +static int +file_button_release_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + FrWindow *window = data; + GtkTreeSelection *selection; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view))) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + if (window->priv->path_clicked == NULL) + return FALSE; + + if ((event->type == GDK_BUTTON_RELEASE) + && (event->button == 1) + && (window->priv->path_clicked != NULL)) { + GtkTreePath *path = NULL; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + + if ((gtk_tree_path_compare (window->priv->path_clicked, path) == 0) + && window->priv->single_click + && ! ((event->state & GDK_CONTROL_MASK) || (event->state & GDK_SHIFT_MASK))) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), + path, + NULL, + FALSE); + gtk_tree_view_row_activated (GTK_TREE_VIEW (widget), + path, + NULL); + } + } + + if (path != NULL) + gtk_tree_path_free (path); + } + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + return FALSE; +} + + +static gboolean +file_motion_notify_callback (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + FrWindow *window = user_data; + GdkCursor *cursor; + GtkTreePath *last_hover_path; + GtkTreeIter iter; + + if (! window->priv->single_click) + return FALSE; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view))) + return FALSE; + + last_hover_path = window->priv->list_hover_path; + + gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), + event->x, event->y, + &window->priv->list_hover_path, + NULL, NULL, NULL); + + if (window->priv->list_hover_path != NULL) + cursor = gdk_cursor_new (GDK_HAND2); + else + cursor = NULL; + + gdk_window_set_cursor (event->window, cursor); + + /* only redraw if the hover row has changed */ + if (!(last_hover_path == NULL && window->priv->list_hover_path == NULL) && + (!(last_hover_path != NULL && window->priv->list_hover_path != NULL) || + gtk_tree_path_compare (last_hover_path, window->priv->list_hover_path))) + { + if (last_hover_path) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, last_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (window->priv->list_store), + last_hover_path, &iter); + } + + if (window->priv->list_hover_path) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, window->priv->list_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (window->priv->list_store), + window->priv->list_hover_path, &iter); + } + } + + gtk_tree_path_free (last_hover_path); + + return FALSE; +} + + +static gboolean +file_leave_notify_callback (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkTreeIter iter; + + if (window->priv->single_click && (window->priv->list_hover_path != NULL)) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, + window->priv->list_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (window->priv->list_store), + window->priv->list_hover_path, + &iter); + + gtk_tree_path_free (window->priv->list_hover_path); + window->priv->list_hover_path = NULL; + } + + return FALSE; +} + + +/* -- drag and drop -- */ + + +static GList * +get_uri_list_from_selection_data (char *uri_list) +{ + GList *list = NULL; + char **uris; + int i; + + if (uri_list == NULL) + return NULL; + + uris = g_uri_list_extract_uris (uri_list); + for (i = 0; uris[i] != NULL; i++) + list = g_list_prepend (list, g_strdup (uris[i])); + g_strfreev (uris); + + return g_list_reverse (list); +} + + +static gboolean +fr_window_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + gpointer user_data) +{ + FrWindow *window = user_data; + + if ((gtk_drag_get_source_widget (context) == window->priv->list_view) + || (gtk_drag_get_source_widget (context) == window->priv->tree_view)) + { + gdk_drag_status (context, 0, time); + return FALSE; + } + + return TRUE; +} + + +static void fr_window_paste_from_clipboard_data (FrWindow *window, FrClipboardData *data); + + +static FrClipboardData* +get_clipboard_data_from_selection_data (FrWindow *window, + const char *data) +{ + FrClipboardData *clipboard_data; + char **uris; + int i; + + clipboard_data = fr_clipboard_data_new (); + + uris = g_strsplit (data, "\r\n", -1); + + clipboard_data->archive_filename = g_strdup (uris[0]); + if (window->priv->password_for_paste != NULL) + clipboard_data->archive_password = g_strdup (window->priv->password_for_paste); + else if (strcmp (uris[1], "") != 0) + clipboard_data->archive_password = g_strdup (uris[1]); + clipboard_data->op = (strcmp (uris[2], "copy") == 0) ? FR_CLIPBOARD_OP_COPY : FR_CLIPBOARD_OP_CUT; + clipboard_data->base_dir = g_strdup (uris[3]); + for (i = 4; uris[i] != NULL; i++) + if (uris[i][0] != '\0') + clipboard_data->files = g_list_prepend (clipboard_data->files, g_strdup (uris[i])); + clipboard_data->files = g_list_reverse (clipboard_data->files); + + g_strfreev (uris); + + return clipboard_data; +} + + +static void +fr_window_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer extra_data) +{ + FrWindow *window = extra_data; + GList *list; + gboolean one_file; + gboolean is_an_archive; + + debug (DEBUG_INFO, "::DragDataReceived -->\n"); + + if ((gtk_drag_get_source_widget (context) == window->priv->list_view) + || (gtk_drag_get_source_widget (context) == window->priv->tree_view)) + { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + if (! ((gtk_selection_data_get_length (data) >= 0) && (gtk_selection_data_get_format (data) == 8))) { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + if (window->priv->activity_ref > 0) { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + gtk_drag_finish (context, TRUE, FALSE, time); + + if (gtk_selection_data_get_target (data) == XFR_ATOM) { + FrClipboardData *dnd_data; + + dnd_data = get_clipboard_data_from_selection_data (window, (char*) gtk_selection_data_get_data (data)); + dnd_data->current_dir = g_strdup (fr_window_get_current_location (window)); + fr_window_paste_from_clipboard_data (window, dnd_data); + + return; + } + + list = get_uri_list_from_selection_data ((char*) gtk_selection_data_get_data (data)); + if (list == NULL) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + NULL, + _("Could not perform the operation"), + NULL); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy(d); + + return; + } + + one_file = (list->next == NULL); + if (one_file) + is_an_archive = uri_is_archive (list->data); + else + is_an_archive = FALSE; + + if (window->priv->archive_present + && (window->archive != NULL) + && ! window->archive->read_only + && ! window->archive->is_compressed_file) + { + if (one_file && is_an_archive) { + GtkWidget *d; + gint r; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + _("Do you want to add this file to the current archive or open it as a new archive?"), + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_ADD, 0, + GTK_STOCK_OPEN, 1, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), 2); + + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + if (r == 0) /* Add */ + fr_window_archive_add_dropped_items (window, list, FALSE); + else if (r == 1) /* Open */ + fr_window_archive_open (window, list->data, GTK_WINDOW (window)); + } + else + fr_window_archive_add_dropped_items (window, list, FALSE); + } + else { + if (one_file && is_an_archive) + fr_window_archive_open (window, list->data, GTK_WINDOW (window)); + else { + GtkWidget *d; + int r; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + _("Do you want to create a new archive with these files?"), + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Create _Archive"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + if (r == GTK_RESPONSE_YES) { + char *first_item; + char *folder; + char *local_path = NULL; + char *utf8_path = NULL; + const char *archive_name; + + fr_window_free_batch_data (window); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_ADD, + path_list_dup (list), + (GFreeFunc) path_list_free); + + first_item = (char*) list->data; + folder = remove_level_from_path (first_item); + if (folder != NULL) + fr_window_set_open_default_dir (window, folder); + + if ((list->next != NULL) && (folder != NULL)) { + archive_name = file_name_from_path (folder); + } + else { + if (uri_is_local (first_item)) { + local_path = g_filename_from_uri (first_item, NULL, NULL); + if (local_path) + utf8_path = g_filename_to_utf8 (local_path, -1, NULL, NULL, NULL); + if (!utf8_path) + utf8_path= g_strdup (first_item); + g_free (local_path); + } + else { + utf8_path = g_strdup (first_item); + } + archive_name = file_name_from_path (utf8_path); + } + + show_new_archive_dialog (window, archive_name); + g_free (utf8_path); + + g_free (folder); + } + } + } + + path_list_free (list); + + debug (DEBUG_INFO, "::DragDataReceived <--\n"); +} + + +static gboolean +file_list_drag_begin (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + FrWindow *window = data; + + debug (DEBUG_INFO, "::DragBegin -->\n"); + + if (window->priv->activity_ref > 0) + return FALSE; + + g_free (window->priv->drag_destination_folder); + window->priv->drag_destination_folder = NULL; + + g_free (window->priv->drag_base_dir); + window->priv->drag_base_dir = NULL; + + gdk_property_change (gdk_drag_context_get_source_window (context), + XDS_ATOM, TEXT_ATOM, + 8, GDK_PROP_MODE_REPLACE, + (guchar *) XDS_FILENAME, + strlen (XDS_FILENAME)); + + return TRUE; +} + + +static void +file_list_drag_end (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + FrWindow *window = data; + + debug (DEBUG_INFO, "::DragEnd -->\n"); + + gdk_property_delete (gdk_drag_context_get_source_window (context), XDS_ATOM); + + if (window->priv->drag_error != NULL) { + _gtk_error_dialog_run (GTK_WINDOW (window), + _("Extraction not performed"), + "%s", + window->priv->drag_error->message); + g_clear_error (&window->priv->drag_error); + } + else if (window->priv->drag_destination_folder != NULL) { + fr_window_archive_extract (window, + window->priv->drag_file_list, + window->priv->drag_destination_folder, + window->priv->drag_base_dir, + FALSE, + TRUE, + FALSE, + FALSE); + path_list_free (window->priv->drag_file_list); + window->priv->drag_file_list = NULL; + } + + debug (DEBUG_INFO, "::DragEnd <--\n"); +} + + +/* The following three functions taken from bugzilla + * (http://bugzilla.mate.org/attachment.cgi?id=49362&action=view) + * Author: Christian Neumair + * Copyright: 2005 Free Software Foundation, Inc + * License: GPL */ +static char * +get_xds_atom_value (GdkDragContext *context) +{ + char *ret; + + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (gdk_drag_context_get_source_window (context) != NULL, NULL); + + if (gdk_property_get (gdk_drag_context_get_source_window (context), + XDS_ATOM, TEXT_ATOM, + 0, MAX_XDS_ATOM_VAL_LEN, + FALSE, NULL, NULL, NULL, + (unsigned char **) &ret)) + return ret; + + return NULL; +} + + +static gboolean +context_offers_target (GdkDragContext *context, + GdkAtom target) +{ + return (g_list_find (gdk_drag_context_list_targets (context), target) != NULL); +} + + +static gboolean +caja_xds_dnd_is_valid_xds_context (GdkDragContext *context) +{ + char *tmp; + gboolean ret; + + g_return_val_if_fail (context != NULL, FALSE); + + tmp = NULL; + if (context_offers_target (context, XDS_ATOM)) { + tmp = get_xds_atom_value (context); + } + + ret = (tmp != NULL); + g_free (tmp); + + return ret; +} + + +static char * +get_selection_data_from_clipboard_data (FrWindow *window, + FrClipboardData *data) +{ + GString *list; + char *local_filename; + GList *scan; + + list = g_string_new (NULL); + + local_filename = g_file_get_uri (window->archive->local_copy); + g_string_append (list, local_filename); + g_free (local_filename); + + g_string_append (list, "\r\n"); + if (window->priv->password != NULL) + g_string_append (list, window->priv->password); + g_string_append (list, "\r\n"); + g_string_append (list, (data->op == FR_CLIPBOARD_OP_COPY) ? "copy" : "cut"); + g_string_append (list, "\r\n"); + g_string_append (list, data->base_dir); + g_string_append (list, "\r\n"); + for (scan = data->files; scan; scan = scan->next) { + g_string_append (list, scan->data); + g_string_append (list, "\r\n"); + } + + return g_string_free (list, FALSE); +} + + +static gboolean +fr_window_folder_tree_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer user_data) +{ + FrWindow *window = user_data; + GList *file_list; + char *destination; + char *destination_folder; + + debug (DEBUG_INFO, "::DragDataGet -->\n"); + + if (window->priv->activity_ref > 0) + return FALSE; + + file_list = fr_window_get_folder_tree_selection (window, TRUE, NULL); + if (file_list == NULL) + return FALSE; + + if (gtk_selection_data_get_target (selection_data) == XFR_ATOM) { + FrClipboardData *tmp; + char *data; + + tmp = fr_clipboard_data_new (); + tmp->files = file_list; + tmp->op = FR_CLIPBOARD_OP_COPY; + tmp->base_dir = g_strdup (fr_window_get_current_location (window)); + + data = get_selection_data_from_clipboard_data (window, tmp); + gtk_selection_data_set (selection_data, XFR_ATOM, 8, (guchar *) data, strlen (data)); + + fr_clipboard_data_unref (tmp); + g_free (data); + + return TRUE; + } + + if (! caja_xds_dnd_is_valid_xds_context (context)) + return FALSE; + + destination = get_xds_atom_value (context); + g_return_val_if_fail (destination != NULL, FALSE); + + destination_folder = remove_level_from_path (destination); + g_free (destination); + + /* check whether the extraction can be performed in the destination + * folder */ + + g_clear_error (&window->priv->drag_error); + + if (! check_permissions (destination_folder, R_OK | W_OK)) { + char *destination_folder_display_name; + + destination_folder_display_name = g_filename_display_name (destination_folder); + window->priv->drag_error = g_error_new (FR_ERROR, 0, _("You don't have the right permissions to extract archives in the folder \"%s\""), destination_folder_display_name); + g_free (destination_folder_display_name); + } + + if (window->priv->drag_error == NULL) { + g_free (window->priv->drag_destination_folder); + g_free (window->priv->drag_base_dir); + path_list_free (window->priv->drag_file_list); + window->priv->drag_destination_folder = g_strdup (destination_folder); + window->priv->drag_base_dir = fr_window_get_selected_folder_in_tree_view (window); + window->priv->drag_file_list = file_list; + } + + g_free (destination_folder); + + /* sends back the response */ + + gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *) ((window->priv->drag_error == NULL) ? "S" : "E"), 1); + + debug (DEBUG_INFO, "::DragDataGet <--\n"); + + return TRUE; +} + + +gboolean +fr_window_file_list_drag_data_get (FrWindow *window, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list) +{ + char *destination; + char *destination_folder; + + debug (DEBUG_INFO, "::DragDataGet -->\n"); + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + if (window->priv->activity_ref > 0) + return FALSE; + + if (gtk_selection_data_get_target (selection_data) == XFR_ATOM) { + FrClipboardData *tmp; + char *data; + + tmp = fr_clipboard_data_new (); + tmp->files = fr_window_get_file_list_selection (window, TRUE, NULL); + tmp->op = FR_CLIPBOARD_OP_COPY; + tmp->base_dir = g_strdup (fr_window_get_current_location (window)); + + data = get_selection_data_from_clipboard_data (window, tmp); + gtk_selection_data_set (selection_data, XFR_ATOM, 8, (guchar *) data, strlen (data)); + + fr_clipboard_data_unref (tmp); + g_free (data); + + return TRUE; + } + + if (! caja_xds_dnd_is_valid_xds_context (context)) + return FALSE; + + destination = get_xds_atom_value (context); + g_return_val_if_fail (destination != NULL, FALSE); + + destination_folder = remove_level_from_path (destination); + g_free (destination); + + /* check whether the extraction can be performed in the destination + * folder */ + + g_clear_error (&window->priv->drag_error); + + if (! check_permissions (destination_folder, R_OK | W_OK)) { + char *destination_folder_display_name; + + destination_folder_display_name = g_filename_display_name (destination_folder); + window->priv->drag_error = g_error_new (FR_ERROR, 0, _("You don't have the right permissions to extract archives in the folder \"%s\""), destination_folder_display_name); + g_free (destination_folder_display_name); + } + + if (window->priv->drag_error == NULL) { + g_free (window->priv->drag_destination_folder); + g_free (window->priv->drag_base_dir); + path_list_free (window->priv->drag_file_list); + window->priv->drag_destination_folder = g_strdup (destination_folder); + window->priv->drag_base_dir = g_strdup (fr_window_get_current_location (window)); + window->priv->drag_file_list = fr_window_get_file_list_from_path_list (window, path_list, NULL); + } + + g_free (destination_folder); + + /* sends back the response */ + + gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *) ((window->priv->drag_error == NULL) ? "S" : "E"), 1); + + debug (DEBUG_INFO, "::DragDataGet <--\n"); + + return TRUE; +} + + +/* -- window_new -- */ + + +static void +fr_window_deactivate_filter (FrWindow *window) +{ + window->priv->filter_mode = FALSE; + window->priv->list_mode = window->priv->last_list_mode; + + gtk_entry_set_text (GTK_ENTRY (window->priv->filter_entry), ""); + fr_window_update_filter_bar_visibility (window); + + gtk_list_store_clear (window->priv->list_store); + + fr_window_update_columns_visibility (window); + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); +} + + +static gboolean +key_press_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + FrWindow *window = data; + gboolean retval = FALSE; + gboolean alt; + + if (gtk_widget_has_focus (window->priv->location_entry)) + return FALSE; + + if (gtk_widget_has_focus (window->priv->filter_entry)) { + switch (event->keyval) { + case GDK_Escape: + fr_window_deactivate_filter (window); + retval = TRUE; + break; + default: + break; + } + return retval; + } + + alt = (event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK; + + switch (event->keyval) { + case GDK_Escape: + activate_action_stop (NULL, window); + if (window->priv->filter_mode) + fr_window_deactivate_filter (window); + retval = TRUE; + break; + + case GDK_F10: + if (event->state & GDK_SHIFT_MASK) { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + gtk_menu_popup (GTK_MENU (window->priv->file_popup_menu), + NULL, NULL, NULL, + window, + 3, + GDK_CURRENT_TIME); + retval = TRUE; + } + break; + + case GDK_Up: + case GDK_KP_Up: + if (alt) { + fr_window_go_up_one_level (window); + retval = TRUE; + } + break; + + case GDK_BackSpace: + fr_window_go_up_one_level (window); + retval = TRUE; + break; + + case GDK_Right: + case GDK_KP_Right: + if (alt) { + fr_window_go_forward (window); + retval = TRUE; + } + break; + + case GDK_Left: + case GDK_KP_Left: + if (alt) { + fr_window_go_back (window); + retval = TRUE; + } + break; + + case GDK_Home: + case GDK_KP_Home: + if (alt) { + fr_window_go_to_location (window, "/", FALSE); + retval = TRUE; + } + break; + + default: + break; + } + + return retval; +} + + +static gboolean +dir_tree_selection_changed_cb (GtkTreeSelection *selection, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { + char *path; + + gtk_tree_model_get (GTK_TREE_MODEL (window->priv->tree_store), + &iter, + TREE_COLUMN_PATH, &path, + -1); + fr_window_go_to_location (window, path, FALSE); + g_free (path); + } + + return FALSE; +} + + +static gboolean +selection_changed_cb (GtkTreeSelection *selection, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_update_statusbar_list_info (window); + fr_window_update_sensitivity (window); + + return FALSE; +} + + +static void +fr_window_delete_event_cb (GtkWidget *caller, + GdkEvent *event, + FrWindow *window) +{ + fr_window_close (window); +} + + +static gboolean +is_single_click_policy (void) +{ + char *value; + gboolean result; + + value = eel_mateconf_get_string (PREF_CAJA_CLICK_POLICY, "double"); + result = strncmp (value, "single", 6) == 0; + g_free (value); + + return result; +} + + +static void +filename_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + FrWindow *window) +{ + char *text; + GtkTreePath *path; + PangoUnderline underline; + + gtk_tree_model_get (model, iter, + COLUMN_NAME, &text, + -1); + + if (window->priv->single_click) { + path = gtk_tree_model_get_path (model, iter); + + if ((window->priv->list_hover_path == NULL) + || gtk_tree_path_compare (path, window->priv->list_hover_path)) + underline = PANGO_UNDERLINE_NONE; + else + underline = PANGO_UNDERLINE_SINGLE; + + gtk_tree_path_free (path); + } + else + underline = PANGO_UNDERLINE_NONE; + + g_object_set (G_OBJECT (renderer), + "text", text, + "underline", underline, + NULL); + + g_free (text); +} + + +static void +add_dir_tree_columns (FrWindow *window, + GtkTreeView *treeview) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GValue value = { 0, }; + + /* First column. */ + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Folders")); + + /* icon */ + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", TREE_COLUMN_ICON, + NULL); + + /* name */ + + renderer = gtk_cell_renderer_text_new (); + + g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE); + g_value_set_enum (&value, PANGO_ELLIPSIZE_END); + g_object_set_property (G_OBJECT (renderer), "ellipsize", &value); + g_value_unset (&value); + + gtk_tree_view_column_pack_start (column, + renderer, + TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", TREE_COLUMN_NAME, + "weight", TREE_COLUMN_WEIGHT, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_NAME); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); +} + + +static void +add_file_list_columns (FrWindow *window, + GtkTreeView *treeview) +{ + static char *titles[] = {NC_("File", "Size"), + NC_("File", "Type"), + NC_("File", "Date Modified"), + NC_("File", "Location")}; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GValue value = { 0, }; + int i, j, w; + + /* First column. */ + + window->priv->filename_column = column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, C_("File", "Name")); + + /* emblem */ + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_end (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", COLUMN_EMBLEM, + NULL); + + /* icon */ + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", COLUMN_ICON, + NULL); + + /* name */ + + window->priv->single_click = is_single_click_policy (); + + renderer = gtk_cell_renderer_text_new (); + + g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE); + g_value_set_enum (&value, PANGO_ELLIPSIZE_END); + g_object_set_property (G_OBJECT (renderer), "ellipsize", &value); + g_value_unset (&value); + + gtk_tree_view_column_pack_start (column, + renderer, + TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", COLUMN_NAME, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + w = eel_mateconf_get_integer (PREF_NAME_COLUMN_WIDTH, DEFAULT_NAME_COLUMN_WIDTH); + if (w <= 0) + w = DEFAULT_NAME_COLUMN_WIDTH; + gtk_tree_view_column_set_fixed_width (column, w); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + gtk_tree_view_column_set_cell_data_func (column, renderer, + (GtkTreeCellDataFunc) filename_cell_data_func, + window, NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + /* Other columns */ + + for (j = 0, i = COLUMN_SIZE; i < NUMBER_OF_COLUMNS; i++, j++) { + GValue value = { 0, }; + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_(titles[j]), + renderer, + "text", i, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, OTHER_COLUMNS_WIDTH); + gtk_tree_view_column_set_resizable (column, TRUE); + + gtk_tree_view_column_set_sort_column_id (column, i); + + g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE); + g_value_set_enum (&value, PANGO_ELLIPSIZE_END); + g_object_set_property (G_OBJECT (renderer), "ellipsize", &value); + g_value_unset (&value); + + gtk_tree_view_append_column (treeview, column); + } +} + + +static int +name_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_name (&fdata1, &fdata2); +} + + +static int +size_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_size (&fdata1, &fdata2); +} + + +static int +type_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_type (&fdata1, &fdata2); +} + + +static int +time_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_time (&fdata1, &fdata2); +} + + +static int +path_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_path (&fdata1, &fdata2); +} + + +static int +no_sort_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + return -1; +} + + +static void +sort_column_changed_cb (GtkTreeSortable *sortable, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkSortType order; + int column_id; + + if (! gtk_tree_sortable_get_sort_column_id (sortable, + &column_id, + &order)) + return; + + window->priv->sort_method = get_sort_method_from_column (column_id); + window->priv->sort_type = order; + + /*set_active (window, get_action_from_sort_method (window->priv->sort_method), TRUE); + set_active (window, "SortReverseOrder", (window->priv->sort_type == GTK_SORT_DESCENDING));*/ +} + + +static gboolean +fr_window_show_cb (GtkWidget *widget, + FrWindow *window) +{ + fr_window_update_current_location (window); + + set_active (window, "ViewToolbar", eel_mateconf_get_boolean (PREF_UI_TOOLBAR, TRUE)); + set_active (window, "ViewStatusbar", eel_mateconf_get_boolean (PREF_UI_STATUSBAR, TRUE)); + + window->priv->view_folders = eel_mateconf_get_boolean (PREF_UI_FOLDERS, FALSE); + set_active (window, "ViewFolders", window->priv->view_folders); + + fr_window_update_filter_bar_visibility (window); + + return TRUE; +} + + +/* preferences changes notification callbacks */ + + +static void +pref_history_len_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (window->priv->recent_chooser_menu), eel_mateconf_get_integer (PREF_UI_HISTORY_LEN, MAX_HISTORY_LEN)); + gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (window->priv->recent_chooser_toolbar), eel_mateconf_get_integer (PREF_UI_HISTORY_LEN, MAX_HISTORY_LEN)); +} + + +static void +pref_view_toolbar_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + g_return_if_fail (window != NULL); + + fr_window_set_toolbar_visibility (window, mateconf_value_get_bool (mateconf_entry_get_value (entry))); +} + + +static void +pref_view_statusbar_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_set_statusbar_visibility (window, mateconf_value_get_bool (mateconf_entry_get_value (entry))); +} + + +static void +pref_view_folders_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_set_folders_visibility (window, mateconf_value_get_bool (mateconf_entry_get_value (entry))); +} + + +static void +pref_show_field_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_update_columns_visibility (window); +} + + +static void +pref_click_policy_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + GdkWindow *win = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view)); + GdkDisplay *display; + + window->priv->single_click = is_single_click_policy (); + + gdk_window_set_cursor (win, NULL); + display = gtk_widget_get_display (GTK_WIDGET (window->priv->list_view)); + if (display != NULL) + gdk_display_flush (display); +} + + +static void gh_unref_pixbuf (gpointer key, + gpointer value, + gpointer user_data); + + +static void +pref_use_mime_icons_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + if (pixbuf_hash != NULL) { + g_hash_table_foreach (pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (pixbuf_hash); + pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + if (tree_pixbuf_hash != NULL) { + g_hash_table_foreach (tree_pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (tree_pixbuf_hash); + tree_pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + + fr_window_update_file_list (window, FALSE); + fr_window_update_dir_tree (window); +} + + +static void +theme_changed_cb (GtkIconTheme *theme, FrWindow *window) +{ + int icon_width, icon_height; + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + FILE_LIST_ICON_SIZE, + &icon_width, &icon_height); + file_list_icon_size = MAX (icon_width, icon_height); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + DIR_TREE_ICON_SIZE, + &icon_width, &icon_height); + dir_tree_icon_size = MAX (icon_width, icon_height); + + if (pixbuf_hash != NULL) { + g_hash_table_foreach (pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (pixbuf_hash); + pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + if (tree_pixbuf_hash != NULL) { + g_hash_table_foreach (tree_pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (tree_pixbuf_hash); + tree_pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); +} + + +static gboolean +fr_window_stoppable_cb (FrCommand *command, + gboolean stoppable, + FrWindow *window) +{ + window->priv->stoppable = stoppable; + set_sensitive (window, "Stop", stoppable); + if (window->priv->progress_dialog != NULL) + gtk_dialog_set_response_sensitive (GTK_DIALOG (window->priv->progress_dialog), + GTK_RESPONSE_OK, + stoppable); + return TRUE; +} + + +static gboolean +fr_window_fake_load (FrArchive *archive, + gpointer data) +{ + /* fake loads are disabled to allow exact progress dialogs (#153281) */ + + return FALSE; + +#if 0 + FrWindow *window = data; + gboolean add_after_opening = FALSE; + gboolean extract_after_opening = FALSE; + GList *scan; + + /* fake loads are used only in batch mode to avoid unnecessary + * archive loadings. */ + + if (! window->priv->batch_mode) + return FALSE; + + /* Check whether there is an ADD or EXTRACT action in the batch list. */ + + for (scan = window->priv->batch_action; scan; scan = scan->next) { + FRBatchAction *action; + + action = (FRBatchAction *) scan->data; + if (action->type == FR_BATCH_ACTION_ADD) { + add_after_opening = TRUE; + break; + } + if ((action->type == FR_BATCH_ACTION_EXTRACT) + || (action->type == FR_BATCH_ACTION_EXTRACT_HERE) + || (action->type == FR_BATCH_ACTION_EXTRACT_INTERACT)) + { + extract_after_opening = TRUE; + break; + } + } + + /* use fake load when in batch mode and the archive type supports all + * of the required features */ + + return (window->priv->batch_mode + && ! (add_after_opening && window->priv->update_dropped_files && ! archive->command->propAddCanUpdate) + && ! (add_after_opening && ! window->priv->update_dropped_files && ! archive->command->propAddCanReplace) + && ! (extract_after_opening && !archive->command->propCanExtractAll)); +#endif +} + + +static gboolean +fr_window_add_is_stoppable (FrArchive *archive, + gpointer data) +{ + FrWindow *window = data; + return window->priv->archive_new; +} + + +static void +menu_item_select_cb (GtkMenuItem *proxy, + FrWindow *window) +{ + GtkAction *action; + char *message; + + action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy)); + g_return_if_fail (action != NULL); + + g_object_get (G_OBJECT (action), "tooltip", &message, NULL); + if (message) { + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->help_message_cid, message); + g_free (message); + } +} + + +static void +menu_item_deselect_cb (GtkMenuItem *proxy, + FrWindow *window) +{ + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->help_message_cid); +} + + +static void +disconnect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + FrWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) { + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_select_cb), window); + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_deselect_cb), window); + } +} + + +static void +connect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + FrWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) { + g_signal_connect (proxy, "select", + G_CALLBACK (menu_item_select_cb), window); + g_signal_connect (proxy, "deselect", + G_CALLBACK (menu_item_deselect_cb), window); + } +} + + +static void +view_as_radio_action (GtkAction *action, + GtkRadioAction *current, + gpointer data) +{ + FrWindow *window = data; + fr_window_set_list_mode (window, gtk_radio_action_get_current_value (current)); +} + + +static void +sort_by_radio_action (GtkAction *action, + GtkRadioAction *current, + gpointer data) +{ + FrWindow *window = data; + + window->priv->sort_method = gtk_radio_action_get_current_value (current); + window->priv->sort_type = GTK_SORT_ASCENDING; + fr_window_update_list_order (window); +} + + +static void +recent_chooser_item_activated_cb (GtkRecentChooser *chooser, + FrWindow *window) +{ + char *uri; + + uri = gtk_recent_chooser_get_current_uri (chooser); + if (uri != NULL) { + fr_window_archive_open (window, uri, GTK_WINDOW (window)); + g_free (uri); + } +} + + +static void +fr_window_init_recent_chooser (FrWindow *window, + GtkRecentChooser *chooser) +{ + GtkRecentFilter *filter; + int i; + + g_return_if_fail (chooser != NULL); + + filter = gtk_recent_filter_new (); + gtk_recent_filter_set_name (filter, _("All archives")); + for (i = 0; open_type[i] != -1; i++) + gtk_recent_filter_add_mime_type (filter, mime_type_desc[open_type[i]].mime_type); + gtk_recent_filter_add_application (filter, "File Roller"); + gtk_recent_chooser_add_filter (chooser, filter); + + gtk_recent_chooser_set_local_only (chooser, FALSE); + gtk_recent_chooser_set_limit (chooser, eel_mateconf_get_integer (PREF_UI_HISTORY_LEN, MAX_HISTORY_LEN)); + gtk_recent_chooser_set_show_not_found (chooser, TRUE); + gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU); + + g_signal_connect (G_OBJECT (chooser), + "item_activated", + G_CALLBACK (recent_chooser_item_activated_cb), + window); +} + + +static void +close_sidepane_button_clicked_cb (GtkButton *button, + FrWindow *window) +{ + fr_window_set_folders_visibility (window, FALSE); +} + + +static void +fr_window_activate_filter (FrWindow *window) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (window->priv->list_view); + GtkTreeViewColumn *column; + + fr_window_update_filter_bar_visibility (window); + window->priv->list_mode = FR_WINDOW_LIST_MODE_FLAT; + + gtk_list_store_clear (window->priv->list_store); + + column = gtk_tree_view_get_column (tree_view, 4); + gtk_tree_view_column_set_visible (column, TRUE); + + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); +} + + +static void +filter_entry_activate_cb (GtkEntry *entry, + FrWindow *window) +{ + fr_window_activate_filter (window); +} + + +static void +filter_entry_icon_release_cb (GtkEntry *entry, + GtkEntryIconPosition icon_pos, + GdkEventButton *event, + gpointer user_data) +{ + FrWindow *window = FR_WINDOW (user_data); + + if ((event->button == 1) && (icon_pos == GTK_ENTRY_ICON_SECONDARY)) + fr_window_deactivate_filter (window); +} + + +static void +fr_window_attach (FrWindow *window, + GtkWidget *child, + FrWindowArea area) +{ + int position; + + g_return_if_fail (window != NULL); + g_return_if_fail (FR_IS_WINDOW (window)); + g_return_if_fail (child != NULL); + g_return_if_fail (GTK_IS_WIDGET (child)); + + switch (area) { + case FR_WINDOW_AREA_MENUBAR: + position = 0; + break; + case FR_WINDOW_AREA_TOOLBAR: + position = 1; + break; + case FR_WINDOW_AREA_LOCATIONBAR: + position = 2; + break; + case FR_WINDOW_AREA_CONTENTS: + position = 3; + if (window->priv->contents != NULL) + gtk_widget_destroy (window->priv->contents); + window->priv->contents = child; + break; + case FR_WINDOW_AREA_FILTERBAR: + position = 4; + break; + case FR_WINDOW_AREA_STATUSBAR: + position = 5; + break; + default: + g_critical ("%s: area not recognized!", G_STRFUNC); + return; + break; + } + + gtk_table_attach (GTK_TABLE (window->priv->layout), + child, + 0, 1, + position, position + 1, + GTK_EXPAND | GTK_FILL, + ((area == FR_WINDOW_AREA_CONTENTS) ? GTK_EXPAND : 0) | GTK_FILL, + 0, 0); +} + + +static void +set_action_important (GtkUIManager *ui, + const char *action_name) +{ + GtkAction *action; + + action = gtk_ui_manager_get_action (ui, action_name); + g_object_set (action, "is_important", TRUE, NULL); + g_object_unref (action); +} + + +static void +fr_window_construct (FrWindow *window) +{ + GtkWidget *menubar; + GtkWidget *toolbar; + GtkWidget *list_scrolled_window; + GtkWidget *location_box; + GtkStatusbar *statusbar; + GtkWidget *statusbar_box; + GtkWidget *filter_box; + GtkWidget *tree_scrolled_window; + GtkWidget *sidepane_title; + GtkWidget *sidepane_title_box; + GtkWidget *sidepane_title_label; + GtkWidget *close_sidepane_button; + GtkTreeSelection *selection; + int i; + int icon_width, icon_height; + GtkActionGroup *actions; + GtkUIManager *ui; + GtkToolItem *open_recent_tool_item; + GtkWidget *menu_item; + GError *error = NULL; + + /* data common to all windows. */ + + if (pixbuf_hash == NULL) + pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + if (tree_pixbuf_hash == NULL) + tree_pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + + if (icon_theme == NULL) + icon_theme = gtk_icon_theme_get_default (); + + /* Create the application. */ + + window->priv->layout = gtk_table_new (4, 1, FALSE); + gtk_container_add (GTK_CONTAINER (window), window->priv->layout); + gtk_widget_show (window->priv->layout); + + gtk_window_set_title (GTK_WINDOW (window), _("Archive Manager")); + + g_signal_connect (G_OBJECT (window), + "delete_event", + G_CALLBACK (fr_window_delete_event_cb), + window); + + g_signal_connect (G_OBJECT (window), + "show", + G_CALLBACK (fr_window_show_cb), + window); + + window->priv->theme_changed_handler_id = + g_signal_connect (icon_theme, + "changed", + G_CALLBACK (theme_changed_cb), + window); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + FILE_LIST_ICON_SIZE, + &icon_width, &icon_height); + file_list_icon_size = MAX (icon_width, icon_height); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + DIR_TREE_ICON_SIZE, + &icon_width, &icon_height); + dir_tree_icon_size = MAX (icon_width, icon_height); + + gtk_window_set_default_size (GTK_WINDOW (window), + eel_mateconf_get_integer (PREF_UI_WINDOW_WIDTH, DEF_WIN_WIDTH), + eel_mateconf_get_integer (PREF_UI_WINDOW_HEIGHT, DEF_WIN_HEIGHT)); + + gtk_drag_dest_set (GTK_WIDGET (window), + GTK_DEST_DEFAULT_ALL, + target_table, G_N_ELEMENTS (target_table), + GDK_ACTION_COPY); + + g_signal_connect (G_OBJECT (window), + "drag_data_received", + G_CALLBACK (fr_window_drag_data_received), + window); + g_signal_connect (G_OBJECT (window), + "drag_motion", + G_CALLBACK (fr_window_drag_motion), + window); + + g_signal_connect (G_OBJECT (window), + "key_press_event", + G_CALLBACK (key_press_cb), + window); + + /* Initialize Data. */ + + window->archive = fr_archive_new (); + g_signal_connect (G_OBJECT (window->archive), + "start", + G_CALLBACK (action_started), + window); + g_signal_connect (G_OBJECT (window->archive), + "done", + G_CALLBACK (action_performed), + window); + g_signal_connect (G_OBJECT (window->archive), + "progress", + G_CALLBACK (fr_window_progress_cb), + window); + g_signal_connect (G_OBJECT (window->archive), + "message", + G_CALLBACK (fr_window_message_cb), + window); + g_signal_connect (G_OBJECT (window->archive), + "stoppable", + G_CALLBACK (fr_window_stoppable_cb), + window); + g_signal_connect (G_OBJECT (window->archive), + "working_archive", + G_CALLBACK (fr_window_working_archive_cb), + window); + + fr_archive_set_fake_load_func (window->archive, + fr_window_fake_load, + window); + fr_archive_set_add_is_stoppable_func (window->archive, + fr_window_add_is_stoppable, + window); + + window->priv->sort_method = preferences_get_sort_method (); + window->priv->sort_type = preferences_get_sort_type (); + + window->priv->list_mode = window->priv->last_list_mode = preferences_get_list_mode (); + window->priv->history = NULL; + window->priv->history_current = NULL; + + window->priv->action = FR_ACTION_NONE; + + eel_mateconf_set_boolean (PREF_LIST_SHOW_PATH, (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT)); + + window->priv->open_default_dir = g_strdup (get_home_uri ()); + window->priv->add_default_dir = g_strdup (get_home_uri ()); + window->priv->extract_default_dir = g_strdup (get_home_uri ()); + + window->priv->give_focus_to_the_list = FALSE; + + window->priv->activity_ref = 0; + window->priv->activity_timeout_handle = 0; + + window->priv->update_timeout_handle = 0; + + window->priv->archive_present = FALSE; + window->priv->archive_new = FALSE; + window->priv->archive_uri = NULL; + + window->priv->drag_destination_folder = NULL; + window->priv->drag_base_dir = NULL; + window->priv->drag_error = NULL; + window->priv->drag_file_list = NULL; + + window->priv->batch_mode = FALSE; + window->priv->batch_action_list = NULL; + window->priv->batch_action = NULL; + window->priv->extract_interact_use_default_dir = FALSE; + window->priv->non_interactive = FALSE; + + window->priv->password = NULL; + window->priv->compression = preferences_get_compression_level (); + window->priv->encrypt_header = eel_mateconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE); + window->priv->volume_size = 0; + + window->priv->convert_data.converting = FALSE; + window->priv->convert_data.temp_dir = NULL; + window->priv->convert_data.new_archive = NULL; + window->priv->convert_data.password = NULL; + window->priv->convert_data.encrypt_header = FALSE; + window->priv->convert_data.volume_size = 0; + + window->priv->stoppable = TRUE; + + window->priv->batch_adding_one_file = FALSE; + + window->priv->path_clicked = NULL; + + window->priv->current_view_length = 0; + + window->priv->current_batch_action.type = FR_BATCH_ACTION_NONE; + window->priv->current_batch_action.data = NULL; + window->priv->current_batch_action.free_func = NULL; + + window->priv->pd_last_archive = NULL; + + /* Create the widgets. */ + + /* * File list. */ + + window->priv->list_store = fr_list_model_new (NUMBER_OF_COLUMNS, + G_TYPE_POINTER, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + g_object_set_data (G_OBJECT (window->priv->list_store), "FrWindow", window); + window->priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->list_store)); + + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (window->priv->list_view), TRUE); + add_file_list_columns (window, GTK_TREE_VIEW (window->priv->list_view)); + gtk_tree_view_set_enable_search (GTK_TREE_VIEW (window->priv->list_view), + FALSE); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (window->priv->list_view), + COLUMN_NAME); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_NAME, name_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_SIZE, size_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_TYPE, type_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_TIME, time_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_PATH, path_column_sort_func, + NULL, NULL); + + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + no_sort_column_sort_func, + NULL, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + + g_signal_connect (selection, + "changed", + G_CALLBACK (selection_changed_cb), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "row_activated", + G_CALLBACK (row_activated_cb), + window); + + g_signal_connect (G_OBJECT (window->priv->list_view), + "button_press_event", + G_CALLBACK (file_button_press_cb), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "button_release_event", + G_CALLBACK (file_button_release_cb), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "motion_notify_event", + G_CALLBACK (file_motion_notify_callback), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "leave_notify_event", + G_CALLBACK (file_leave_notify_callback), + window); + + g_signal_connect (G_OBJECT (window->priv->list_store), + "sort_column_changed", + G_CALLBACK (sort_column_changed_cb), + window); + + g_signal_connect (G_OBJECT (window->priv->list_view), + "drag_begin", + G_CALLBACK (file_list_drag_begin), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "drag_end", + G_CALLBACK (file_list_drag_end), + window); + egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (window->priv->list_view)); + + list_scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (list_scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (list_scrolled_window), window->priv->list_view); + + /* filter bar */ + + window->priv->filter_bar = filter_box = gtk_hbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (filter_box), 3); + fr_window_attach (FR_WINDOW (window), window->priv->filter_bar, FR_WINDOW_AREA_FILTERBAR); + + gtk_box_pack_start (GTK_BOX (filter_box), + gtk_label_new (_("Find:")), FALSE, FALSE, 0); + + /* * filter entry */ + + window->priv->filter_entry = GTK_WIDGET (gtk_entry_new ()); + gtk_entry_set_icon_from_stock (GTK_ENTRY (window->priv->filter_entry), + GTK_ENTRY_ICON_SECONDARY, + GTK_STOCK_CLEAR); + + gtk_widget_set_size_request (window->priv->filter_entry, 300, -1); + gtk_box_pack_start (GTK_BOX (filter_box), + window->priv->filter_entry, FALSE, FALSE, 6); + + g_signal_connect (G_OBJECT (window->priv->filter_entry), + "activate", + G_CALLBACK (filter_entry_activate_cb), + window); + g_signal_connect (G_OBJECT (window->priv->filter_entry), + "icon-release", + G_CALLBACK (filter_entry_icon_release_cb), + window); + + gtk_widget_show_all (filter_box); + + /* tree view */ + + window->priv->tree_store = gtk_tree_store_new (TREE_NUMBER_OF_COLUMNS, + G_TYPE_STRING, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + PANGO_TYPE_WEIGHT); + window->priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->tree_store)); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (window->priv->tree_view), FALSE); + add_dir_tree_columns (window, GTK_TREE_VIEW (window->priv->tree_view)); + + g_signal_connect (G_OBJECT (window->priv->tree_view), + "button_press_event", + G_CALLBACK (dir_tree_button_press_cb), + window); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + g_signal_connect (selection, + "changed", + G_CALLBACK (dir_tree_selection_changed_cb), + window); + + g_signal_connect (G_OBJECT (window->priv->tree_view), + "drag_begin", + G_CALLBACK (file_list_drag_begin), + window); + g_signal_connect (G_OBJECT (window->priv->tree_view), + "drag_end", + G_CALLBACK (file_list_drag_end), + window); + g_signal_connect (G_OBJECT (window->priv->tree_view), + "drag_data_get", + G_CALLBACK (fr_window_folder_tree_drag_data_get), + window); + gtk_drag_source_set (window->priv->tree_view, + GDK_BUTTON1_MASK, + folder_tree_targets, G_N_ELEMENTS (folder_tree_targets), + GDK_ACTION_COPY); + + tree_scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (tree_scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (tree_scrolled_window), window->priv->tree_view); + + /* side pane */ + + window->priv->sidepane = gtk_vbox_new (FALSE, 0); + + sidepane_title = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (sidepane_title), GTK_SHADOW_ETCHED_IN); + + sidepane_title_box = gtk_hbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (sidepane_title_box), 2); + gtk_container_add (GTK_CONTAINER (sidepane_title), sidepane_title_box); + sidepane_title_label = gtk_label_new (_("Folders")); + + gtk_misc_set_alignment (GTK_MISC (sidepane_title_label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (sidepane_title_box), sidepane_title_label, TRUE, TRUE, 0); + + close_sidepane_button = gtk_button_new (); + gtk_container_add (GTK_CONTAINER (close_sidepane_button), gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)); + gtk_button_set_relief (GTK_BUTTON (close_sidepane_button), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text (close_sidepane_button, _("Close the folders pane")); + g_signal_connect (close_sidepane_button, + "clicked", + G_CALLBACK (close_sidepane_button_clicked_cb), + window); + gtk_box_pack_end (GTK_BOX (sidepane_title_box), close_sidepane_button, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (window->priv->sidepane), sidepane_title, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (window->priv->sidepane), tree_scrolled_window, TRUE, TRUE, 0); + + /* main content */ + + window->priv->paned = gtk_hpaned_new (); + gtk_paned_pack1 (GTK_PANED (window->priv->paned), window->priv->sidepane, FALSE, TRUE); + gtk_paned_pack2 (GTK_PANED (window->priv->paned), list_scrolled_window, TRUE, TRUE); + gtk_paned_set_position (GTK_PANED (window->priv->paned), eel_mateconf_get_integer (PREF_UI_SIDEBAR_WIDTH, DEF_SIDEBAR_WIDTH)); + + fr_window_attach (FR_WINDOW (window), window->priv->paned, FR_WINDOW_AREA_CONTENTS); + gtk_widget_show_all (window->priv->paned); + + /* Build the menu and the toolbar. */ + + ui = gtk_ui_manager_new (); + + window->priv->actions = actions = gtk_action_group_new ("Actions"); + gtk_action_group_set_translation_domain (actions, NULL); + gtk_action_group_add_actions (actions, + action_entries, + n_action_entries, + window); + gtk_action_group_add_toggle_actions (actions, + action_toggle_entries, + n_action_toggle_entries, + window); + gtk_action_group_add_radio_actions (actions, + view_as_entries, + n_view_as_entries, + window->priv->list_mode, + G_CALLBACK (view_as_radio_action), + window); + gtk_action_group_add_radio_actions (actions, + sort_by_entries, + n_sort_by_entries, + window->priv->sort_type, + G_CALLBACK (sort_by_radio_action), + window); + + g_signal_connect (ui, "connect_proxy", + G_CALLBACK (connect_proxy_cb), window); + g_signal_connect (ui, "disconnect_proxy", + G_CALLBACK (disconnect_proxy_cb), window); + + gtk_ui_manager_insert_action_group (ui, actions, 0); + gtk_window_add_accel_group (GTK_WINDOW (window), + gtk_ui_manager_get_accel_group (ui)); + + /* Add a hidden short cut Ctrl-Q for power users */ + gtk_accel_group_connect (gtk_ui_manager_get_accel_group (ui), + GDK_q, GDK_CONTROL_MASK, 0, + g_cclosure_new_swap (G_CALLBACK (fr_window_close), window, NULL)); + + + if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error)) { + g_message ("building menus failed: %s", error->message); + g_error_free (error); + } + + menubar = gtk_ui_manager_get_widget (ui, "/MenuBar"); + fr_window_attach (FR_WINDOW (window), menubar, FR_WINDOW_AREA_MENUBAR); + gtk_widget_show (menubar); + + window->priv->toolbar = toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar"); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE); + set_action_important (ui, "/ToolBar/Extract_Toolbar"); + + /* location bar */ + + window->priv->location_bar = gtk_ui_manager_get_widget (ui, "/LocationBar"); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (window->priv->location_bar), FALSE); + gtk_toolbar_set_style (GTK_TOOLBAR (window->priv->location_bar), GTK_TOOLBAR_BOTH_HORIZ); + set_action_important (ui, "/LocationBar/GoBack"); + + /* current location */ + + location_box = gtk_hbox_new (FALSE, 6); + /* Translators: after the colon there is a folder name. */ + window->priv->location_label = gtk_label_new_with_mnemonic (_("_Location:")); + gtk_box_pack_start (GTK_BOX (location_box), + window->priv->location_label, FALSE, FALSE, 5); + + window->priv->location_entry = gtk_entry_new (); + gtk_entry_set_icon_from_stock (GTK_ENTRY (window->priv->location_entry), + GTK_ENTRY_ICON_PRIMARY, + GTK_STOCK_DIRECTORY); + + gtk_box_pack_start (GTK_BOX (location_box), + window->priv->location_entry, TRUE, TRUE, 5); + + g_signal_connect (G_OBJECT (window->priv->location_entry), + "key_press_event", + G_CALLBACK (location_entry_key_press_event_cb), + window); + + { + GtkToolItem *tool_item; + + tool_item = gtk_separator_tool_item_new (); + gtk_widget_show_all (GTK_WIDGET (tool_item)); + gtk_toolbar_insert (GTK_TOOLBAR (window->priv->location_bar), tool_item, -1); + + tool_item = gtk_tool_item_new (); + gtk_tool_item_set_expand (tool_item, TRUE); + gtk_container_add (GTK_CONTAINER (tool_item), location_box); + gtk_widget_show_all (GTK_WIDGET (tool_item)); + gtk_toolbar_insert (GTK_TOOLBAR (window->priv->location_bar), tool_item, -1); + } + + fr_window_attach (FR_WINDOW (window), window->priv->location_bar, FR_WINDOW_AREA_LOCATIONBAR); + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) + gtk_widget_hide (window->priv->location_bar); + else + gtk_widget_show (window->priv->location_bar); + + /* Recent manager */ + + window->priv->recent_manager = gtk_recent_manager_get_default (); + + window->priv->recent_chooser_menu = gtk_recent_chooser_menu_new_for_manager (window->priv->recent_manager); + gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (window->priv->recent_chooser_menu), GTK_RECENT_SORT_MRU); + fr_window_init_recent_chooser (window, GTK_RECENT_CHOOSER (window->priv->recent_chooser_menu)); + menu_item = gtk_ui_manager_get_widget (ui, "/MenuBar/Archive/OpenRecentMenu"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), window->priv->recent_chooser_menu); + + window->priv->recent_chooser_toolbar = gtk_recent_chooser_menu_new_for_manager (window->priv->recent_manager); + fr_window_init_recent_chooser (window, GTK_RECENT_CHOOSER (window->priv->recent_chooser_toolbar)); + + /* Add the recent menu tool item */ + + open_recent_tool_item = gtk_menu_tool_button_new_from_stock (GTK_STOCK_OPEN); + gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_recent_tool_item), window->priv->recent_chooser_toolbar); + gtk_tool_item_set_homogeneous (open_recent_tool_item, FALSE); + gtk_widget_set_tooltip_text (GTK_WIDGET (open_recent_tool_item), _("Open archive")); + gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_recent_tool_item), _("Open a recently used archive")); + + window->priv->open_action = gtk_action_new ("Toolbar_Open", _("Open"), _("Open archive"), GTK_STOCK_OPEN); + g_object_set (window->priv->open_action, "is_important", TRUE, NULL); + g_signal_connect (window->priv->open_action, + "activate", + G_CALLBACK (activate_action_open), + window); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_recent_tool_item), window->priv->open_action); + + gtk_widget_show (GTK_WIDGET (open_recent_tool_item)); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), open_recent_tool_item, 1); + + /**/ + + fr_window_attach (FR_WINDOW (window), window->priv->toolbar, FR_WINDOW_AREA_TOOLBAR); + if (eel_mateconf_get_boolean (PREF_UI_TOOLBAR, TRUE)) + gtk_widget_show (toolbar); + else + gtk_widget_hide (toolbar); + + window->priv->file_popup_menu = gtk_ui_manager_get_widget (ui, "/FilePopupMenu"); + window->priv->folder_popup_menu = gtk_ui_manager_get_widget (ui, "/FolderPopupMenu"); + window->priv->sidebar_folder_popup_menu = gtk_ui_manager_get_widget (ui, "/SidebarFolderPopupMenu"); + + /* Create the statusbar. */ + + window->priv->statusbar = gtk_statusbar_new (); + window->priv->help_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "help_message"); + window->priv->list_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "list_info"); + window->priv->progress_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "progress"); + + statusbar = GTK_STATUSBAR (window->priv->statusbar); +#if GTK_CHECK_VERSION (2, 19, 1) + statusbar_box = gtk_statusbar_get_message_area (statusbar); + gtk_box_set_homogeneous (GTK_BOX (statusbar_box), FALSE); + gtk_box_set_spacing (GTK_BOX (statusbar_box), 4); + gtk_box_set_child_packing (GTK_BOX (statusbar_box), gtk_statusbar_get_message_area (statusbar), TRUE, TRUE, 0, GTK_PACK_START ); +#else + statusbar_box = gtk_hbox_new (FALSE, 4); + g_object_ref (statusbar->label); + gtk_container_remove (GTK_CONTAINER (statusbar->frame), statusbar->label); + gtk_box_pack_start (GTK_BOX (statusbar_box), statusbar->label, TRUE, TRUE, 0); + g_object_unref (statusbar->label); + gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar_box); +#endif + + window->priv->progress_bar = gtk_progress_bar_new (); + gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (window->priv->progress_bar), ACTIVITY_PULSE_STEP); + gtk_widget_set_size_request (window->priv->progress_bar, -1, PROGRESS_BAR_HEIGHT); + { + GtkWidget *vbox; + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (statusbar_box), vbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), window->priv->progress_bar, TRUE, TRUE, 1); + gtk_widget_show (vbox); + } + gtk_widget_show (statusbar_box); + gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE); + + fr_window_attach (FR_WINDOW (window), window->priv->statusbar, FR_WINDOW_AREA_STATUSBAR); + if (eel_mateconf_get_boolean (PREF_UI_STATUSBAR, TRUE)) + gtk_widget_show (window->priv->statusbar); + else + gtk_widget_hide (window->priv->statusbar); + + /**/ + + fr_window_update_title (window); + fr_window_update_sensitivity (window); + fr_window_update_file_list (window, FALSE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); + fr_window_update_columns_visibility (window); + + /* Add notification callbacks. */ + + i = 0; + + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_HISTORY_LEN, + pref_history_len_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_TOOLBAR, + pref_view_toolbar_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_STATUSBAR, + pref_view_statusbar_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_FOLDERS, + pref_view_folders_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_TYPE, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_SIZE, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_TIME, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_PATH, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_USE_MIME_ICONS, + pref_use_mime_icons_changed, + window); + + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_CAJA_CLICK_POLICY, + pref_click_policy_changed, + window); + + /* Give focus to the list. */ + + gtk_widget_grab_focus (window->priv->list_view); +} + + +GtkWidget * +fr_window_new (void) +{ + GtkWidget *window; + + window = g_object_new (FR_TYPE_WINDOW, NULL); + fr_window_construct ((FrWindow*) window); + + return window; +} + + +static void +fr_window_set_archive_uri (FrWindow *window, + const char *uri) +{ + if (window->priv->archive_uri != NULL) + g_free (window->priv->archive_uri); + window->priv->archive_uri = g_strdup (uri); +} + + +gboolean +fr_window_archive_new (FrWindow *window, + const char *uri) +{ + g_return_val_if_fail (window != NULL, FALSE); + + if (! fr_archive_create (window->archive, uri)) { + GtkWindow *file_sel = g_object_get_data (G_OBJECT (window), "fr_file_sel"); + + window->priv->load_error_parent_window = file_sel; + fr_archive_action_completed (window->archive, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_PROC_ERROR_GENERIC, + _("Archive type not supported.")); + + return FALSE; + } + + fr_window_set_archive_uri (window, uri); + window->priv->archive_present = TRUE; + window->priv->archive_new = TRUE; + + fr_archive_action_completed (window->archive, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_PROC_ERROR_NONE, + NULL); + + return TRUE; +} + + +FrWindow * +fr_window_archive_open (FrWindow *current_window, + const char *uri, + GtkWindow *parent) +{ + FrWindow *window = current_window; + + if (current_window->priv->archive_present) + window = (FrWindow *) fr_window_new (); + + g_return_val_if_fail (window != NULL, FALSE); + + fr_window_archive_close (window); + + fr_window_set_archive_uri (window, uri); + window->priv->archive_present = FALSE; + window->priv->give_focus_to_the_list = TRUE; + window->priv->load_error_parent_window = parent; + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (window->priv->archive_uri), + (GFreeFunc) g_free); + + fr_archive_load (window->archive, window->priv->archive_uri, window->priv->password); + + return window; +} + + +void +fr_window_archive_close (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (! window->priv->archive_new && ! window->priv->archive_present) + return; + + fr_window_free_open_files (window); + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + + fr_window_set_password (window, NULL); + fr_window_set_volume_size(window, 0); + fr_window_history_clear (window); + + window->priv->archive_new = FALSE; + window->priv->archive_present = FALSE; + + fr_window_update_title (window); + fr_window_update_sensitivity (window); + fr_window_update_file_list (window, FALSE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); + fr_window_update_statusbar_list_info (window); +} + + +const char * +fr_window_get_archive_uri (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + return window->priv->archive_uri; +} + + +const char * +fr_window_get_paste_archive_uri (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + if (window->priv->clipboard_data != NULL) + return window->priv->clipboard_data->archive_filename; + else + return NULL; +} + + +gboolean +fr_window_archive_is_present (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, FALSE); + + return window->priv->archive_present; +} + + +typedef struct { + char *uri; + char *password; + gboolean encrypt_header; + guint volume_size; +} SaveAsData; + + +static SaveAsData * +save_as_data_new (const char *uri, + const char *password, + gboolean encrypt_header, + guint volume_size) +{ + SaveAsData *sdata; + + sdata = g_new0 (SaveAsData, 1); + if (uri != NULL) + sdata->uri = g_strdup (uri); + if (password != NULL) + sdata->password = g_strdup (password); + sdata->encrypt_header = encrypt_header; + sdata->volume_size = volume_size; + + return sdata; +} + + +static void +save_as_data_free (SaveAsData *sdata) +{ + if (sdata == NULL) + return; + g_free (sdata->uri); + g_free (sdata->password); + g_free (sdata); +} + + +void +fr_window_archive_save_as (FrWindow *window, + const char *uri, + const char *password, + gboolean encrypt_header, + guint volume_size) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (uri != NULL); + g_return_if_fail (window->archive != NULL); + + fr_window_convert_data_free (window, TRUE); + window->priv->convert_data.new_file = g_strdup (uri); + + /* create the new archive */ + + window->priv->convert_data.new_archive = fr_archive_new (); + if (! fr_archive_create (window->priv->convert_data.new_archive, uri)) { + GtkWidget *d; + char *utf8_name; + char *message; + + utf8_name = g_uri_display_basename (uri); + message = g_strdup_printf (_("Could not save the archive \"%s\""), utf8_name); + g_free (utf8_name); + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + message, + "%s", + _("Archive type not supported.")); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + + g_free (message); + + g_object_unref (window->priv->convert_data.new_archive); + window->priv->convert_data.new_archive = NULL; + + return; + } + + g_return_if_fail (window->priv->convert_data.new_archive->command != NULL); + + if (password != NULL) { + window->priv->convert_data.password = g_strdup (password); + window->priv->convert_data.encrypt_header = encrypt_header; + } + else + window->priv->convert_data.encrypt_header = FALSE; + window->priv->convert_data.volume_size = volume_size; + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_SAVE_AS, + save_as_data_new (uri, password, encrypt_header, volume_size), + (GFreeFunc) save_as_data_free); + + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "start", + G_CALLBACK (action_started), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "done", + G_CALLBACK (convert__action_performed), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "progress", + G_CALLBACK (fr_window_progress_cb), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "message", + G_CALLBACK (fr_window_message_cb), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "stoppable", + G_CALLBACK (fr_window_stoppable_cb), + window); + + window->priv->convert_data.converting = TRUE; + window->priv->convert_data.temp_dir = get_temp_work_dir (NULL); + + fr_process_clear (window->archive->process); + fr_archive_extract_to_local (window->archive, + NULL, + window->priv->convert_data.temp_dir, + NULL, + TRUE, + FALSE, + FALSE, + window->priv->password); + fr_process_start (window->archive->process); +} + + +void +fr_window_archive_reload (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->activity_ref > 0) + return; + if (window->priv->archive_new) + return; + + fr_archive_reload (window->archive, window->priv->password); +} + + +void +fr_window_archive_rename (FrWindow *window, + const char *uri) +{ + g_return_if_fail (window != NULL); + + if (window->priv->archive_new) { + fr_window_archive_new (window, uri); + return; + } + + fr_archive_rename (window->archive, uri); + fr_window_set_archive_uri (window, uri); + + fr_window_update_title (window); + fr_window_add_to_recent_list (window, window->priv->archive_uri); +} + + +/**/ + + +void +fr_window_archive_add_files (FrWindow *window, + GList *file_list, /* GFile list */ + gboolean update) +{ + GFile *base; + char *base_dir; + int base_len; + GList *files = NULL; + GList *scan; + char *base_uri; + + base = g_file_get_parent ((GFile *) file_list->data); + base_dir = g_file_get_path (base); + base_len = 0; + if (strcmp (base_dir, "/") != 0) + base_len = strlen (base_dir); + + for (scan = file_list; scan; scan = scan->next) { + GFile *file = scan->data; + char *path; + char *rel_path; + + path = g_file_get_path (file); + rel_path = g_strdup (path + base_len + 1); + files = g_list_prepend (files, rel_path); + + g_free (path); + } + + base_uri = g_file_get_uri (base); + + fr_archive_add_files (window->archive, + files, + base_uri, + fr_window_get_current_location (window), + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + + g_free (base_uri); + path_list_free (files); + g_free (base_dir); + g_object_unref (base); +} + + +void +fr_window_archive_add_with_wildcard (FrWindow *window, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + const char *base_dir, + const char *dest_dir, + gboolean update, + gboolean follow_links) +{ + fr_archive_add_with_wildcard (window->archive, + include_files, + exclude_files, + exclude_folders, + base_dir, + (dest_dir == NULL)? fr_window_get_current_location (window): dest_dir, + update, + follow_links, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_add_directory (FrWindow *window, + const char *directory, + const char *base_dir, + const char *dest_dir, + gboolean update) +{ + fr_archive_add_directory (window->archive, + directory, + base_dir, + (dest_dir == NULL)? fr_window_get_current_location (window): dest_dir, + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_add_items (FrWindow *window, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update) +{ + fr_archive_add_items (window->archive, + item_list, + base_dir, + (dest_dir == NULL)? fr_window_get_current_location (window): dest_dir, + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_add_dropped_items (FrWindow *window, + GList *item_list, + gboolean update) +{ + fr_archive_add_dropped_items (window->archive, + item_list, + fr_window_get_current_location (window), + fr_window_get_current_location (window), + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_remove (FrWindow *window, + GList *file_list) +{ + fr_window_clipboard_remove_file_list (window, file_list); + + fr_process_clear (window->archive->process); + fr_archive_remove (window->archive, file_list, window->priv->compression); + fr_process_start (window->archive->process); +} + + +/* -- window_archive_extract -- */ + + +static ExtractData* +extract_data_new (GList *file_list, + const char *extract_to_dir, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + gboolean extract_here) +{ + ExtractData *edata; + + edata = g_new0 (ExtractData, 1); + edata->file_list = path_list_dup (file_list); + if (extract_to_dir != NULL) + edata->extract_to_dir = g_strdup (extract_to_dir); + edata->skip_older = skip_older; + edata->overwrite = overwrite; + edata->junk_paths = junk_paths; + if (base_dir != NULL) + edata->base_dir = g_strdup (base_dir); + edata->extract_here = extract_here; + + return edata; +} + + +static ExtractData* +extract_to_data_new (const char *extract_to_dir) +{ + return extract_data_new (NULL, + extract_to_dir, + NULL, + FALSE, + TRUE, + FALSE, + FALSE); +} + + +static void +extract_data_free (ExtractData *edata) +{ + g_return_if_fail (edata != NULL); + + path_list_free (edata->file_list); + g_free (edata->extract_to_dir); + g_free (edata->base_dir); + + g_free (edata); +} + + +static gboolean +archive_is_encrypted (FrWindow *window, + GList *file_list) +{ + gboolean encrypted = FALSE; + + if (file_list == NULL) { + int i; + + for (i = 0; ! encrypted && i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + + if (fdata->encrypted) + encrypted = TRUE; + } + } + else { + + GHashTable *file_hash; + int i; + GList *scan; + + file_hash = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + g_hash_table_insert (file_hash, fdata->original_path, fdata); + } + + for (scan = file_list; ! encrypted && scan; scan = scan->next) { + char *filename = scan->data; + FileData *fdata; + + fdata = g_hash_table_lookup (file_hash, filename); + g_return_val_if_fail (fdata != NULL, FALSE); + + if (fdata->encrypted) + encrypted = TRUE; + } + + g_hash_table_destroy (file_hash); + } + + return encrypted; +} + + +void +fr_window_archive_extract_here (FrWindow *window, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths) +{ + ExtractData *edata; + + edata = extract_data_new (NULL, + NULL, + NULL, + skip_older, + overwrite, + junk_paths, + TRUE); + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_EXTRACT, + edata, + (GFreeFunc) extract_data_free); + + if (archive_is_encrypted (window, NULL) && (window->priv->password == NULL)) { + dlg_ask_password (window); + return; + } + + window->priv->ask_to_open_destination_after_extraction = FALSE; + + fr_process_clear (window->archive->process); + if (fr_archive_extract_here (window->archive, + edata->skip_older, + edata->overwrite, + edata->junk_paths, + window->priv->password)) + { + fr_process_start (window->archive->process); + } +} + + +void +fr_window_archive_extract (FrWindow *window, + GList *file_list, + const char *extract_to_dir, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + gboolean ask_to_open_destination) +{ + ExtractData *edata; + gboolean do_not_extract = FALSE; + GError *error = NULL; + + edata = extract_data_new (file_list, + extract_to_dir, + base_dir, + skip_older, + overwrite, + junk_paths, + FALSE); + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_EXTRACT, + edata, + (GFreeFunc) extract_data_free); + + if (archive_is_encrypted (window, edata->file_list) && (window->priv->password == NULL)) { + dlg_ask_password (window); + return; + } + + if (! uri_is_dir (edata->extract_to_dir)) { + if (! ForceDirectoryCreation) { + GtkWidget *d; + int r; + char *folder_name; + char *msg; + + folder_name = g_filename_display_name (edata->extract_to_dir); + msg = g_strdup_printf (_("Destination folder \"%s\" does not exist.\n\nDo you want to create it?"), folder_name); + g_free (folder_name); + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + msg, + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Create _Folder"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (msg); + + if (r != GTK_RESPONSE_YES) + do_not_extract = TRUE; + } + + if (! do_not_extract && ! ensure_dir_exists (edata->extract_to_dir, 0755, &error)) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + 0, + NULL, + _("Extraction not performed"), + _("Could not create the destination folder: %s."), + error->message); + g_clear_error (&error); + fr_window_show_error_dialog (window, d, GTK_WINDOW (window)); + fr_window_stop_batch (window); + + return; + } + } + + if (do_not_extract) { + GtkWidget *d; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + 0, + GTK_STOCK_DIALOG_WARNING, + _("Extraction not performed"), + NULL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK); + fr_window_show_error_dialog (window, d, GTK_WINDOW (window)); + fr_window_stop_batch (window); + + return; + } + + window->priv->ask_to_open_destination_after_extraction = ask_to_open_destination; + + fr_process_clear (window->archive->process); + fr_archive_extract (window->archive, + edata->file_list, + edata->extract_to_dir, + edata->base_dir, + edata->skip_older, + edata->overwrite, + edata->junk_paths, + window->priv->password); + fr_process_start (window->archive->process); +} + + +void +fr_window_archive_test (FrWindow *window) +{ + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_TEST, + NULL, + NULL); + fr_archive_test (window->archive, window->priv->password); +} + + +void +fr_window_set_password (FrWindow *window, + const char *password) +{ + g_return_if_fail (window != NULL); + + if (window->priv->password != NULL) { + g_free (window->priv->password); + window->priv->password = NULL; + } + + if ((password != NULL) && (password[0] != '\0')) + window->priv->password = g_strdup (password); +} + +void +fr_window_set_password_for_paste (FrWindow *window, + const char *password) +{ + g_return_if_fail (window != NULL); + + if (window->priv->password_for_paste != NULL) { + g_free (window->priv->password_for_paste); + window->priv->password_for_paste = NULL; + } + + if ((password != NULL) && (password[0] != '\0')) + window->priv->password_for_paste = g_strdup (password); +} + +const char * +fr_window_get_password (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + return window->priv->password; +} + + +void +fr_window_set_encrypt_header (FrWindow *window, + gboolean encrypt_header) +{ + g_return_if_fail (window != NULL); + + window->priv->encrypt_header = encrypt_header; +} + + +gboolean +fr_window_get_encrypt_header (FrWindow *window) +{ + return window->priv->encrypt_header; +} + + +void +fr_window_set_compression (FrWindow *window, + FrCompression compression) +{ + g_return_if_fail (window != NULL); + + window->priv->compression = compression; +} + + +FrCompression +fr_window_get_compression (FrWindow *window) +{ + return window->priv->compression; +} + + +void +fr_window_set_volume_size (FrWindow *window, + guint volume_size) +{ + g_return_if_fail (window != NULL); + + window->priv->volume_size = volume_size; +} + + +guint +fr_window_get_volume_size (FrWindow *window) +{ + return window->priv->volume_size; +} + + +void +fr_window_go_to_location (FrWindow *window, + const char *path, + gboolean force_update) +{ + char *dir; + + g_return_if_fail (window != NULL); + g_return_if_fail (path != NULL); + + if (force_update) { + g_free (window->priv->last_location); + window->priv->last_location = NULL; + } + + if (path[strlen (path) - 1] != '/') + dir = g_strconcat (path, "/", NULL); + else + dir = g_strdup (path); + + if ((window->priv->last_location == NULL) || (strcmp (window->priv->last_location, dir) != 0)) { + g_free (window->priv->last_location); + window->priv->last_location = dir; + + fr_window_history_add (window, dir); + fr_window_update_file_list (window, TRUE); + fr_window_update_current_location (window); + } + else + g_free (dir); +} + + +const char * +fr_window_get_current_location (FrWindow *window) +{ + if (window->priv->history_current == NULL) { + fr_window_history_add (window, "/"); + return window->priv->history_current->data; + } + else + return (const char*) window->priv->history_current->data; +} + + +void +fr_window_go_up_one_level (FrWindow *window) +{ + char *parent_dir; + + g_return_if_fail (window != NULL); + + parent_dir = get_parent_dir (fr_window_get_current_location (window)); + fr_window_go_to_location (window, parent_dir, FALSE); + g_free (parent_dir); +} + + +void +fr_window_go_back (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->history == NULL) + return; + if (window->priv->history_current == NULL) + return; + if (window->priv->history_current->next == NULL) + return; + window->priv->history_current = window->priv->history_current->next; + + fr_window_go_to_location (window, window->priv->history_current->data, FALSE); +} + + +void +fr_window_go_forward (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->history == NULL) + return; + if (window->priv->history_current == NULL) + return; + if (window->priv->history_current->prev == NULL) + return; + window->priv->history_current = window->priv->history_current->prev; + + fr_window_go_to_location (window, window->priv->history_current->data, FALSE); +} + + +void +fr_window_set_list_mode (FrWindow *window, + FrWindowListMode list_mode) +{ + g_return_if_fail (window != NULL); + + window->priv->list_mode = window->priv->last_list_mode = list_mode; + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + fr_window_history_clear (window); + fr_window_history_add (window, "/"); + } + + preferences_set_list_mode (window->priv->last_list_mode); + eel_mateconf_set_boolean (PREF_LIST_SHOW_PATH, (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT)); + + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); +} + + +GtkTreeModel * +fr_window_get_list_store (FrWindow *window) +{ + return GTK_TREE_MODEL (window->priv->list_store); +} + + +void +fr_window_find (FrWindow *window) +{ + window->priv->filter_mode = TRUE; + gtk_widget_show (window->priv->filter_bar); + gtk_widget_grab_focus (window->priv->filter_entry); +} + + +void +fr_window_select_all (FrWindow *window) +{ + gtk_tree_selection_select_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view))); +} + + +void +fr_window_unselect_all (FrWindow *window) +{ + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view))); +} + + +void +fr_window_set_sort_type (FrWindow *window, + GtkSortType sort_type) +{ + window->priv->sort_type = sort_type; + fr_window_update_list_order (window); +} + + +void +fr_window_stop (FrWindow *window) +{ + if (! window->priv->stoppable) + return; + + if (window->priv->activity_ref > 0) + fr_archive_stop (window->archive); + + if (window->priv->convert_data.converting) + fr_window_convert_data_free (window, TRUE); +} + + +/* -- start/stop activity mode -- */ + + +static int +activity_cb (gpointer data) +{ + FrWindow *window = data; + + if ((window->priv->pd_progress_bar != NULL) && window->priv->progress_pulse) + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (window->priv->pd_progress_bar)); + if (window->priv->progress_pulse) + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (window->priv->progress_bar)); + + return TRUE; +} + + +void +fr_window_start_activity_mode (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->activity_ref++ > 0) + return; + + window->priv->activity_timeout_handle = g_timeout_add (ACTIVITY_DELAY, + activity_cb, + window); + fr_window_update_sensitivity (window); +} + + +void +fr_window_stop_activity_mode (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->activity_ref == 0) + return; + + window->priv->activity_ref--; + + if (window->priv->activity_ref > 0) + return; + + if (window->priv->activity_timeout_handle == 0) + return; + + g_source_remove (window->priv->activity_timeout_handle); + window->priv->activity_timeout_handle = 0; + + if (! gtk_widget_get_realized (GTK_WIDGET (window))) + return; + + if (window->priv->progress_dialog != NULL) + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->pd_progress_bar), 0.0); + + if (! window->priv->batch_mode) { + if (window->priv->progress_bar != NULL) + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->progress_bar), 0.0); + fr_window_update_sensitivity (window); + } +} + + +static gboolean +last_output_window__unrealize_cb (GtkWidget *widget, + gpointer data) +{ + pref_util_save_window_geometry (GTK_WINDOW (widget), LAST_OUTPUT_DIALOG_NAME); + return FALSE; +} + + +void +fr_window_view_last_output (FrWindow *window, + const char *title) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *text_view; + GtkWidget *scrolled; + GtkTextBuffer *text_buffer; + GtkTextIter iter; + GList *scan; + + if (title == NULL) + title = _("Last Output"); + + dialog = gtk_dialog_new_with_buttons (title, + GTK_WINDOW (window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 6); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 8); + + gtk_widget_set_size_request (dialog, 500, 300); + + /* Add text */ + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), + GTK_SHADOW_ETCHED_IN); + + text_buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_create_tag (text_buffer, "monospace", + "family", "monospace", NULL); + + text_view = gtk_text_view_new_with_buffer (text_buffer); + g_object_unref (text_buffer); + gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE); + + /**/ + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + + gtk_container_add (GTK_CONTAINER (scrolled), text_view); + gtk_box_pack_start (GTK_BOX (vbox), scrolled, + TRUE, TRUE, 0); + + gtk_widget_show_all (vbox); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + vbox, + TRUE, TRUE, 0); + + /* signals */ + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + g_signal_connect (G_OBJECT (dialog), + "unrealize", + G_CALLBACK (last_output_window__unrealize_cb), + NULL); + + /**/ + + gtk_text_buffer_get_iter_at_offset (text_buffer, &iter, 0); + scan = window->archive->process->out.raw; + for (; scan; scan = scan->next) { + char *line = scan->data; + char *utf8_line; + gsize bytes_written; + + utf8_line = g_locale_to_utf8 (line, -1, NULL, &bytes_written, NULL); + gtk_text_buffer_insert_with_tags_by_name (text_buffer, + &iter, + utf8_line, + bytes_written, + "monospace", NULL); + g_free (utf8_line); + gtk_text_buffer_insert (text_buffer, &iter, "\n", 1); + } + + /**/ + + pref_util_restore_window_geometry (GTK_WINDOW (dialog), LAST_OUTPUT_DIALOG_NAME); +} + + +/* -- fr_window_rename_selection -- */ + + +typedef struct { + char *path_to_rename; + char *old_name; + char *new_name; + char *current_dir; + gboolean is_dir; + gboolean dir_in_archive; + char *original_path; +} RenameData; + + +static RenameData* +rename_data_new (const char *path_to_rename, + const char *old_name, + const char *new_name, + const char *current_dir, + gboolean is_dir, + gboolean dir_in_archive, + const char *original_path) +{ + RenameData *rdata; + + rdata = g_new0 (RenameData, 1); + rdata->path_to_rename = g_strdup (path_to_rename); + if (old_name != NULL) + rdata->old_name = g_strdup (old_name); + if (new_name != NULL) + rdata->new_name = g_strdup (new_name); + if (current_dir != NULL) + rdata->current_dir = g_strdup (current_dir); + rdata->is_dir = is_dir; + rdata->dir_in_archive = dir_in_archive; + if (original_path != NULL) + rdata->original_path = g_strdup (original_path); + + return rdata; +} + + +static void +rename_data_free (RenameData *rdata) +{ + g_return_if_fail (rdata != NULL); + + g_free (rdata->path_to_rename); + g_free (rdata->old_name); + g_free (rdata->new_name); + g_free (rdata->current_dir); + g_free (rdata->original_path); + g_free (rdata); +} + + +static void +rename_selection (FrWindow *window, + const char *path_to_rename, + const char *old_name, + const char *new_name, + const char *current_dir, + gboolean is_dir, + gboolean dir_in_archive, + const char *original_path) +{ + FrArchive *archive = window->archive; + RenameData *rdata; + char *tmp_dir; + GList *file_list; + gboolean added_dir; + char *new_dirname; + GList *new_file_list; + GList *scan; + + rdata = rename_data_new (path_to_rename, + old_name, + new_name, + current_dir, + is_dir, + dir_in_archive, + original_path); + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_RENAME, + rdata, + (GFreeFunc) rename_data_free); + + fr_process_clear (archive->process); + + tmp_dir = get_temp_work_dir (NULL); + + if (is_dir) + file_list = get_dir_list_from_path (window, rdata->path_to_rename); + else + file_list = g_list_append (NULL, g_strdup (rdata->path_to_rename)); + + fr_archive_extract_to_local (archive, + file_list, + tmp_dir, + NULL, + FALSE, + TRUE, + FALSE, + window->priv->password); + + /* temporarily add the dir to rename to the list if it's stored in the + * archive, this way it will be removed from the archive... */ + added_dir = FALSE; + if (is_dir && dir_in_archive && ! g_list_find_custom (file_list, original_path, (GCompareFunc) strcmp)) { + file_list = g_list_prepend (file_list, g_strdup (original_path)); + added_dir = TRUE; + } + + fr_archive_remove (archive, file_list, window->priv->compression); + fr_window_clipboard_remove_file_list (window, file_list); + + /* ...and remove it from the list again */ + if (added_dir) { + GList *tmp; + + tmp = file_list; + file_list = g_list_remove_link (file_list, tmp); + + g_free (tmp->data); + g_list_free (tmp); + } + + /* rename the files. */ + + new_dirname = g_build_filename (rdata->current_dir + 1, rdata->new_name, "/", NULL); + new_file_list = NULL; + if (rdata->is_dir) { + char *old_path; + char *new_path; + + old_path = g_build_filename (tmp_dir, rdata->current_dir, rdata->old_name, NULL); + new_path = g_build_filename (tmp_dir, rdata->current_dir, rdata->new_name, NULL); + + fr_process_begin_command (archive->process, "mv"); + fr_process_add_arg (archive->process, "-f"); + fr_process_add_arg (archive->process, old_path); + fr_process_add_arg (archive->process, new_path); + fr_process_end_command (archive->process); + + g_free (old_path); + g_free (new_path); + } + + for (scan = file_list; scan; scan = scan->next) { + const char *current_dir_relative = rdata->current_dir + 1; + const char *filename = (char*) scan->data; + char *old_path = NULL, *common = NULL, *new_path = NULL; + char *new_filename; + + old_path = g_build_filename (tmp_dir, filename, NULL); + + if (strlen (filename) > (strlen (rdata->current_dir) + strlen (rdata->old_name))) + common = g_strdup (filename + strlen (rdata->current_dir) + strlen (rdata->old_name)); + new_path = g_build_filename (tmp_dir, rdata->current_dir, rdata->new_name, common, NULL); + + if (! rdata->is_dir) { + fr_process_begin_command (archive->process, "mv"); + fr_process_add_arg (archive->process, "-f"); + fr_process_add_arg (archive->process, old_path); + fr_process_add_arg (archive->process, new_path); + fr_process_end_command (archive->process); + } + + new_filename = g_build_filename (current_dir_relative, rdata->new_name, common, NULL); + new_file_list = g_list_prepend (new_file_list, new_filename); + + g_free (old_path); + g_free (common); + g_free (new_path); + } + new_file_list = g_list_reverse (new_file_list); + + /* FIXME: this is broken for tar archives. + if (is_dir && dir_in_archive && ! g_list_find_custom (new_file_list, new_dirname, (GCompareFunc) strcmp)) + new_file_list = g_list_prepend (new_file_list, g_build_filename (rdata->current_dir + 1, rdata->new_name, NULL)); + */ + + fr_archive_add (archive, + new_file_list, + tmp_dir, + NULL, + FALSE, + FALSE, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + + g_free (new_dirname); + path_list_free (new_file_list); + path_list_free (file_list); + + /* remove the tmp dir */ + + fr_process_begin_command (archive->process, "rm"); + fr_process_set_working_dir (archive->process, g_get_tmp_dir ()); + fr_process_set_sticky (archive->process, TRUE); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, tmp_dir); + fr_process_end_command (archive->process); + + fr_process_start (archive->process); + + g_free (tmp_dir); +} + + +static gboolean +valid_name (const char *new_name, + const char *old_name, + char **reason) +{ + char *utf8_new_name; + gboolean retval = TRUE; + + new_name = eat_spaces (new_name); + utf8_new_name = g_filename_display_name (new_name); + + if (*new_name == '\0') { + /* Translators: the name references to a filename. This message can appear when renaming a file. */ + *reason = g_strdup_printf ("%s\n\n%s", _("The new name is void."), _("Please use a different name.")); + retval = FALSE; + } + else if (strcmp (new_name, old_name) == 0) { + /* Translators: the name references to a filename. This message can appear when renaming a file. */ + *reason = g_strdup_printf ("%s\n\n%s", _("The new name is equal to the old one."), _("Please use a different name.")); + retval = FALSE; + } + else if (strchrs (new_name, BAD_CHARS)) { + /* Translators: the name references to a filename. This message can appear when renaming a file. */ + *reason = g_strdup_printf (_("The name \"%s\" is not valid because it cannot contain the characters: %s\n\n%s"), utf8_new_name, BAD_CHARS, _("Please use a different name.")); + retval = FALSE; + } + + g_free (utf8_new_name); + + return retval; +} + + +static gboolean +name_is_present (FrWindow *window, + const char *current_dir, + const char *new_name, + char **reason) +{ + gboolean retval = FALSE; + int i; + char *new_filename; + int new_filename_l; + + *reason = NULL; + + new_filename = g_build_filename (current_dir, new_name, NULL); + new_filename_l = strlen (new_filename); + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + const char *filename = fdata->full_path; + + if ((strncmp (filename, new_filename, new_filename_l) == 0) + && ((filename[new_filename_l] == '\0') + || (filename[new_filename_l] == G_DIR_SEPARATOR))) { + char *utf8_name = g_filename_display_name (new_name); + + if (filename[new_filename_l] == G_DIR_SEPARATOR) + *reason = g_strdup_printf (_("A folder named \"%s\" already exists.\n\n%s"), utf8_name, _("Please use a different name.")); + else + *reason = g_strdup_printf (_("A file named \"%s\" already exists.\n\n%s"), utf8_name, _("Please use a different name.")); + + retval = TRUE; + break; + } + } + + g_free (new_filename); + + return retval; +} + + +void +fr_window_rename_selection (FrWindow *window, + gboolean from_sidebar) +{ + char *path_to_rename; + char *parent_dir; + char *old_name; + gboolean renaming_dir = FALSE; + gboolean dir_in_archive = FALSE; + char *original_path = NULL; + char *utf8_old_name; + char *utf8_new_name; + + if (from_sidebar) { + path_to_rename = fr_window_get_selected_folder_in_tree_view (window); + if (path_to_rename == NULL) + return; + parent_dir = remove_level_from_path (path_to_rename); + old_name = g_strdup (file_name_from_path (path_to_rename)); + renaming_dir = TRUE; + } + else { + FileData *selected_item; + + selected_item = fr_window_get_selected_item_from_file_list (window); + if (selected_item == NULL) + return; + + renaming_dir = file_data_is_dir (selected_item); + dir_in_archive = selected_item->dir; + original_path = g_strdup (selected_item->original_path); + + if (renaming_dir && ! dir_in_archive) { + parent_dir = g_strdup (fr_window_get_current_location (window)); + old_name = g_strdup (selected_item->list_name); + path_to_rename = g_build_filename (parent_dir, old_name, NULL); + } + else { + if (renaming_dir) { + path_to_rename = remove_ending_separator (selected_item->full_path); + parent_dir = remove_level_from_path (path_to_rename); + } + else { + path_to_rename = g_strdup (selected_item->original_path); + parent_dir = remove_level_from_path (selected_item->full_path); + } + old_name = g_strdup (selected_item->name); + } + + file_data_free (selected_item); + } + + retry__rename_selection: + utf8_old_name = g_locale_to_utf8 (old_name, -1 ,0 ,0 ,0); + utf8_new_name = _gtk_request_dialog_run (GTK_WINDOW (window), + (GTK_DIALOG_DESTROY_WITH_PARENT + | GTK_DIALOG_MODAL), + _("Rename"), + (renaming_dir ? _("New folder name") : _("New file name")), + utf8_old_name, + 1024, + GTK_STOCK_CANCEL, + _("_Rename")); + g_free (utf8_old_name); + + if (utf8_new_name != NULL) { + char *new_name; + char *reason = NULL; + + new_name = g_filename_from_utf8 (utf8_new_name, -1, 0, 0, 0); + g_free (utf8_new_name); + + if (! valid_name (new_name, old_name, &reason)) { + char *utf8_name = g_filename_display_name (new_name); + GtkWidget *dlg; + + dlg = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + (renaming_dir ? _("Could not rename the folder") : _("Could not rename the file")), + "%s", + reason); + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + + g_free (reason); + g_free (utf8_name); + g_free (new_name); + + goto retry__rename_selection; + } + + if (name_is_present (window, parent_dir, new_name, &reason)) { + GtkWidget *dlg; + int r; + + dlg = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + (renaming_dir ? _("Could not rename the folder") : _("Could not rename the file")), + reason, + GTK_STOCK_CLOSE, GTK_RESPONSE_OK, + NULL); + r = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + g_free (reason); + g_free (new_name); + goto retry__rename_selection; + } + + rename_selection (window, + path_to_rename, + old_name, + new_name, + parent_dir, + renaming_dir, + dir_in_archive, + original_path); + + g_free (new_name); + } + + g_free (old_name); + g_free (parent_dir); + g_free (path_to_rename); + g_free (original_path); +} + + +static void +fr_clipboard_get (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer user_data_or_owner) +{ + FrWindow *window = user_data_or_owner; + char *data; + + if (gtk_selection_data_get_target (selection_data) != FR_SPECIAL_URI_LIST) + return; + + data = get_selection_data_from_clipboard_data (window, window->priv->copy_data); + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, + (guchar *) data, + strlen (data)); + g_free (data); +} + + +static void +fr_clipboard_clear (GtkClipboard *clipboard, + gpointer user_data_or_owner) +{ + FrWindow *window = user_data_or_owner; + + if (window->priv->copy_data != NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + } +} + + +GList * +fr_window_get_selection (FrWindow *window, + gboolean from_sidebar, + char **return_base_dir) +{ + GList *files; + char *base_dir; + + if (from_sidebar) { + char *selected_folder; + char *parent_folder; + + files = fr_window_get_folder_tree_selection (window, TRUE, NULL); + selected_folder = fr_window_get_selected_folder_in_tree_view (window); + parent_folder = remove_level_from_path (selected_folder); + if (parent_folder == NULL) + base_dir = g_strdup ("/"); + else if (parent_folder[strlen (parent_folder) - 1] == '/') + base_dir = g_strdup (parent_folder); + else + base_dir = g_strconcat (parent_folder, "/", NULL); + g_free (selected_folder); + g_free (parent_folder); + } + else { + files = fr_window_get_file_list_selection (window, TRUE, NULL); + base_dir = g_strdup (fr_window_get_current_location (window)); + } + + if (return_base_dir) + *return_base_dir = base_dir; + else + g_free (base_dir); + + return files; +} + + +static void +fr_window_copy_or_cut_selection (FrWindow *window, + FRClipboardOp op, + gboolean from_sidebar) +{ + GList *files; + char *base_dir; + GtkClipboard *clipboard; + + files = fr_window_get_selection (window, from_sidebar, &base_dir); + + if (window->priv->copy_data != NULL) + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = fr_clipboard_data_new (); + window->priv->copy_data->files = files; + window->priv->copy_data->op = op; + window->priv->copy_data->base_dir = base_dir; + + clipboard = gtk_clipboard_get (FR_CLIPBOARD); + gtk_clipboard_set_with_owner (clipboard, + clipboard_targets, + G_N_ELEMENTS (clipboard_targets), + fr_clipboard_get, + fr_clipboard_clear, + G_OBJECT (window)); + + fr_window_update_sensitivity (window); +} + + +void +fr_window_copy_selection (FrWindow *window, + gboolean from_sidebar) +{ + fr_window_copy_or_cut_selection (window, FR_CLIPBOARD_OP_COPY, from_sidebar); +} + + +void +fr_window_cut_selection (FrWindow *window, + gboolean from_sidebar) +{ + fr_window_copy_or_cut_selection (window, FR_CLIPBOARD_OP_CUT, from_sidebar); +} + + +static gboolean +always_fake_load (FrArchive *archive, + gpointer data) +{ + return TRUE; +} + + +static void +add_pasted_files (FrWindow *window, + FrClipboardData *data) +{ + const char *current_dir_relative = data->current_dir + 1; + GList *scan; + GList *new_file_list = NULL; + + if (window->priv->password_for_paste != NULL) { + g_free (window->priv->password_for_paste); + window->priv->password_for_paste = NULL; + } + + fr_process_clear (window->archive->process); + for (scan = data->files; scan; scan = scan->next) { + const char *old_name = (char*) scan->data; + char *new_name = g_build_filename (current_dir_relative, old_name + strlen (data->base_dir) - 1, NULL); + + /* skip folders */ + + if ((strcmp (old_name, new_name) != 0) + && (old_name[strlen (old_name) - 1] != '/')) + { + fr_process_begin_command (window->archive->process, "mv"); + fr_process_set_working_dir (window->archive->process, data->tmp_dir); + fr_process_add_arg (window->archive->process, "-f"); + fr_process_add_arg (window->archive->process, old_name); + fr_process_add_arg (window->archive->process, new_name); + fr_process_end_command (window->archive->process); + } + + new_file_list = g_list_prepend (new_file_list, new_name); + } + + fr_archive_add (window->archive, + new_file_list, + data->tmp_dir, + NULL, + FALSE, + FALSE, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + + path_list_free (new_file_list); + + /* remove the tmp dir */ + + fr_process_begin_command (window->archive->process, "rm"); + fr_process_set_working_dir (window->archive->process, g_get_tmp_dir ()); + fr_process_set_sticky (window->archive->process, TRUE); + fr_process_add_arg (window->archive->process, "-rf"); + fr_process_add_arg (window->archive->process, data->tmp_dir); + fr_process_end_command (window->archive->process); + + fr_process_start (window->archive->process); +} + + +static void +copy_from_archive_action_performed_cb (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer data) +{ + FrWindow *window = data; + gboolean continue_batch = FALSE; + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [DONE] (FR::Window)\n", action_names[action]); +#endif + + fr_window_stop_activity_mode (window); + fr_window_pop_message (window); + close_progress_dialog (window, FALSE); + + if (error->type == FR_PROC_ERROR_ASK_PASSWORD) { + dlg_ask_password_for_paste_operation (window); + return; + } + + continue_batch = handle_errors (window, archive, action, error); + + if (error->type != FR_PROC_ERROR_NONE) { + fr_clipboard_data_unref (window->priv->clipboard_data); + window->priv->clipboard_data = NULL; + return; + } + + switch (action) { + case FR_ACTION_LISTING_CONTENT: + fr_process_clear (window->priv->copy_from_archive->process); + fr_archive_extract_to_local (window->priv->copy_from_archive, + window->priv->clipboard_data->files, + window->priv->clipboard_data->tmp_dir, + NULL, + FALSE, + TRUE, + FALSE, + window->priv->clipboard_data->archive_password); + fr_process_start (window->priv->copy_from_archive->process); + break; + + case FR_ACTION_EXTRACTING_FILES: + if (window->priv->clipboard_data->op == FR_CLIPBOARD_OP_CUT) { + fr_process_clear (window->priv->copy_from_archive->process); + fr_archive_remove (window->priv->copy_from_archive, + window->priv->clipboard_data->files, + window->priv->compression); + fr_process_start (window->priv->copy_from_archive->process); + } + else + add_pasted_files (window, window->priv->clipboard_data); + break; + + case FR_ACTION_DELETING_FILES: + add_pasted_files (window, window->priv->clipboard_data); + break; + + default: + break; + } +} + + +static void +fr_window_paste_from_clipboard_data (FrWindow *window, + FrClipboardData *data) +{ + const char *current_dir_relative; + GHashTable *created_dirs; + GList *scan; + + if (window->priv->password_for_paste != NULL) + fr_clipboard_data_set_password (data, window->priv->password_for_paste); + + if (window->priv->clipboard_data != data) { + fr_clipboard_data_unref (window->priv->clipboard_data); + window->priv->clipboard_data = data; + } + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_PASTE, + fr_clipboard_data_ref (data), + (GFreeFunc) fr_clipboard_data_unref); + + current_dir_relative = data->current_dir + 1; + + data->tmp_dir = get_temp_work_dir (NULL); + created_dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + for (scan = data->files; scan; scan = scan->next) { + const char *old_name = (char*) scan->data; + char *new_name = g_build_filename (current_dir_relative, old_name + strlen (data->base_dir) - 1, NULL); + char *dir = remove_level_from_path (new_name); + + if ((dir != NULL) && (g_hash_table_lookup (created_dirs, dir) == NULL)) { + char *dir_path; + + dir_path = g_build_filename (data->tmp_dir, dir, NULL); + debug (DEBUG_INFO, "mktree %s\n", dir_path); + make_directory_tree_from_path (dir_path, 0700, NULL); + + g_free (dir_path); + g_hash_table_replace (created_dirs, g_strdup (dir), "1"); + } + + g_free (dir); + g_free (new_name); + } + g_hash_table_destroy (created_dirs); + + /**/ + + if (window->priv->copy_from_archive == NULL) { + window->priv->copy_from_archive = fr_archive_new (); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "start", + G_CALLBACK (action_started), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "done", + G_CALLBACK (copy_from_archive_action_performed_cb), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "progress", + G_CALLBACK (fr_window_progress_cb), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "message", + G_CALLBACK (fr_window_message_cb), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "stoppable", + G_CALLBACK (fr_window_stoppable_cb), + window); + fr_archive_set_fake_load_func (window->priv->copy_from_archive, always_fake_load, NULL); + } + fr_archive_load_local (window->priv->copy_from_archive, + data->archive_filename, + data->archive_password); +} + + +static void +fr_window_paste_selection_to (FrWindow *window, + const char *current_dir) +{ + GtkClipboard *clipboard; + GtkSelectionData *selection_data; + FrClipboardData *paste_data; + + clipboard = gtk_clipboard_get (FR_CLIPBOARD); + selection_data = gtk_clipboard_wait_for_contents (clipboard, FR_SPECIAL_URI_LIST); + if (selection_data == NULL) + return; + + paste_data = get_clipboard_data_from_selection_data (window, (char*) gtk_selection_data_get_data (selection_data)); + paste_data->current_dir = g_strdup (current_dir); + fr_window_paste_from_clipboard_data (window, paste_data); + + gtk_selection_data_free (selection_data); +} + + +void +fr_window_paste_selection (FrWindow *window, + gboolean from_sidebar) +{ + char *utf8_path, *utf8_old_path, *destination; + char *current_dir; + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) + return; + + /**/ + + utf8_old_path = g_filename_to_utf8 (fr_window_get_current_location (window), -1, NULL, NULL, NULL); + utf8_path = _gtk_request_dialog_run (GTK_WINDOW (window), + (GTK_DIALOG_DESTROY_WITH_PARENT + | GTK_DIALOG_MODAL), + _("Paste Selection"), + _("Destination folder"), + utf8_old_path, + 1024, + GTK_STOCK_CANCEL, + GTK_STOCK_PASTE); + g_free (utf8_old_path); + if (utf8_path == NULL) + return; + + destination = g_filename_from_utf8 (utf8_path, -1, NULL, NULL, NULL); + g_free (utf8_path); + + if (destination[0] != '/') + current_dir = build_uri (fr_window_get_current_location (window), destination, NULL); + else + current_dir = g_strdup (destination); + g_free (destination); + + fr_window_paste_selection_to (window, current_dir); + + g_free (current_dir); +} + + +/* -- fr_window_open_files -- */ + + +void +fr_window_open_files_with_command (FrWindow *window, + GList *file_list, + char *command) +{ + GAppInfo *app; + GError *error = NULL; + + app = g_app_info_create_from_commandline (command, NULL, G_APP_INFO_CREATE_NONE, &error); + if (error != NULL) { + _gtk_error_dialog_run (GTK_WINDOW (window), + _("Could not perform the operation"), + "%s", + error->message); + g_clear_error (&error); + return; + } + + fr_window_open_files_with_application (window, file_list, app); +} + + +void +fr_window_open_files_with_application (FrWindow *window, + GList *file_list, + GAppInfo *app) +{ + GList *uris = NULL, *scan; + GError *error = NULL; + + if (window->priv->activity_ref > 0) + return; + + for (scan = file_list; scan; scan = scan->next) + uris = g_list_prepend (uris, g_filename_to_uri (scan->data, NULL, NULL)); + + if (! g_app_info_launch_uris (app, uris, NULL, &error)) { + _gtk_error_dialog_run (GTK_WINDOW (window), + _("Could not perform the operation"), + "%s", + error->message); + g_clear_error (&error); + } + else { + char *uri; + const char *mime_type; + + uri = g_filename_to_uri (file_list->data, NULL, NULL); + mime_type = get_file_mime_type (uri, FALSE); + if (mime_type != NULL) + g_app_info_set_as_default_for_type (app, mime_type, NULL); + g_free (uri); + } + + path_list_free (uris); +} + + +typedef struct { + FrWindow *window; + GList *file_list; + gboolean ask_application; + CommandData *cdata; +} OpenFilesData; + + +static OpenFilesData* +open_files_data_new (FrWindow *window, + GList *file_list, + gboolean ask_application) + +{ + OpenFilesData *odata; + GList *scan; + + odata = g_new0 (OpenFilesData, 1); + odata->window = window; + odata->file_list = path_list_dup (file_list); + odata->ask_application = ask_application; + odata->cdata = g_new0 (CommandData, 1); + odata->cdata->temp_dir = get_temp_work_dir (NULL); + odata->cdata->file_list = NULL; + for (scan = file_list; scan; scan = scan->next) { + char *file = scan->data; + char *filename; + + filename = g_strconcat (odata->cdata->temp_dir, + "/", + file, + NULL); + odata->cdata->file_list = g_list_prepend (odata->cdata->file_list, filename); + } + + /* Add to CommandList so the cdata is released on exit. */ + CommandList = g_list_prepend (CommandList, odata->cdata); + + return odata; +} + + +static void +open_files_data_free (OpenFilesData *odata) +{ + g_return_if_fail (odata != NULL); + + path_list_free (odata->file_list); + g_free (odata); +} + + +void +fr_window_update_dialog_closed (FrWindow *window) +{ + window->priv->update_dialog = NULL; +} + + +gboolean +fr_window_update_files (FrWindow *window, + GList *file_list) +{ + GList *scan; + + if (window->priv->activity_ref > 0) + return FALSE; + + if (window->archive->read_only) + return FALSE; + + fr_process_clear (window->archive->process); + + for (scan = file_list; scan; scan = scan->next) { + OpenFile *file = scan->data; + GList *local_file_list; + + local_file_list = g_list_append (NULL, file->path); + fr_archive_add (window->archive, + local_file_list, + file->temp_dir, + "/", + FALSE, + FALSE, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + g_list_free (local_file_list); + } + + fr_process_start (window->archive->process); + + return TRUE; +} + + +static void +open_file_modified_cb (GFileMonitor *monitor, + GFile *monitor_file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FrWindow *window = user_data; + char *monitor_uri; + OpenFile *file; + GList *scan; + + if ((event_type != G_FILE_MONITOR_EVENT_CHANGED) + && (event_type != G_FILE_MONITOR_EVENT_CREATED)) + { + return; + } + + monitor_uri = g_file_get_uri (monitor_file); + file = NULL; + for (scan = window->priv->open_files; scan; scan = scan->next) { + OpenFile *test = scan->data; + if (uricmp (test->extracted_uri, monitor_uri) == 0) { + file = test; + break; + } + } + g_free (monitor_uri); + + g_return_if_fail (file != NULL); + + if (window->priv->update_dialog == NULL) + window->priv->update_dialog = dlg_update (window); + dlg_update_add_file (window->priv->update_dialog, file); +} + + +static void +fr_window_monitor_open_file (FrWindow *window, + OpenFile *file) +{ + GFile *f; + + window->priv->open_files = g_list_prepend (window->priv->open_files, file); + f = g_file_new_for_uri (file->extracted_uri); + file->monitor = g_file_monitor_file (f, 0, NULL, NULL); + g_signal_connect (file->monitor, + "changed", + G_CALLBACK (open_file_modified_cb), + window); + g_object_unref (f); +} + + +static void +monitor_extracted_files (OpenFilesData *odata) +{ + FrWindow *window = odata->window; + GList *scan1, *scan2; + + for (scan1 = odata->file_list, scan2 = odata->cdata->file_list; + scan1 && scan2; + scan1 = scan1->next, scan2 = scan2->next) + { + OpenFile *ofile; + const char *file = scan1->data; + const char *extracted_path = scan2->data; + + ofile = open_file_new (file, extracted_path, odata->cdata->temp_dir); + if (ofile != NULL) + fr_window_monitor_open_file (window, ofile); + } +} + + +static gboolean +fr_window_open_extracted_files (OpenFilesData *odata) +{ + GList *file_list = odata->cdata->file_list; + gboolean result = FALSE; + const char *first_file; + const char *first_mime_type; + GAppInfo *app; + GList *files_to_open = NULL; + GError *error = NULL; + + g_return_val_if_fail (file_list != NULL, FALSE); + + first_file = (char*) file_list->data; + if (first_file == NULL) + return FALSE; + + if (! odata->window->archive->read_only) + monitor_extracted_files (odata); + + if (odata->ask_application) { + dlg_open_with (odata->window, file_list); + return FALSE; + } + + first_mime_type = get_file_mime_type_for_path (first_file, FALSE); + app = g_app_info_get_default_for_type (first_mime_type, FALSE); + + if (app == NULL) { + dlg_open_with (odata->window, file_list); + return FALSE; + } + + files_to_open = g_list_append (files_to_open, g_filename_to_uri (first_file, NULL, NULL)); + + if (g_app_info_supports_files (app)) { + GList *scan; + + for (scan = file_list->next; scan; scan = scan->next) { + const char *path = scan->data; + const char *mime_type; + + mime_type = get_file_mime_type_for_path (path, FALSE); + if (mime_type == NULL) + continue; + + if (strcmp (mime_type, first_mime_type) == 0) { + files_to_open = g_list_append (files_to_open, g_filename_to_uri (path, NULL, NULL)); + } + else { + GAppInfo *app2; + + app2 = g_app_info_get_default_for_type (mime_type, FALSE); + if (g_app_info_equal (app, app2)) + files_to_open = g_list_append (files_to_open, g_filename_to_uri (path, NULL, NULL)); + g_object_unref (app2); + } + } + } + + result = g_app_info_launch_uris (app, files_to_open, NULL, &error); + if (! result) { + _gtk_error_dialog_run (GTK_WINDOW (odata->window), + _("Could not perform the operation"), + "%s", + error->message); + g_clear_error (&error); + } + + g_object_unref (app); + path_list_free (files_to_open); + + return result; +} + + +static void +fr_window_open_files__extract_done_cb (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer callback_data) +{ + OpenFilesData *odata = callback_data; + + g_signal_handlers_disconnect_matched (G_OBJECT (archive), + G_SIGNAL_MATCH_DATA, + 0, + 0, NULL, + 0, + odata); + + if (error->type == FR_PROC_ERROR_NONE) + fr_window_open_extracted_files (odata); +} + + +void +fr_window_open_files (FrWindow *window, + GList *file_list, + gboolean ask_application) +{ + OpenFilesData *odata; + + if (window->priv->activity_ref > 0) + return; + + odata = open_files_data_new (window, file_list, ask_application); + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_OPEN_FILES, + odata, + (GFreeFunc) open_files_data_free); + + g_signal_connect (G_OBJECT (window->archive), + "done", + G_CALLBACK (fr_window_open_files__extract_done_cb), + odata); + + fr_process_clear (window->archive->process); + fr_archive_extract_to_local (window->archive, + odata->file_list, + odata->cdata->temp_dir, + NULL, + FALSE, + TRUE, + FALSE, + window->priv->password); + fr_process_start (window->archive->process); +} + + +/**/ + + +static char* +get_default_dir (const char *dir) +{ + if (! is_temp_dir (dir)) + return g_strdup (dir); + else + return NULL; +} + + +void +fr_window_set_open_default_dir (FrWindow *window, + const char *default_dir) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + if (window->priv->open_default_dir != NULL) + g_free (window->priv->open_default_dir); + window->priv->open_default_dir = get_default_dir (default_dir); +} + + +const char * +fr_window_get_open_default_dir (FrWindow *window) +{ + if (window->priv->open_default_dir == NULL) + return get_home_uri (); + else + return window->priv->open_default_dir; +} + + +void +fr_window_set_add_default_dir (FrWindow *window, + const char *default_dir) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + if (window->priv->add_default_dir != NULL) + g_free (window->priv->add_default_dir); + window->priv->add_default_dir = get_default_dir (default_dir); +} + + +const char * +fr_window_get_add_default_dir (FrWindow *window) +{ + if (window->priv->add_default_dir == NULL) + return get_home_uri (); + else + return window->priv->add_default_dir; +} + + +void +fr_window_set_extract_default_dir (FrWindow *window, + const char *default_dir, + gboolean freeze) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + /* do not change this dir while it's used by the non-interactive + * extraction operation. */ + if (window->priv->extract_interact_use_default_dir) + return; + + window->priv->extract_interact_use_default_dir = freeze; + + if (window->priv->extract_default_dir != NULL) + g_free (window->priv->extract_default_dir); + window->priv->extract_default_dir = get_default_dir (default_dir); +} + + +const char * +fr_window_get_extract_default_dir (FrWindow *window) +{ + if (window->priv->extract_default_dir == NULL) + return get_home_uri (); + else + return window->priv->extract_default_dir; +} + + +void +fr_window_set_default_dir (FrWindow *window, + const char *default_dir, + gboolean freeze) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + window->priv->freeze_default_dir = freeze; + + fr_window_set_open_default_dir (window, default_dir); + fr_window_set_add_default_dir (window, default_dir); + fr_window_set_extract_default_dir (window, default_dir, FALSE); +} + + +void +fr_window_update_columns_visibility (FrWindow *window) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (window->priv->list_view); + GtkTreeViewColumn *column; + + column = gtk_tree_view_get_column (tree_view, 1); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_SIZE, TRUE)); + + column = gtk_tree_view_get_column (tree_view, 2); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_TYPE, TRUE)); + + column = gtk_tree_view_get_column (tree_view, 3); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_TIME, TRUE)); + + column = gtk_tree_view_get_column (tree_view, 4); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_PATH, TRUE)); +} + + +void +fr_window_set_toolbar_visibility (FrWindow *window, + gboolean visible) +{ + g_return_if_fail (window != NULL); + + if (visible) + gtk_widget_show (window->priv->toolbar); + else + gtk_widget_hide (window->priv->toolbar); + + set_active (window, "ViewToolbar", visible); +} + + +void +fr_window_set_statusbar_visibility (FrWindow *window, + gboolean visible) +{ + g_return_if_fail (window != NULL); + + if (visible) + gtk_widget_show (window->priv->statusbar); + else + gtk_widget_hide (window->priv->statusbar); + + set_active (window, "ViewStatusbar", visible); +} + + +void +fr_window_set_folders_visibility (FrWindow *window, + gboolean value) +{ + g_return_if_fail (window != NULL); + + window->priv->view_folders = value; + fr_window_update_dir_tree (window); + + set_active (window, "ViewFolders", window->priv->view_folders); +} + + +/* -- batch mode procedures -- */ + + +static void fr_window_exec_current_batch_action (FrWindow *window); + + +static void +fr_window_exec_batch_action (FrWindow *window, + FRBatchAction *action) +{ + ExtractData *edata; + RenameData *rdata; + OpenFilesData *odata; + SaveAsData *sdata; + + switch (action->type) { + case FR_BATCH_ACTION_LOAD: + debug (DEBUG_INFO, "[BATCH] LOAD\n"); + + if (! uri_exists ((char*) action->data)) + fr_window_archive_new (window, (char*) action->data); + else + fr_window_archive_open (window, (char*) action->data, GTK_WINDOW (window)); + break; + + case FR_BATCH_ACTION_ADD: + debug (DEBUG_INFO, "[BATCH] ADD\n"); + + fr_window_archive_add_dropped_items (window, (GList*) action->data, FALSE); + break; + + case FR_BATCH_ACTION_OPEN: + debug (DEBUG_INFO, "[BATCH] OPEN\n"); + + fr_window_push_message (window, _("Add files to an archive")); + dlg_batch_add_files (window, (GList*) action->data); + break; + + case FR_BATCH_ACTION_EXTRACT: + debug (DEBUG_INFO, "[BATCH] EXTRACT\n"); + + edata = action->data; + fr_window_archive_extract (window, + edata->file_list, + edata->extract_to_dir, + edata->base_dir, + edata->skip_older, + edata->overwrite, + edata->junk_paths, + TRUE); + break; + + case FR_BATCH_ACTION_EXTRACT_HERE: + debug (DEBUG_INFO, "[BATCH] EXTRACT HERE\n"); + + edata = action->data; + fr_window_archive_extract_here (window, + FALSE, + TRUE, + FALSE); + break; + + case FR_BATCH_ACTION_EXTRACT_INTERACT: + debug (DEBUG_INFO, "[BATCH] EXTRACT_INTERACT\n"); + + if (window->priv->extract_interact_use_default_dir + && (window->priv->extract_default_dir != NULL)) + { + fr_window_archive_extract (window, + NULL, + window->priv->extract_default_dir, + NULL, + FALSE, + TRUE, + FALSE, + TRUE); + } + else { + fr_window_push_message (window, _("Extract archive")); + dlg_extract (NULL, window); + } + break; + + case FR_BATCH_ACTION_RENAME: + debug (DEBUG_INFO, "[BATCH] RENAME\n"); + + rdata = action->data; + rename_selection (window, + rdata->path_to_rename, + rdata->old_name, + rdata->new_name, + rdata->current_dir, + rdata->is_dir, + rdata->dir_in_archive, + rdata->original_path); + break; + + case FR_BATCH_ACTION_PASTE: + debug (DEBUG_INFO, "[BATCH] PASTE\n"); + + fr_window_paste_from_clipboard_data (window, (FrClipboardData*) action->data); + break; + + case FR_BATCH_ACTION_OPEN_FILES: + debug (DEBUG_INFO, "[BATCH] OPEN FILES\n"); + + odata = action->data; + fr_window_open_files (window, odata->file_list, odata->ask_application); + break; + + case FR_BATCH_ACTION_SAVE_AS: + debug (DEBUG_INFO, "[BATCH] SAVE_AS\n"); + + sdata = action->data; + fr_window_archive_save_as (window, + sdata->uri, + sdata->password, + sdata->encrypt_header, + sdata->volume_size); + break; + + case FR_BATCH_ACTION_TEST: + debug (DEBUG_INFO, "[BATCH] TEST\n"); + + fr_window_archive_test (window); + break; + + case FR_BATCH_ACTION_CLOSE: + debug (DEBUG_INFO, "[BATCH] CLOSE\n"); + + fr_window_archive_close (window); + fr_window_exec_next_batch_action (window); + break; + + case FR_BATCH_ACTION_QUIT: + debug (DEBUG_INFO, "[BATCH] QUIT\n"); + + gtk_widget_destroy (GTK_WIDGET (window)); + break; + + default: + break; + } +} + + +void +fr_window_reset_current_batch_action (FrWindow *window) +{ + FRBatchAction *adata = &window->priv->current_batch_action; + + if ((adata->data != NULL) && (adata->free_func != NULL)) + (*adata->free_func) (adata->data); + adata->type = FR_BATCH_ACTION_NONE; + adata->data = NULL; + adata->free_func = NULL; +} + + +void +fr_window_set_current_batch_action (FrWindow *window, + FrBatchActionType action, + void *data, + GFreeFunc free_func) +{ + FRBatchAction *adata = &window->priv->current_batch_action; + + fr_window_reset_current_batch_action (window); + + adata->type = action; + adata->data = data; + adata->free_func = free_func; +} + + +void +fr_window_restart_current_batch_action (FrWindow *window) +{ + fr_window_exec_batch_action (window, &window->priv->current_batch_action); +} + + +void +fr_window_append_batch_action (FrWindow *window, + FrBatchActionType action, + void *data, + GFreeFunc free_func) +{ + FRBatchAction *a_desc; + + g_return_if_fail (window != NULL); + + a_desc = g_new0 (FRBatchAction, 1); + a_desc->type = action; + a_desc->data = data; + a_desc->free_func = free_func; + + window->priv->batch_action_list = g_list_append (window->priv->batch_action_list, a_desc); +} + + +static void +fr_window_exec_current_batch_action (FrWindow *window) +{ + FRBatchAction *action; + + if (window->priv->batch_action == NULL) { + window->priv->batch_mode = FALSE; + return; + } + action = (FRBatchAction *) window->priv->batch_action->data; + fr_window_exec_batch_action (window, action); +} + + +static void +fr_window_exec_next_batch_action (FrWindow *window) +{ + if (window->priv->batch_action != NULL) + window->priv->batch_action = g_list_next (window->priv->batch_action); + else + window->priv->batch_action = window->priv->batch_action_list; + fr_window_exec_current_batch_action (window); +} + + +void +fr_window_start_batch (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->batch_mode) + return; + + if (window->priv->batch_action_list == NULL) + return; + + window->priv->batch_mode = TRUE; + window->priv->batch_action = window->priv->batch_action_list; + window->archive->can_create_compressed_file = window->priv->batch_adding_one_file; + + fr_window_exec_current_batch_action (window); +} + + +void +fr_window_stop_batch (FrWindow *window) +{ + if (! window->priv->non_interactive) + return; + + window->priv->extract_interact_use_default_dir = FALSE; + window->archive->can_create_compressed_file = FALSE; + + if (window->priv->batch_mode) { + if (! window->priv->showing_error_dialog) { + gtk_widget_destroy (GTK_WIDGET (window)); + return; + } + } + else { + gtk_window_present (GTK_WINDOW (window)); + fr_window_archive_close (window); + } + + window->priv->batch_mode = FALSE; +} + + +void +fr_window_resume_batch (FrWindow *window) +{ + fr_window_exec_current_batch_action (window); +} + + +gboolean +fr_window_is_batch_mode (FrWindow *window) +{ + return window->priv->batch_mode; +} + + +void +fr_window_new_batch (FrWindow *window) +{ + fr_window_free_batch_data (window); + window->priv->non_interactive = TRUE; +} + + +void +fr_window_set_batch__extract_here (FrWindow *window, + const char *filename) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (filename != NULL); + + fr_window_append_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (filename), + (GFreeFunc) g_free); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_EXTRACT_HERE, + extract_to_data_new (NULL), + (GFreeFunc) extract_data_free); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_CLOSE, + NULL, + NULL); +} + + +void +fr_window_set_batch__extract (FrWindow *window, + const char *filename, + const char *dest_dir) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (filename != NULL); + + fr_window_append_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (filename), + (GFreeFunc) g_free); + if (dest_dir != NULL) + fr_window_append_batch_action (window, + FR_BATCH_ACTION_EXTRACT, + extract_to_data_new (dest_dir), + (GFreeFunc) extract_data_free); + else + fr_window_append_batch_action (window, + FR_BATCH_ACTION_EXTRACT_INTERACT, + NULL, + NULL); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_CLOSE, + NULL, + NULL); +} + + +void +fr_window_set_batch__add (FrWindow *window, + const char *archive, + GList *file_list) +{ + window->priv->batch_adding_one_file = (file_list->next == NULL) && (uri_is_file (file_list->data)); + + if (archive != NULL) + fr_window_append_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (archive), + (GFreeFunc) g_free); + else + fr_window_append_batch_action (window, + FR_BATCH_ACTION_OPEN, + file_list, + NULL); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_ADD, + file_list, + NULL); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_CLOSE, + NULL, + NULL); +} diff --git a/src/fr-window.h b/src/fr-window.h new file mode 100644 index 0000000..adde1bf --- /dev/null +++ b/src/fr-window.h @@ -0,0 +1,315 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef FR_WINDOW_H +#define FR_WINDOW_H + +#include <gio/gio.h> +#include <gtk/gtk.h> +#include "typedefs.h" +#include "fr-archive.h" + +#define MATECONF_NOTIFICATIONS 10 + +enum { + COLUMN_FILE_DATA, + COLUMN_ICON, + COLUMN_NAME, + COLUMN_EMBLEM, + COLUMN_SIZE, + COLUMN_TYPE, + COLUMN_TIME, + COLUMN_PATH, + NUMBER_OF_COLUMNS +}; + +enum { + TREE_COLUMN_PATH, + TREE_COLUMN_ICON, + TREE_COLUMN_NAME, + TREE_COLUMN_WEIGHT, + TREE_NUMBER_OF_COLUMNS +}; + +typedef enum { + FR_BATCH_ACTION_NONE, + FR_BATCH_ACTION_LOAD, + FR_BATCH_ACTION_OPEN, + FR_BATCH_ACTION_ADD, + FR_BATCH_ACTION_EXTRACT, + FR_BATCH_ACTION_EXTRACT_HERE, + FR_BATCH_ACTION_EXTRACT_INTERACT, + FR_BATCH_ACTION_RENAME, + FR_BATCH_ACTION_PASTE, + FR_BATCH_ACTION_OPEN_FILES, + FR_BATCH_ACTION_SAVE_AS, + FR_BATCH_ACTION_TEST, + FR_BATCH_ACTION_CLOSE, + FR_BATCH_ACTION_QUIT, + FR_BATCH_ACTIONS +} FrBatchActionType; + +/* -- FrWindow -- */ + +#define FR_TYPE_WINDOW (fr_window_get_type ()) +#define FR_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_WINDOW, FrWindow)) +#define FR_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_WINDOW_TYPE, FrWindowClass)) +#define FR_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_WINDOW)) +#define FR_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_WINDOW)) +#define FR_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_WINDOW, FrWindowClass)) + +typedef struct _FrWindow FrWindow; +typedef struct _FrWindowClass FrWindowClass; +typedef struct _FrWindowPrivateData FrWindowPrivateData; + +struct _FrWindow +{ + GtkWindow __parent; + FrArchive *archive; + FrWindowPrivateData *priv; +}; + +struct _FrWindowClass +{ + GtkWindowClass __parent_class; + + /*<signals>*/ + + void (*archive_loaded) (FrWindow *window, + gboolean success); +}; + +GType fr_window_get_type (void); +GtkWidget * fr_window_new (void); +void fr_window_close (FrWindow *window); + +/* archive operations */ + +gboolean fr_window_archive_new (FrWindow *window, + const char *uri); +FrWindow * fr_window_archive_open (FrWindow *window, + const char *uri, + GtkWindow *parent); +void fr_window_archive_close (FrWindow *window); +const char *fr_window_get_archive_uri (FrWindow *window); +const char *fr_window_get_paste_archive_uri (FrWindow *window); +gboolean fr_window_archive_is_present (FrWindow *window); +void fr_window_archive_save_as (FrWindow *window, + const char *filename, + const char *password, + gboolean encrypt_header, + guint volume_size); +void fr_window_archive_reload (FrWindow *window); +void fr_window_archive_rename (FrWindow *window, + const char *filename); +void fr_window_archive_add_files (FrWindow *window, + GList *file_list, /* GFile list */ + gboolean update); +void fr_window_archive_add_with_wildcard (FrWindow *window, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + const char *base_dir, + const char *dest_dir, + gboolean update, + gboolean follow_links); +void fr_window_archive_add_directory (FrWindow *window, + const char *directory, + const char *base_dir, + const char *dest_dir, + gboolean update); +void fr_window_archive_add_items (FrWindow *window, + GList *dir_list, + const char *base_dir, + const char *dest_dir, + gboolean update); +void fr_window_archive_add_dropped_items (FrWindow *window, + GList *item_list, + gboolean update); +void fr_window_archive_remove (FrWindow *window, + GList *file_list); +void fr_window_archive_extract (FrWindow *window, + GList *file_list, + const char *extract_to_dir, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + gboolean ask_to_open_destination); +void fr_window_archive_extract_here (FrWindow *window, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths); +void fr_window_archive_test (FrWindow *window); + +/**/ + +void fr_window_set_password (FrWindow *window, + const char *password); +void fr_window_set_password_for_paste (FrWindow *window, + const char *password); +const char *fr_window_get_password (FrWindow *window); +void fr_window_set_encrypt_header (FrWindow *window, + gboolean encrypt_header); +gboolean fr_window_get_encrypt_header (FrWindow *window); +void fr_window_set_compression (FrWindow *window, + FrCompression compression); +FrCompression fr_window_get_compression (FrWindow *window); +void fr_window_set_volume_size (FrWindow *window, + guint volume_size); +guint fr_window_get_volume_size (FrWindow *window); + +/**/ + +void fr_window_go_to_location (FrWindow *window, + const char *path, + gboolean force_update); +const char*fr_window_get_current_location (FrWindow *window); +void fr_window_go_up_one_level (FrWindow *window); +void fr_window_go_back (FrWindow *window); +void fr_window_go_forward (FrWindow *window); +void fr_window_current_folder_activated (FrWindow *window, + gboolean from_sidebar); +void fr_window_set_list_mode (FrWindow *window, + FrWindowListMode list_mode); + +/**/ + +void fr_window_update_list_order (FrWindow *window); +GList * fr_window_get_file_list_selection (FrWindow *window, + gboolean recursive, + gboolean *has_dirs); +GList * fr_window_get_file_list_from_path_list (FrWindow *window, + GList *path_list, + gboolean *has_dirs); +GList * fr_window_get_file_list_pattern (FrWindow *window, + const char *pattern); +int fr_window_get_n_selected_files (FrWindow *window); +GList * fr_window_get_folder_tree_selection (FrWindow *window, + gboolean recursive, + gboolean *has_dirs); +GList * fr_window_get_selection (FrWindow *window, + gboolean from_sidebar, + char **return_base_dir); +GtkTreeModel * + fr_window_get_list_store (FrWindow *window); +void fr_window_find (FrWindow *window); +void fr_window_select_all (FrWindow *window); +void fr_window_unselect_all (FrWindow *window); +void fr_window_set_sort_type (FrWindow *window, + GtkSortType sort_type); + +/**/ + +void fr_window_rename_selection (FrWindow *window, + gboolean from_sidebar); +void fr_window_cut_selection (FrWindow *window, + gboolean from_sidebar); +void fr_window_copy_selection (FrWindow *window, + gboolean from_sidebar); +void fr_window_paste_selection (FrWindow *window, + gboolean from_sidebar); + +/**/ + +void fr_window_stop (FrWindow *window); +void fr_window_start_activity_mode (FrWindow *window); +void fr_window_stop_activity_mode (FrWindow *window); + +/**/ + +void fr_window_view_last_output (FrWindow *window, + const char *title); + +void fr_window_open_files (FrWindow *window, + GList *file_list, + gboolean ask_application); +void fr_window_open_files_with_command (FrWindow *window, + GList *file_list, + char *command); +void fr_window_open_files_with_application (FrWindow *window, + GList *file_list, + GAppInfo *app); +gboolean fr_window_update_files (FrWindow *window, + GList *file_list); +void fr_window_update_columns_visibility (FrWindow *window); +void fr_window_update_history_list (FrWindow *window); +void fr_window_set_default_dir (FrWindow *window, + const char *default_dir, + gboolean freeze); +void fr_window_set_open_default_dir (FrWindow *window, + const char *default_dir); +const char *fr_window_get_open_default_dir (FrWindow *window); +void fr_window_set_add_default_dir (FrWindow *window, + const char *default_dir); +const char *fr_window_get_add_default_dir (FrWindow *window); +void fr_window_set_extract_default_dir (FrWindow *window, + const char *default_dir, + gboolean freeze); +const char *fr_window_get_extract_default_dir (FrWindow *window); +void fr_window_push_message (FrWindow *window, + const char *msg); +void fr_window_pop_message (FrWindow *window); +void fr_window_set_toolbar_visibility (FrWindow *window, + gboolean value); +void fr_window_set_statusbar_visibility (FrWindow *window, + gboolean value); +void fr_window_set_folders_visibility (FrWindow *window, + gboolean value); + +/* batch mode procedures. */ + +void fr_window_new_batch (FrWindow *window); +void fr_window_set_current_batch_action (FrWindow *window, + FrBatchActionType action, + void *data, + GFreeFunc free_func); +void fr_window_reset_current_batch_action (FrWindow *window); +void fr_window_restart_current_batch_action (FrWindow *window); +void fr_window_append_batch_action (FrWindow *window, + FrBatchActionType action, + void *data, + GFreeFunc free_func); +void fr_window_start_batch (FrWindow *window); +void fr_window_stop_batch (FrWindow *window); +void fr_window_resume_batch (FrWindow *window); +gboolean fr_window_is_batch_mode (FrWindow *window); +void fr_window_set_batch__extract (FrWindow *window, + const char *filename, + const char *dest_dir); +void fr_window_set_batch__extract_here (FrWindow *window, + const char *filename); +void fr_window_set_batch__add (FrWindow *window, + const char *archive, + GList *file_list); +void fr_window_destroy_with_error_dialog (FrWindow *window); + +/**/ + +gboolean fr_window_file_list_drag_data_get (FrWindow *window, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list); + +void fr_window_update_dialog_closed (FrWindow *window); + +#endif /* FR_WINDOW_H */ diff --git a/src/gio-utils.c b/src/gio-utils.c new file mode 100644 index 0000000..1123865 --- /dev/null +++ b/src/gio-utils.c @@ -0,0 +1,1497 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <glib.h> +#include <gio/gio.h> +#include "glib-utils.h" +#include "file-utils.h" +#include "gio-utils.h" + + +#define N_FILES_PER_REQUEST 128 + + +/* -- filter -- */ + + +typedef enum { + FILTER_DEFAULT = 0, + FILTER_NODOTFILES = 1 << 1, + FILTER_IGNORECASE = 1 << 2, + FILTER_NOBACKUPFILES = 1 << 3 +} FilterOptions; + + +typedef struct { + char *pattern; + FilterOptions options; + GRegex **regexps; +} Filter; + + +static Filter * +filter_new (const char *pattern, + FilterOptions options) +{ + Filter *filter; + GRegexCompileFlags flags; + + filter = g_new0 (Filter, 1); + + if ((pattern != NULL) && (strcmp (pattern, "*") != 0)) + filter->pattern = g_strdup (pattern); + + filter->options = options; + if (filter->options & FILTER_IGNORECASE) + flags = G_REGEX_CASELESS; + else + flags = 0; + filter->regexps = search_util_get_regexps (pattern, flags); + + return filter; +} + + +static void +filter_destroy (Filter *filter) +{ + if (filter == NULL) + return; + + g_free (filter->pattern); + if (filter->regexps != NULL) + free_regexps (filter->regexps); + g_free (filter); +} + + +static gboolean +filter_matches (Filter *filter, + const char *name) +{ + const char *file_name; + char *utf8_name; + gboolean matched; + + g_return_val_if_fail (name != NULL, FALSE); + + file_name = file_name_from_path (name); + + if ((filter->options & FILTER_NODOTFILES) + && ((file_name[0] == '.') || (strstr (file_name, "/.") != NULL))) + return FALSE; + + if ((filter->options & FILTER_NOBACKUPFILES) + && (file_name[strlen (file_name) - 1] == '~')) + return FALSE; + + if (filter->pattern == NULL) + return TRUE; + + utf8_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL); + matched = match_regexps (filter->regexps, utf8_name, 0); + g_free (utf8_name); + + return matched; +} + + +static gboolean +filter_empty (Filter *filter) +{ + return ((filter->pattern == NULL) || (strcmp (filter->pattern, "*") == 0)); +} + + +/* -- g_directory_foreach_child -- */ + + +typedef struct { + GFile *base_directory; + gboolean recursive; + gboolean follow_links; + StartDirCallback start_dir_func; + ForEachChildCallback for_each_file_func; + ForEachDoneCallback done_func; + gpointer user_data; + + /* private */ + + GFile *current; + GHashTable *already_visited; + GList *to_visit; + GCancellable *cancellable; + GFileEnumerator *enumerator; + GError *error; + guint source_id; +} ForEachChildData; + + +static void +for_each_child_data_free (ForEachChildData *fec) +{ + if (fec == NULL) + return; + + if (fec->base_directory != NULL) + g_object_unref (fec->base_directory); + if (fec->current != NULL) + g_object_unref (fec->current); + if (fec->already_visited) + g_hash_table_destroy (fec->already_visited); + if (fec->to_visit != NULL) + g_list_free (fec->to_visit); + g_free (fec); +} + + +static gboolean +for_each_child_done_cb (gpointer user_data) +{ + ForEachChildData *fec = user_data; + + g_source_remove (fec->source_id); + if (fec->current != NULL) { + g_object_unref (fec->current); + fec->current = NULL; + } + if (fec->done_func) + fec->done_func (fec->error, fec->user_data); + for_each_child_data_free (fec); + + return FALSE; +} + + +static void +for_each_child_done (ForEachChildData *fec) +{ + fec->source_id = g_idle_add (for_each_child_done_cb, fec); +} + + +static void for_each_child_start_current (ForEachChildData *fec); + + +static gboolean +for_each_child_start_cb (gpointer user_data) +{ + ForEachChildData *fec = user_data; + + g_source_remove (fec->source_id); + for_each_child_start_current (fec); + + return FALSE; +} + + +static void +for_each_child_start (ForEachChildData *fec) +{ + fec->source_id = g_idle_add (for_each_child_start_cb, fec); +} + + +static void +for_each_child_set_current_uri (ForEachChildData *fec, + const char *directory) +{ + if (fec->current != NULL) + g_object_unref (fec->current); + fec->current = g_file_new_for_uri (directory); +} + + +static void +for_each_child_set_current (ForEachChildData *fec, + GFile *directory) +{ + if (fec->current != NULL) + g_object_unref (fec->current); + fec->current = g_file_dup (directory); +} + +static void +for_each_child_start_next_sub_directory (ForEachChildData *fec) +{ + char *sub_directory = NULL; + + if (fec->to_visit != NULL) { + GList *tmp; + + sub_directory = (char*) fec->to_visit->data; + tmp = fec->to_visit; + fec->to_visit = g_list_remove_link (fec->to_visit, tmp); + g_list_free (tmp); + } + + if (sub_directory != NULL) { + for_each_child_set_current_uri (fec, sub_directory); + for_each_child_start (fec); + } + else + for_each_child_done (fec); +} + + +static void +for_each_child_close_enumerator (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + ForEachChildData *fec = user_data; + GError *error = NULL; + + if (! g_file_enumerator_close_finish (fec->enumerator, + result, + &error)) + { + if (fec->error == NULL) + fec->error = g_error_copy (error); + else + g_clear_error (&error); + } + + if ((fec->error == NULL) && fec->recursive) + for_each_child_start_next_sub_directory (fec); + else + for_each_child_done (fec); +} + + +static void +for_each_child_next_files_ready (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + ForEachChildData *fec = user_data; + GList *children, *scan; + + children = g_file_enumerator_next_files_finish (fec->enumerator, + result, + &(fec->error)); + + if (children == NULL) { + g_file_enumerator_close_async (fec->enumerator, + G_PRIORITY_DEFAULT, + fec->cancellable, + for_each_child_close_enumerator, + fec); + return; + } + + for (scan = children; scan; scan = scan->next) { + GFileInfo *child_info = scan->data; + GFile *f; + char *uri; + + f = g_file_get_child (fec->current, g_file_info_get_name (child_info)); + uri = g_file_get_uri (f); + + if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) { + /* avoid to visit a directory more than once */ + + if (g_hash_table_lookup (fec->already_visited, uri) == NULL) { + char *sub_directory; + + sub_directory = g_strdup (uri); + g_hash_table_insert (fec->already_visited, sub_directory, GINT_TO_POINTER (1)); + fec->to_visit = g_list_append (fec->to_visit, sub_directory); + } + } + + fec->for_each_file_func (uri, child_info, fec->user_data); + + g_free (uri); + g_object_unref (f); + } + + g_file_enumerator_next_files_async (fec->enumerator, + N_FILES_PER_REQUEST, + G_PRIORITY_DEFAULT, + fec->cancellable, + for_each_child_next_files_ready, + fec); +} + +static void +for_each_child_ready (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + ForEachChildData *fec = user_data; + + fec->enumerator = g_file_enumerate_children_finish (fec->current, result, &(fec->error)); + if (fec->enumerator == NULL) { + for_each_child_done (fec); + return; + } + + g_file_enumerator_next_files_async (fec->enumerator, + N_FILES_PER_REQUEST, + G_PRIORITY_DEFAULT, + fec->cancellable, + for_each_child_next_files_ready, + fec); +} + + +static void +for_each_child_start_current (ForEachChildData *fec) +{ + if (fec->start_dir_func != NULL) { + char *directory; + DirOp op; + + directory = g_file_get_uri (fec->current); + op = fec->start_dir_func (directory, &(fec->error), fec->user_data); + g_free (directory); + + switch (op) { + case DIR_OP_SKIP: + for_each_child_start_next_sub_directory (fec); + return; + case DIR_OP_STOP: + for_each_child_done (fec); + return; + case DIR_OP_CONTINUE: + break; + } + } + + g_file_enumerate_children_async (fec->current, + "standard::name,standard::type", + fec->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + G_PRIORITY_DEFAULT, + fec->cancellable, + for_each_child_ready, + fec); +} + +/** + * g_directory_foreach_child: + * @directory: The directory to visit. + * @recursive: Whether to traverse the @directory recursively. + * @follow_links: Whether to dereference the symbolic links. + * @cancellable: An optional @GCancellable object, used to cancel the process. + * @start_dir_func: the function called for each sub-directory, or %NULL if + * not needed. + * @for_each_file_func: the function called for each file. Can't be %NULL. + * @done_func: the function called at the end of the traversing process. + * Can't be %NULL. + * @user_data: data to pass to @done_func + * + * Traverse the @directory's filesystem structure calling the + * @for_each_file_func function for each file in the directory; the + * @start_dir_func function on each directory before it's going to be + * traversed, this includes @directory too; the @done_func function is + * called at the end of the process. + * Some traversing options are available: if @recursive is TRUE the + * directory is traversed recursively; if @follow_links is TRUE, symbolic + * links are dereferenced, otherwise they are returned as links. + * Each callback uses the same @user_data additional parameter. + */ +void +g_directory_foreach_child (GFile *directory, + gboolean recursive, + gboolean follow_links, + GCancellable *cancellable, + StartDirCallback start_dir_func, + ForEachChildCallback for_each_file_func, + ForEachDoneCallback done_func, + gpointer user_data) +{ + ForEachChildData *fec; + + g_return_if_fail (for_each_file_func != NULL); + + fec = g_new0 (ForEachChildData, 1); + + fec->base_directory = g_object_ref (directory); + fec->recursive = recursive; + fec->follow_links = follow_links; + fec->cancellable = cancellable; + fec->start_dir_func = start_dir_func; + fec->for_each_file_func = for_each_file_func; + fec->done_func = done_func; + fec->user_data = user_data; + fec->already_visited = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + for_each_child_set_current (fec, fec->base_directory); + for_each_child_start_current (fec); +} + + +/* -- get_file_list_data -- */ + + +typedef struct { + GList *files; + GList *dirs; + GFile *directory; + GFile *base_dir; + GCancellable *cancellable; + ListReadyCallback done_func; + gpointer done_data; + GList *to_visit; + GList *current_dir; + Filter *include_filter; + Filter *exclude_filter; + Filter *exclude_folders_filter; + guint visit_timeout; +} GetFileListData; + + +static void +get_file_list_data_free (GetFileListData *gfl) +{ + if (gfl == NULL) + return; + + filter_destroy (gfl->include_filter); + filter_destroy (gfl->exclude_filter); + filter_destroy (gfl->exclude_folders_filter); + path_list_free (gfl->files); + path_list_free (gfl->dirs); + path_list_free (gfl->to_visit); + if (gfl->directory != NULL) + g_object_unref (gfl->directory); + if (gfl->base_dir != NULL) + g_object_unref (gfl->base_dir); + g_free (gfl); +} + + +/* -- g_directory_list_async -- */ + + +static GList* +get_relative_file_list (GList *rel_list, + GList *file_list, + GFile *base_dir) +{ + GList *scan; + + if (base_dir == NULL) + return NULL; + + for (scan = file_list; scan; scan = scan->next) { + char *full_path = scan->data; + GFile *f; + char *relative_path; + + f = g_file_new_for_commandline_arg (full_path); + relative_path = g_file_get_relative_path (base_dir, f); + if (relative_path != NULL) + rel_list = g_list_prepend (rel_list, relative_path); + g_object_unref (f); + } + + return rel_list; +} + + +static GList* +get_dir_list_from_file_list (GHashTable *h_dirs, + const char *base_dir, + GList *files, + gboolean is_dir_list) +{ + GList *scan; + GList *dir_list = NULL; + int base_dir_len; + + if (base_dir == NULL) + base_dir = ""; + base_dir_len = strlen (base_dir); + + for (scan = files; scan; scan = scan->next) { + char *filename = scan->data; + char *dir_name; + + if (strlen (filename) <= base_dir_len) + continue; + + if (is_dir_list) + dir_name = g_strdup (filename + base_dir_len + 1); + else + dir_name = remove_level_from_path (filename + base_dir_len + 1); + + while ((dir_name != NULL) && (dir_name[0] != '\0') && (strcmp (dir_name, "/") != 0)) { + char *tmp; + char *dir; + + /* avoid to insert duplicated folders */ + + dir = g_strconcat (base_dir, "/", dir_name, NULL); + if (g_hash_table_lookup (h_dirs, dir) == NULL) { + g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1)); + dir_list = g_list_prepend (dir_list, dir); + } + else + g_free (dir); + + tmp = dir_name; + dir_name = remove_level_from_path (tmp); + g_free (tmp); + } + + g_free (dir_name); + } + + return dir_list; +} + + +static void +get_file_list_done (GError *error, + gpointer user_data) +{ + GetFileListData *gfl = user_data; + GHashTable *h_dirs; + GList *scan; + char *uri; + + gfl->files = g_list_reverse (gfl->files); + gfl->dirs = g_list_reverse (gfl->dirs); + + if (! filter_empty (gfl->include_filter) || (gfl->exclude_filter->pattern != NULL)) { + path_list_free (gfl->dirs); + gfl->dirs = NULL; + } + + h_dirs = g_hash_table_new (g_str_hash, g_str_equal); + + /* Always include the base directory, this way empty base + * directories are added to the archive as well. */ + + if (gfl->base_dir != NULL) { + char *dir; + + dir = g_file_get_uri (gfl->base_dir); + gfl->dirs = g_list_prepend (gfl->dirs, dir); + g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1)); + } + + /* Add all the parent directories in gfl->files/gfl->dirs to the + * gfl->dirs list, the hash table is used to avoid duplicated + * entries. */ + + for (scan = gfl->dirs; scan; scan = scan->next) + g_hash_table_insert (h_dirs, (char*)scan->data, GINT_TO_POINTER (1)); + + uri = g_file_get_uri (gfl->base_dir); + gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->files, FALSE)); + + if (filter_empty (gfl->include_filter)) + gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->dirs, TRUE)); + + g_free (uri); + /**/ + + if (error == NULL) { + GList *rel_files, *rel_dirs; + + if (gfl->base_dir != NULL) { + rel_files = get_relative_file_list (NULL, gfl->files, gfl->base_dir); + rel_dirs = get_relative_file_list (NULL, gfl->dirs, gfl->base_dir); + } + else { + rel_files = gfl->files; + rel_dirs = gfl->dirs; + gfl->files = NULL; + gfl->dirs = NULL; + } + + /* rel_files/rel_dirs must be deallocated in done_func */ + gfl->done_func (rel_files, rel_dirs, NULL, gfl->done_data); + } + else + gfl->done_func (NULL, NULL, error, gfl->done_data); + + g_hash_table_destroy (h_dirs); + get_file_list_data_free (gfl); +} + + +static void +get_file_list_for_each_file (const char *uri, + GFileInfo *info, + gpointer user_data) +{ + GetFileListData *gfl = user_data; + + switch (g_file_info_get_file_type (info)) { + case G_FILE_TYPE_REGULAR: + if (filter_matches (gfl->include_filter, uri)) + if ((gfl->exclude_filter->pattern == NULL) || ! filter_matches (gfl->exclude_filter, uri)) + gfl->files = g_list_prepend (gfl->files, g_strdup (uri)); + break; + default: + break; + } +} + + +static DirOp +get_file_list_start_dir (const char *uri, + GError **error, + gpointer user_data) +{ + GetFileListData *gfl = user_data; + + if ((gfl->exclude_folders_filter->pattern == NULL) || ! filter_matches (gfl->exclude_folders_filter, uri)) { + gfl->dirs = g_list_prepend (gfl->dirs, g_strdup (uri)); + return DIR_OP_CONTINUE; + } + else + return DIR_OP_SKIP; +} + + +void +g_directory_list_async (const char *directory, + const char *base_dir, + gboolean recursive, + gboolean follow_links, + gboolean no_backup_files, + gboolean no_dot_files, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + gboolean ignorecase, + GCancellable *cancellable, + ListReadyCallback done_func, + gpointer done_data) +{ + GetFileListData *gfl; + FilterOptions filter_options; + + gfl = g_new0 (GetFileListData, 1); + gfl->directory = g_file_new_for_commandline_arg (directory); + gfl->base_dir = g_file_new_for_commandline_arg (base_dir); + gfl->done_func = done_func; + gfl->done_data = done_data; + + filter_options = FILTER_DEFAULT; + if (no_backup_files) + filter_options |= FILTER_NOBACKUPFILES; + if (no_dot_files) + filter_options |= FILTER_NODOTFILES; + if (ignorecase) + filter_options |= FILTER_IGNORECASE; + gfl->include_filter = filter_new (include_files, filter_options); + gfl->exclude_filter = filter_new (exclude_files, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT); + gfl->exclude_folders_filter = filter_new (exclude_folders, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT); + + g_directory_foreach_child (gfl->directory, + recursive, + follow_links, + cancellable, + get_file_list_start_dir, + get_file_list_for_each_file, + get_file_list_done, + gfl); +} + + +/* -- g_list_items_async -- */ + + +static void get_items_for_current_dir (GetFileListData *gfl); + + +static gboolean +get_items_for_next_dir_idle_cb (gpointer data) +{ + GetFileListData *gfl = data; + + g_source_remove (gfl->visit_timeout); + gfl->visit_timeout = 0; + + gfl->current_dir = g_list_next (gfl->current_dir); + get_items_for_current_dir (gfl); + + return FALSE; +} + + +static void +get_items_for_current_dir_done (GList *files, + GList *dirs, + GError *error, + gpointer data) +{ + GetFileListData *gfl = data; + + if (error != NULL) { + if (gfl->done_func) + gfl->done_func (NULL, NULL, error, gfl->done_data); + path_list_free (files); + path_list_free (dirs); + get_file_list_data_free (gfl); + return; + } + + gfl->files = g_list_concat (gfl->files, files); + gfl->dirs = g_list_concat (gfl->dirs, dirs); + + gfl->visit_timeout = g_idle_add (get_items_for_next_dir_idle_cb, gfl); +} + + +static void +get_items_for_current_dir (GetFileListData *gfl) +{ + GFile *current_dir; + char *directory_name; + GFile *directory_file; + char *directory_uri; + char *base_dir_uri; + + if (gfl->current_dir == NULL) { + if (gfl->done_func) { + /* gfl->files/gfl->dirs must be deallocated in gfl->done_func */ + gfl->done_func (gfl->files, gfl->dirs, NULL, gfl->done_data); + gfl->files = NULL; + gfl->dirs = NULL; + } + get_file_list_data_free (gfl); + return; + } + + current_dir = g_file_new_for_uri ((char*) gfl->current_dir->data); + directory_name = g_file_get_basename (current_dir); + directory_file = g_file_get_child (gfl->base_dir, directory_name); + directory_uri = g_file_get_uri (directory_file); + base_dir_uri = g_file_get_uri (gfl->base_dir); + + g_directory_list_all_async (directory_uri, + base_dir_uri, + TRUE, + gfl->cancellable, + get_items_for_current_dir_done, + gfl); + + g_free (base_dir_uri); + g_free (directory_uri); + g_object_unref (directory_file); + g_free (directory_name); + g_object_unref (current_dir); +} + + +void +g_list_items_async (GList *items, + const char *base_dir, + GCancellable *cancellable, + ListReadyCallback done_func, + gpointer done_data) +{ + GetFileListData *gfl; + int base_len; + GList *scan; + + g_return_if_fail (base_dir != NULL); + + gfl = g_new0 (GetFileListData, 1); + gfl->base_dir = g_file_new_for_commandline_arg (base_dir); + gfl->cancellable = cancellable; + gfl->done_func = done_func; + gfl->done_data = done_data; + + base_len = 0; + if (strcmp (base_dir, "/") != 0) + base_len = strlen (base_dir); + + for (scan = items; scan; scan = scan->next) { + char *uri = scan->data; + + /* FIXME: this is not async */ + if (uri_is_dir (uri)) { + gfl->to_visit = g_list_prepend (gfl->to_visit, g_strdup (uri)); + } + else { + char *rel_path = g_uri_unescape_string (uri + base_len + 1, NULL); + gfl->files = g_list_prepend (gfl->files, rel_path); + } + } + + gfl->current_dir = gfl->to_visit; + get_items_for_current_dir (gfl); +} + + +/* -- g_copy_files_async -- */ + + +typedef struct { + GList *sources; + GList *destinations; + GFileCopyFlags flags; + int io_priority; + GCancellable *cancellable; + CopyProgressCallback progress_callback; + gpointer progress_callback_data; + CopyDoneCallback callback; + gpointer user_data; + + GList *source; + GList *destination; + int n_file; + int tot_files; +} CopyFilesData; + + +static CopyFilesData* +copy_files_data_new (GList *sources, + GList *destinations, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data) +{ + CopyFilesData *cfd; + + cfd = g_new0 (CopyFilesData, 1); + cfd->sources = gio_file_list_dup (sources); + cfd->destinations = gio_file_list_dup (destinations); + cfd->flags = flags; + cfd->io_priority = io_priority; + cfd->cancellable = cancellable; + cfd->progress_callback = progress_callback; + cfd->progress_callback_data = progress_callback_data; + cfd->callback = callback; + cfd->user_data = user_data; + + cfd->source = cfd->sources; + cfd->destination = cfd->destinations; + cfd->n_file = 1; + cfd->tot_files = g_list_length (cfd->sources); + + return cfd; +} + + +static void +copy_files_data_free (CopyFilesData *cfd) +{ + if (cfd == NULL) + return; + gio_file_list_free (cfd->sources); + gio_file_list_free (cfd->destinations); + g_free (cfd); +} + + +static void g_copy_current_file (CopyFilesData *cfd); + + +static void +g_copy_next_file (CopyFilesData *cfd) +{ + cfd->source = g_list_next (cfd->source); + cfd->destination = g_list_next (cfd->destination); + cfd->n_file++; + + g_copy_current_file (cfd); +} + + +static void +g_copy_files_ready_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + CopyFilesData *cfd = user_data; + GFile *source = cfd->source->data; + GError *error = NULL; + + if (! g_file_copy_finish (source, result, &error)) { + /* source and target are directories, ignore the error */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE)) + g_clear_error (&error); + /* source is directory, create target directory */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE)) { + g_clear_error (&error); + g_file_make_directory ((GFile*) cfd->destination->data, + cfd->cancellable, + &error); + } + } + + if (error) { + if (cfd->callback) + cfd->callback (error, cfd->user_data); + g_clear_error (&error); + copy_files_data_free (cfd); + return; + } + + g_copy_next_file (cfd); +} + + +static void +g_copy_files_progess_cb (goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + CopyFilesData *cfd = user_data; + + if (cfd->progress_callback) + cfd->progress_callback (cfd->n_file, + cfd->tot_files, + (GFile*) cfd->source->data, + (GFile*) cfd->destination->data, + current_num_bytes, + total_num_bytes, + cfd->progress_callback_data); +} + + +static void +g_copy_current_file (CopyFilesData *cfd) +{ + if ((cfd->source == NULL) || (cfd->destination == NULL)) { + if (cfd->callback) + cfd->callback (NULL, cfd->user_data); + copy_files_data_free (cfd); + return; + } + + g_file_copy_async ((GFile*) cfd->source->data, + (GFile*) cfd->destination->data, + cfd->flags, + cfd->io_priority, + cfd->cancellable, + g_copy_files_progess_cb, + cfd, + g_copy_files_ready_cb, + cfd); +} + + +void +g_copy_files_async (GList *sources, + GList *destinations, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data) +{ + CopyFilesData *cfd; + + cfd = copy_files_data_new (sources, + destinations, + flags, + io_priority, + cancellable, + progress_callback, + progress_callback_data, + callback, + user_data); + g_copy_current_file (cfd); +} + + +void +g_copy_file_async (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data) +{ + GList *source_files; + GList *destination_files; + + source_files = g_list_append (NULL, (gpointer) source); + destination_files = g_list_append (NULL, (gpointer) destination); + + g_copy_files_async (source_files, + destination_files, + flags, + io_priority, + cancellable, + progress_callback, + progress_callback_data, + callback, + user_data); + + g_list_free (source_files); + g_list_free (destination_files); +} + + +void +g_copy_uris_async (GList *sources, + GList *destinations, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data) +{ + GList *source_files, *destination_files; + + source_files = gio_file_list_new_from_uri_list (sources); + destination_files = gio_file_list_new_from_uri_list (destinations); + + g_copy_files_async (source_files, + destination_files, + flags, + io_priority, + cancellable, + progress_callback, + progress_callback_data, + callback, + user_data); + + gio_file_list_free (source_files); + gio_file_list_free (destination_files); +} + + +void +g_copy_uri_async (const char *source, + const char *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data) +{ + GList *source_list; + GList *destination_list; + + source_list = g_list_append (NULL, (gpointer)source); + destination_list = g_list_append (NULL, (gpointer)destination); + + g_copy_uris_async (source_list, + destination_list, + flags, + io_priority, + cancellable, + progress_callback, + progress_callback_data, + callback, + user_data); + + g_list_free (source_list); + g_list_free (destination_list); +} + + +/* -- g_directory_copy_async -- */ + + +typedef struct { + char *uri; + GFileInfo *info; +} ChildData; + + +static ChildData* +child_data_new (const char *uri, + GFileInfo *info) +{ + ChildData *data; + + data = g_new0 (ChildData, 1); + data->uri = g_strdup (uri); + data->info = g_file_info_dup (info); + + return data; +} + + +static void +child_data_free (ChildData *child) +{ + if (child == NULL) + return; + g_free (child->uri); + g_object_unref (child->info); + g_free (child); +} + + +typedef struct { + GFile *source; + GFile *destination; + GFileCopyFlags flags; + int io_priority; + GCancellable *cancellable; + CopyProgressCallback progress_callback; + gpointer progress_callback_data; + CopyDoneCallback callback; + gpointer user_data; + GError *error; + + GList *to_copy; + GList *current; + GFile *current_source; + GFile *current_destination; + int n_file, tot_files; + guint source_id; +} DirectoryCopyData; + + +static void +directory_copy_data_free (DirectoryCopyData *dcd) +{ + if (dcd == NULL) + return; + + if (dcd->source != NULL) + g_object_unref (dcd->source); + if (dcd->destination != NULL) + g_object_unref (dcd->destination); + if (dcd->current_source != NULL) { + g_object_unref (dcd->current_source); + dcd->current_source = NULL; + } + if (dcd->current_destination != NULL) { + g_object_unref (dcd->current_destination); + dcd->current_destination = NULL; + } + g_list_foreach (dcd->to_copy, (GFunc) child_data_free, NULL); + g_list_free (dcd->to_copy); + g_free (dcd); +} + + +static gboolean +g_directory_copy_done (gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + g_source_remove (dcd->source_id); + + if (dcd->callback) + dcd->callback (dcd->error, dcd->user_data); + if (dcd->error != NULL) + g_clear_error (&(dcd->error)); + directory_copy_data_free (dcd); + + return FALSE; +} + + +static GFile * +get_destination_for_uri (DirectoryCopyData *dcd, + const char *uri) +{ + GFile *f_uri; + GFile *destination_file; + char *relative_path; + + f_uri = g_file_new_for_uri (uri); + relative_path = g_file_get_relative_path (dcd->source, f_uri); + if (relative_path != NULL) + destination_file = g_file_resolve_relative_path (dcd->destination, relative_path); + else + destination_file = g_file_dup (dcd->destination); + + g_free (relative_path); + g_object_unref (f_uri); + + return destination_file; +} + + +static void g_directory_copy_current_child (DirectoryCopyData *dcd); + + +static gboolean +g_directory_copy_next_child (gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + g_source_remove (dcd->source_id); + + dcd->current = g_list_next (dcd->current); + dcd->n_file++; + g_directory_copy_current_child (dcd); + + return FALSE; +} + + +static void +g_directory_copy_child_done_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + if (! g_file_copy_finish ((GFile*)source_object, result, &(dcd->error))) { + dcd->source_id = g_idle_add (g_directory_copy_done, dcd); + return; + } + + dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd); +} + + +static void +g_directory_copy_child_progess_cb (goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + if (dcd->progress_callback) + dcd->progress_callback (dcd->n_file, + dcd->tot_files, + dcd->current_source, + dcd->current_destination, + current_num_bytes, + total_num_bytes, + dcd->progress_callback_data); +} + + +static void +g_directory_copy_current_child (DirectoryCopyData *dcd) +{ + ChildData *child; + gboolean async_op = FALSE; + + if (dcd->current == NULL) { + dcd->source_id = g_idle_add (g_directory_copy_done, dcd); + return; + } + + if (dcd->current_source != NULL) { + g_object_unref (dcd->current_source); + dcd->current_source = NULL; + } + if (dcd->current_destination != NULL) { + g_object_unref (dcd->current_destination); + dcd->current_destination = NULL; + } + + child = dcd->current->data; + dcd->current_source = g_file_new_for_uri (child->uri); + dcd->current_destination = get_destination_for_uri (dcd, child->uri); + if (dcd->current_destination == NULL) { + dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd); + return; + } + + switch (g_file_info_get_file_type (child->info)) { + case G_FILE_TYPE_DIRECTORY: + /* FIXME: how to make a directory asynchronously ? */ + + /* doesn't check the returned error for now, because when an + * error occurs the code is not returned (for example when + * a directory already exists the G_IO_ERROR_EXISTS code is + * *not* returned), so we cannot discriminate between warnings + * and fatal errors. (see bug #525155) */ + + g_file_make_directory (dcd->current_destination, + NULL, + NULL); + + /*if (! g_file_make_directory (dcd->current_destination, + dcd->cancellable, + &(dcd->error))) + { + dcd->source_id = g_idle_add (g_directory_copy_done, dcd); + return; + }*/ + break; + case G_FILE_TYPE_SYMBOLIC_LINK: + /* FIXME: how to make a link asynchronously ? */ + + g_file_make_symbolic_link (dcd->current_destination, + g_file_info_get_symlink_target (child->info), + NULL, + NULL); + + /*if (! g_file_make_symbolic_link (dcd->current_destination, + g_file_info_get_symlink_target (child->info), + dcd->cancellable, + &(dcd->error))) + { + dcd->source_id = g_idle_add (g_directory_copy_done, dcd); + return; + }*/ + break; + case G_FILE_TYPE_REGULAR: + g_file_copy_async (dcd->current_source, + dcd->current_destination, + dcd->flags, + dcd->io_priority, + dcd->cancellable, + g_directory_copy_child_progess_cb, + dcd, + g_directory_copy_child_done_cb, + dcd); + async_op = TRUE; + break; + default: + break; + } + + if (! async_op) + dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd); +} + + +static gboolean +g_directory_copy_start_copying (gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + g_source_remove (dcd->source_id); + + dcd->to_copy = g_list_reverse (dcd->to_copy); + dcd->current = dcd->to_copy; + dcd->n_file = 1; + g_directory_copy_current_child (dcd); + + return FALSE; +} + + +static void +g_directory_copy_list_ready (GError *error, + gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + if (error != NULL) { + dcd->error = g_error_copy (error); + dcd->source_id = g_idle_add (g_directory_copy_done, dcd); + return; + } + + dcd->source_id = g_idle_add (g_directory_copy_start_copying, dcd); +} + + +static void +g_directory_copy_for_each_file (const char *uri, + GFileInfo *info, + gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + + dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info)); + dcd->tot_files++; +} + + +static DirOp +g_directory_copy_start_dir (const char *uri, + GError **error, + gpointer user_data) +{ + DirectoryCopyData *dcd = user_data; + GFileInfo *info; + + info = g_file_info_new (); + g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY); + dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info)); + g_object_unref (info); + + dcd->tot_files++; + + return DIR_OP_CONTINUE; +} + + +void +g_directory_copy_async (const char *source, + const char *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data) +{ + DirectoryCopyData *dcd; + + /* Creating GFile objects here will save us lot of effort in path construction */ + dcd = g_new0 (DirectoryCopyData, 1); + dcd->source = g_file_new_for_commandline_arg (source); + dcd->destination = g_file_new_for_commandline_arg (destination); + dcd->flags = flags; + dcd->io_priority = io_priority; + dcd->cancellable = cancellable; + dcd->progress_callback = progress_callback; + dcd->progress_callback_data = progress_callback_data; + dcd->callback = callback; + dcd->user_data = user_data; + + g_directory_foreach_child (dcd->source, + TRUE, + TRUE, + dcd->cancellable, + g_directory_copy_start_dir, + g_directory_copy_for_each_file, + g_directory_copy_list_ready, + dcd); +} + + +gboolean +g_load_file_in_buffer (GFile *file, + void *buffer, + gsize size, + GError **error) +{ + GFileInputStream *istream; + int n; + + istream = g_file_read (file, NULL, error); + if (istream == NULL) + return FALSE; + + n = g_input_stream_read (G_INPUT_STREAM (istream), buffer, size, NULL, error); + g_object_unref (istream); + + return (n >= 0); +} + diff --git a/src/gio-utils.h b/src/gio-utils.h new file mode 100644 index 0000000..7dfe306 --- /dev/null +++ b/src/gio-utils.h @@ -0,0 +1,155 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2008 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef _GIO_UTILS_H +#define _GIO_UTILS_H + +#include <glib.h> +#include <gio/gio.h> + +/* callback types */ + +typedef enum { + DIR_OP_CONTINUE, + DIR_OP_SKIP, + DIR_OP_STOP +} DirOp; + +typedef DirOp (*StartDirCallback) (const char *uri, + GError **error, + gpointer user_data); +typedef void (*ForEachChildCallback) (const char *uri, + GFileInfo *info, + gpointer user_data); +typedef void (*ForEachDoneCallback) (GError *error, + gpointer data); +typedef void (*ListReadyCallback) (GList *files, + GList *dirs, + GError *error, + gpointer user_data); +typedef void (*CopyProgressCallback) (goffset current_file, + goffset total_files, + GFile *source, + GFile *destination, + goffset current_num_bytes, + goffset total_num_bytes, + gpointer user_data); +typedef void (*CopyDoneCallback) (GError *error, + gpointer user_data); + +/* asynchronous recursive list functions */ + +void g_directory_foreach_child (GFile *directory, + gboolean recursive, + gboolean follow_links, + GCancellable *cancellable, + StartDirCallback start_dir_func, + ForEachChildCallback for_each_file_func, + ForEachDoneCallback done_func, + gpointer user_data); +void g_directory_list_async (const char *directory, + const char *base_dir, + gboolean recursive, + gboolean follow_links, + gboolean no_backup_files, + gboolean no_dot_files, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + gboolean ignorecase, + GCancellable *cancellable, + ListReadyCallback done_func, + gpointer done_data); +void g_list_items_async (GList *items, + const char *base_dir, + GCancellable *cancellable, + ListReadyCallback done_func, + gpointer done_data); + +/* asynchronous copy functions */ + +void g_copy_files_async (GList *sources, + GList *destinations, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data); +void g_copy_uris_async (GList *sources, + GList *destinations, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data); +void g_copy_file_async (GFile *source, + GFile *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data); +void g_copy_uri_async (const char *source, + const char *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data); +void g_directory_copy_async (const char *source, + const char *destination, + GFileCopyFlags flags, + int io_priority, + GCancellable *cancellable, + CopyProgressCallback progress_callback, + gpointer progress_callback_data, + CopyDoneCallback callback, + gpointer user_data); +gboolean g_load_file_in_buffer (GFile *file, + void *buffer, + gsize size, + GError **error); + +/* convenience macros */ + +/** + * g_directory_list_all_async: + * @directory: + * @base_dir: + * @recursive: + * @cancellable: + * @done_func: + * @done_data: + * + */ +#define g_directory_list_all_async(directory, base_dir, recursive, cancellable, done_func, done_data) \ + g_directory_list_async ((directory), (base_dir), (recursive), TRUE, FALSE, FALSE, NULL, NULL, NULL, FALSE, (cancellable), (done_func), (done_data)) + +#endif /* _GIO_UTILS_H */ diff --git a/src/glib-utils.c b/src/glib-utils.c new file mode 100644 index 0000000..7935f13 --- /dev/null +++ b/src/glib-utils.c @@ -0,0 +1,645 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <stdio.h> +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gprintf.h> +#include "glib-utils.h" + + +#define MAX_PATTERNS 128 + + +gboolean +strchrs (const char *str, + const char *chars) +{ + const char *c; + for (c = chars; *c != '\0'; c++) + if (strchr (str, *c) != NULL) + return TRUE; + return FALSE; +} + + +char * +str_substitute (const char *str, + const char *from_str, + const char *to_str) +{ + char **tokens; + int i; + GString *gstr; + + if (str == NULL) + return NULL; + + if (from_str == NULL) + return g_strdup (str); + + if (strcmp (str, from_str) == 0) + return g_strdup (to_str); + + tokens = g_strsplit (str, from_str, -1); + + gstr = g_string_new (NULL); + for (i = 0; tokens[i] != NULL; i++) { + gstr = g_string_append (gstr, tokens[i]); + if ((to_str != NULL) && (tokens[i+1] != NULL)) + gstr = g_string_append (gstr, to_str); + } + + return g_string_free (gstr, FALSE); +} + + +int +strcmp_null_tolerant (const char *s1, const char *s2) +{ + if ((s1 == NULL) && (s2 == NULL)) + return 0; + else if ((s1 != NULL) && (s2 == NULL)) + return 1; + else if ((s1 == NULL) && (s2 != NULL)) + return -1; + else + return strcmp (s1, s2); +} + + +/* counts how many characters to escape in @str. */ +static int +count_chars_to_escape (const char *str, + const char *meta_chars) +{ + int meta_chars_n = strlen (meta_chars); + const char *s; + int n = 0; + + for (s = str; *s != 0; s++) { + int i; + for (i = 0; i < meta_chars_n; i++) + if (*s == meta_chars[i]) { + n++; + break; + } + } + return n; +} + + +char* +escape_str_common (const char *str, + const char *meta_chars, + const char prefix, + const char postfix) +{ + int meta_chars_n = strlen (meta_chars); + char *escaped; + int i, new_l, extra_chars = 0; + const char *s; + char *t; + + if (str == NULL) + return NULL; + + if (prefix) + extra_chars++; + if (postfix) + extra_chars++; + + new_l = strlen (str) + (count_chars_to_escape (str, meta_chars) * extra_chars); + escaped = g_malloc (new_l + 1); + + s = str; + t = escaped; + while (*s) { + gboolean is_bad = FALSE; + for (i = 0; (i < meta_chars_n) && !is_bad; i++) + is_bad = (*s == meta_chars[i]); + if (is_bad && prefix) + *t++ = prefix; + *t++ = *s++; + if (is_bad && postfix) + *t++ = postfix; + } + *t = 0; + + return escaped; +} + + +/* escape with backslash the string @str. */ +char* +escape_str (const char *str, + const char *meta_chars) +{ + return escape_str_common (str, meta_chars, '\\', 0); +} + + +/* escape with backslash the file name. */ +char* +shell_escape (const char *filename) +{ + return escape_str (filename, "$'`\"\\!?* ()[]&|:;<>#"); +} + + +static const char * +g_utf8_strstr (const char *haystack, const char *needle) +{ + const char *s; + gsize i; + gsize haystack_len = g_utf8_strlen (haystack, -1); + gsize needle_len = g_utf8_strlen (needle, -1); + int needle_size = strlen (needle); + + s = haystack; + for (i = 0; i <= haystack_len - needle_len; i++) { + if (strncmp (s, needle, needle_size) == 0) + return s; + s = g_utf8_next_char(s); + } + + return NULL; +} + + +static char** +g_utf8_strsplit (const char *string, + const char *delimiter, + int max_tokens) +{ + GSList *string_list = NULL, *slist; + char **str_array; + const char *s; + guint n = 0; + const char *remainder; + + g_return_val_if_fail (string != NULL, NULL); + g_return_val_if_fail (delimiter != NULL, NULL); + g_return_val_if_fail (delimiter[0] != '\0', NULL); + + if (max_tokens < 1) + max_tokens = G_MAXINT; + + remainder = string; + s = g_utf8_strstr (remainder, delimiter); + if (s != NULL) { + gsize delimiter_size = strlen (delimiter); + + while (--max_tokens && (s != NULL)) { + gsize size = s - remainder; + char *new_string; + + new_string = g_new (char, size + 1); + strncpy (new_string, remainder, size); + new_string[size] = 0; + + string_list = g_slist_prepend (string_list, new_string); + n++; + remainder = s + delimiter_size; + s = g_utf8_strstr (remainder, delimiter); + } + } + if (*string) { + n++; + string_list = g_slist_prepend (string_list, g_strdup (remainder)); + } + + str_array = g_new (char*, n + 1); + + str_array[n--] = NULL; + for (slist = string_list; slist; slist = slist->next) + str_array[n--] = slist->data; + + g_slist_free (string_list); + + return str_array; +} + + +static char* +g_utf8_strchug (char *string) +{ + char *scan; + gunichar c; + + g_return_val_if_fail (string != NULL, NULL); + + scan = string; + c = g_utf8_get_char (scan); + while (g_unichar_isspace (c)) { + scan = g_utf8_next_char (scan); + c = g_utf8_get_char (scan); + } + + g_memmove (string, scan, strlen (scan) + 1); + + return string; +} + + +static char* +g_utf8_strchomp (char *string) +{ + char *scan; + gsize len; + + g_return_val_if_fail (string != NULL, NULL); + + len = g_utf8_strlen (string, -1); + + if (len == 0) + return string; + + scan = g_utf8_offset_to_pointer (string, len - 1); + + while (len--) { + gunichar c = g_utf8_get_char (scan); + if (g_unichar_isspace (c)) + *scan = '\0'; + else + break; + scan = g_utf8_find_prev_char (string, scan); + } + + return string; +} + + +#define g_utf8_strstrip(string) g_utf8_strchomp (g_utf8_strchug (string)) + + +gboolean +match_regexps (GRegex **regexps, + const char *string, + GRegexMatchFlags match_options) +{ + gboolean matched; + int i; + + if ((regexps == NULL) || (regexps[0] == NULL)) + return TRUE; + + if (string == NULL) + return FALSE; + + matched = FALSE; + for (i = 0; regexps[i] != NULL; i++) + if (g_regex_match (regexps[i], string, match_options, NULL)) { + matched = TRUE; + break; + } + + return matched; +} + + +void +free_regexps (GRegex **regexps) +{ + int i; + + if (regexps == NULL) + return; + + for (i = 0; regexps[i] != NULL; i++) + g_regex_unref (regexps[i]); + g_free (regexps); +} + + +char ** +search_util_get_patterns (const char *pattern_string) +{ + char **patterns; + int i; + + if (pattern_string == NULL) + return NULL; + + patterns = g_utf8_strsplit (pattern_string, ";", MAX_PATTERNS); + for (i = 0; patterns[i] != NULL; i++) { + char *p1, *p2; + + p1 = g_utf8_strstrip (patterns[i]); + p2 = str_substitute (p1, ".", "\\."); + patterns[i] = str_substitute (p2, "*", ".*"); + + g_free (p2); + g_free (p1); + } + + return patterns; +} + + +GRegex ** +search_util_get_regexps (const char *pattern_string, + GRegexCompileFlags compile_options) +{ + char **patterns; + GRegex **regexps; + int i; + + patterns = search_util_get_patterns (pattern_string); + if (patterns == NULL) + return NULL; + + regexps = g_new0 (GRegex*, n_fields (patterns) + 1); + for (i = 0; patterns[i] != NULL; i++) + regexps[i] = g_regex_new (patterns[i], + G_REGEX_OPTIMIZE | compile_options, + G_REGEX_MATCH_NOTEMPTY, + NULL); + g_strfreev (patterns); + + return regexps; +} + + +char * +_g_strdup_with_max_size (const char *s, + int max_size) +{ + char *result; + int l = strlen (s); + + if (l > max_size) { + char *first_half; + char *second_half; + int offset; + int half_max_size = max_size / 2 + 1; + + first_half = g_strndup (s, half_max_size); + offset = half_max_size + l - max_size; + second_half = g_strndup (s + offset, half_max_size); + + result = g_strconcat (first_half, "...", second_half, NULL); + + g_free (first_half); + g_free (second_half); + } else + result = g_strdup (s); + + return result; +} + + +const char * +eat_spaces (const char *line) +{ + if (line == NULL) + return NULL; + while ((*line == ' ') && (*line != 0)) + line++; + return line; +} + + +const char * +eat_void_chars (const char *line) +{ + if (line == NULL) + return NULL; + while (((*line == ' ') || (*line == '\t')) && (*line != 0)) + line++; + return line; +} + + +char ** +split_line (const char *line, + int n_fields) +{ + char **fields; + const char *scan, *field_end; + int i; + + fields = g_new0 (char *, n_fields + 1); + fields[n_fields] = NULL; + + scan = eat_spaces (line); + for (i = 0; i < n_fields; i++) { + if (scan == NULL) { + fields[i] = NULL; + continue; + } + field_end = strchr (scan, ' '); + if (field_end != NULL) { + fields[i] = g_strndup (scan, field_end - scan); + scan = eat_spaces (field_end); + } + } + + return fields; +} + + +const char * +get_last_field (const char *line, + int last_field) +{ + const char *field; + int i; + + if (line == NULL) + return NULL; + + last_field--; + field = eat_spaces (line); + for (i = 0; i < last_field; i++) { + if (field == NULL) + return NULL; + field = strchr (field, ' '); + field = eat_spaces (field); + } + + return field; +} + + +int +n_fields (char **str_array) +{ + int i; + + if (str_array == NULL) + return 0; + + i = 0; + while (str_array[i] != NULL) + i++; + return i; +} + + +void +debug (const char *file, + int line, + const char *function, + const char *format, ...) +{ +#ifdef DEBUG + va_list args; + char *str; + + g_return_if_fail (format != NULL); + + va_start (args, format); + str = g_strdup_vprintf (format, args); + va_end (args); + + g_fprintf (stderr, "[FR] %s:%d (%s):\n\t%s\n", file, line, function, str); + + g_free (str); +#else /* ! DEBUG */ +#endif +} + + +char * +get_time_string (time_t time) +{ + struct tm *tm; + char s_time[256]; + char *locale_format = NULL; + char *time_utf8; + + tm = localtime (&time); + /* This is the time format used in the "Date Modified" column and + * in the Properties dialog. See the man page of strftime for an + * explanation of the values. */ + locale_format = g_locale_from_utf8 (_("%d %B %Y, %H:%M"), -1, NULL, NULL, NULL); + strftime (s_time, sizeof (s_time) - 1, locale_format, tm); + g_free (locale_format); + time_utf8 = g_locale_to_utf8 (s_time, -1, NULL, NULL, NULL); + + return time_utf8; +} + + +GPtrArray * +g_ptr_array_copy (GPtrArray *array) +{ + GPtrArray *new_array; + + if (array == NULL) + return NULL; + + new_array = g_ptr_array_sized_new (array->len); + memcpy (new_array->pdata, array->pdata, array->len * sizeof (gpointer)); + new_array->len = array->len; + + return new_array; +} + + +void +g_ptr_array_free_full (GPtrArray *array, + GFunc free_func, + gpointer user_data) +{ + g_ptr_array_foreach (array, free_func, user_data); + g_ptr_array_free (array, TRUE); +} + + +void +g_ptr_array_reverse (GPtrArray *array) +{ + int i, j; + gpointer tmp; + + for (i = 0; i < array->len / 2; i++) { + j = array->len - i - 1; + tmp = g_ptr_array_index (array, i); + g_ptr_array_index (array, i) = g_ptr_array_index (array, j); + g_ptr_array_index (array, j) = tmp; + } +} + + +int +g_ptr_array_binary_search (GPtrArray *array, + gpointer value, + GCompareFunc func) +{ + int l, r, p, cmp = -1; + + l = 0; + r = array->len; + while (l < r) { + p = l + ((r - l) / 2); + cmp = func(value, &g_ptr_array_index (array, p)); + if (cmp == 0) + return p; + else if (cmp < 0) + r = p; + else + l = p + 1; + } + + return -1; +} + + +GHashTable *static_strings = NULL; + + +const char * +get_static_string (const char *s) +{ + const char *result; + + if (s == NULL) + return NULL; + + if (static_strings == NULL) + static_strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + if (! g_hash_table_lookup_extended (static_strings, s, (gpointer*) &result, NULL)) { + result = g_strdup (s); + g_hash_table_insert (static_strings, + (gpointer) result, + GINT_TO_POINTER (1)); + } + + return result; +} + + +char* +g_uri_display_basename (const char *uri) +{ + char *e_name, *name; + + e_name = g_filename_display_basename (uri); + name = g_uri_unescape_string (e_name, ""); + g_free (e_name); + + return name; +} diff --git a/src/glib-utils.h b/src/glib-utils.h new file mode 100644 index 0000000..3ef92d2 --- /dev/null +++ b/src/glib-utils.h @@ -0,0 +1,86 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2005 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef _GLIB_UTILS_H +#define _GLIB_UTILS_H + +#include <time.h> + +#define g_signal_handlers_disconnect_by_data(instance, data) \ + g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, \ + 0, 0, NULL, NULL, (data)) + +gboolean strchrs (const char *str, + const char *chars); +char * str_substitute (const char *str, + const char *from_str, + const char *to_str); +int strcmp_null_tolerant (const char *s1, const char *s2); +char* escape_str_common (const char *str, + const char *meta_chars, + const char prefix, + const char postfix); +char* escape_str (const char *str, + const char *meta_chars); +gchar * shell_escape (const gchar *filename); +gboolean match_regexps (GRegex **regexps, + const char *string, + GRegexMatchFlags match_options); +char ** search_util_get_patterns (const char *pattern_string); +GRegex ** search_util_get_regexps (const char *pattern_string, + GRegexCompileFlags compile_options); +void free_regexps (GRegex **regexps); +char * _g_strdup_with_max_size (const char *s, + int max_size); +const char * eat_spaces (const char *line); +const char * eat_void_chars (const char *line); +char ** split_line (const char *line, + int n_fields); +const char * get_last_field (const char *line, + int last_field); +int n_fields (char **str_array); +char * get_time_string (time_t time); +GPtrArray * g_ptr_array_copy (GPtrArray *array); +void g_ptr_array_free_full (GPtrArray *array, + GFunc func, + gpointer user_data); +void g_ptr_array_reverse (GPtrArray *array); +int g_ptr_array_binary_search (GPtrArray *array, + gpointer value, + GCompareFunc func); +const char * get_static_string (const char *s); +char* g_uri_display_basename (const char *uri); + +/**/ + +#ifndef __GNUC__ +#define __FUNCTION__ "" +#endif + +#define DEBUG_INFO __FILE__, __LINE__, __FUNCTION__ + +void debug (const char *file, + int line, + const char *function, + const char *format, ...); + +#endif /* _GLIB_UTILS_H */ diff --git a/src/gtk-utils.c b/src/gtk-utils.c new file mode 100644 index 0000000..c18ec62 --- /dev/null +++ b/src/gtk-utils.c @@ -0,0 +1,823 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include "gtk-utils.h" + +#define LOAD_BUFFER_SIZE 65536 + + +static void +count_selected (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + int *n = data; + *n = *n + 1; +} + + +int +_gtk_count_selected (GtkTreeSelection *selection) +{ + int n = 0; + + if (selection == NULL) + return 0; + gtk_tree_selection_selected_foreach (selection, count_selected, &n); + return n; +} + + +GtkWidget* +_gtk_message_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + const char *stock_id, + const char *message, + const char *secondary_message, + const gchar *first_button_text, + ...) +{ + GtkWidget *dialog; + GtkWidget *label; + GtkWidget *image; + GtkWidget *hbox; + GtkWidget *content_area; + va_list args; + const gchar *text; + int response_id; + char *markup_text; + + g_return_val_if_fail ((message != NULL) || (secondary_message != NULL), NULL); + + if (stock_id == NULL) + stock_id = GTK_STOCK_DIALOG_INFO; + + dialog = gtk_dialog_new_with_buttons ("", parent, flags, NULL); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 6); + gtk_box_set_spacing (GTK_BOX (content_area), 8); + + /* Add label and image */ + + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + + label = gtk_label_new (""); + + if (message != NULL) { + char *escaped_message; + + escaped_message = g_markup_escape_text (message, -1); + if (secondary_message != NULL) { + char *escaped_secondary_message = g_markup_escape_text (secondary_message, -1); + markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", + escaped_message, + escaped_secondary_message); + g_free (escaped_secondary_message); + } + else + markup_text = g_strdup (escaped_message); + g_free (escaped_message); + } + else + markup_text = g_markup_escape_text (secondary_message, -1); + + gtk_label_set_markup (GTK_LABEL (label), markup_text); + g_free (markup_text); + + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + + hbox = gtk_hbox_new (FALSE, 24); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + + gtk_box_pack_start (GTK_BOX (hbox), image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), label, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (content_area), + hbox, + FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + + /* Add buttons */ + + if (first_button_text == NULL) + return dialog; + + va_start (args, first_button_text); + + text = first_button_text; + response_id = va_arg (args, gint); + + while (text != NULL) { + gtk_dialog_add_button (GTK_DIALOG (dialog), text, response_id); + + text = va_arg (args, char*); + if (text == NULL) + break; + response_id = va_arg (args, int); + } + + va_end (args); + + return dialog; +} + + +static GtkWidget * +create_button (const char *stock_id, + const char *text) +{ + GtkWidget *button; + GtkWidget *hbox; + GtkWidget *image; + GtkWidget *label; + GtkWidget *align; + const char *label_text; + gboolean text_is_stock; + GtkStockItem stock_item; + + if (gtk_stock_lookup (text, &stock_item)) { + label_text = stock_item.label; + text_is_stock = TRUE; + } else { + label_text = text; + text_is_stock = FALSE; + } + + if (text_is_stock) + image = gtk_image_new_from_stock (text, GTK_ICON_SIZE_BUTTON); + else + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON); + button = gtk_button_new_with_mnemonic (label_text); + gtk_button_set_image (GTK_BUTTON (button), image); + + gtk_widget_set_can_default (button, TRUE); + + gtk_widget_show (button); + + return button; +} + + +char * +_gtk_request_dialog_run (GtkWindow *parent, + GtkDialogFlags flags, + const char *title, + const char *message, + const char *default_value, + int max_length, + const gchar *no_button_text, + const gchar *yes_button_text) +{ + GtkWidget *dialog; + GtkWidget *label; + GtkWidget *image; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *entry; + GtkWidget *button; + GtkWidget *content_area; + char *stock_id; + char *result; + + stock_id = GTK_STOCK_DIALOG_QUESTION; + + dialog = gtk_dialog_new_with_buttons (title, parent, flags, NULL); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 6); + gtk_box_set_spacing (GTK_BOX (content_area), 8); + + /* Add label and image */ + + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + + label = gtk_label_new (message); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_selectable (GTK_LABEL (label), FALSE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0); + + entry = gtk_entry_new (); + gtk_widget_set_size_request (entry, 250, -1); + gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); + gtk_entry_set_max_length (GTK_ENTRY (entry), max_length); + gtk_entry_set_text (GTK_ENTRY (entry), default_value); + + hbox = gtk_hbox_new (FALSE, 24); + vbox = gtk_vbox_new (FALSE, 6); + + gtk_container_set_border_width (GTK_CONTAINER (hbox), 5); + gtk_box_pack_start (GTK_BOX (hbox), image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), vbox, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), label, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (vbox), entry, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (content_area), + hbox, + FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + + /* Add buttons */ + + button = create_button (GTK_STOCK_CANCEL, no_button_text); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), + button, + GTK_RESPONSE_CANCEL); + + button = create_button (GTK_STOCK_OK, yes_button_text); + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), + button, + GTK_RESPONSE_YES); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES); + + /* Run dialog */ + + gtk_widget_grab_focus (entry); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) + result = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); + else + result = NULL; + + gtk_widget_destroy (dialog); + + return result; +} + + +GtkWidget* +_gtk_yesno_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + const char *message, + const char *no_button_text, + const char *yes_button_text) +{ + GtkWidget *d; + GtkWidget *label; + GtkWidget *image; + GtkWidget *hbox; + GtkWidget *button; + GtkWidget *content_area; + char *stock_id = GTK_STOCK_DIALOG_WARNING; + + d = gtk_dialog_new_with_buttons ("", parent, flags, NULL); + gtk_window_set_resizable (GTK_WINDOW (d), FALSE); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (d)); + + gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (d), 6); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 6); + gtk_box_set_spacing (GTK_BOX (content_area), 8); + + /* Add label and image */ + + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + + label = gtk_label_new (message); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + + hbox = gtk_hbox_new (FALSE, 24); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 6); + + gtk_box_pack_start (GTK_BOX (hbox), image, + FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (hbox), label, + TRUE, TRUE, 0); + + gtk_box_pack_start (GTK_BOX (content_area), + hbox, + FALSE, FALSE, 0); + + gtk_widget_show_all (hbox); + + /* Add buttons */ + + button = create_button (GTK_STOCK_CANCEL, no_button_text); + gtk_dialog_add_action_widget (GTK_DIALOG (d), + button, + GTK_RESPONSE_CANCEL); + + /**/ + + button = create_button (GTK_STOCK_OK, yes_button_text); + gtk_dialog_add_action_widget (GTK_DIALOG (d), + button, + GTK_RESPONSE_YES); + + /**/ + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + + return d; +} + + +GtkWidget* +_gtk_error_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + GList *row_output, + const char *primary_text, + const char *secondary_text, + ...) +{ + GtkWidget *dialog; + GtkWidget *label; + GtkWidget *image; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *text_view; + GtkWidget *scrolled = NULL; + GtkWidget *expander; + GtkWidget *content_area; + GtkTextBuffer *text_buf; + GtkTextIter iter; + char *stock_id; + GList *scan; + char *escaped_message, *markup_text; + va_list args; + gboolean view_output = (row_output != NULL); + + stock_id = GTK_STOCK_DIALOG_ERROR; + + dialog = gtk_dialog_new_with_buttons ("", + parent, + flags, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_container_set_border_width (GTK_CONTAINER (content_area), 6); + gtk_box_set_spacing (GTK_BOX (content_area), 8); + + gtk_widget_set_size_request (dialog, 500, -1); + + /* Add label and image */ + + image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + + label = gtk_label_new (""); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_selectable (GTK_LABEL (label), TRUE); + + escaped_message = g_markup_escape_text (primary_text, -1); + if (secondary_text != NULL) { + char *secondary_message; + char *escaped_secondary_message; + + va_start (args, secondary_text); + secondary_message = g_strdup_vprintf (secondary_text, args); + va_end (args); + escaped_secondary_message = g_markup_escape_text (secondary_message, -1); + + markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n", + escaped_message, + escaped_secondary_message); + + g_free (escaped_secondary_message); + g_free (secondary_message); + } + else + markup_text = g_strdup (escaped_message); + gtk_label_set_markup (GTK_LABEL (label), markup_text); + g_free (markup_text); + g_free (escaped_message); + + if (view_output) { + /* Expander */ + + expander = gtk_expander_new_with_mnemonic (_("Command _Line Output")); + gtk_expander_set_expanded (GTK_EXPANDER (expander), secondary_text == NULL); + + /* Add text */ + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), + GTK_SHADOW_ETCHED_IN); + gtk_widget_set_size_request (scrolled, -1, 200); + + text_buf = gtk_text_buffer_new (NULL); + gtk_text_buffer_create_tag (text_buf, "monospace", + "family", "monospace", NULL); + gtk_text_buffer_get_iter_at_offset (text_buf, &iter, 0); + for (scan = row_output; scan; scan = scan->next) { + char *line = scan->data; + char *utf8_line; + gsize bytes_written; + + utf8_line = g_locale_to_utf8 (line, -1, NULL, &bytes_written, NULL); + gtk_text_buffer_insert_with_tags_by_name (text_buf, + &iter, + utf8_line, + bytes_written, + "monospace", NULL); + g_free (utf8_line); + + gtk_text_buffer_insert (text_buf, &iter, "\n", 1); + } + text_view = gtk_text_view_new_with_buffer (text_buf); + g_object_unref (text_buf); + gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE); + } + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + if (view_output) { + gtk_container_add (GTK_CONTAINER (scrolled), text_view); + gtk_container_add (GTK_CONTAINER (expander), scrolled); + gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0); + } + + gtk_box_pack_start (GTK_BOX (content_area), + vbox, + FALSE, FALSE, 0); + gtk_widget_show_all (vbox); + + return dialog; +} + + +void +_gtk_error_dialog_run (GtkWindow *parent, + const gchar *main_message, + const gchar *format, + ...) +{ + GtkWidget *d; + char *message; + va_list args; + + va_start (args, format); + message = g_strdup_vprintf (format, args); + va_end (args); + + d = _gtk_message_dialog_new (parent, + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_ERROR, + main_message, + message, + GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL, + NULL); + g_free (message); + + g_signal_connect (G_OBJECT (d), "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_widget_show (d); +} + + +void +_gtk_entry_set_locale_text (GtkEntry *entry, + const char *text) +{ + char *utf8_text; + + if (text == NULL) + return; + + utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL); + if (utf8_text != NULL) + gtk_entry_set_text (entry, utf8_text); + else + gtk_entry_set_text (entry, ""); + g_free (utf8_text); +} + + +char * +_gtk_entry_get_locale_text (GtkEntry *entry) +{ + const char *utf8_text; + char *text; + + utf8_text = gtk_entry_get_text (entry); + if (utf8_text == NULL) + return NULL; + + text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL); + + return text; +} + + +void +_gtk_label_set_locale_text (GtkLabel *label, + const char *text) +{ + char *utf8_text; + + utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL); + if (utf8_text != NULL) { + gtk_label_set_text (label, utf8_text); + g_free (utf8_text); + } else + gtk_label_set_text (label, ""); +} + + +char * +_gtk_label_get_locale_text (GtkLabel *label) +{ + const char *utf8_text; + char *text; + + utf8_text = gtk_label_get_text (label); + if (utf8_text == NULL) + return NULL; + + text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL); + + return text; +} + + +void +_gtk_entry_set_filename_text (GtkEntry *entry, + const char *text) +{ + char *utf8_text; + + utf8_text = g_filename_to_utf8 (text, -1, NULL, NULL, NULL); + if (utf8_text != NULL) { + gtk_entry_set_text (entry, utf8_text); + g_free (utf8_text); + } else + gtk_entry_set_text (entry, ""); +} + + +char * +_gtk_entry_get_filename_text (GtkEntry *entry) +{ + const char *utf8_text; + char *text; + + utf8_text = gtk_entry_get_text (entry); + if (utf8_text == NULL) + return NULL; + + text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL); + + return text; +} + + +void +_gtk_label_set_filename_text (GtkLabel *label, + const char *text) +{ + char *utf8_text; + + utf8_text = g_filename_display_name (text); + gtk_label_set_text (label, utf8_text); + g_free (utf8_text); +} + + +char * +_gtk_label_get_filename_text (GtkLabel *label) +{ + const char *utf8_text; + char *text; + + utf8_text = gtk_label_get_text (label); + if (utf8_text == NULL) + return NULL; + + text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL); + + return text; +} + + +static GdkPixbuf * +get_themed_icon_pixbuf (GThemedIcon *icon, + int size, + GtkIconTheme *icon_theme) +{ + char **icon_names; + GtkIconInfo *icon_info; + GdkPixbuf *pixbuf; + GError *error = NULL; + + g_object_get (icon, "names", &icon_names, NULL); + + icon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)icon_names, size, 0); + if (icon_info == NULL) + icon_info = gtk_icon_theme_lookup_icon (icon_theme, "text-x-generic", size, GTK_ICON_LOOKUP_USE_BUILTIN); + + pixbuf = gtk_icon_info_load_icon (icon_info, &error); + if (pixbuf == NULL) { + g_warning ("could not load icon pixbuf: %s\n", error->message); + g_clear_error (&error); + } + + gtk_icon_info_free (icon_info); + g_strfreev (icon_names); + + return pixbuf; +} + + +static GdkPixbuf * +get_file_icon_pixbuf (GFileIcon *icon, + int size) +{ + GFile *file; + char *filename; + GdkPixbuf *pixbuf; + + file = g_file_icon_get_file (icon); + filename = g_file_get_path (file); + pixbuf = gdk_pixbuf_new_from_file_at_size (filename, size, -1, NULL); + g_free (filename); + g_object_unref (file); + + return pixbuf; +} + + +GdkPixbuf * +get_icon_pixbuf (GIcon *icon, + int size, + GtkIconTheme *theme) +{ + if (icon == NULL) + return NULL; + if (G_IS_THEMED_ICON (icon)) + return get_themed_icon_pixbuf (G_THEMED_ICON (icon), size, theme); + if (G_IS_FILE_ICON (icon)) + return get_file_icon_pixbuf (G_FILE_ICON (icon), size); + return NULL; +} + + +GdkPixbuf * +get_mime_type_pixbuf (const char *mime_type, + int icon_size, + GtkIconTheme *icon_theme) +{ + GdkPixbuf *pixbuf = NULL; + GIcon *icon; + + if (icon_theme == NULL) + icon_theme = gtk_icon_theme_get_default (); + + icon = g_content_type_get_icon (mime_type); + pixbuf = get_icon_pixbuf (icon, icon_size, icon_theme); + g_object_unref (icon); + + return pixbuf; +} + + +int +get_folder_pixbuf_size_for_list (GtkWidget *widget) +{ + int icon_width, icon_height; + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget), + GTK_ICON_SIZE_SMALL_TOOLBAR, + &icon_width, &icon_height); + return MAX (icon_width, icon_height); +} + + +gboolean +show_uri (GdkScreen *screen, + const char *uri, + guint32 timestamp, + GError **error) +{ + gboolean result; + + result = gtk_show_uri (screen, uri, timestamp, error); + + return result; +} + + +void +show_help_dialog (GtkWindow *parent, + const char *section) +{ + char *uri; + GError *error = NULL; + + uri = g_strconcat ("ghelp:file-roller", section ? "?" : NULL, section, NULL); + if (! show_uri (gtk_window_get_screen (parent), uri, GDK_CURRENT_TIME, &error)) { + GtkWidget *dialog; + + dialog = _gtk_message_dialog_new (parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_DIALOG_ERROR, + _("Could not display help"), + error->message, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + g_signal_connect (G_OBJECT (dialog), "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + gtk_widget_show (dialog); + + g_clear_error (&error); + } + g_free (uri); +} + + +GtkBuilder * +_gtk_builder_new_from_file (const char *ui_file) +{ + char *filename; + GtkBuilder *builder; + GError *error = NULL; + + filename = g_build_filename (UI_DIR, ui_file, NULL); + builder = gtk_builder_new (); + if (! gtk_builder_add_from_file (builder, filename, &error)) { + g_warning ("%s\n", error->message); + g_clear_error (&error); + } + g_free (filename); + + return builder; +} + + +GtkWidget * +_gtk_builder_get_widget (GtkBuilder *builder, + const char *name) +{ + return (GtkWidget *) gtk_builder_get_object (builder, name); +} diff --git a/src/gtk-utils.h b/src/gtk-utils.h new file mode 100644 index 0000000..c2c5837 --- /dev/null +++ b/src/gtk-utils.h @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef GTK_UTILS_H +#define GTK_UTILS_H + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gtk/gtk.h> + +int _gtk_count_selected (GtkTreeSelection *selection); +GtkWidget* _gtk_message_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + const char *stock_id, + const char *message, + const char *secondary_message, + const char *first_button_text, + ...); +gchar* _gtk_request_dialog_run (GtkWindow *parent, + GtkDialogFlags flags, + const char *title, + const char *message, + const char *default_value, + int max_length, + const char *no_button_text, + const char *yes_button_text); +GtkWidget* _gtk_yesno_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + const char *message, + const char *no_button_text, + const char *yes_button_text); +GtkWidget* _gtk_error_dialog_new (GtkWindow *parent, + GtkDialogFlags flags, + GList *row_output, + const char *primary_text, + const char *secondary_text, + ...) G_GNUC_PRINTF (5, 6); +void _gtk_error_dialog_run (GtkWindow *parent, + const gchar *main_message, + const gchar *format, + ...); +void _gtk_entry_set_locale_text (GtkEntry *entry, + const char *text); +char * _gtk_entry_get_locale_text (GtkEntry *entry); +void _gtk_label_set_locale_text (GtkLabel *label, + const char *text); +char * _gtk_label_get_locale_text (GtkLabel *label); +void _gtk_entry_set_filename_text (GtkEntry *entry, + const char *text); +char * _gtk_entry_get_filename_text (GtkEntry *entry); +void _gtk_label_set_filename_text (GtkLabel *label, + const char *text); +char * _gtk_label_get_filename_text (GtkLabel *label); +GdkPixbuf * get_icon_pixbuf (GIcon *icon, + int size, + GtkIconTheme *icon_theme); +GdkPixbuf * get_mime_type_pixbuf (const char *mime_type, + int icon_size, + GtkIconTheme *icon_theme); +int get_folder_pixbuf_size_for_list (GtkWidget *widget); +gboolean show_uri (GdkScreen *screen, + const char *uri, + guint32 timestamp, + GError **error); +void show_help_dialog (GtkWindow *parent, + const char *section); +GtkBuilder * + _gtk_builder_new_from_file (const char *filename); +GtkWidget * + _gtk_builder_get_widget (GtkBuilder *builder, + const char *name); + +#endif diff --git a/src/java-utils.c b/src/java-utils.c new file mode 100644 index 0000000..b48bdd0 --- /dev/null +++ b/src/java-utils.c @@ -0,0 +1,441 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <glib.h> +#include "java-utils.h" + + +/* + * The following code conforms to the JVM specification.(Java 2 Platform) + * For further changes to the classfile structure, please update the + * following macros. + */ + + +/* Tags that identify structures */ + +#define CONST_CLASS 7 +#define CONST_FIELDREF 9 +#define CONST_METHODREF 10 +#define CONST_INTERFACEMETHODREF 11 +#define CONST_STRING 8 +#define CONST_INTEGER 3 +#define CONST_FLOAT 4 +#define CONST_LONG 5 +#define CONST_DOUBLE 6 +#define CONST_NAMEANDTYPE 12 +#define CONST_UTF8 1 + +/* Sizes of structures */ + +#define CONST_CLASS_INFO 2 +#define CONST_FIELDREF_INFO 4 +#define CONST_METHODREF_INFO 4 +#define CONST_INTERFACEMETHODREF_INFO 4 +#define CONST_STRING_INFO 2 +#define CONST_INTEGER_INFO 4 +#define CONST_FLOAT_INFO 4 +#define CONST_LONG_INFO 8 +#define CONST_DOUBLE_INFO 8 +#define CONST_NAMEANDTYPE_INFO 4 + + +/* represents the utf8 strings in class file */ +struct utf_string +{ + guint16 index; + guint16 length; + char *str; +}; + +/* structure that holds class information in a class file */ +struct class_info +{ + guint16 index; + guint16 name_index; /* index into the utf_strings */ +}; + +typedef struct { + int fd; + + guint32 magic_no; /* 0xCAFEBABE (JVM Specification) :) */ + + guint16 major; /* versions */ + guint16 minor; + + guint16 const_pool_count; + GSList *const_pool_class; /* (const_pool_count - 1) elements of tye 'CONST_class_info' */ + GSList *const_pool_utf; /* (const_pool_count - 1) elements of type 'utf_strings' */ + + guint16 access_flags; + guint16 this_class; /* the index of the class the file is named after. */ + +#if 0 /* not needed */ + guint16 super_class; + guint16 interfaces_count; + guint16 *interfaces; + guint16 fields_count; + field_info *fields; + guint16 methods_count; + method_info *methods; + guint16 attributes_count; + attribute_info *attributes; +#endif +} JavaClassFile; + + +static JavaClassFile* +java_class_file_new (void) +{ + JavaClassFile *cfile; + + cfile = g_new0 (JavaClassFile, 1); + cfile->fd = -1; + + return cfile; +} + + +static void +java_class_file_free (JavaClassFile *cfile) +{ + GSList *scan; + + if (cfile->const_pool_class != NULL) { + g_slist_foreach (cfile->const_pool_class, (GFunc)g_free, NULL); + g_slist_free (cfile->const_pool_class); + } + + for (scan = cfile->const_pool_utf; scan ; scan = scan->next) { + struct utf_string *string = scan->data; + g_free (string->str); + } + + if (cfile->const_pool_utf != NULL) { + g_slist_foreach (cfile->const_pool_utf, (GFunc)g_free, NULL); + g_slist_free (cfile->const_pool_utf); + } + + if (cfile->fd != -1) + close (cfile->fd); + + g_free (cfile); +} + + +/* The following function loads the utf8 strings and class structures from the + * class file. */ +static void +load_constant_pool_utfs (JavaClassFile *cfile) +{ + guint8 tag; + guint16 i = 0; /* should be comparable with const_pool_count */ + + while ((i < cfile->const_pool_count - 1) && (read (cfile->fd, &tag, 1) != -1)) { + struct utf_string *txt = NULL; + struct class_info *class = NULL; + + switch (tag) { + case CONST_CLASS: + class = g_new0 (struct class_info, 1); + class->index = i + 1; + if (read (cfile->fd, &class->name_index, 2) != 2) { + g_free (class); + return; /* error reading */ + } + class->name_index = GUINT16_FROM_BE (class->name_index); + cfile->const_pool_class = g_slist_append (cfile->const_pool_class, class); + break; + + case CONST_FIELDREF: + lseek (cfile->fd, CONST_FIELDREF_INFO, SEEK_CUR); + break; + + case CONST_METHODREF: + lseek (cfile->fd, CONST_METHODREF_INFO, SEEK_CUR); + break; + + case CONST_INTERFACEMETHODREF: + lseek (cfile->fd, CONST_INTERFACEMETHODREF_INFO, SEEK_CUR); + break; + + case CONST_STRING: + lseek (cfile->fd, CONST_STRING_INFO, SEEK_CUR); + break; + + case CONST_INTEGER: + lseek (cfile->fd, CONST_INTEGER_INFO, SEEK_CUR); + break; + + case CONST_FLOAT: + lseek (cfile->fd, CONST_FLOAT_INFO, SEEK_CUR); + break; + + case CONST_LONG: + lseek (cfile->fd, CONST_LONG_INFO, SEEK_CUR); + break; + + case CONST_DOUBLE: + lseek (cfile->fd, CONST_DOUBLE_INFO, SEEK_CUR); + break; + + case CONST_NAMEANDTYPE: + lseek (cfile->fd, CONST_NAMEANDTYPE_INFO, SEEK_CUR); + break; + + case CONST_UTF8: + txt = g_new0 (struct utf_string, 1); + txt->index = i + 1; + if (read (cfile->fd, &(txt->length), 2) == -1) { + g_free (txt); + return; /* error while reading */ + } + txt->length = GUINT16_FROM_BE (txt->length); + txt->str = g_new0 (char, txt->length); + if (read (cfile->fd, txt->str, txt->length) == -1) { + g_free (txt); + return; /* error while reading */ + } + cfile->const_pool_utf = g_slist_append (cfile->const_pool_utf, txt); + break; + + default: + return; /* error - unknown tag in class file */ + break; + } + i++; + } + +#ifdef DEBUG + g_print( "Number of Entries: %d\n", i ); +#endif +} + + +static char* +close_and_exit (JavaClassFile *cfile) +{ + java_class_file_free (cfile); + return NULL; +} + + +/* This function extracts the package name from a class file */ +char* +get_package_name_from_class_file (char *fname) +{ + char *package = NULL; + JavaClassFile *cfile; + guint16 length = 0, end = 0, utf_index = 0; + guint32 magic; + guint16 major, minor, count; + int i = 0; + + if (! g_file_test (fname, G_FILE_TEST_EXISTS)) + return NULL; + + cfile = java_class_file_new (); + cfile->fd = open (fname, O_RDONLY); + if (cfile->fd == -1) + return close_and_exit (cfile); + + if ((i = read (cfile->fd, &magic, 4)) != 4) + return close_and_exit (cfile); + cfile->magic_no = GUINT32_FROM_BE (magic); + + if (read (cfile->fd, &major, 2 ) != 2) + return close_and_exit (cfile); + cfile->major = GUINT16_FROM_BE (major); + + if (read (cfile->fd, &minor, 2) != 2) + return close_and_exit (cfile); + cfile->minor = GUINT16_FROM_BE (minor); + + if (read (cfile->fd, &count, 2) != 2) + return close_and_exit (cfile); + cfile->const_pool_count = GUINT16_FROM_BE(count); + load_constant_pool_utfs (cfile); + + if (read (cfile->fd, &cfile->access_flags, 2) != 2) + return close_and_exit (cfile); + cfile->access_flags = GUINT16_FROM_BE (cfile->access_flags); + + if (read (cfile->fd, &cfile->this_class, 2) != 2) + return close_and_exit (cfile); + cfile->this_class = GUINT16_FROM_BE(cfile->this_class); + + /* now search for the class structure with index = cfile->this_class */ + + for (i = 0; (i < g_slist_length (cfile->const_pool_class)) && (utf_index == 0); i++ ) { + struct class_info *class = g_slist_nth_data (cfile->const_pool_class, i); + if (class->index == cfile->this_class) + utf_index = class->name_index; /* terminates loop */ + } + + /* now search for the utf8 string with index = utf_index */ + + for (i = 0; i < g_slist_length (cfile->const_pool_utf); i++) { + struct utf_string *data = g_slist_nth_data (cfile->const_pool_utf, i); + if (data->index == utf_index) { + package = g_strndup (data->str, data->length); + length = data->length; + break; + } + } + + if (package != NULL) { + for (i = length; (i >= 0) && (end == 0); i-- ) + if (package[i] == '/') + end = i; + package = g_strndup (package, end); + } + + java_class_file_free (cfile); + + return package; +} + + +/* This function consumes a comment from the java file + * multiline = TRUE implies that comment is multiline */ +static void +consume_comment (int fdesc, + gboolean multiline) +{ + gboolean escaped = FALSE; + gboolean star = FALSE; + char ch; + + while (read (fdesc, &ch, 1) == 1) { + switch (ch) { + case '/': + if (escaped) + break; + else if (star) + return; + break; + + case '\n': + if (! multiline) + return; + break; + + case '*': + escaped = FALSE; + star = TRUE; + break; + + case '\\': + escaped = ! escaped; + break; + + default: + escaped = FALSE; + star = FALSE; + break; + } + } +} + + +/* This function extracts package name from a java file */ +char* +get_package_name_from_java_file (char *fname) +{ + char *package = NULL; + JavaClassFile *cfile; + gboolean prev_char_is_bslash = FALSE; + gboolean valid_char_found = FALSE; + char ch; + + if (! g_file_test (fname, G_FILE_TEST_EXISTS)) + return NULL; + + cfile = java_class_file_new (); + cfile->fd = open (fname, O_RDONLY); + if (cfile->fd == -1) + return close_and_exit (cfile); + + while (! valid_char_found && (read (cfile->fd, &ch, 1) == 1)) { + switch (ch) { + case '/': + if (prev_char_is_bslash == TRUE) { + consume_comment (cfile->fd, FALSE); + prev_char_is_bslash = FALSE; + } + else + prev_char_is_bslash = TRUE; + break; + + case '*': + if (prev_char_is_bslash == TRUE) + consume_comment (cfile->fd, TRUE); + prev_char_is_bslash = FALSE; + break; + + case ' ': + case '\t': + case '\r': + case '\n': + prev_char_is_bslash = FALSE; + break; + + default: + prev_char_is_bslash = FALSE; + valid_char_found = TRUE; + break; + } + } + + if (ch == 'p') { + char first_valid_word[8] = ""; + + first_valid_word[0] = 'p'; + if (read (cfile->fd, &first_valid_word[1], 6) != 6) + return close_and_exit (cfile); + + first_valid_word[7] = 0; + if (g_ascii_strcasecmp (first_valid_word, "package") == 0) { + char buffer[500]; + int index = 0; + + while (read (cfile->fd, &ch, 1) == 1) { + if (ch == ';') + break; + if (ch == '.') + buffer[index++] = '/'; + else + buffer[index++] = ch; + } + buffer[index] = 0; + package = g_strdup (buffer); + } + } + + java_class_file_free (cfile); + + return package; +} diff --git a/src/java-utils.h b/src/java-utils.h new file mode 100644 index 0000000..3cea4cb --- /dev/null +++ b/src/java-utils.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2006 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef JAVA_UTILS_H +#define JAVA_UTILS_H + + +char* get_package_name_from_class_file (char *fname); +char* get_package_name_from_java_file (char *fname); + + +#endif /* JAVA_UTILS_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f94956a --- /dev/null +++ b/src/main.c @@ -0,0 +1,1030 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> +#include <stdlib.h> +#include <gio/gio.h> +#include "file-utils.h" +#include "glib-utils.h" +#include "fr-command.h" +#include "fr-command-ace.h" +#include "fr-command-alz.h" +#include "fr-command-ar.h" +#include "fr-command-arj.h" +#include "fr-command-cfile.h" +#include "fr-command-cpio.h" +#include "fr-command-dpkg.h" +#include "fr-command-iso.h" +#include "fr-command-jar.h" +#include "fr-command-lha.h" +#include "fr-command-rar.h" +#include "fr-command-rpm.h" +#include "fr-command-tar.h" +#include "fr-command-unstuff.h" +#include "fr-command-zip.h" +#include "fr-command-zoo.h" +#include "fr-command-7z.h" +#include "fr-command-lrzip.h" +#include "fr-process.h" +#include "fr-stock.h" +#include "mateconf-utils.h" +#include "fr-window.h" +#include "typedefs.h" +#include "preferences.h" +#include "file-data.h" +#include "main.h" + +#include "eggsmclient.h" + +static void prepare_app (void); +static void initialize_data (void); +static void release_data (void); + +GList *WindowList = NULL; +GList *CommandList = NULL; +gint ForceDirectoryCreation; +GHashTable *ProgramsCache = NULL; +GPtrArray *Registered_Commands = NULL; + +static char **remaining_args; + +static char *add_to = NULL; +static int add; +static char *extract_to = NULL; +static int extract; +static int extract_here; +static char *default_url = NULL; + +/* The capabilities are computed automatically in + * compute_supported_archive_types() so it's correct to initialize to 0 here. */ +FrMimeTypeDescription mime_type_desc[] = { + { "application/x-7z-compressed", ".7z", N_("7-Zip (.7z)"), 0 }, + { "application/x-7z-compressed-tar", ".tar.7z", N_("Tar compressed with 7z (.tar.7z)"), 0 }, + { "application/x-ace", ".ace", N_("Ace (.ace)"), 0 }, + { "application/x-alz", ".alz", NULL, 0 }, + { "application/x-ar", ".ar", N_("Ar (.ar)"), 0 }, + { "application/x-arj", ".arj", N_("Arj (.arj)"), 0 }, + { "application/x-bzip", ".bz2", NULL, 0 }, + { "application/x-bzip-compressed-tar", ".tar.bz2", N_("Tar compressed with bzip2 (.tar.bz2)"), 0 }, + { "application/x-bzip1", ".bz", NULL, 0 }, + { "application/x-bzip1-compressed-tar", ".tar.bz", N_("Tar compressed with bzip (.tar.bz)"), 0 }, + { "application/vnd.ms-cab-compressed", ".cab", N_("Cabinet (.cab)"), 0 }, + { "application/x-cbr", ".cbr", N_("Rar Archived Comic Book (.cbr)"), 0 }, + { "application/x-cbz", ".cbz", N_("Zip Archived Comic Book (.cbz)"), 0 }, + { "application/x-cd-image", ".iso", NULL, 0 }, + { "application/x-compress", ".Z", NULL, 0 }, + { "application/x-compressed-tar", ".tar.gz", N_("Tar compressed with gzip (.tar.gz)"), 0 }, + { "application/x-cpio", ".cpio", NULL, 0 }, + { "application/x-deb", ".deb", NULL, 0 }, + { "application/x-ear", ".ear", N_("Ear (.ear)"), 0 }, + { "application/x-ms-dos-executable", ".exe", N_("Self-extracting zip (.exe)"), 0 }, + { "application/x-gzip", ".gz", NULL, 0 }, + { "application/x-java-archive", ".jar", N_("Jar (.jar)"), 0 }, + { "application/x-lha", ".lzh", N_("Lha (.lzh)"), 0 }, + { "application/x-lrzip", ".lrz", N_("Lrzip (.lrz)"), 0}, + { "application/x-lrzip-compressed-tar", ".tar.lrz", N_("Tar compressed with lrzip (.tar.lrz)"), 0 }, + { "application/x-lzip", ".lz", NULL, 0 }, + { "application/x-lzip-compressed-tar", ".tar.lz", N_("Tar compressed with lzip (.tar.lz)"), 0 }, + { "application/x-lzma", ".lzma", NULL, 0 }, + { "application/x-lzma-compressed-tar", ".tar.lzma", N_("Tar compressed with lzma (.tar.lzma)"), 0 }, + { "application/x-lzop", ".lzo", NULL, 0 }, + { "application/x-lzop-compressed-tar", ".tar.lzo", N_("Tar compressed with lzop (.tar.lzo)"), 0 }, + { "application/x-rar", ".rar", N_("Rar (.rar)"), 0 }, + { "application/x-rpm", ".rpm", NULL, 0 }, + { "application/x-rzip", ".rz", NULL, 0 }, + { "application/x-tar", ".tar", N_("Tar uncompressed (.tar)"), 0 }, + { "application/x-tarz", ".tar.Z", N_("Tar compressed with compress (.tar.Z)"), 0 }, + { "application/x-stuffit", ".sit", NULL, 0 }, + { "application/x-war", ".war", N_("War (.war)"), 0 }, + { "application/x-xz", ".xz", N_("Xz (.xz)"), 0 }, + { "application/x-xz-compressed-tar", ".tar.xz", N_("Tar compressed with xz (.tar.xz)"), 0 }, + { "application/x-zoo", ".zoo", N_("Zoo (.zoo)"), 0 }, + { "application/zip", ".zip", N_("Zip (.zip)"), 0 }, + { NULL, NULL, NULL, 0 } +}; + +FrExtensionType file_ext_type[] = { + { ".7z", "application/x-7z-compressed" }, + { ".ace", "application/x-ace" }, + { ".alz", "application/x-alz" }, + { ".ar", "application/x-ar" }, + { ".arj", "application/x-arj" }, + { ".bin", "application/x-stuffit" }, + { ".bz", "application/x-bzip" }, + { ".bz2", "application/x-bzip" }, + { ".cab", "application/vnd.ms-cab-compressed" }, + { ".cbr", "application/x-cbr" }, + { ".cbz", "application/x-cbz" }, + { ".cpio", "application/x-cpio" }, + { ".deb", "application/x-deb" }, + { ".ear", "application/x-ear" }, + { ".exe", "application/x-ms-dos-executable" }, + { ".gz", "application/x-gzip" }, + { ".iso", "application/x-cd-image" }, + { ".jar", "application/x-java-archive" }, + { ".lha", "application/x-lha" }, + { ".lrz", "application/x-lrzip" }, + { ".lzh", "application/x-lha" }, + { ".lz", "application/x-lzip" }, + { ".lzma", "application/x-lzma" }, + { ".lzo", "application/x-lzop" }, + { ".rar", "application/x-rar" }, + { ".rpm", "application/x-rpm" }, + { ".rz", "application/x-rzip" }, + { ".sit", "application/x-stuffit" }, + { ".tar", "application/x-tar" }, + { ".tar.bz", "application/x-bzip-compressed-tar" }, + { ".tar.bz2", "application/x-bzip-compressed-tar" }, + { ".tar.gz", "application/x-compressed-tar" }, + { ".tar.lrz", "application/x-lrzip-compressed-tar" }, + { ".tar.lz", "application/x-lzip-compressed-tar" }, + { ".tar.lzma", "application/x-lzma-compressed-tar" }, + { ".tar.lzo", "application/x-lzop-compressed-tar" }, + { ".tar.7z", "application/x-7z-compressed-tar" }, + { ".tar.xz", "application/x-xz-compressed-tar" }, + { ".tar.Z", "application/x-tarz" }, + { ".taz", "application/x-tarz" }, + { ".tbz", "application/x-bzip-compressed-tar" }, + { ".tbz2", "application/x-bzip-compressed-tar" }, + { ".tgz", "application/x-compressed-tar" }, + { ".txz", "application/x-xz-compressed-tar" }, + { ".tlz", "application/x-lzip-compressed-tar" }, + { ".tzma", "application/x-lzma-compressed-tar" }, + { ".tzo", "application/x-lzop-compressed-tar" }, + { ".war", "application/x-war" }, + { ".xz", "application/x-xz" }, + { ".z", "application/x-gzip" }, + { ".Z", "application/x-compress" }, + { ".zip", "application/zip" }, + { ".zoo", "application/x-zoo" }, + { NULL, NULL } +}; + +int single_file_save_type[64]; +int save_type[64]; +int open_type[64]; +int create_type[64]; + +static const GOptionEntry options[] = { + { "add-to", 'a', 0, G_OPTION_ARG_STRING, &add_to, + N_("Add files to the specified archive and quit the program"), + N_("ARCHIVE") }, + + { "add", 'd', 0, G_OPTION_ARG_NONE, &add, + N_("Add files asking the name of the archive and quit the program"), + NULL }, + + { "extract-to", 'e', 0, G_OPTION_ARG_STRING, &extract_to, + N_("Extract archives to the specified folder and quit the program"), + N_("FOLDER") }, + + { "extract", 'f', 0, G_OPTION_ARG_NONE, &extract, + N_("Extract archives asking the destination folder and quit the program"), + NULL }, + + { "extract-here", 'h', 0, G_OPTION_ARG_NONE, &extract_here, + N_("Extract the contents of the archives in the archive folder and quit the program"), + NULL }, + + { "default-dir", '\0', 0, G_OPTION_ARG_STRING, &default_url, + N_("Default folder to use for the '--add' and '--extract' commands"), + N_("FOLDER") }, + + { "force", '\0', 0, G_OPTION_ARG_NONE, &ForceDirectoryCreation, + N_("Create destination folder without asking confirmation"), + NULL }, + + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args, + NULL, + NULL }, + + { NULL } +}; + + +/* -- Main -- */ + + +static guint startup_id = 0; + +/* argv[0] from main(); used as the command to restart the program */ +static const char *program_argv0 = NULL; + + +static gboolean +startup_cb (gpointer data) +{ + g_source_remove (startup_id); + startup_id = 0; + + initialize_data (); + prepare_app (); + + return FALSE; +} + + +static void +fr_save_state (EggSMClient *client, GKeyFile *state, gpointer user_data) +{ + /* discard command is automatically set by EggSMClient */ + + GList *window; + const char *argv[2] = { NULL }; + guint i; + + /* restart command */ + argv[0] = program_argv0; + argv[1] = NULL; + + egg_sm_client_set_restart_command (client, 1, argv); + + /* state */ + for (window = WindowList, i = 0; window; window = window->next, i++) { + FrWindow *session = window->data; + gchar *key; + + key = g_strdup_printf ("archive%d", i); + if ((session->archive == NULL) || (session->archive->file == NULL)) { + g_key_file_set_string (state, "Session", key, ""); + } else { + gchar *uri; + + uri = g_file_get_uri (session->archive->file); + g_key_file_set_string (state, "Session", key, uri); + g_free (uri); + } + g_free (key); + } + + g_key_file_set_integer (state, "Session", "archives", i); +} + +int +main (int argc, char **argv) +{ + GError *error = NULL; + EggSMClient *client = NULL; + GOptionContext *context = NULL; + + program_argv0 = argv[0]; + + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + context = g_option_context_new (N_("- Create and modify an archive")); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE); + + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + g_option_context_add_group (context, egg_sm_client_get_option_group ()); + + if (! g_option_context_parse (context, &argc, &argv, &error)) { + g_critical ("Failed to parse arguments: %s", error->message); + g_error_free (error); + g_option_context_free (context); + return EXIT_FAILURE; + } + + g_option_context_free (context); + + g_set_application_name (_("File Roller")); + gtk_window_set_default_icon_name ("file-roller"); + + client = egg_sm_client_get (); + g_signal_connect (client, "save-state", G_CALLBACK (fr_save_state), NULL); + + gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (), + PKG_DATA_DIR G_DIR_SEPARATOR_S "icons"); + + fr_stock_init (); + /* init_session (argv); */ + startup_id = g_idle_add (startup_cb, NULL); + gtk_main (); + release_data (); + + return 0; +} + +/* Initialize application data. */ + + +static void +initialize_data (void) +{ + eel_mateconf_monitor_add ("/apps/file-roller"); + eel_mateconf_monitor_add (PREF_CAJA_CLICK_POLICY); + + ProgramsCache = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); +} + +/* Free application data. */ + + +void +command_done (CommandData *cdata) +{ + if (cdata == NULL) + return; + + if ((cdata->temp_dir != NULL) && path_is_dir (cdata->temp_dir)) { + char *argv[4]; + + argv[0] = "rm"; + argv[1] = "-rf"; + argv[2] = cdata->temp_dir; + argv[3] = NULL; + g_spawn_sync (g_get_tmp_dir (), argv, NULL, + G_SPAWN_SEARCH_PATH, + NULL, NULL, + NULL, NULL, NULL, + NULL); + } + + g_free (cdata->command); + if (cdata->app != NULL) + g_object_unref (cdata->app); + path_list_free (cdata->file_list); + g_free (cdata->temp_dir); + if (cdata->process != NULL) + g_object_unref (cdata->process); + + CommandList = g_list_remove (CommandList, cdata); + g_free (cdata); +} + + +static void +release_data () +{ + g_hash_table_destroy (ProgramsCache); + + eel_global_client_free (); + + while (CommandList != NULL) { + CommandData *cdata = CommandList->data; + command_done (cdata); + } +} + +/* Create the windows. */ + + +static void +migrate_dir_from_to (const char *from_dir, + const char *to_dir) +{ + char *from_path; + char *to_path; + + from_path = get_home_relative_path (from_dir); + to_path = get_home_relative_path (to_dir); + + if (uri_is_dir (from_path) && ! uri_exists (to_path)) { + char *line; + char *e1; + char *e2; + + e1 = g_shell_quote (from_path); + e2 = g_shell_quote (to_path); + line = g_strdup_printf ("mv -f %s %s", e1, e2); + g_free (e1); + g_free (e2); + + g_spawn_command_line_sync (line, NULL, NULL, NULL, NULL); + g_free (line); + } + + g_free (from_path); + g_free (to_path); +} + + +static void +migrate_file_from_to (const char *from_file, + const char *to_file) +{ + char *from_path; + char *to_path; + + from_path = get_home_relative_path (from_file); + to_path = get_home_relative_path (to_file); + + if (uri_is_file (from_path) && ! uri_exists (to_path)) { + char *line; + char *e1; + char *e2; + + e1 = g_shell_quote (from_path); + e2 = g_shell_quote (to_path); + line = g_strdup_printf ("mv -f %s %s", e1, e2); + g_free (e1); + g_free (e2); + + g_spawn_command_line_sync (line, NULL, NULL, NULL, NULL); + g_free (line); + } + + g_free (from_path); + g_free (to_path); +} + + +static void +migrate_to_new_directories (void) +{ + migrate_dir_from_to (OLD_RC_OPTIONS_DIR, RC_OPTIONS_DIR); + migrate_file_from_to (OLD_RC_BOOKMARKS_FILE, RC_BOOKMARKS_FILE); + migrate_file_from_to (OLD_RC_RECENT_FILE, RC_RECENT_FILE); + + eel_mateconf_set_boolean (PREF_MIGRATE_DIRECTORIES, FALSE); +} + + +/* -- FrRegisteredCommand -- */ + + +FrRegisteredCommand * +fr_registered_command_new (GType command_type) +{ + FrRegisteredCommand *reg_com; + FrCommand *command; + const char **mime_types; + int i; + + reg_com = g_new0 (FrRegisteredCommand, 1); + reg_com->ref = 1; + reg_com->type = command_type; + reg_com->caps = g_ptr_array_new (); + reg_com->packages = g_ptr_array_new (); + + command = (FrCommand*) g_object_new (reg_com->type, NULL); + mime_types = fr_command_get_mime_types (command); + for (i = 0; mime_types[i] != NULL; i++) { + const char *mime_type; + FrMimeTypeCap *cap; + FrMimeTypePackages *packages; + + mime_type = get_static_string (mime_types[i]); + + cap = g_new0 (FrMimeTypeCap, 1); + cap->mime_type = mime_type; + cap->current_capabilities = fr_command_get_capabilities (command, mime_type, TRUE); + cap->potential_capabilities = fr_command_get_capabilities (command, mime_type, FALSE); + g_ptr_array_add (reg_com->caps, cap); + + packages = g_new0 (FrMimeTypePackages, 1); + packages->mime_type = mime_type; + packages->packages = fr_command_get_packages (command, mime_type); + g_ptr_array_add (reg_com->packages, packages); + } + + g_object_unref (command); + + return reg_com; +} + + +void +fr_registered_command_ref (FrRegisteredCommand *reg_com) +{ + reg_com->ref++; +} + + +void +fr_registered_command_unref (FrRegisteredCommand *reg_com) +{ + if (--(reg_com->ref) != 0) + return; + + g_ptr_array_foreach (reg_com->caps, (GFunc) g_free, NULL); + g_ptr_array_free (reg_com->caps, TRUE); + g_free (reg_com); +} + + +FrCommandCaps +fr_registered_command_get_capabilities (FrRegisteredCommand *reg_com, + const char *mime_type) +{ + int i; + + for (i = 0; i < reg_com->caps->len; i++) { + FrMimeTypeCap *cap; + + cap = g_ptr_array_index (reg_com->caps, i); + if (strcmp (mime_type, cap->mime_type) == 0) + return cap->current_capabilities; + } + + return FR_COMMAND_CAN_DO_NOTHING; +} + + +FrCommandCaps +fr_registered_command_get_potential_capabilities (FrRegisteredCommand *reg_com, + const char *mime_type) +{ + int i; + + if (mime_type == NULL) + return FR_COMMAND_CAN_DO_NOTHING; + + for (i = 0; i < reg_com->caps->len; i++) { + FrMimeTypeCap *cap; + + cap = g_ptr_array_index (reg_com->caps, i); + if ((cap->mime_type != NULL) && (strcmp (mime_type, cap->mime_type) == 0)) + return cap->potential_capabilities; + } + + return FR_COMMAND_CAN_DO_NOTHING; +} + + +void +register_command (GType command_type) +{ + if (Registered_Commands == NULL) + Registered_Commands = g_ptr_array_sized_new (5); + g_ptr_array_add (Registered_Commands, fr_registered_command_new (command_type)); +} + + +gboolean +unregister_command (GType command_type) +{ + int i; + + for (i = 0; i < Registered_Commands->len; i++) { + FrRegisteredCommand *command; + + command = g_ptr_array_index (Registered_Commands, i); + if (command->type == command_type) { + g_ptr_array_remove_index (Registered_Commands, i); + fr_registered_command_unref (command); + return TRUE; + } + } + + return FALSE; +} + + +static void +register_commands (void) +{ + /* The order here is important. Commands registered earlier have higher + * priority. However commands that can read and write a file format + * have higher priority over commands that can only read the same + * format, regardless of the registration order. */ + + register_command (FR_TYPE_COMMAND_TAR); + register_command (FR_TYPE_COMMAND_CFILE); + register_command (FR_TYPE_COMMAND_7Z); + register_command (FR_TYPE_COMMAND_DPKG); + + register_command (FR_TYPE_COMMAND_ACE); + register_command (FR_TYPE_COMMAND_ALZ); + register_command (FR_TYPE_COMMAND_AR); + register_command (FR_TYPE_COMMAND_ARJ); + register_command (FR_TYPE_COMMAND_CPIO); + register_command (FR_TYPE_COMMAND_ISO); + register_command (FR_TYPE_COMMAND_JAR); + register_command (FR_TYPE_COMMAND_LHA); + register_command (FR_TYPE_COMMAND_RAR); + register_command (FR_TYPE_COMMAND_RPM); + register_command (FR_TYPE_COMMAND_UNSTUFF); + register_command (FR_TYPE_COMMAND_ZIP); + register_command (FR_TYPE_COMMAND_LRZIP); + register_command (FR_TYPE_COMMAND_ZOO); +} + + +GType +get_command_type_from_mime_type (const char *mime_type, + FrCommandCaps requested_capabilities) +{ + int i; + + if (mime_type == NULL) + return 0; + + for (i = 0; i < Registered_Commands->len; i++) { + FrRegisteredCommand *command; + FrCommandCaps capabilities; + + command = g_ptr_array_index (Registered_Commands, i); + capabilities = fr_registered_command_get_capabilities (command, mime_type); + + /* the command must support all the requested capabilities */ + if (((capabilities ^ requested_capabilities) & requested_capabilities) == 0) + return command->type; + } + + return 0; +} + + +GType +get_preferred_command_for_mime_type (const char *mime_type, + FrCommandCaps requested_capabilities) +{ + int i; + + for (i = 0; i < Registered_Commands->len; i++) { + FrRegisteredCommand *command; + FrCommandCaps capabilities; + + command = g_ptr_array_index (Registered_Commands, i); + capabilities = fr_registered_command_get_potential_capabilities (command, mime_type); + + /* the command must support all the requested capabilities */ + if (((capabilities ^ requested_capabilities) & requested_capabilities) == 0) + return command->type; + } + + return 0; +} + + +void +update_registered_commands_capabilities (void) +{ + int i; + + g_hash_table_remove_all (ProgramsCache); + + for (i = 0; i < Registered_Commands->len; i++) { + FrRegisteredCommand *reg_com; + FrCommand *command; + int j; + + reg_com = g_ptr_array_index (Registered_Commands, i); + command = (FrCommand*) g_object_new (reg_com->type, NULL); + for (j = 0; j < reg_com->caps->len; j++) { + FrMimeTypeCap *cap = g_ptr_array_index (reg_com->caps, j); + + cap->current_capabilities = fr_command_get_capabilities (command, cap->mime_type, TRUE); + cap->potential_capabilities = fr_command_get_capabilities (command, cap->mime_type, FALSE); + } + + g_object_unref (command); + } +} + + +const char * +get_mime_type_from_extension (const char *ext) +{ + int i; + + if (ext == NULL) + return NULL; + + for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--) { + if (file_ext_type[i].ext == NULL) + continue; + if (strcasecmp (ext, file_ext_type[i].ext) == 0) + return get_static_string (file_ext_type[i].mime_type); + } + + return NULL; +} + + +const char * +get_archive_filename_extension (const char *filename) +{ + const char *ext; + int i; + + if (filename == NULL) + return NULL; + + ext = get_file_extension (filename); + if (ext == NULL) + return NULL; + + for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--) { + if (file_ext_type[i].ext == NULL) + continue; + if (strcasecmp (ext, file_ext_type[i].ext) == 0) + return ext; + } + + return NULL; +} + + +int +get_mime_type_index (const char *mime_type) +{ + int i; + + for (i = 0; mime_type_desc[i].mime_type != NULL; i++) + if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) + return i; + return -1; +} + + +static void +add_if_non_present (int *a, + int *n, + int o) +{ + int i; + + for (i = 0; i < *n; i++) { + if (a[i] == o) + return; + } + a[*n] = o; + *n = *n + 1; +} + + +static int +cmp_mime_type_by_extension (const void *p1, + const void *p2) +{ + int i1 = * (int*) p1; + int i2 = * (int*) p2; + + return strcmp (mime_type_desc[i1].default_ext, mime_type_desc[i2].default_ext); +} + + +static int +cmp_mime_type_by_description (const void *p1, + const void *p2) +{ + int i1 = * (int*) p1; + int i2 = * (int*) p2; + + return g_utf8_collate (_(mime_type_desc[i1].name), _(mime_type_desc[i2].name)); +} + + +static void +sort_mime_types (int *a, + int(*compar)(const void *, const void *)) +{ + int n = 0; + + while (a[n] != -1) + n++; + qsort (a, n, sizeof (int), compar); +} + + +void +sort_mime_types_by_extension (int *a) +{ + sort_mime_types (a, cmp_mime_type_by_extension); +} + + +void +sort_mime_types_by_description (int *a) +{ + sort_mime_types (a, cmp_mime_type_by_description); +} + + +static void +compute_supported_archive_types (void) +{ + int sf_i = 0, s_i = 0, o_i = 0, c_i = 0; + int i; + + for (i = 0; i < Registered_Commands->len; i++) { + FrRegisteredCommand *reg_com; + int j; + + reg_com = g_ptr_array_index (Registered_Commands, i); + for (j = 0; j < reg_com->caps->len; j++) { + FrMimeTypeCap *cap; + int idx; + + cap = g_ptr_array_index (reg_com->caps, j); + idx = get_mime_type_index (cap->mime_type); + if (idx < 0) { + g_warning ("mime type not recognized: %s", cap->mime_type); + continue; + } + mime_type_desc[idx].capabilities |= cap->current_capabilities; + if (cap->current_capabilities & FR_COMMAND_CAN_READ) + add_if_non_present (open_type, &o_i, idx); + if (cap->current_capabilities & FR_COMMAND_CAN_WRITE) { + if (cap->current_capabilities & FR_COMMAND_CAN_ARCHIVE_MANY_FILES) { + add_if_non_present (save_type, &s_i, idx); + if (cap->current_capabilities & FR_COMMAND_CAN_WRITE) + add_if_non_present (create_type, &c_i, idx); + } + add_if_non_present (single_file_save_type, &sf_i, idx); + } + } + } + + open_type[o_i] = -1; + save_type[s_i] = -1; + single_file_save_type[sf_i] = -1; + create_type[c_i] = -1; +} + + +static char * +get_uri_from_command_line (const char *path) +{ + GFile *file; + char *uri; + + file = g_file_new_for_commandline_arg (path); + uri = g_file_get_uri (file); + g_object_unref (file); + + return uri; +} + + +static void +fr_restore_session (EggSMClient *client) +{ + GKeyFile *state = NULL; + guint i; + + state = egg_sm_client_get_state_file (client); + + i = g_key_file_get_integer (state, "Session", "archives", NULL); + + for (; i > 0; i--) { + GtkWidget *window; + gchar *key, *archive; + + key = g_strdup_printf ("archive%d", i); + archive = g_key_file_get_string (state, "Session", key, NULL); + g_free (key); + + window = fr_window_new (); + gtk_widget_show (window); + if (strlen (archive)) + fr_window_archive_open (FR_WINDOW (window), archive, GTK_WINDOW (window)); + + g_free (archive); + } +} + +static void +prepare_app (void) +{ + char *uri; + char *extract_to_path = NULL; + char *add_to_path = NULL; + EggSMClient *client = NULL; + + /* create the config dir if necessary. */ + + uri = get_home_relative_uri (RC_DIR); + + if (uri_is_file (uri)) { /* before the mateconf port this was a file, now it's folder. */ + GFile *file; + + file = g_file_new_for_uri (uri); + g_file_delete (file, NULL, NULL); + g_object_unref (file); + } + + ensure_dir_exists (uri, 0700, NULL); + g_free (uri); + + if (eel_mateconf_get_boolean (PREF_MIGRATE_DIRECTORIES, TRUE)) + migrate_to_new_directories (); + + register_commands (); + compute_supported_archive_types (); + + client = egg_sm_client_get (); + if (egg_sm_client_is_resumed (client)) { + fr_restore_session (client); + return; + } + + /**/ + + if (remaining_args == NULL) { /* No archive specified. */ + gtk_widget_show (fr_window_new ()); + return; + } + + if (extract_to != NULL) + extract_to_path = get_uri_from_command_line (extract_to); + + if (add_to != NULL) + add_to_path = get_uri_from_command_line (add_to); + + if ((add_to != NULL) || (add == 1)) { /* Add files to an archive */ + GtkWidget *window; + GList *file_list = NULL; + const char *filename; + int i = 0; + + window = fr_window_new (); + if (default_url != NULL) + fr_window_set_default_dir (FR_WINDOW (window), default_url, TRUE); + + while ((filename = remaining_args[i++]) != NULL) + file_list = g_list_prepend (file_list, get_uri_from_command_line (filename)); + file_list = g_list_reverse (file_list); + + fr_window_new_batch (FR_WINDOW (window)); + fr_window_set_batch__add (FR_WINDOW (window), add_to_path, file_list); + fr_window_append_batch_action (FR_WINDOW (window), + FR_BATCH_ACTION_QUIT, + NULL, + NULL); + fr_window_start_batch (FR_WINDOW (window)); + } + else if ((extract_to != NULL) + || (extract == 1) + || (extract_here == 1)) { /* Extract all archives. */ + GtkWidget *window; + const char *archive; + int i = 0; + + window = fr_window_new (); + if (default_url != NULL) + fr_window_set_default_dir (FR_WINDOW (window), default_url, TRUE); + + fr_window_new_batch (FR_WINDOW (window)); + while ((archive = remaining_args[i++]) != NULL) { + char *archive_uri; + + archive_uri = get_uri_from_command_line (archive); + if (extract_here == 1) + fr_window_set_batch__extract_here (FR_WINDOW (window), + archive_uri); + else + fr_window_set_batch__extract (FR_WINDOW (window), + archive_uri, + extract_to_path); + g_free (archive_uri); + } + fr_window_append_batch_action (FR_WINDOW (window), + FR_BATCH_ACTION_QUIT, + NULL, + NULL); + + fr_window_start_batch (FR_WINDOW (window)); + } + else { /* Open each archive in a window */ + const char *filename = NULL; + + int i = 0; + while ((filename = remaining_args[i++]) != NULL) { + GtkWidget *window; + GFile *file; + char *uri; + + window = fr_window_new (); + gtk_widget_show (window); + + file = g_file_new_for_commandline_arg (filename); + uri = g_file_get_uri (file); + fr_window_archive_open (FR_WINDOW (window), uri, GTK_WINDOW (window)); + g_free (uri); + g_object_unref (file); + } + } + + g_free (add_to_path); + g_free (extract_to_path); +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..fa88514 --- /dev/null +++ b/src/main.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2008 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef MAIN_H +#define MAIN_H + + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> +#include "preferences.h" +#include "fr-process.h" +#include "fr-window.h" + + +typedef struct { + FrWindow *window; + FrProcess *process; + char *filename; + char *e_filename; + char *temp_dir; +} ViewerData; + +typedef struct { + FrWindow *window; + FrProcess *process; + char *command; + GAppInfo *app; + GList *file_list; + char *temp_dir; +} CommandData; + + +void viewer_done (ViewerData *vdata); +void command_done (CommandData *cdata); + + +extern GList *WindowList; +extern GList *CommandList; +extern gint ForceDirectoryCreation; +extern GHashTable *ProgramsCache; +extern GPtrArray *Registered_Commands; + +extern FrMimeTypeDescription mime_type_desc[]; +extern FrExtensionType file_ext_type[]; +extern int single_file_save_type[]; /* File types that can be saved when + * a single file is selected. + * Includes single file compressors + * such as gzip, compress, etc. */ +extern int save_type[]; /* File types that can be saved. */ +extern int open_type[]; /* File types that can be opened. */ +extern int create_type[]; /* File types that can be created. */ + +GType get_command_type_from_mime_type (const char *mime_type, + FrCommandCaps requested_capabilities); +GType get_preferred_command_for_mime_type (const char *mime_type, + FrCommandCaps requested_capabilities); +void update_registered_commands_capabilities (void); +const char * get_mime_type_from_extension (const char *ext); +const char * get_archive_filename_extension (const char *uri); +int get_mime_type_index (const char *mime_type); +void sort_mime_types_by_extension (int *a); +void sort_mime_types_by_description (int *a); + +#endif /* MAIN_H */ diff --git a/src/mateconf-utils.c b/src/mateconf-utils.c new file mode 100644 index 0000000..f851200 --- /dev/null +++ b/src/mateconf-utils.c @@ -0,0 +1,862 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +/* eel-mateconf-extensions.c - Stuff to make MateConf easier to use. + + Copyright (C) 2000, 2001 Eazel, Inc. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Ramiro Estrugo <[email protected]> +*/ + +/* Modified by Paolo Bacchilega <[email protected]> for File Roller. */ + +#include <config.h> +#include <string.h> +#include <errno.h> + +#include <mateconf/mateconf-client.h> +#include <mateconf/mateconf.h> +#include "mateconf-utils.h" +#include "gtk-utils.h" +#include "fr-error.h" + +static MateConfClient *global_mateconf_client = NULL; + + +void +eel_global_client_free (void) +{ + if (global_mateconf_client == NULL) { + return; + } + + g_object_unref (global_mateconf_client); + global_mateconf_client = NULL; +} + + +MateConfClient * +eel_mateconf_client_get_global (void) +{ + /* Initialize mateconf if needed */ + if (!mateconf_is_initialized ()) { + char *argv[] = { "eel-preferences", NULL }; + GError *error = NULL; + + if (!mateconf_init (1, argv, &error)) { + if (eel_mateconf_handle_error (&error)) { + return NULL; + } + } + } + + if (global_mateconf_client == NULL) + global_mateconf_client = mateconf_client_get_default (); + + return global_mateconf_client; +} + + +gboolean +eel_mateconf_handle_error (GError **error) +{ + static gboolean shown_dialog = FALSE; + + g_return_val_if_fail (error != NULL, FALSE); + + if (*error != NULL) { + g_warning ("MateConf error:\n %s", (*error)->message); + if (! shown_dialog) { + GtkWidget *d; + shown_dialog = TRUE; + d = _gtk_error_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + "MateConf error", + "MateConf error: %s\n" + "All further errors " + "shown only on terminal", + (*error)->message); + g_signal_connect (d, "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + gtk_widget_show (d); + } + + g_error_free (*error); + *error = NULL; + + return TRUE; + } + + return FALSE; +} + + +static gboolean +check_type (const char *key, + MateConfValue *val, + MateConfValueType t, + GError **err) +{ + if (val->type != t) { + g_set_error (err, + FR_ERROR, + errno, + "Type mismatch for key %s", + key); + return FALSE; + } else + return TRUE; +} + + +void +eel_mateconf_set_boolean (const char *key, + gboolean boolean_value) +{ + MateConfClient *client; + GError *error = NULL; + + g_return_if_fail (key != NULL); + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + mateconf_client_set_bool (client, key, boolean_value, &error); + eel_mateconf_handle_error (&error); +} + + +gboolean +eel_mateconf_get_boolean (const char *key, + gboolean def) +{ + GError *error = NULL; + gboolean result = def; + MateConfClient *client; + MateConfValue *val; + + g_return_val_if_fail (key != NULL, def); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, def); + + val = mateconf_client_get (client, key, &error); + + if (val != NULL) { + if (check_type (key, val, MATECONF_VALUE_BOOL, &error)) + result = mateconf_value_get_bool (val); + else + eel_mateconf_handle_error (&error); + mateconf_value_free (val); + + } else if (error != NULL) + eel_mateconf_handle_error (&error); + + return result; +} + + +void +eel_mateconf_set_integer (const char *key, + int int_value) +{ + MateConfClient *client; + GError *error = NULL; + + g_return_if_fail (key != NULL); + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + mateconf_client_set_int (client, key, int_value, &error); + eel_mateconf_handle_error (&error); +} + + +int +eel_mateconf_get_integer (const char *key, + int def) +{ + GError *error = NULL; + int result = def; + MateConfClient *client; + MateConfValue *val; + + g_return_val_if_fail (key != NULL, def); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, def); + + val = mateconf_client_get (client, key, &error); + + if (val != NULL) { + if (check_type (key, val, MATECONF_VALUE_INT, &error)) + result = mateconf_value_get_int (val); + else + eel_mateconf_handle_error (&error); + mateconf_value_free (val); + + } else if (error != NULL) + eel_mateconf_handle_error (&error); + + return result; +} + + +void +eel_mateconf_set_float (const char *key, + float float_value) +{ + MateConfClient *client; + GError *error = NULL; + + g_return_if_fail (key != NULL); + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + mateconf_client_set_float (client, key, float_value, &error); + eel_mateconf_handle_error (&error); +} + + +float +eel_mateconf_get_float (const char *key, + float def) +{ + GError *error = NULL; + float result = def; + MateConfClient *client; + MateConfValue *val; + + g_return_val_if_fail (key != NULL, def); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, def); + + val = mateconf_client_get (client, key, &error); + + if (val != NULL) { + if (check_type (key, val, MATECONF_VALUE_FLOAT, &error)) + result = mateconf_value_get_float (val); + else + eel_mateconf_handle_error (&error); + mateconf_value_free (val); + + } else if (error != NULL) + eel_mateconf_handle_error (&error); + + return result; +} + + +void +eel_mateconf_set_string (const char *key, + const char *string_value) +{ + MateConfClient *client; + GError *error = NULL; + + g_return_if_fail (key != NULL); + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + mateconf_client_set_string (client, key, string_value, &error); + eel_mateconf_handle_error (&error); +} + + +char * +eel_mateconf_get_string (const char *key, + const char *def) +{ + GError *error = NULL; + char *result; + MateConfClient *client; + char *val; + + if (def != NULL) + result = g_strdup (def); + else + result = NULL; + + g_return_val_if_fail (key != NULL, result); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, result); + + val = mateconf_client_get_string (client, key, &error); + + if (val != NULL) { + g_return_val_if_fail (error == NULL, result); + g_free (result); + result = g_strdup (val); + g_free (val); + } else if (error != NULL) + eel_mateconf_handle_error (&error); + + return result; +} + + +void +eel_mateconf_set_locale_string (const char *key, + const char *string_value) +{ + char *utf8; + + utf8 = g_locale_to_utf8 (string_value, -1, 0, 0, 0); + + if (utf8 != NULL) { + eel_mateconf_set_string (key, utf8); + g_free (utf8); + } +} + + +char * +eel_mateconf_get_locale_string (const char *key, + const char *def) +{ + char *utf8; + char *result; + + utf8 = eel_mateconf_get_string (key, def); + + if (utf8 == NULL) + return NULL; + + result = g_locale_from_utf8 (utf8, -1, 0, 0, 0); + g_free (utf8); + + return result; +} + + +void +eel_mateconf_set_string_list (const char *key, + const GSList *slist) +{ + MateConfClient *client; + GError *error; + + g_return_if_fail (key != NULL); + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + error = NULL; + mateconf_client_set_list (client, key, MATECONF_VALUE_STRING, + /* Need cast cause of MateConf api bug */ + (GSList *) slist, + &error); + eel_mateconf_handle_error (&error); +} + + +GSList * +eel_mateconf_get_string_list (const char *key) +{ + GSList *slist; + MateConfClient *client; + GError *error; + + g_return_val_if_fail (key != NULL, NULL); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, NULL); + + error = NULL; + slist = mateconf_client_get_list (client, key, MATECONF_VALUE_STRING, &error); + if (eel_mateconf_handle_error (&error)) { + slist = NULL; + } + + return slist; +} + + +GSList * +eel_mateconf_get_locale_string_list (const char *key) +{ + GSList *utf8_slist, *slist, *scan; + + utf8_slist = eel_mateconf_get_string_list (key); + + slist = NULL; + for (scan = utf8_slist; scan; scan = scan->next) { + char *utf8 = scan->data; + char *locale = g_locale_from_utf8 (utf8, -1, 0, 0, 0); + slist = g_slist_prepend (slist, locale); + } + + g_slist_foreach (utf8_slist, (GFunc) g_free, NULL); + g_slist_free (utf8_slist); + + return g_slist_reverse (slist); +} + + +void +eel_mateconf_set_locale_string_list (const char *key, + const GSList *string_list_value) +{ + GSList *utf8_slist; + const GSList *scan; + + utf8_slist = NULL; + for (scan = string_list_value; scan; scan = scan->next) { + char *locale = scan->data; + char *utf8 = g_locale_to_utf8 (locale, -1, 0, 0, 0); + utf8_slist = g_slist_prepend (utf8_slist, utf8); + } + + utf8_slist = g_slist_reverse (utf8_slist); + + eel_mateconf_set_string_list (key, utf8_slist); + + g_slist_foreach (utf8_slist, (GFunc) g_free, NULL); + g_slist_free (utf8_slist); +} + + +gboolean +eel_mateconf_is_default (const char *key) +{ + gboolean result; + MateConfValue *value; + GError *error = NULL; + + g_return_val_if_fail (key != NULL, FALSE); + + value = mateconf_client_get_without_default (eel_mateconf_client_get_global (), key, &error); + + if (eel_mateconf_handle_error (&error)) { + if (value != NULL) { + mateconf_value_free (value); + } + return FALSE; + } + + result = (value == NULL); + eel_mateconf_value_free (value); + return result; +} + + +gboolean +eel_mateconf_monitor_add (const char *directory) +{ + GError *error = NULL; + MateConfClient *client; + + g_return_val_if_fail (directory != NULL, FALSE); + + client = mateconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + mateconf_client_add_dir (client, + directory, + MATECONF_CLIENT_PRELOAD_NONE, + &error); + + if (eel_mateconf_handle_error (&error)) { + return FALSE; + } + + return TRUE; +} + + +gboolean +eel_mateconf_monitor_remove (const char *directory) +{ + GError *error = NULL; + MateConfClient *client; + + if (directory == NULL) { + return FALSE; + } + + client = mateconf_client_get_default (); + g_return_val_if_fail (client != NULL, FALSE); + + mateconf_client_remove_dir (client, + directory, + &error); + + if (eel_mateconf_handle_error (&error)) { + return FALSE; + } + + return TRUE; +} + + +void +eel_mateconf_preload_cache (const char *directory, + MateConfClientPreloadType preload_type) +{ + GError *error = NULL; + MateConfClient *client; + + if (directory == NULL) { + return; + } + + client = mateconf_client_get_default (); + g_return_if_fail (client != NULL); + + mateconf_client_preload (client, + directory, + preload_type, + &error); + + eel_mateconf_handle_error (&error); +} + + +void +eel_mateconf_suggest_sync (void) +{ + MateConfClient *client; + GError *error = NULL; + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + mateconf_client_suggest_sync (client, &error); + eel_mateconf_handle_error (&error); +} + + +MateConfValue* +eel_mateconf_get_value (const char *key) +{ + MateConfValue *value = NULL; + MateConfClient *client; + GError *error = NULL; + + g_return_val_if_fail (key != NULL, NULL); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, NULL); + + value = mateconf_client_get (client, key, &error); + + if (eel_mateconf_handle_error (&error)) { + if (value != NULL) { + mateconf_value_free (value); + value = NULL; + } + } + + return value; +} + + +MateConfValue* +eel_mateconf_get_default_value (const char *key) +{ + MateConfValue *value = NULL; + MateConfClient *client; + GError *error = NULL; + + g_return_val_if_fail (key != NULL, NULL); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, NULL); + + value = mateconf_client_get_default_from_schema (client, key, &error); + + if (eel_mateconf_handle_error (&error)) { + if (value != NULL) { + mateconf_value_free (value); + value = NULL; + } + } + + return value; +} + + +static int +eel_strcmp (const char *string_a, const char *string_b) +{ + /* FIXME bugzilla.eazel.com 5450: Maybe we need to make this + * treat 'NULL < ""', or have a flavor that does that. If we + * didn't have code that already relies on 'NULL == ""', I + * would change it right now. + */ + return strcmp (string_a == NULL ? "" : string_a, + string_b == NULL ? "" : string_b); +} + + +static gboolean +eel_str_is_equal (const char *string_a, const char *string_b) +{ + /* FIXME bugzilla.eazel.com 5450: Maybe we need to make this + * treat 'NULL != ""', or have a flavor that does that. If we + * didn't have code that already relies on 'NULL == ""', I + * would change it right now. + */ + return eel_strcmp (string_a, string_b) == 0; +} + + +static gboolean +simple_value_is_equal (const MateConfValue *a, + const MateConfValue *b) +{ + g_return_val_if_fail (a != NULL, FALSE); + g_return_val_if_fail (b != NULL, FALSE); + + switch (a->type) { + case MATECONF_VALUE_STRING: + return eel_str_is_equal (mateconf_value_get_string (a), + mateconf_value_get_string (b)); + break; + + case MATECONF_VALUE_INT: + return mateconf_value_get_int (a) == + mateconf_value_get_int (b); + break; + + case MATECONF_VALUE_FLOAT: + return mateconf_value_get_float (a) == + mateconf_value_get_float (b); + break; + + case MATECONF_VALUE_BOOL: + return mateconf_value_get_bool (a) == + mateconf_value_get_bool (b); + break; + default: + g_assert_not_reached (); + break; + } + + return FALSE; +} + + +gboolean +eel_mateconf_value_is_equal (const MateConfValue *a, + const MateConfValue *b) +{ + GSList *node_a; + GSList *node_b; + + if (a == NULL && b == NULL) { + return TRUE; + } + + if (a == NULL || b == NULL) { + return FALSE; + } + + if (a->type != b->type) { + return FALSE; + } + + switch (a->type) { + case MATECONF_VALUE_STRING: + case MATECONF_VALUE_INT: + case MATECONF_VALUE_FLOAT: + case MATECONF_VALUE_BOOL: + return simple_value_is_equal (a, b); + break; + + case MATECONF_VALUE_LIST: + if (mateconf_value_get_list_type (a) != + mateconf_value_get_list_type (b)) { + return FALSE; + } + + node_a = mateconf_value_get_list (a); + node_b = mateconf_value_get_list (b); + + if (node_a == NULL && node_b == NULL) { + return TRUE; + } + + if (g_slist_length (node_a) != + g_slist_length (node_b)) { + return FALSE; + } + + for (; + node_a != NULL && node_b != NULL; + node_a = node_a->next, node_b = node_b->next) { + g_assert (node_a->data != NULL); + g_assert (node_b->data != NULL); + if (!simple_value_is_equal (node_a->data, node_b->data)) { + return FALSE; + } + } + + return TRUE; + default: + /* FIXME: pair ? */ + g_assert (0); + break; + } + + g_assert_not_reached (); + return FALSE; +} + + +void +eel_mateconf_value_free (MateConfValue *value) +{ + if (value == NULL) { + return; + } + + mateconf_value_free (value); +} + + +guint +eel_mateconf_notification_add (const char *key, + MateConfClientNotifyFunc notification_callback, + gpointer callback_data) +{ + guint notification_id; + MateConfClient *client; + GError *error = NULL; + + g_return_val_if_fail (key != NULL, EEL_MATECONF_UNDEFINED_CONNECTION); + g_return_val_if_fail (notification_callback != NULL, EEL_MATECONF_UNDEFINED_CONNECTION); + + client = eel_mateconf_client_get_global (); + g_return_val_if_fail (client != NULL, EEL_MATECONF_UNDEFINED_CONNECTION); + + notification_id = mateconf_client_notify_add (client, + key, + notification_callback, + callback_data, + NULL, + &error); + + if (eel_mateconf_handle_error (&error)) { + if (notification_id != EEL_MATECONF_UNDEFINED_CONNECTION) { + mateconf_client_notify_remove (client, notification_id); + notification_id = EEL_MATECONF_UNDEFINED_CONNECTION; + } + } + + return notification_id; +} + + +void +eel_mateconf_notification_remove (guint notification_id) +{ + MateConfClient *client; + + if (notification_id == EEL_MATECONF_UNDEFINED_CONNECTION) { + return; + } + + client = eel_mateconf_client_get_global (); + g_return_if_fail (client != NULL); + + mateconf_client_notify_remove (client, notification_id); +} + + +GSList * +eel_mateconf_value_get_string_list (const MateConfValue *value) +{ + GSList *result; + const GSList *slist; + const GSList *node; + const char *string; + const MateConfValue *next_value; + + if (value == NULL) { + return NULL; + } + + g_return_val_if_fail (value->type == MATECONF_VALUE_LIST, NULL); + g_return_val_if_fail (mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING, NULL); + + slist = mateconf_value_get_list (value); + result = NULL; + for (node = slist; node != NULL; node = node->next) { + next_value = node->data; + g_return_val_if_fail (next_value != NULL, NULL); + g_return_val_if_fail (next_value->type == MATECONF_VALUE_STRING, NULL); + string = mateconf_value_get_string (next_value); + result = g_slist_append (result, g_strdup (string)); + } + return result; +} + + +void +eel_mateconf_value_set_string_list (MateConfValue *value, + const GSList *string_list) +{ + const GSList *node; + MateConfValue *next_value; + GSList *value_list; + + g_return_if_fail (value->type == MATECONF_VALUE_LIST); + g_return_if_fail (mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING); + + value_list = NULL; + for (node = string_list; node != NULL; node = node->next) { + next_value = mateconf_value_new (MATECONF_VALUE_STRING); + mateconf_value_set_string (next_value, node->data); + value_list = g_slist_append (value_list, next_value); + } + + mateconf_value_set_list (value, value_list); + + for (node = value_list; node != NULL; node = node->next) { + mateconf_value_free (node->data); + } + g_slist_free (value_list); +} diff --git a/src/mateconf-utils.h b/src/mateconf-utils.h new file mode 100644 index 0000000..1f24b68 --- /dev/null +++ b/src/mateconf-utils.h @@ -0,0 +1,137 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +/* eel-mateconf-extensions.h - Stuff to make MateConf easier to use. + + Copyright (C) 2000, 2001 Eazel, Inc. + + The Mate Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Mate Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Mate Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Authors: Ramiro Estrugo <[email protected]> +*/ + +/* Modified by Paolo Bacchilega <[email protected]> for File Roller. */ + +#ifndef MATECONF_UTILS_H +#define MATECONF_UTILS_H + +#include <glib.h> +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> + +G_BEGIN_DECLS + +#define EEL_MATECONF_UNDEFINED_CONNECTION 0 + +MateConfClient *eel_mateconf_client_get_global (void); + +void eel_global_client_free (void); + +gboolean eel_mateconf_handle_error (GError **error); + +gboolean eel_mateconf_get_boolean (const char *key, + gboolean def_val); + +void eel_mateconf_set_boolean (const char *key, + gboolean value); + +int eel_mateconf_get_integer (const char *key, + int def_val); + +void eel_mateconf_set_integer (const char *key, + int value); + +float eel_mateconf_get_float (const char *key, + float def_val); + +void eel_mateconf_set_float (const char *key, + float value); + +char * eel_mateconf_get_string (const char *key, + const char *def_val); + +void eel_mateconf_set_string (const char *key, + const char *value); + +char * eel_mateconf_get_locale_string (const char *key, + const char *def_val); + +void eel_mateconf_set_locale_string (const char *key, + const char *value); + +GSList * eel_mateconf_get_string_list (const char *key); + +void eel_mateconf_set_string_list (const char *key, + const GSList *string_list_value); + +GSList * eel_mateconf_get_locale_string_list(const char *key); + +void eel_mateconf_set_locale_string_list(const char *key, + const GSList *string_list_value); + +gboolean eel_mateconf_is_default (const char *key); + +gboolean eel_mateconf_monitor_add (const char *directory); + +gboolean eel_mateconf_monitor_remove (const char *directory); + +void eel_mateconf_preload_cache (const char *directory, + MateConfClientPreloadType preload_type); + +void eel_mateconf_suggest_sync (void); + +MateConfValue* eel_mateconf_get_value (const char *key); + +MateConfValue* eel_mateconf_get_default_value (const char *key); + +gboolean eel_mateconf_value_is_equal (const MateConfValue *a, + const MateConfValue *b); + +void eel_mateconf_value_free (MateConfValue *value); + +guint eel_mateconf_notification_add (const char *key, + MateConfClientNotifyFunc notification_callback, + gpointer callback_data); + +void eel_mateconf_notification_remove (guint notification_id); + +GSList * eel_mateconf_value_get_string_list (const MateConfValue *value); + +void eel_mateconf_value_set_string_list (MateConfValue *value, + const GSList *string_list); + +G_END_DECLS + +#endif /* MATECONF_UTILS_H */ diff --git a/src/mkdtemp.c b/src/mkdtemp.c new file mode 100644 index 0000000..1508102 --- /dev/null +++ b/src/mkdtemp.c @@ -0,0 +1,201 @@ +/* Copyright (C) 1999, 2001-2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* Extracted from misc/mkdtemp.c and sysdeps/posix/tempname.c. */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* Specification. */ +#include "mkdtemp.h" + +#include <errno.h> +#ifndef __set_errno +# define __set_errno(Val) errno = (Val) +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <stdio.h> +#ifndef TMP_MAX +# define TMP_MAX 238328 +#endif + +#if HAVE_STDINT_H_WITH_UINTMAX || _LIBC +# include <stdint.h> +#endif + +#if HAVE_INTTYPES_H_WITH_UINTMAX || _LIBC +# include <inttypes.h> +#endif + +#if HAVE_UNISTD_H || _LIBC +# include <unistd.h> +#endif + +#if HAVE_GETTIMEOFDAY || _LIBC +# if HAVE_SYS_TIME_H || _LIBC +# include <sys/time.h> +# endif +#else +# if HAVE_TIME_H || _LIBC +# include <time.h> +# endif +#endif + +#include <sys/stat.h> +#if STAT_MACROS_BROKEN +# undef S_ISDIR +#endif +#if !defined S_ISDIR && defined S_IFDIR +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !S_IRUSR && S_IREAD +# define S_IRUSR S_IREAD +#endif +#if !S_IRUSR +# define S_IRUSR 00400 +#endif +#if !S_IWUSR && S_IWRITE +# define S_IWUSR S_IWRITE +#endif +#if !S_IWUSR +# define S_IWUSR 00200 +#endif +#if !S_IXUSR && S_IEXEC +# define S_IXUSR S_IEXEC +#endif +#if !S_IXUSR +# define S_IXUSR 00100 +#endif + +#if !_LIBC +# define __getpid getpid +# define __gettimeofday gettimeofday +# define __mkdir mkdir +#endif + +/* Use the widest available unsigned type if uint64_t is not + available. The algorithm below extracts a number less than 62**6 + (approximately 2**35.725) from uint64_t, so ancient hosts where + uintmax_t is only 32 bits lose about 3.725 bits of randomness, + which is better than not having mkstemp at all. */ +#if !defined UINT64_MAX && !defined uint64_t +# define uint64_t uintmax_t +#endif + +/* These are the characters used in temporary filenames. */ +static const char letters[] = +"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + +/* Generate a temporary file name based on TMPL. TMPL must match the + rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed + does not exist at the time of the call to __gen_tempname. TMPL is + overwritten with the result. + + KIND is: + __GT_DIR: create a directory, which will be mode 0700. + + We use a clever algorithm to get hard-to-predict names. */ +static int +gen_tempname (tmpl) + char *tmpl; +{ + int len; + char *XXXXXX; + static uint64_t value; + uint64_t random_time_bits; + int count, fd = -1; + int save_errno = errno; + + len = strlen (tmpl); + if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX")) + { + __set_errno (EINVAL); + return -1; + } + + /* This is where the Xs start. */ + XXXXXX = &tmpl[len - 6]; + + /* Get some more or less random data. */ +#ifdef RANDOM_BITS + RANDOM_BITS (random_time_bits); +#else +# if HAVE_GETTIMEOFDAY || _LIBC + { + struct timeval tv; + __gettimeofday (&tv, NULL); + random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; + } +# else + random_time_bits = time (NULL); +# endif +#endif + value += random_time_bits ^ __getpid (); + + for (count = 0; count < TMP_MAX; value += 7777, ++count) + { + uint64_t v = value; + + /* Fill in the random bits. */ + XXXXXX[0] = letters[v % 62]; + v /= 62; + XXXXXX[1] = letters[v % 62]; + v /= 62; + XXXXXX[2] = letters[v % 62]; + v /= 62; + XXXXXX[3] = letters[v % 62]; + v /= 62; + XXXXXX[4] = letters[v % 62]; + v /= 62; + XXXXXX[5] = letters[v % 62]; + + fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); + + if (fd >= 0) + { + __set_errno (save_errno); + return fd; + } + else if (errno != EEXIST) + return -1; + } + + /* We got out of the loop because we ran out of combinations to try. */ + __set_errno (EEXIST); + return -1; +} + +/* Generate a unique temporary directory from TEMPLATE. + The last six characters of TEMPLATE must be "XXXXXX"; + they are replaced with a string that makes the filename unique. + The directory is created, mode 700, and its name is returned. + (This function comes from OpenBSD.) */ +char * +mkdtemp (template) + char *template; +{ + if (gen_tempname (template)) + return NULL; + else + return template; +} diff --git a/src/mkdtemp.h b/src/mkdtemp.h new file mode 100644 index 0000000..dbc47ac --- /dev/null +++ b/src/mkdtemp.h @@ -0,0 +1,40 @@ +/* Creating a private temporary directory. + Copyright (C) 2001 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PARAMS +# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES +# define PARAMS(Args) Args +# else +# define PARAMS(Args) () +# endif +#endif + +#if HAVE_MKDTEMP + +/* Get mkdtemp() declaration. */ +#include <stdlib.h> + +#else + +/* Create a unique temporary directory from TEMPLATE. + The last six characters of TEMPLATE must be "XXXXXX"; + they are replaced with a string that makes the directory name unique. + Returns TEMPLATE, or a null pointer if it cannot get a unique name. + The directory is created mode 700. */ +extern char * mkdtemp PARAMS ((char *template)); + +#endif diff --git a/src/open-file.c b/src/open-file.c new file mode 100644 index 0000000..d182d07 --- /dev/null +++ b/src/open-file.c @@ -0,0 +1,87 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001-2008 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include "open-file.h" +#include "file-utils.h" + + +OpenFile* +open_file_new (const char *path, + const char *extracted_path, + const char *temp_dir) +{ + OpenFile *ofile; + + ofile = g_new0 (OpenFile, 1); + ofile->path = g_strdup (path); + ofile->extracted_uri = g_filename_to_uri (extracted_path, NULL, NULL); + if (! uri_exists (ofile->extracted_uri)) { + open_file_free (ofile); + return NULL; + } + ofile->temp_dir = g_strdup (temp_dir); + ofile->last_modified = get_file_mtime (ofile->extracted_uri); + + return ofile; +} + + +void +open_file_free (OpenFile *ofile) +{ + if (ofile == NULL) + return; + if (ofile->monitor != NULL) + g_object_unref (ofile->monitor); + g_free (ofile->path); + g_free (ofile->extracted_uri); + g_free (ofile->temp_dir); + g_free (ofile); +} + + +OpenFile * +open_file_copy (OpenFile *src) +{ + OpenFile *ofile; + + ofile = g_new0 (OpenFile, 1); + ofile->path = g_strdup (src->path); + ofile->extracted_uri = g_strdup (src->extracted_uri); + ofile->temp_dir = g_strdup (src->temp_dir); + ofile->last_modified = src->last_modified; + + return ofile; +} + + +GType +open_file_get_type (void) +{ + static GType type = 0; + + if (type == 0) + type = g_boxed_type_register_static ("FROpenFile", (GBoxedCopyFunc) open_file_copy, (GBoxedFreeFunc) open_file_free); + + return type; +} diff --git a/src/open-file.h b/src/open-file.h new file mode 100644 index 0000000..605f25e --- /dev/null +++ b/src/open-file.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001-2008 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef OPEN_FILE_H +#define OPEN_FILE_H + +#include <glib.h> +#include <glib-object.h> +#include <gio/gio.h> +#include <time.h> + +typedef struct { + char *path; + char *extracted_uri; + char *temp_dir; + time_t last_modified; + GFileMonitor *monitor; +} OpenFile; + +#define FR_TYPE_OPEN_FILE (open_file_get_type ()) + +GType open_file_get_type (void); +OpenFile * open_file_new (const char *path, + const char *extracted_path, + const char *temp_dir); +OpenFile * open_file_copy (OpenFile *src); +void open_file_free (OpenFile *ofile); + +#endif /* OPEN_FILE_H */ diff --git a/src/preferences.c b/src/preferences.c new file mode 100644 index 0000000..6ca6d70 --- /dev/null +++ b/src/preferences.c @@ -0,0 +1,268 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <mateconf/mateconf-client.h> +#include "typedefs.h" +#include "preferences.h" +#include "main.h" +#include "file-utils.h" +#include "mateconf-utils.h" +#include "fr-window.h" + + +#define DIALOG_PREFIX "/apps/file-roller/dialogs/" + + +typedef struct { + int i_value; + char *s_value; +} EnumStringTable; + + +static int +get_enum_from_string (EnumStringTable *table, + const char *s_value) +{ + int i; + + /* return the first value if s_value is invalid */ + + if (s_value == NULL) + return table[0].i_value; + + for (i = 0; table[i].s_value != NULL; i++) + if (strcmp (s_value, table[i].s_value) == 0) + return table[i].i_value; + + return table[0].i_value; +} + + +static char * +get_string_from_enum (EnumStringTable *table, + int i_value) +{ + int i; + + for (i = 0; table[i].s_value != NULL; i++) + if (i_value == table[i].i_value) + return table[i].s_value; + + /* return the first value if i_value is invalid */ + + return table[0].s_value; +} + + +/* --------------- */ + + +static EnumStringTable sort_method_table [] = { + { FR_WINDOW_SORT_BY_NAME, "name" }, + { FR_WINDOW_SORT_BY_SIZE, "size" }, + { FR_WINDOW_SORT_BY_TYPE, "type" }, + { FR_WINDOW_SORT_BY_TIME, "time" }, + { FR_WINDOW_SORT_BY_PATH, "path" }, + { 0, NULL } +}; + +static EnumStringTable sort_type_table [] = { + { GTK_SORT_ASCENDING, "ascending" }, + { GTK_SORT_DESCENDING, "descending" }, + { 0, NULL } +}; + +static EnumStringTable list_mode_table [] = { + { FR_WINDOW_LIST_MODE_FLAT, "all_files" }, + { FR_WINDOW_LIST_MODE_AS_DIR, "as_folder" }, + { 0, NULL } +}; + +static EnumStringTable compression_level_table [] = { + { FR_COMPRESSION_VERY_FAST, "very_fast" }, + { FR_COMPRESSION_FAST, "fast" }, + { FR_COMPRESSION_NORMAL, "normal" }, + { FR_COMPRESSION_MAXIMUM, "maximum" }, + { 0, NULL } +}; + + +/* --------------- */ + + +FrWindowSortMethod +preferences_get_sort_method (void) +{ + char *s_value; + int i_value; + + s_value = eel_mateconf_get_string (PREF_LIST_SORT_METHOD, "name"); + i_value = get_enum_from_string (sort_method_table, s_value); + g_free (s_value); + + return (FrWindowSortMethod) i_value; +} + + +void +preferences_set_sort_method (FrWindowSortMethod i_value) +{ + char *s_value; + + s_value = get_string_from_enum (sort_method_table, i_value); + eel_mateconf_set_string (PREF_LIST_SORT_METHOD, s_value); +} + + +GtkSortType +preferences_get_sort_type (void) +{ + char *s_value; + int i_value; + + s_value = eel_mateconf_get_string (PREF_LIST_SORT_TYPE, "ascending"); + i_value = get_enum_from_string (sort_type_table, s_value); + g_free (s_value); + + return (GtkSortType) i_value; +} + + +void +preferences_set_sort_type (GtkSortType i_value) +{ + char *s_value; + + s_value = get_string_from_enum (sort_type_table, i_value); + eel_mateconf_set_string (PREF_LIST_SORT_TYPE, s_value); +} + + +FrWindowListMode +preferences_get_list_mode (void) +{ + char *s_value; + int i_value; + + s_value = eel_mateconf_get_string (PREF_LIST_MODE, "as_folder"); + i_value = get_enum_from_string (list_mode_table, s_value); + g_free (s_value); + + return (FrWindowListMode) i_value; +} + + +void +preferences_set_list_mode (FrWindowListMode i_value) +{ + char *s_value; + + s_value = get_string_from_enum (list_mode_table, i_value); + eel_mateconf_set_string (PREF_LIST_MODE, s_value); +} + + +FrCompression +preferences_get_compression_level (void) +{ + char *s_value; + int i_value; + + s_value = eel_mateconf_get_string (PREF_ADD_COMPRESSION_LEVEL, "normal"); + i_value = get_enum_from_string (compression_level_table, s_value); + g_free (s_value); + + return (FrCompression) i_value; +} + + +void +preferences_set_compression_level (FrCompression i_value) +{ + char *s_value; + + s_value = get_string_from_enum (compression_level_table, i_value); + eel_mateconf_set_string (PREF_ADD_COMPRESSION_LEVEL, s_value); +} + + +static void +set_dialog_property_int (const char *dialog, + const char *property, + int value) +{ + char *key; + + key = g_strconcat (DIALOG_PREFIX, dialog, "/", property, NULL); + eel_mateconf_set_integer (key, value); + g_free (key); +} + + +void +pref_util_save_window_geometry (GtkWindow *window, + const char *dialog) +{ + int x, y, width, height; + + gtk_window_get_position (window, &x, &y); + set_dialog_property_int (dialog, "x", x); + set_dialog_property_int (dialog, "y", y); + + gtk_window_get_size (window, &width, &height); + set_dialog_property_int (dialog, "width", width); + set_dialog_property_int (dialog, "height", height); +} + + +static int +get_dialog_property_int (const char *dialog, + const char *property) +{ + char *key; + int value; + + key = g_strconcat (DIALOG_PREFIX, dialog, "/", property, NULL); + value = eel_mateconf_get_integer (key, -1); + g_free (key); + + return value; +} + + +void +pref_util_restore_window_geometry (GtkWindow *window, + const char *dialog) +{ + int x, y, width, height; + + x = get_dialog_property_int (dialog, "x"); + y = get_dialog_property_int (dialog, "y"); + width = get_dialog_property_int (dialog, "width"); + height = get_dialog_property_int (dialog, "height"); + + if (width != -1 && height != 1) + gtk_window_set_default_size (window, width, height); + + gtk_window_present (window); +} diff --git a/src/preferences.h b/src/preferences.h new file mode 100644 index 0000000..6a9224f --- /dev/null +++ b/src/preferences.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001, 2003 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef PREFERENCES_H +#define PREFERENCES_H + +#include <gtk/gtk.h> +#include "typedefs.h" +#include "fr-window.h" + +#define PREF_LIST_SORT_METHOD "/apps/file-roller/listing/sort_method" +#define PREF_LIST_SORT_TYPE "/apps/file-roller/listing/sort_type" +#define PREF_LIST_MODE "/apps/file-roller/listing/list_mode" +#define PREF_LIST_SHOW_TYPE "/apps/file-roller/listing/show_type" +#define PREF_LIST_SHOW_SIZE "/apps/file-roller/listing/show_size" +#define PREF_LIST_SHOW_TIME "/apps/file-roller/listing/show_time" +#define PREF_LIST_SHOW_PATH "/apps/file-roller/listing/show_path" +#define PREF_LIST_USE_MIME_ICONS "/apps/file-roller/listing/use_mime_icons" +#define PREF_NAME_COLUMN_WIDTH "/apps/file-roller/listing/name_column_width" + +#define PREF_UI_WINDOW_WIDTH "/apps/file-roller/ui/window_width" +#define PREF_UI_WINDOW_HEIGHT "/apps/file-roller/ui/window_height" +#define PREF_UI_SIDEBAR_WIDTH "/apps/file-roller/ui/sidebar_width" +#define PREF_UI_HISTORY_LEN "/apps/file-roller/ui/history_len" +#define PREF_UI_TOOLBAR "/apps/file-roller/ui/view_toolbar" +#define PREF_UI_STATUSBAR "/apps/file-roller/ui/view_statusbar" +#define PREF_UI_FOLDERS "/apps/file-roller/ui/view_folders" + +#define PREF_EDIT_EDITORS "/apps/file-roller/general/editors" +#define PREF_ADD_COMPRESSION_LEVEL "/apps/file-roller/general/compression_level" +#define PREF_ENCRYPT_HEADER "/apps/file-roller/general/encrypt_header" +#define PREF_MIGRATE_DIRECTORIES "/apps/file-roller/general/migrate_directories" + +#define PREF_EXTRACT_OVERWRITE "/apps/file-roller/dialogs/extract/overwrite" +#define PREF_EXTRACT_SKIP_NEWER "/apps/file-roller/dialogs/extract/skip_newer" +#define PREF_EXTRACT_RECREATE_FOLDERS "/apps/file-roller/dialogs/extract/recreate_folders" + +#define PREF_ADD_CURRENT_FOLDER "/apps/file-roller/dialogs/add/current_folder" +#define PREF_ADD_FILENAME "/apps/file-roller/dialogs/add/filename" +#define PREF_ADD_INCLUDE_FILES "/apps/file-roller/dialogs/add/include_files" +#define PREF_ADD_EXCLUDE_FILES "/apps/file-roller/dialogs/add/exclude_files" +#define PREF_ADD_EXCLUDE_FOLDERS "/apps/file-roller/dialogs/add/exclude_folders" +#define PREF_ADD_UPDATE "/apps/file-roller/dialogs/add/update" +#define PREF_ADD_RECURSIVE "/apps/file-roller/dialogs/add/recursive" +#define PREF_ADD_NO_SYMLINKS "/apps/file-roller/dialogs/add/no_symlinks" + +#define PREF_BATCH_ADD_DEFAULT_EXTENSION "/apps/file-roller/dialogs/batch-add/default_extension" +#define PREF_BATCH_OTHER_OPTIONS "/apps/file-roller/dialogs/batch-add/other_options" +#define PREF_BATCH_VOLUME_SIZE "/apps/file-roller/dialogs/batch-add/volume_size" + +#define PREF_DESKTOP_ICON_THEME "/desktop/mate/file_views/icon_theme" +#define PREF_DESKTOP_MENUS_HAVE_TEAROFF "/desktop/mate/interface/menus_have_tearoff" +#define PREF_DESKTOP_MENUBAR_DETACHABLE "/desktop/mate/interface/menubar_detachable" +#define PREF_DESKTOP_TOOLBAR_DETACHABLE "/desktop/mate/interface/toolbar_detachable" +#define PREF_CAJA_CLICK_POLICY "/apps/caja/preferences/click_policy" + +FrWindowSortMethod preferences_get_sort_method (void); +void preferences_set_sort_method (FrWindowSortMethod i_value); +GtkSortType preferences_get_sort_type (void); +void preferences_set_sort_type (GtkSortType i_value); +FrWindowListMode preferences_get_list_mode (void); +void preferences_set_list_mode (FrWindowListMode i_value); +FrCompression preferences_get_compression_level (void); +void preferences_set_compression_level (FrCompression i_value); +void pref_util_save_window_geometry (GtkWindow *window, + const char *dialog); +void pref_util_restore_window_geometry (GtkWindow *window, + const char *dialog); + +#endif /* PREFERENCES_H */ diff --git a/src/sh/Makefile.am b/src/sh/Makefile.am new file mode 100644 index 0000000..25f0f89 --- /dev/null +++ b/src/sh/Makefile.am @@ -0,0 +1,5 @@ +shdir = $(libexecdir)/$(PACKAGE) +sh_DATA = isoinfo.sh + +EXTRA_DIST = $(sh_DATA) +-include $(top_srcdir)/git.mk diff --git a/src/sh/isoinfo.sh b/src/sh/isoinfo.sh new file mode 100644 index 0000000..8bcdf35 --- /dev/null +++ b/src/sh/isoinfo.sh @@ -0,0 +1,27 @@ +filename=$2 + +JOLIET=true +ROCK_RIDGE=true + +ISOINFO=`isoinfo -d -i "$filename"` +if echo $ISOINFO | grep "NO Joliet present" >/dev/null 2>&1; then + JOLIET=false +fi +if echo $ISOINFO | grep "NO Rock Ridge present" >/dev/null 2>&1; then + ROCK_RIDGE=false +fi + +iso_extensions="" +if test $ROCK_RIDGE = true; then + iso_extensions="-R" +elif test $JOLIET = true; then + iso_extensions="-J" +fi + +if test "x$3" = x-x; then + file_to_extract=$4 + outfile=$5 + isoinfo $iso_extensions -i "$filename" -x "$file_to_extract" > "$outfile" +else + isoinfo $iso_extensions -i "$filename" -l +fi diff --git a/src/typedefs.h b/src/typedefs.h new file mode 100644 index 0000000..36f2c90 --- /dev/null +++ b/src/typedefs.h @@ -0,0 +1,122 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2001 The Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef TYPEDEFS_H +#define TYPEDEFS_H + +#define MEGABYTE (1024 * 1024) + +#define RC_DIR ".mate2/file-roller" +#define RC_BOOKMARKS_FILE ".mate2/file-roller/bookmarks" +#define RC_RECENT_FILE ".mate2/file-roller/recents" +#define RC_OPTIONS_DIR ".mate2/file-roller/options" + +#define OLD_RC_BOOKMARKS_FILE ".file-roller/bookmarks" +#define OLD_RC_RECENT_FILE ".file-roller/recents" +#define OLD_RC_OPTIONS_DIR ".file-roller/options" + +typedef enum { /*< skip >*/ + FR_WINDOW_SORT_BY_NAME = 0, + FR_WINDOW_SORT_BY_SIZE = 1, + FR_WINDOW_SORT_BY_TYPE = 2, + FR_WINDOW_SORT_BY_TIME = 3, + FR_WINDOW_SORT_BY_PATH = 4 +} FrWindowSortMethod; + +typedef enum { /*< skip >*/ + FR_WINDOW_LIST_MODE_FLAT, + FR_WINDOW_LIST_MODE_AS_DIR +} FrWindowListMode; + +typedef enum { + FR_COMPRESSION_VERY_FAST, + FR_COMPRESSION_FAST, + FR_COMPRESSION_NORMAL, + FR_COMPRESSION_MAXIMUM +} FrCompression; + +typedef enum { /*< skip >*/ + FR_PROC_ERROR_NONE, + FR_PROC_ERROR_GENERIC, + FR_PROC_ERROR_COMMAND_ERROR, + FR_PROC_ERROR_COMMAND_NOT_FOUND, + FR_PROC_ERROR_EXITED_ABNORMALLY, + FR_PROC_ERROR_SPAWN, + FR_PROC_ERROR_STOPPED, + FR_PROC_ERROR_ASK_PASSWORD, + FR_PROC_ERROR_MISSING_VOLUME, + FR_PROC_ERROR_IO_CHANNEL, + FR_PROC_ERROR_BAD_CHARSET, + FR_PROC_ERROR_UNSUPPORTED_FORMAT +} FrProcErrorType; + +typedef struct { + FrProcErrorType type; + int status; + GError *gerror; +} FrProcError; + +typedef enum { /*< skip >*/ + FR_COMMAND_CAN_DO_NOTHING = 0, + FR_COMMAND_CAN_READ = 1 << 0, + FR_COMMAND_CAN_WRITE = 1 << 1, + FR_COMMAND_CAN_ARCHIVE_MANY_FILES = 1 << 2, + FR_COMMAND_CAN_ENCRYPT = 1 << 3, + FR_COMMAND_CAN_ENCRYPT_HEADER = 1 << 4, + FR_COMMAND_CAN_CREATE_VOLUMES = 1 << 5 +} FrCommandCap; + +#define FR_COMMAND_CAN_READ_WRITE (FR_COMMAND_CAN_READ | FR_COMMAND_CAN_WRITE) + +typedef guint8 FrCommandCaps; + +typedef struct { + const char *mime_type; + FrCommandCaps current_capabilities; + FrCommandCaps potential_capabilities; +} FrMimeTypeCap; + +typedef struct { + const char *mime_type; + const char *packages; +} FrMimeTypePackages; + +typedef struct { + int ref; + GType type; + GPtrArray *caps; /* array of FrMimeTypeCap */ + GPtrArray *packages; /* array of FrMimeTypePackages */ +} FrRegisteredCommand; + +typedef struct { + const char *mime_type; + char *default_ext; + char *name; + FrCommandCaps capabilities; +} FrMimeTypeDescription; + +typedef struct { + char *ext; + const char *mime_type; +} FrExtensionType; + +#endif /* TYPEDEFS_H */ diff --git a/src/ui.h b/src/ui.h new file mode 100644 index 0000000..3287196 --- /dev/null +++ b/src/ui.h @@ -0,0 +1,396 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2004 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#ifndef UI_H +#define UI_H + + +#include "actions.h" +#include "fr-stock.h" + + +static GtkActionEntry action_entries[] = { + { "FileMenu", NULL, N_("_Archive") }, + { "EditMenu", NULL, N_("_Edit") }, + { "ViewMenu", NULL, N_("_View") }, + { "HelpMenu", NULL, N_("_Help") }, + { "ArrangeFilesMenu", NULL, N_("_Arrange Files") }, + /* Translators: this is the label for the "open recent file" sub-menu. */ + { "OpenRecentMenu", NULL, N_("Open _Recent") }, + + { "About", GTK_STOCK_ABOUT, + NULL, NULL, + N_("Information about the program"), + G_CALLBACK (activate_action_about) }, + { "AddFiles", FR_STOCK_ADD_FILES, + N_("_Add Files..."), NULL, + N_("Add files to the archive"), + G_CALLBACK (activate_action_add_files) }, + { "AddFiles_Toolbar", FR_STOCK_ADD_FILES, + N_("Add Files"), NULL, + N_("Add files to the archive"), + G_CALLBACK (activate_action_add_files) }, + { "AddFolder", FR_STOCK_ADD_FOLDER, + N_("Add a _Folder..."), NULL, + N_("Add a folder to the archive"), + G_CALLBACK (activate_action_add_folder) }, + { "AddFolder_Toolbar", FR_STOCK_ADD_FOLDER, + N_("Add Folder"), NULL, + N_("Add a folder to the archive"), + G_CALLBACK (activate_action_add_folder) }, + { "Close", GTK_STOCK_CLOSE, + NULL, NULL, + N_("Close the current archive"), + G_CALLBACK (activate_action_close) }, + { "Contents", GTK_STOCK_HELP, + N_("Contents"), "F1", + N_("Display the File Roller Manual"), + G_CALLBACK (activate_action_manual) }, + + { "Copy", GTK_STOCK_COPY, + NULL, NULL, + N_("Copy the selection"), + G_CALLBACK (activate_action_copy) }, + { "Cut", GTK_STOCK_CUT, + NULL, NULL, + N_("Cut the selection"), + G_CALLBACK (activate_action_cut) }, + { "Paste", GTK_STOCK_PASTE, + NULL, NULL, + N_("Paste the clipboard"), + G_CALLBACK (activate_action_paste) }, + { "Rename", NULL, + N_("_Rename..."), "F2", + N_("Rename the selection"), + G_CALLBACK (activate_action_rename) }, + { "Delete", GTK_STOCK_DELETE, + NULL, "Delete", + N_("Delete the selection from the archive"), + G_CALLBACK (activate_action_delete) }, + + { "CopyFolderFromSidebar", GTK_STOCK_COPY, + NULL, NULL, + N_("Copy the selection"), + G_CALLBACK (activate_action_copy_folder_from_sidebar) }, + { "CutFolderFromSidebar", GTK_STOCK_CUT, + NULL, NULL, + N_("Cut the selection"), + G_CALLBACK (activate_action_cut_folder_from_sidebar) }, + { "PasteFolderFromSidebar", GTK_STOCK_PASTE, + NULL, NULL, + N_("Paste the clipboard"), + G_CALLBACK (activate_action_paste_folder_from_sidebar) }, + { "RenameFolderFromSidebar", NULL, + N_("_Rename..."), "F2", + N_("Rename the selection"), + G_CALLBACK (activate_action_rename_folder_from_sidebar) }, + { "DeleteFolderFromSidebar", GTK_STOCK_DELETE, + NULL, NULL, + N_("Delete the selection from the archive"), + G_CALLBACK (activate_action_delete_folder_from_sidebar) }, + + { "DeselectAll", NULL, + N_("Dese_lect All"), "<shift><control>A", + N_("Deselect all files"), + G_CALLBACK (activate_action_deselect_all) }, + { "Extract", FR_STOCK_EXTRACT, + N_("_Extract..."), "<control>E", + N_("Extract files from the archive"), + G_CALLBACK (activate_action_extract) }, + { "ExtractFolderFromSidebar", FR_STOCK_EXTRACT, + N_("_Extract..."), NULL, + N_("Extract files from the archive"), + G_CALLBACK (activate_action_extract_folder_from_sidebar) }, + { "Extract_Toolbar", FR_STOCK_EXTRACT, + N_("Extract"), NULL, + N_("Extract files from the archive"), + G_CALLBACK (activate_action_extract) }, + { "Find", GTK_STOCK_FIND, + N_("Find..."), NULL, + NULL, + G_CALLBACK (activate_action_find) }, + + { "LastOutput", NULL, + N_("_Last Output"), NULL, + N_("View the output produced by the last executed command"), + G_CALLBACK (activate_action_last_output) }, + { "New", GTK_STOCK_NEW, + NC_("File", "New..."), NULL, + N_("Create a new archive"), + G_CALLBACK (activate_action_new) }, + { "Open", GTK_STOCK_OPEN, + NC_("File", "Open..."), NULL, + N_("Open archive"), + G_CALLBACK (activate_action_open) }, + { "Open_Toolbar", GTK_STOCK_OPEN, + NULL, NULL, + N_("Open archive"), + G_CALLBACK (activate_action_open) }, + { "OpenSelection", NULL, + N_("_Open With..."), NULL, + N_("Open selected files with an application"), + G_CALLBACK (activate_action_open_with) }, + { "Password", NULL, + N_("Pass_word..."), NULL, + N_("Specify a password for this archive"), + G_CALLBACK (activate_action_password) }, + { "Properties", GTK_STOCK_PROPERTIES, + NULL, "<alt>Return", + N_("Show archive properties"), + G_CALLBACK (activate_action_properties) }, + { "Reload", GTK_STOCK_REFRESH, + NULL, "<control>R", + N_("Reload current archive"), + G_CALLBACK (activate_action_reload) }, + { "SaveAs", GTK_STOCK_SAVE_AS, + NC_("File", "Save As..."), NULL, + N_("Save the current archive with a different name"), + G_CALLBACK (activate_action_save_as) }, + { "SelectAll", GTK_STOCK_SELECT_ALL, + NULL, "<control>A", + N_("Select all files"), + G_CALLBACK (activate_action_select_all) }, + { "Stop", GTK_STOCK_STOP, + NULL, "Escape", + N_("Stop current operation"), + G_CALLBACK (activate_action_stop) }, + { "TestArchive", NULL, + N_("_Test Integrity"), NULL, + N_("Test whether the archive contains errors"), + G_CALLBACK (activate_action_test_archive) }, + { "ViewSelection", GTK_STOCK_OPEN, + NULL, NULL, + N_("Open the selected file"), + G_CALLBACK (activate_action_view_or_open) }, + { "ViewSelection_Toolbar", GTK_STOCK_OPEN, + NULL, NULL, + N_("Open the selected file"), + G_CALLBACK (activate_action_view_or_open) }, + { "OpenFolder", GTK_STOCK_OPEN, + NULL, NULL, + N_("Open the selected folder"), + G_CALLBACK (activate_action_open_folder) }, + { "OpenFolderFromSidebar", GTK_STOCK_OPEN, + NULL, NULL, + N_("Open the selected folder"), + G_CALLBACK (activate_action_open_folder_from_sidebar) }, + + { "GoBack", GTK_STOCK_GO_BACK, + NULL, NULL, + N_("Go to the previous visited location"), + G_CALLBACK (activate_action_go_back) }, + { "GoForward", GTK_STOCK_GO_FORWARD, + NULL, NULL, + N_("Go to the next visited location"), + G_CALLBACK (activate_action_go_forward) }, + { "GoUp", GTK_STOCK_GO_UP, + NULL, NULL, + N_("Go up one level"), + G_CALLBACK (activate_action_go_up) }, + { "GoHome", GTK_STOCK_HOME, + NULL, NULL, + /* Translators: the home location is the home folder. */ + N_("Go to the home location"), + G_CALLBACK (activate_action_go_home) }, +}; +static guint n_action_entries = G_N_ELEMENTS (action_entries); + + +static GtkToggleActionEntry action_toggle_entries[] = { + { "ViewToolbar", NULL, + N_("_Toolbar"), NULL, + N_("View the main toolbar"), + G_CALLBACK (activate_action_view_toolbar), + TRUE }, + { "ViewStatusbar", NULL, + N_("Stat_usbar"), NULL, + N_("View the statusbar"), + G_CALLBACK (activate_action_view_statusbar), + TRUE }, + { "SortReverseOrder", NULL, + N_("_Reversed Order"), NULL, + N_("Reverse the list order"), + G_CALLBACK (activate_action_sort_reverse_order), + FALSE }, + { "ViewFolders", NULL, + N_("_Folders"), "F9", + N_("View the folders pane"), + G_CALLBACK (activate_action_view_folders), + FALSE }, +}; +static guint n_action_toggle_entries = G_N_ELEMENTS (action_toggle_entries); + + +static GtkRadioActionEntry view_as_entries[] = { + { "ViewAllFiles", NULL, + N_("View All _Files"), "<control>1", + " ", FR_WINDOW_LIST_MODE_FLAT }, + { "ViewAsFolder", NULL, + N_("View as a F_older"), "<control>2", + " ", FR_WINDOW_LIST_MODE_AS_DIR }, +}; +static guint n_view_as_entries = G_N_ELEMENTS (view_as_entries); + + +static GtkRadioActionEntry sort_by_entries[] = { + { "SortByName", NULL, + N_("by _Name"), NULL, + N_("Sort file list by name"), FR_WINDOW_SORT_BY_NAME }, + { "SortBySize", NULL, + N_("by _Size"), NULL, + N_("Sort file list by file size"), FR_WINDOW_SORT_BY_SIZE }, + { "SortByType", NULL, + N_("by T_ype"), NULL, + N_("Sort file list by type"), FR_WINDOW_SORT_BY_TYPE }, + { "SortByDate", NULL, + N_("by _Date Modified"), NULL, + N_("Sort file list by modification time"), FR_WINDOW_SORT_BY_TIME }, + { "SortByLocation", NULL, + /* Translators: this is the "sort by file location" menu item */ + N_("by _Location"), NULL, + /* Translators: location is the file location */ + N_("Sort file list by location"), FR_WINDOW_SORT_BY_PATH }, +}; +static guint n_sort_by_entries = G_N_ELEMENTS (sort_by_entries); + + +static const gchar *ui_info = +"<ui>" +" <menubar name='MenuBar'>" +" <menu name='Archive' action='FileMenu'>" +" <menuitem action='New'/>" +" <menuitem action='Open'/>" +" <menu name='OpenRecentMenu' action='OpenRecentMenu'>" +" <menuitem action='Open'/>" +" </menu>" +" <menuitem action='SaveAs'/>" +" <separator/>" +" <menuitem action='Extract'/>" +" <menuitem action='TestArchive'/>" +" <separator/>" +" <menuitem action='Properties'/>" +" <separator/>" +" <menuitem action='Close'/>" +" </menu>" +" <menu action='EditMenu'>" +" <menuitem action='Cut'/>" +" <menuitem action='Copy'/>" +" <menuitem action='Paste'/>" +" <menuitem action='Rename'/>" +" <menuitem action='Delete'/>" +" <separator/>" +" <menuitem action='SelectAll'/>" +" <menuitem action='DeselectAll'/>" +" <separator/>" +" <menuitem action='Find'/>" +" <separator/>" +" <menuitem action='AddFiles'/>" +" <menuitem action='AddFolder'/>" +" <separator/>" +" <menuitem action='Password'/>" +" </menu>" +" <menu action='ViewMenu'>" +" <menuitem action='ViewToolbar'/>" +" <menuitem action='ViewStatusbar'/>" +" <menuitem action='ViewFolders'/>" +" <separator/>" +" <menuitem action='ViewAllFiles'/>" +" <menuitem action='ViewAsFolder'/>" +/*" <separator/>" +" <menu action='ArrangeFilesMenu'>" +" <menuitem action='SortByName'/>" +" <menuitem action='SortBySize'/>" +" <menuitem action='SortByType'/>" +" <menuitem action='SortByDate'/>" +" <menuitem action='SortByLocation'/>" +" <separator/>" +" <menuitem action='SortReverseOrder'/>" +" </menu>"*/ +" <separator/>" +" <menuitem action='LastOutput'/>" +" <separator/>" +" <menuitem action='Stop'/>" +" <menuitem action='Reload'/>" +" </menu>" +" <menu action='HelpMenu'>" +" <menuitem action='Contents'/>" +" <menuitem action='About'/>" +" </menu>" +" </menubar>" +" <toolbar name='ToolBar'>" +" <toolitem action='New'/>" +" <separator/>" +" <toolitem action='Extract_Toolbar'/>" +" <separator/>" +" <toolitem action='AddFiles_Toolbar'/>" +" <toolitem action='AddFolder_Toolbar'/>" +" <separator/>" +" <toolitem action='Stop'/>" +" </toolbar>" +" <toolbar name='LocationBar'>" +" <toolitem action='GoBack'/>" +" <toolitem action='GoForward'/>" +" <toolitem action='GoUp'/>" +" <toolitem action='GoHome'/>" +" </toolbar>" +" <popup name='FilePopupMenu'>" +" <menuitem action='ViewSelection'/>" +" <menuitem action='OpenSelection'/>" +" <separator/>" +" <menuitem action='Extract'/>" +" <separator/>" +" <menuitem action='Cut'/>" +" <menuitem action='Copy'/>" +" <menuitem action='Paste'/>" +" <menuitem action='Rename'/>" +" <menuitem action='Delete'/>" +" </popup>" +" <popup name='FolderPopupMenu'>" +" <menuitem action='OpenFolder'/>" +" <separator/>" +" <menuitem action='Extract'/>" +" <separator/>" +" <menuitem action='Cut'/>" +" <menuitem action='Copy'/>" +" <menuitem action='Paste'/>" +" <menuitem action='Rename'/>" +" <menuitem action='Delete'/>" +" </popup>" +" <popup name='AddMenu'>" +" <menuitem action='AddFiles'/>" +" <menuitem action='AddFolder'/>" +" </popup>" +" <popup name='SidebarFolderPopupMenu'>" +" <menuitem action='OpenFolderFromSidebar'/>" +" <separator/>" +" <menuitem action='ExtractFolderFromSidebar'/>" +" <separator/>" +" <menuitem action='CutFolderFromSidebar'/>" +" <menuitem action='CopyFolderFromSidebar'/>" +" <menuitem action='PasteFolderFromSidebar'/>" +" <menuitem action='RenameFolderFromSidebar'/>" +" <menuitem action='DeleteFolderFromSidebar'/>" +" </popup>" +"</ui>"; + + +#endif /* UI_H */ |