[file-roller: 64/123] libarchive: implemented the add_files function



commit cb385203d33ba28a4ca48b756ea9982279149153
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sat Jul 28 20:11:14 2012 +0200

    libarchive: implemented the add_files function

 src/fr-archive-libarchive.c |  551 ++++++++++++++++++++++++++++++++++++++++++-
 src/fr-archive.c            |   16 +-
 src/gio-utils.c             |    2 +
 src/glib-utils.c            |   21 ++
 src/glib-utils.h            |    1 +
 5 files changed, 580 insertions(+), 11 deletions(-)
---
diff --git a/src/fr-archive-libarchive.c b/src/fr-archive-libarchive.c
index 02c06fa..c2e6a72 100644
--- a/src/fr-archive-libarchive.c
+++ b/src/fr-archive-libarchive.c
@@ -20,6 +20,9 @@
  */
 
 #include <config.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
 #include <glib.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
@@ -32,6 +35,10 @@
 #include "typedefs.h"
 
 
+#define BUFFER_SIZE (10 * 1024)
+#define FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY ("standard::*,time::*,access::*,unix::*")
+
+
 G_DEFINE_TYPE (FrArchiveLibarchive, fr_archive_libarchive, FR_TYPE_ARCHIVE)
 
 
@@ -80,7 +87,7 @@ fr_archive_libarchive_get_capabilities (FrArchive  *archive,
 					const char *mime_type,
 					gboolean    check_command)
 {
-	return FR_ARCHIVE_CAN_STORE_MANY_FILES | FR_ARCHIVE_CAN_READ;
+	return FR_ARCHIVE_CAN_STORE_MANY_FILES | FR_ARCHIVE_CAN_READ | FR_ARCHIVE_CAN_WRITE;
 }
 
 
@@ -95,7 +102,6 @@ fr_archive_libarchive_get_packages (FrArchive  *archive,
 /* -- load -- */
 
 
-#define BUFFER_SIZE_FOR_READING (10 * 1024)
 #define LOAD_DATA(x) ((LoadData *)(x))
 
 
@@ -258,7 +264,7 @@ fr_archive_libarchive_load (FrArchive           *archive,
 						       callback,
 						       user_data,
 						       fr_archive_load);
-	load_data->buffer_size = BUFFER_SIZE_FOR_READING;
+	load_data->buffer_size = BUFFER_SIZE;
 	load_data->buffer = g_new (char, load_data->buffer_size);
 
 	g_simple_async_result_set_op_res_gpointer (load_data->result, load_data, NULL);
@@ -269,11 +275,484 @@ fr_archive_libarchive_load (FrArchive           *archive,
 }
 
 
+/* -- save data -- */
+
+
+#define SAVE_DATA(x) ((SaveData *)(x))
+
+
+typedef struct {
+	LoadData       parent;
+	GFile         *tmp_file;
+	GOutputStream *ostream;
+	GHashTable    *usernames;
+	GHashTable    *groupnames;
+} SaveData;
+
+
+static void
+save_data_init (SaveData *save_data)
+{
+	save_data->usernames = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free);
+	save_data->groupnames = g_hash_table_new_full (g_int64_hash, g_int64_equal, g_free, g_free);
+}
+
+
+static void
+save_data_free (SaveData *save_data)
+{
+	g_hash_table_unref (save_data->groupnames);
+	g_hash_table_unref (save_data->usernames);
+	_g_object_unref (save_data->ostream);
+	_g_object_unref (save_data->tmp_file);
+	load_data_free (LOAD_DATA (save_data));
+}
+
+
+static int
+save_data_open (struct archive *a,
+	        void           *client_data)
+{
+	SaveData *save_data = client_data;
+	LoadData *load_data = LOAD_DATA (save_data);
+	GFile    *parent;
+	char     *name;
+
+	/* FIXME: use a better temp filename */
+	parent = g_file_get_parent (fr_archive_get_file (load_data->archive));
+	name = _g_filename_get_random (16);
+	save_data->tmp_file = g_file_get_child (parent, name);
+	save_data->ostream = (GOutputStream *) g_file_create (save_data->tmp_file, G_FILE_CREATE_NONE, load_data->cancellable, &load_data->error);
+
+	g_free (name);
+	g_object_unref (parent);
+
+	return (save_data->ostream != NULL) ? ARCHIVE_OK : ARCHIVE_FATAL;
+}
+
+
+static ssize_t
+save_data_write (struct archive *a,
+		 void           *client_data,
+		 const void     *buff,
+		 size_t          n)
+{
+	SaveData *save_data = client_data;
+	LoadData *load_data = LOAD_DATA (save_data);
+
+	return g_output_stream_write (save_data->ostream, buff, n, load_data->cancellable, &load_data->error);
+}
+
+
+static int
+save_data_close (struct archive *a,
+		 void           *client_data)
+{
+	SaveData *save_data = client_data;
+	LoadData *load_data = LOAD_DATA (save_data);
+
+	if (save_data->ostream != NULL)
+		g_output_stream_close (save_data->ostream, load_data->cancellable, &load_data->error);
+
+	if (load_data->error == NULL)
+		g_file_move (save_data->tmp_file,
+			     fr_archive_get_file (load_data->archive),
+			     G_FILE_COPY_OVERWRITE | G_FILE_COPY_TARGET_DEFAULT_PERMS,
+			     load_data->cancellable,
+			     NULL,
+			     NULL,
+			     &load_data->error);
+	else
+		g_file_delete (save_data->tmp_file, NULL, NULL);
+
+	return 0;
+}
+
+
 /* -- add -- */
 
 
+typedef struct {
+	SaveData       parent;
+	GList         *file_list;
+	GFile         *base_dir;
+	char          *dest_dir;
+	gboolean       update;
+	gboolean       recursive;
+	char          *password;
+	gboolean       encrypt_header;
+	FrCompression  compression;
+	guint          volume_size;
+	GHashTable    *files_to_add;
+	int            n_files_to_add;
+	void          *buffer;
+	gsize          buffer_size;
+} AddData;
+
+
+static void
+add_data_free (AddData *add_data)
+{
+	g_free (add_data->buffer);
+	g_hash_table_unref (add_data->files_to_add);
+	g_free (add_data->password);
+	g_free (add_data->dest_dir);
+	_g_object_unref (add_data->base_dir);
+	_g_string_list_free (add_data->file_list);
+	save_data_free (SAVE_DATA (add_data));
+}
+
+
+typedef struct {
+	GFile *file;
+	char  *pathname;
+} FileToAdd;
+
+
+static FileToAdd *
+file_to_add_new (AddData    *add_data,
+		 const char *relative_name,
+		 const char *full_name)
+{
+	FileToAdd *file_to_add;
+
+	file_to_add = g_new (FileToAdd, 1);
+	file_to_add->file = g_file_get_child (add_data->base_dir, relative_name);
+	file_to_add->pathname = g_strdup (full_name);
+
+	return file_to_add;
+}
+
+
+static void
+file_to_add_free (FileToAdd *file_to_add)
+{
+	g_object_unref (file_to_add->file);
+	g_free (file_to_add->pathname);
+	g_free (file_to_add);
+}
+
+
+static gint64 *
+_g_int64_pointer_new (gint64 i)
+{
+	gint64 *p;
+
+	p = g_new (gint64, 1);
+	*p = i;
+
+	return p;
+}
+
+
+static gboolean
+_archive_entry_copy_file_info (struct archive_entry *entry,
+			       GFileInfo            *info,
+			       SaveData             *save_data)
+{
+	int     filetype;
+	char   *username;
+	char   *groupname;
+	gint64  id;
+
+	switch (g_file_info_get_file_type (info)) {
+	case G_FILE_TYPE_REGULAR:
+		filetype = AE_IFREG;
+		break;
+	case G_FILE_TYPE_DIRECTORY:
+		filetype = AE_IFDIR;
+		break;
+	case G_FILE_TYPE_SYMBOLIC_LINK:
+		filetype = AE_IFLNK;
+		break;
+	default:
+		return FALSE;
+		break;
+	}
+	archive_entry_set_filetype (entry, filetype);
+
+	archive_entry_set_atime (entry,
+				 g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS),
+				 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) * 1000);
+	archive_entry_set_ctime (entry,
+				 g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED),
+				 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CREATED_USEC) * 1000);
+	archive_entry_set_mtime (entry,
+				 g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED),
+				 g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) * 1000);
+	archive_entry_unset_birthtime (entry);
+	archive_entry_set_dev (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE));
+	archive_entry_set_gid (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID));
+	archive_entry_set_uid (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID));
+	archive_entry_set_ino64 (entry, g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE));
+	archive_entry_set_mode (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE));
+	archive_entry_set_nlink (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK));
+	archive_entry_set_rdev (entry, g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV));
+	archive_entry_set_size (entry, g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
+	if (filetype == AE_IFLNK) {
+		g_print ("symlink: %s\n", g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
+		archive_entry_set_symlink (entry, g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET));
+	}
+
+	/* username */
+
+	id = archive_entry_uid (entry);
+	username = g_hash_table_lookup (save_data->usernames, &id);
+	if (username == NULL) {
+		struct passwd *pwd = getpwuid (id);
+		if (pwd != NULL) {
+			username = g_strdup (pwd->pw_name);
+			g_hash_table_insert (save_data->usernames, _g_int64_pointer_new (id), username);
+		}
+	}
+	if (username != NULL)
+		archive_entry_set_uname (entry, username);
+
+	/* groupname */
+
+	id = archive_entry_gid (entry);
+	groupname = g_hash_table_lookup (save_data->groupnames, &id);
+	if (groupname == NULL) {
+		struct group *grp = getgrgid (id);
+		if (grp != NULL) {
+			groupname = g_strdup (grp->gr_name);
+			g_hash_table_insert (save_data->groupnames, _g_int64_pointer_new (id), groupname);
+		}
+	}
+	if (groupname != NULL)
+		archive_entry_set_gname (entry, groupname);
+
+	return TRUE;
+}
+
+
+static void
+_archive_write_set_format_from_filename (struct archive *a,
+					 GFile          *file)
+{
+	char       *uri;
+	const char *ext;
+
+	uri = g_file_get_uri (file);
+	ext = _g_filename_get_extension (uri);
+
+	if (ext == NULL) {
+		/* defaults to an uncompressed tar */
+		ext = ".tar";
+	}
+
+	if ((strcmp (ext, ".tar.bz2") == 0) || (strcmp (ext, ".tbz2") == 0)) {
+		archive_write_add_filter_bzip2 (a);
+		archive_write_set_format_ustar (a);
+	}
+	else if ((strcmp (ext, ".tar.Z") == 0) || (strcmp (ext, ".taz") == 0)) {
+		archive_write_add_filter_compress (a);
+		archive_write_set_format_ustar (a);
+	}
+	else if ((strcmp (ext, ".tar.gz") == 0) || (strcmp (ext, ".tgz") == 0)) {
+		archive_write_add_filter_gzip (a);
+		archive_write_set_format_ustar (a);
+	}
+	else if ((strcmp (ext, ".tar.lz") == 0) || (strcmp (ext, ".tlz") == 0)) {
+		archive_write_add_filter_lzip (a);
+		archive_write_set_format_ustar (a);
+	}
+	else if ((strcmp (ext, ".tar.lzma") == 0) || (strcmp (ext, ".tzma") == 0)) {
+		archive_write_add_filter_lzma (a);
+		archive_write_set_format_ustar (a);
+	}
+	else if ((strcmp (ext, ".tar.xz") == 0) || (strcmp (ext, ".txz") == 0)) {
+		archive_write_add_filter_xz (a);
+		archive_write_set_format_ustar (a);
+	}
+	else {
+		/* defaults to an uncompressed tar */
+		archive_write_add_filter_none (a);
+		archive_write_set_format_ustar (a);
+	}
+
+	/* FIXME: add all the supported formats */
+
+	g_free (uri);
+}
+
+
+typedef enum {
+	WRITE_ACTION_ABORT,
+	WRITE_ACTION_SKIP_ENTRY,
+	WRITE_ACTION_WRITE_ENTRY
+} WriteAction;
+
+
+static WriteAction
+_archive_write_file (struct archive       *b,
+		     AddData              *add_data,
+		     FileToAdd            *file_to_add,
+		     struct archive_entry *r_entry,
+		     GCancellable         *cancellable)
+{
+	LoadData             *load_data = LOAD_DATA (add_data);
+	GFileInfo            *info;
+	struct archive_entry *w_entry;
+
+	/* write the file header */
+
+	info = g_file_query_info (file_to_add->file,
+				  FILE_ATTRIBUTES_NEEDED_BY_ARCHIVE_ENTRY,
+				  G_FILE_QUERY_INFO_NONE,
+				  cancellable,
+				  &load_data->error);
+	if (info == NULL)
+		return WRITE_ACTION_ABORT;
+
+	w_entry = archive_entry_new ();
+	if (! _archive_entry_copy_file_info (w_entry, info, SAVE_DATA (add_data))) {
+		archive_entry_free (w_entry);
+		g_object_unref (info);
+		return WRITE_ACTION_SKIP_ENTRY;
+	}
+
+	/* honor the update flag */
+	if (add_data->update && (r_entry != NULL) && (archive_entry_mtime (w_entry) < archive_entry_mtime (r_entry))) {
+		archive_entry_free (w_entry);
+		g_object_unref (info);
+		return WRITE_ACTION_WRITE_ENTRY;
+	}
+
+	archive_entry_set_pathname (w_entry, file_to_add->pathname);
+	archive_write_header (b, w_entry);
+
+	/* write the file data */
+
+	if (g_file_info_get_file_type (info) == G_FILE_TYPE_REGULAR) {
+		GInputStream *istream;
+
+		istream = (GInputStream *) g_file_read (file_to_add->file, cancellable, &load_data->error);
+		if (istream != NULL) {
+			gssize bytes_read;
+
+			while ((bytes_read = g_input_stream_read (istream, add_data->buffer, add_data->buffer_size, cancellable, &load_data->error)) > 0) {
+				archive_write_data (b, add_data->buffer, bytes_read);
+				fr_archive_progress_inc_completed_bytes (load_data->archive, bytes_read);
+			}
+
+			g_object_unref (istream);
+		}
+	}
+
+	add_data->n_files_to_add--;
+
+	archive_entry_free (w_entry);
+	g_object_unref (info);
+
+	return (load_data->error == NULL) ? WRITE_ACTION_SKIP_ENTRY : WRITE_ACTION_ABORT;
+}
+
+
 static void
-fr_archive_libarchive_add_files (FrArchive           *base,
+add_to_archive_thread  (GSimpleAsyncResult *result,
+			GObject            *object,
+			GCancellable       *cancellable)
+{
+	AddData              *add_data;
+	LoadData             *load_data;
+	struct archive       *a, *b;
+	struct archive_entry *r_entry;
+	int                   ra, rb;
+	GList                *remaining_files;
+	GList                *scan;
+
+	add_data = g_simple_async_result_get_op_res_gpointer (result);
+	load_data = LOAD_DATA (add_data);
+
+	fr_archive_progress_set_total_files (load_data->archive, add_data->n_files_to_add);
+
+	b = archive_write_new ();
+	_archive_write_set_format_from_filename (b, fr_archive_get_file (load_data->archive));
+	archive_write_open (b, add_data, save_data_open, save_data_write, save_data_close);
+
+	a = archive_read_new ();
+	archive_read_support_filter_all (a);
+	archive_read_support_format_all (a);
+	archive_read_open (a, load_data, load_data_open, load_data_read, load_data_close);
+
+	while ((load_data->error == NULL) && (ra = archive_read_next_header (a, &r_entry)) == ARCHIVE_OK) {
+		WriteAction  action;
+		const char  *pathname;
+		FileToAdd   *file_to_add;
+		int64_t      offset;
+
+		if (g_cancellable_is_cancelled (cancellable))
+			break;
+
+		fr_archive_progress_inc_completed_files (load_data->archive, 1);
+
+		action = WRITE_ACTION_WRITE_ENTRY;
+
+		pathname = archive_entry_pathname (r_entry);
+		file_to_add = g_hash_table_lookup (add_data->files_to_add, pathname);
+		if (file_to_add != NULL) {
+			action = _archive_write_file (b, add_data, file_to_add, r_entry, cancellable);
+			g_hash_table_remove (add_data->files_to_add, pathname);
+		}
+
+		if (action == WRITE_ACTION_WRITE_ENTRY) {
+			const void *buffer;
+			size_t      buffer_size;
+
+			rb = archive_write_header (b, r_entry);
+			if (rb != ARCHIVE_OK)
+				break;
+
+			switch (archive_entry_filetype (r_entry)) {
+			case AE_IFREG:
+				while ((ra = archive_read_data_block (a, &buffer, &buffer_size, &offset)) == ARCHIVE_OK) {
+					archive_write_data (b, buffer, buffer_size);
+					fr_archive_progress_inc_completed_bytes (load_data->archive, buffer_size);
+				}
+
+				if (ra != ARCHIVE_EOF)
+					load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (a));
+				break;
+
+			default:
+				break;
+			}
+		}
+	}
+
+	/* allow to add files to a new archive */
+	if (g_error_matches (load_data->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
+		g_clear_error (&load_data->error);
+		ra = ARCHIVE_EOF;
+	}
+
+	remaining_files = g_hash_table_get_values (add_data->files_to_add);
+	for (scan = remaining_files; (load_data->error == NULL) && scan; scan = scan->next) {
+		FileToAdd *file_to_add = scan->data;
+
+		if (_archive_write_file (b, add_data, file_to_add, NULL, cancellable) == WRITE_ACTION_ABORT)
+			break;
+	}
+
+	if ((load_data->error == NULL) && (ra != ARCHIVE_EOF))
+		load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (a));
+	if ((load_data->error == NULL) && (rb != ARCHIVE_OK))
+		load_data->error = g_error_new_literal (FR_ERROR, FR_ERROR_COMMAND_ERROR, archive_error_string (b));
+	if (load_data->error == NULL)
+		g_cancellable_set_error_if_cancelled (cancellable, &load_data->error);
+	if (load_data->error != NULL)
+		g_simple_async_result_set_from_error (result, load_data->error);
+
+	archive_read_free (a);
+	archive_write_free (b);
+	g_list_free (remaining_files);
+	add_data_free (add_data);
+}
+
+
+static void
+fr_archive_libarchive_add_files (FrArchive           *archive,
 				 GList               *file_list,
 				 const char          *base_dir,
 				 const char          *dest_dir,
@@ -287,6 +766,54 @@ fr_archive_libarchive_add_files (FrArchive           *base,
 				 GAsyncReadyCallback  callback,
 				 gpointer             user_data)
 {
+	AddData  *add_data;
+	LoadData *load_data;
+	GList    *scan;
+
+	g_return_if_fail (base_dir != NULL);
+
+	add_data = g_new0 (AddData, 1);
+	save_data_init (SAVE_DATA (add_data));
+
+	load_data = LOAD_DATA (add_data);
+	load_data->archive = g_object_ref (archive);
+	load_data->cancellable = _g_object_ref (cancellable);
+	load_data->result = g_simple_async_result_new (G_OBJECT (archive),
+						       callback,
+						       user_data,
+						       fr_archive_add_files);
+	load_data->buffer_size = BUFFER_SIZE;
+	load_data->buffer = g_new (char, load_data->buffer_size);
+
+	add_data->file_list = _g_string_list_dup (file_list);
+	add_data->base_dir = g_file_new_for_uri (base_dir);
+	add_data->dest_dir = g_strdup (dest_dir[0] == '/' ? dest_dir + 1 : dest_dir);
+	add_data->update = update;
+	add_data->recursive = recursive;
+	add_data->password = g_strdup (password);
+	add_data->encrypt_header = encrypt_header;
+	add_data->compression = compression;
+	add_data->volume_size = volume_size;
+	add_data->files_to_add = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) file_to_add_free);
+	add_data->n_files_to_add = 0;
+	for (scan = add_data->file_list; scan; scan = scan->next) {
+		char *relative_pathname = scan->data;
+		char *full_pathname;
+
+		full_pathname = g_build_filename (add_data->dest_dir, relative_pathname, NULL);
+		g_hash_table_insert (add_data->files_to_add, full_pathname, file_to_add_new (add_data, relative_pathname, full_pathname));
+		add_data->n_files_to_add++;
+
+		g_free (full_pathname);
+	}
+	add_data->buffer_size = BUFFER_SIZE;
+	add_data->buffer = g_new (char, add_data->buffer_size);
+
+	g_simple_async_result_set_op_res_gpointer (load_data->result, add_data, NULL);
+	g_simple_async_result_run_in_thread (load_data->result,
+					     add_to_archive_thread,
+					     G_PRIORITY_DEFAULT,
+					     cancellable);
 }
 
 
@@ -385,7 +912,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
 		}
 
 		fullpath = (*pathname == '/') ? g_strdup (pathname) : g_strconcat ("/", pathname, NULL);
-		file = g_file_get_child (extract_data->destination, _g_path_get_base_name (fullpath, extract_data->base_dir, extract_data->junk_paths));
+		file = g_file_get_child (extract_data->destination, _g_path_get_basename (fullpath, extract_data->base_dir, extract_data->junk_paths));
 
 		/* honor the skip_older and overwrite options */
 
@@ -547,7 +1074,7 @@ fr_archive_libarchive_extract_files (FrArchive           *archive,
 						       callback,
 						       user_data,
 						       fr_archive_load);
-	load_data->buffer_size = BUFFER_SIZE_FOR_READING;
+	load_data->buffer_size = BUFFER_SIZE;
 	load_data->buffer = g_new (char, load_data->buffer_size);
 
 	extract_data->file_list = _g_string_list_dup (file_list);
@@ -680,5 +1207,17 @@ fr_archive_libarchive_class_init (FrArchiveLibarchiveClass *klass)
 static void
 fr_archive_libarchive_init (FrArchiveLibarchive *self)
 {
+	FrArchive *base = FR_ARCHIVE (self);
+
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, FR_TYPE_ARCHIVE_LIBARCHIVE, FrArchiveLibarchivePrivate);
+
+	base->propAddCanReplace = TRUE;
+	base->propAddCanUpdate = TRUE;
+	base->propAddCanStoreFolders = TRUE;
+	base->propExtractCanAvoidOverwrite = TRUE;
+	base->propExtractCanSkipOlder = TRUE;
+	base->propExtractCanJunkPaths = TRUE;
+	base->propCanExtractAll = TRUE;
+	base->propCanDeleteNonEmptyFolders = TRUE; /* FIXME: not implemented yet */
+	base->propCanExtractNonEmptyFolders = TRUE; /* FIXME: not implemented yet */
 }
diff --git a/src/fr-archive.c b/src/fr-archive.c
index b1155c8..e097182 100644
--- a/src/fr-archive.c
+++ b/src/fr-archive.c
@@ -1004,7 +1004,7 @@ add_with_wildcard_data_free (AddWithWildcardData *aww_data)
 
 static void
 add_with_wildcard__step2 (GList    *file_list,
-			  GList    *dirs_list,
+			  GList    *dir_list,
 			  GError   *error,
 			  gpointer  data)
 {
@@ -1022,6 +1022,11 @@ add_with_wildcard__step2 (GList    *file_list,
 		g_simple_async_result_complete_in_idle (result);
 	}
 	else {
+		if (archive->propAddCanStoreFolders) {
+			file_list = g_list_concat (file_list, dir_list);
+			dir_list = NULL;
+		}
+
 		if (file_list == NULL)
 			g_simple_async_result_complete_in_idle (result);
 		else
@@ -1042,7 +1047,7 @@ add_with_wildcard__step2 (GList    *file_list,
 
 	g_object_unref (result);
 	_g_string_list_free (file_list);
-	_g_string_list_free (dirs_list);
+	_g_string_list_free (dir_list);
 	add_with_wildcard_data_free (aww_data);
 }
 
@@ -1150,10 +1155,10 @@ add_directory__step2 (GList    *file_list,
 		g_simple_async_result_complete_in_idle (result);
 	}
 	else {
-		if (archive->propAddCanStoreFolders)
+		if (archive->propAddCanStoreFolders) {
 			file_list = g_list_concat (file_list, dir_list);
-		else
-			_g_string_list_free (dir_list);
+			dir_list = NULL;
+		}
 
 		if (file_list == NULL)
 			g_simple_async_result_complete_in_idle (result);
@@ -1174,6 +1179,7 @@ add_directory__step2 (GList    *file_list,
 	}
 
 	_g_string_list_free (file_list);
+	_g_string_list_free (dir_list);
 	g_object_unref (result);
 	add_directory_data_free (ad_data);
 }
diff --git a/src/gio-utils.c b/src/gio-utils.c
index d070f06..a07caa5 100644
--- a/src/gio-utils.c
+++ b/src/gio-utils.c
@@ -577,10 +577,12 @@ get_file_list_done (GError   *error,
 	gfl->files = g_list_reverse (gfl->files);
 	gfl->dirs = g_list_reverse (gfl->dirs);
 
+	/* FIXME: delete the folders that contain only filtered out files
 	if (! filter_empty (gfl->include_filter) || (gfl->exclude_filter->pattern != NULL)) {
 		_g_string_list_free (gfl->dirs);
 		gfl->dirs = NULL;
 	}
+	*/
 
 	h_dirs = g_hash_table_new (g_str_hash, g_str_equal);
 
diff --git a/src/glib-utils.c b/src/glib-utils.c
index de9cdf8..1904482 100644
--- a/src/glib-utils.c
+++ b/src/glib-utils.c
@@ -1080,6 +1080,27 @@ _g_filename_has_extension (const char *filename,
 }
 
 
+char *
+_g_filename_get_random (int length)
+{
+	const char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+	const int   n_letters = strlen (letters);
+	char       *result, *c;
+	GRand      *rand;
+	int         i;
+
+	result = g_new (char, length + 1);
+
+	rand = g_rand_new ();
+	for (i = 0, c = result; i < length; i++, c++)
+		*c = letters[g_rand_int_range (rand, 0, n_letters)];
+	*c = '\0';
+	g_rand_free (rand);
+
+	return result;
+}
+
+
 gboolean
 _g_mime_type_matches (const char *mime_type,
 		      const char *pattern)
diff --git a/src/glib-utils.h b/src/glib-utils.h
index 0904a3b..a2044b8 100644
--- a/src/glib-utils.h
+++ b/src/glib-utils.h
@@ -144,6 +144,7 @@ gboolean            _g_filename_is_hidden          (const char          *name);
 const char *        _g_filename_get_extension      (const char          *filename);
 gboolean            _g_filename_has_extension      (const char          *filename,
 						    const char          *ext);
+char *              _g_filename_get_random         (int                  length);
 gboolean            _g_mime_type_matches           (const char          *type,
 						    const char          *pattern);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]