1 /*
2  *  Copyright (c) 2008 Giuseppe Torelli <colossus73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17  *  MA 02110-1301 USA.
18  */
19 
20 #include "config.h"
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 #include "archive.h"
28 #include "interface.h"
29 #include "main.h"
30 #include "pref_dialog.h"
31 #include "support.h"
32 #include "window.h"
33 
34 #ifndef NCARGS
35 #define NCARGS _POSIX_ARG_MAX
36 #endif
37 
38 #define MAX_CMD_LEN (NCARGS * 2 / 3)
39 
40 #define MAX_XARCHIVES 100
41 
42 XArchive *archive[MAX_XARCHIVES];
43 
xa_process_stdout(GIOChannel * ioc,GIOCondition cond,XArchive * archive)44 static gboolean xa_process_stdout (GIOChannel *ioc, GIOCondition cond, XArchive *archive)
45 {
46 	GIOStatus status;
47 	gchar *line;
48 
49 	if (cond & G_IO_IN)
50 	{
51 		status = g_io_channel_read_line(ioc, &line, NULL, NULL, NULL);
52 
53 		if (status == G_IO_STATUS_NORMAL)
54 		{
55 			if (xa_main_window)
56 			{
57 				if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(prefs_window->store_output)))
58 					archive->output = g_slist_prepend(archive->output, g_strdup(line));
59 			}
60 
61 			if (archive->parse_output)
62 				(*archive->parse_output)(line, archive);
63 
64 			g_free(line);
65 
66 			while (gtk_events_pending())
67 				gtk_main_iteration();
68 
69 			return TRUE;
70 		}
71 		else if (status == G_IO_STATUS_AGAIN)
72 			return TRUE;
73 		else
74 			cond &= ~G_IO_IN;
75 	}
76 
77 	g_io_channel_shutdown(ioc, FALSE, NULL);
78 	g_io_channel_unref(ioc);
79 
80 	xa_child_processed(XA_CHILD_STDOUT, cond == G_IO_HUP, archive);
81 
82 	return FALSE;
83 }
84 
xa_process_stderr(GIOChannel * ioc,GIOCondition cond,XArchive * archive)85 static gboolean xa_process_stderr (GIOChannel *ioc, GIOCondition cond, XArchive *archive)
86 {
87 	GIOStatus status;
88 	gchar *line;
89 
90 	if (cond & G_IO_IN)
91 	{
92 		status = g_io_channel_read_line(ioc, &line, NULL, NULL, NULL);
93 
94 		if (status == G_IO_STATUS_NORMAL)
95 		{
96 			if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(prefs_window->store_output)))
97 			{
98 				if (!g_utf8_validate(line, -1, NULL))
99 				{
100 					gchar *utf8 = g_locale_to_utf8(line, -1, NULL, NULL, NULL);
101 
102 					if (utf8)
103 					{
104 						g_free(line);
105 						line = utf8;
106 					}
107 				}
108 
109 				archive->output = g_slist_prepend(archive->output, g_strdup(line));
110 			}
111 
112 			g_free(line);
113 
114 			return TRUE;
115 		}
116 		else if (status == G_IO_STATUS_AGAIN)
117 			return TRUE;
118 		else
119 			cond &= ~G_IO_IN;
120 	}
121 
122 	g_io_channel_shutdown(ioc, FALSE, NULL);
123 	g_io_channel_unref(ioc);
124 
125 	xa_child_processed(XA_CHILD_STDERR, cond == G_IO_HUP, archive);
126 
127 	return FALSE;
128 }
129 
xa_process_exit(GPid pid,gint status,XArchive * archive)130 static void xa_process_exit (GPid pid, gint status, XArchive *archive)
131 {
132 	gboolean result;
133 
134 	if (WIFEXITED(status))
135 	{
136 		result = (WEXITSTATUS(status) == 0 || (archive->status == XARCHIVESTATUS_RELOAD &&
137 		                                       !g_file_test(archive->path[0], G_FILE_TEST_EXISTS)));
138 		g_spawn_close_pid(pid);
139 		xa_child_processed(XA_CHILD_EXIT, result, archive);
140 	}
141 }
142 
xa_delete_working_directory(XArchive * archive)143 static void xa_delete_working_directory (XArchive *archive)
144 {
145 	if (xa_main_window)
146 		xa_launch_external_program("rm -rf", archive->working_dir);
147 	else
148 	{
149 		char *argv[4];
150 		argv[0] = "rm";
151 		argv[1] = "-rf";
152 		argv[2] = archive->working_dir;
153 		argv[3] = NULL;
154 		g_spawn_sync (NULL, argv, NULL,G_SPAWN_SEARCH_PATH,NULL, NULL,NULL,NULL, NULL,NULL);
155 	}
156 }
157 
xa_alloc_memory_for_each_row(guint columns,GType column_types[])158 static XEntry *xa_alloc_memory_for_each_row (guint columns, GType column_types[])
159 {
160 	XEntry *entry = NULL;
161 	guint i;
162 	gint size = 0;
163 
164 	entry = g_new0(XEntry,1);
165 	if (entry == NULL)
166 		return NULL;
167 
168 	for (i = 2; i < columns - 1; i++)
169 	{
170 		switch(column_types[i])
171 		{
172 			case G_TYPE_STRING:
173 				size += sizeof(gchar *);
174 			break;
175 
176 			case G_TYPE_UINT64:
177 				size += sizeof(guint64);
178 			break;
179 		}
180 	}
181 	entry->columns = g_malloc0 (size);
182 	return entry;
183 }
184 
xa_find_directory_entry(XEntry * entry,const gchar * name)185 static XEntry *xa_find_directory_entry (XEntry *entry, const gchar *name)
186 {
187 	gchar *filename;
188 
189 	if (entry == NULL)
190 		return NULL;
191 
192 	if (g_utf8_validate(entry->filename, -1, NULL))
193 		filename = g_filename_display_name(name);
194 	else
195 		filename = g_strdup(name);
196 
197 	if (entry->is_dir && strcmp(entry->filename, filename) == 0)
198 	{
199 		g_free(filename);
200 		return entry;
201 	}
202 
203 	g_free(filename);
204 
205   return xa_find_directory_entry(entry->next, name);
206 }
207 
xa_fill_archive_entry_columns_for_each_row(XArchive * archive,XEntry * entry,gpointer * items)208 static gpointer *xa_fill_archive_entry_columns_for_each_row (XArchive *archive, XEntry *entry, gpointer *items)
209 {
210 	guint i;
211 	gpointer current_column;
212 
213 	current_column = entry->columns;
214 
215 	for (i = 2; i < archive->columns - 1; i++)
216 	{
217 		switch(archive->column_types[i])
218 		{
219 			case G_TYPE_STRING:
220 				*(gchar **) current_column = g_strdup((gchar *) items[i - 2]);
221 				//g_message ("%d - %s",i,(*((gchar **)current_column)));
222 				current_column += sizeof(gchar *);
223 			break;
224 
225 			case G_TYPE_UINT64:
226 				*(guint64 *) current_column = g_ascii_strtoull(items[i - 2], NULL, 0);
227 				//g_message ("*%d - %lu",i,(*((guint64 *)current_column)));
228 				current_column += sizeof(guint64);
229 			break;
230 		}
231 	}
232 	return entry->columns;
233 }
234 
xa_build_dir_sidebar(XEntry * entry,GtkTreeStore * model,gchar * path,GtkTreeIter * containing_iter)235 static void xa_build_dir_sidebar (XEntry *entry, GtkTreeStore *model, gchar *path, GtkTreeIter *containing_iter)
236 {
237 	GtkTreeIter child_iter;
238 
239 	if (!entry)
240 		return;
241 
242 	if (strlen(entry->filename) == 0)
243 		return xa_build_dir_sidebar(entry->child, model, path, containing_iter);
244 
245 	if (entry->is_dir)
246 	{
247 		gtk_tree_store_append(model,&child_iter,containing_iter);
248 
249 		if (!g_utf8_validate(entry->filename, -1, NULL))
250 		{
251 			gchar *entry_utf8 = g_filename_display_name(entry->filename);
252 			g_free(entry->filename);
253 			entry->filename = entry_utf8;
254 		}
255 
256 		gtk_tree_store_set(model,&child_iter,0,"gtk-directory",1,entry->filename,2,entry,-1);
257 	}
258 	xa_build_dir_sidebar(entry->child, model, NULL, &child_iter);
259 	xa_build_dir_sidebar(entry->next, model, NULL, containing_iter);
260 
261 }
262 
xa_dir_sidebar_find_row(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer entry)263 static gboolean xa_dir_sidebar_find_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer entry)
264 {
265 	XEntry *entry2;
266 	GtkTreeIter parent;
267 	gboolean value;
268 
269 	gtk_tree_model_get (model,iter,2,&entry2,-1);
270 	if (entry == entry2)
271 	{
272 		gtk_tree_model_iter_parent(model,&parent,iter);
273 		if ( ! gtk_tree_view_row_expanded(GTK_TREE_VIEW(archive_dir_treeview),path))
274 			gtk_tree_view_expand_to_path(GTK_TREE_VIEW(archive_dir_treeview),path);
275 
276 		gtk_tree_selection_select_iter(gtk_tree_view_get_selection (GTK_TREE_VIEW (archive_dir_treeview)),iter);
277 		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW(archive_dir_treeview),path,NULL,FALSE,0,0);
278 		value = TRUE;
279 	}
280 	else
281 		value = FALSE;
282 
283 	return value;
284 }
285 
xa_get_compressed_tar_type(XArchiveType * type)286 gboolean xa_get_compressed_tar_type (XArchiveType *type)
287 {
288 	switch (*type)
289 	{
290 		case XARCHIVETYPE_BZIP2:
291 			*type = XARCHIVETYPE_TAR_BZIP2;
292 			break;
293 
294 		case XARCHIVETYPE_COMPRESS:
295 			*type = XARCHIVETYPE_TAR_COMPRESS;
296 			break;
297 
298 		case XARCHIVETYPE_GZIP:
299 			*type = XARCHIVETYPE_TAR_GZIP;
300 			break;
301 
302 		case XARCHIVETYPE_LRZIP:
303 			*type = XARCHIVETYPE_TAR_LRZIP;
304 			break;
305 
306 		case XARCHIVETYPE_LZ4:
307 			*type = XARCHIVETYPE_TAR_LZ4;
308 			break;
309 
310 		case XARCHIVETYPE_LZIP:
311 			*type = XARCHIVETYPE_TAR_LZIP;
312 			break;
313 
314 		case XARCHIVETYPE_LZMA:
315 			*type = XARCHIVETYPE_TAR_LZMA;
316 			break;
317 
318 		case XARCHIVETYPE_LZOP:
319 			*type = XARCHIVETYPE_TAR_LZOP;
320 			break;
321 
322 		case XARCHIVETYPE_XZ:
323 			*type = XARCHIVETYPE_TAR_XZ;
324 			break;
325 
326 		case XARCHIVETYPE_ZSTD:
327 			*type = XARCHIVETYPE_TAR_ZSTD;
328 			break;
329 
330 		default:
331 			return FALSE;
332 	}
333 
334 	return TRUE;
335 }
336 
xa_init_archive_structure(ArchiveType xa)337 XArchive *xa_init_archive_structure (ArchiveType xa)
338 {
339 	XArchive *archive;
340 	XEntry *entry;
341 
342 	archive = g_new0(XArchive, 1);
343 
344 	if (!archive)
345 		return NULL;
346 
347 	archive->type = xa.type;
348 	archive->tag = xa.tag;
349 
350 	entry = g_new0(XEntry, 1);
351 
352 	if (!entry)
353 	{
354 		g_free(archive);
355 		return NULL;
356 	}
357 
358 	entry->filename = "";
359 	archive->root_entry = entry;
360 
361 	archive->archiver = &archiver[xa.type];
362 
363 	if (archive_dir_treestore)
364 		gtk_tree_store_clear(archive_dir_treestore);
365 
366 	(*archive->archiver->ask)(archive);
367 
368 	return archive;
369 }
370 
xa_spawn_async_process(XArchive * archive,const gchar * command)371 void xa_spawn_async_process (XArchive *archive, const gchar *command)
372 {
373 	gint argc;
374 	gchar **argv;
375 	GError *error = NULL;
376 	GIOChannel *ioc;
377 
378 	g_shell_parse_argv(command, &argc, &argv, NULL);
379 
380 	if (!g_spawn_async_with_pipes(archive->child_dir,
381 	                              argv,
382 	                              NULL,
383 	                              G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
384 	                              NULL,
385 	                              NULL,
386 	                              &archive->child_pid,
387 	                              NULL,
388 	                              &archive->child_fdout,
389 	                              &archive->child_fderr,
390 	                              &error))
391 	{
392 		g_strfreev(argv);
393 
394 		xa_show_message_dialog(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Can't run the archiver executable:"), error->message);
395 		g_error_free(error);
396 
397 		if (xa_main_window)
398 			xa_set_button_state(1, 1, 1, 1, archive->can_test, 1, archive->can_add, archive->can_extract, archive->sorted, archive->can_sfx, archive->has_comment, archive->output, archive->has_password);
399 
400 		archive->status = XARCHIVESTATUS_ERROR;
401 		return;
402 	}
403 
404 	g_strfreev(argv);
405 
406 	g_free(archive->command);
407 	archive->command = g_strdup(command);
408 
409 	archive->child_ref = XA_CHILD_PROCS;
410 
411 	if (xa_main_window)
412 	{
413 		gtk_widget_set_sensitive(Stop_button, TRUE);
414 		g_timeout_add(350, (GSourceFunc) xa_flash_led_indicator, archive);
415 	}
416 	else if (progress && !progress->multi_extract)
417 		g_timeout_add(100, xa_pulse_progress_bar, NULL);
418 
419 	if (archive->output)
420 	{
421 		g_slist_free_full(archive->output, g_free);
422 		archive->output = NULL;
423 	}
424 
425 	ioc = g_io_channel_unix_new(archive->child_fdout);
426 	g_io_channel_set_encoding(ioc, NULL, NULL);
427 	g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
428 	g_io_add_watch(ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) xa_process_stdout, archive);
429 
430 	ioc = g_io_channel_unix_new(archive->child_fderr);
431 	g_io_channel_set_encoding(ioc, NULL, NULL);
432 	g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
433 	g_io_add_watch(ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) xa_process_stderr, archive);
434 
435 	if (archive->parse_output)
436 		g_child_watch_add_full(G_PRIORITY_LOW, archive->child_pid, (GChildWatchFunc) xa_process_exit, archive, NULL);
437 }
438 
439 /*	TODO: workaround for bug #3235
440  *
441  * gchar *xa_split_command_line(XArchive *archive,GSList *list)
442 {
443 	gchar *command = NULL;
444 	GSList *chunks = NULL;
445 	GSList *scan = NULL;
446 	int length;
447 
448 	for (scan = list; scan != NULL;)
449 	{
450 		length = 0;
451 		while ((scan != NULL) && (length < 5000)) //MAX_CMD_LEN
452 		{
453 			length += strlen (scan->data);
454 			chunks = g_slist_prepend(chunks,scan->data);
455 			scan = scan->next;
456 		}
457 		chunks = g_slist_prepend(chunks,"****** ");
458 	}
459 	chunks = g_slist_reverse(chunks);
460 	return command;
461 }
462 */
463 
xa_clean_archive_structure(XArchive * archive)464 void xa_clean_archive_structure (XArchive *archive)
465 {
466 	xa_free_entry(archive, archive->root_entry);
467 
468 	if (archive->working_dir)
469 	{
470 		xa_delete_working_directory(archive);
471 		g_free(archive->working_dir);
472 	}
473 
474 	if (archive->comment)
475 		g_string_free(archive->comment, TRUE);
476 
477 	if (archive->output)
478 		g_slist_free_full(archive->output, g_free);
479 
480 	if (archive->clipboard)
481 		xa_clipboard_clear(NULL, archive);
482 
483 	g_free(archive->column_types);
484 	g_free(archive->path[0]);
485 	g_free(archive->path[1]);
486 	g_free(archive->path[2]);
487 	g_free(archive->destination_path);
488 	g_free(archive->extraction_dir);
489 	g_free(archive->password);
490 	g_free(archive->child_dir);
491 	g_free(archive->command);
492 	g_free(archive);
493 }
494 
xa_create_working_directory(XArchive * archive)495 gboolean xa_create_working_directory (XArchive *archive)
496 {
497 	gchar *tmp_dir;
498 	gchar *value;
499 
500 	if (archive->working_dir != NULL)
501 		return TRUE;
502 
503 	value = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(prefs_window->combo_prefered_temp_dir));
504 
505 	if (value && *value)
506 	{
507 		gchar *value_local = g_filename_from_utf8(value, -1, NULL, NULL, NULL);
508 		tmp_dir = g_strconcat(value_local, "/xa-XXXXXX", NULL);
509 		g_free(value_local);
510 	}
511 	else
512 		tmp_dir = g_strdup("");
513 
514 	g_free(value);
515 
516 	if (!g_mkdtemp(tmp_dir))
517 	{
518 		g_free(tmp_dir);
519 		tmp_dir = NULL;
520 
521 		xa_show_message_dialog (GTK_WINDOW (xa_main_window),GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,_("Can't create temporary directory:"),g_strerror(errno));
522 		return FALSE;
523 	}
524 
525 	archive->working_dir = tmp_dir;
526 	return TRUE;
527 }
528 
xa_create_containing_directory(XArchive * archive,const gchar * path)529 gchar *xa_create_containing_directory (XArchive *archive, const gchar *path)
530 {
531 	gchar *stem, *dir;
532 	char *dot;
533 	guint i;
534 
535 	stem = g_path_get_basename(archive->path[0]);
536 	dot = strrchr(stem, '.');
537 
538 	if (dot)
539 		*dot = 0;
540 
541 	if (g_str_has_suffix(stem, ".tar"))
542 	{
543 		dot = strrchr(stem, '.');
544 		*dot = 0;
545 	}
546 
547 	dir = g_strconcat(path, "/", stem, NULL);
548 
549 	for (i = 0; i < 100; i++)
550 	{
551 		if (i > 0)
552 		{
553 			g_free(dir);
554 			dir = g_strdup_printf("%s/%s-(%d)", path, stem, i);
555 		}
556 
557 		if (!g_file_test(dir, G_FILE_TEST_EXISTS))
558 		{
559 			if (g_mkdir_with_parents(dir, 0700) == 0)
560 			{
561 				g_free(stem);
562 
563 				return dir;
564 			}
565 		}
566 	}
567 
568 	g_free(stem);
569 
570 	return NULL;
571 }
572 
xa_run_command(XArchive * archive,const gchar * command)573 gboolean xa_run_command (XArchive *archive, const gchar *command)
574 {
575 	pid_t pid = 0;
576 	int status;
577 	gboolean result;
578 
579 	/* batch mode and archive's list function has completed */
580 	if (!xa_main_window && archive->column_types)
581 		xa_show_progress_bar(archive);
582 
583 	xa_spawn_async_process(archive, command);
584 
585 	if (archive->child_pid == 0)
586 		result = FALSE;
587 	else
588 	{
589 		while (pid != archive->child_pid && pid != -1)
590 		{
591 			pid = waitpid(archive->child_pid, &status, WNOHANG);
592 
593 			while (gtk_events_pending())
594 				gtk_main_iteration();
595 		}
596 
597 		result = (WIFEXITED(status) && (WEXITSTATUS(status) == 0));
598 	}
599 
600 	xa_child_processed(XA_CHILD_EXIT, result, archive);
601 
602 	return result;
603 }
604 
xa_has_containing_directory(XArchive * archive)605 gboolean xa_has_containing_directory (XArchive *archive)
606 {
607 	XEntry *entry;
608 	guint n = 0;
609 
610 	entry = archive->root_entry->child;
611 
612 	while (entry)
613 	{
614 		if (++n > 1) break;
615 
616 		entry = entry->next;
617 	}
618 
619 	return (n == 1) && archive->root_entry->child->is_dir;
620 }
621 
xa_find_archive_index(gint page_num)622 gint xa_find_archive_index (gint page_num)
623 {
624 	GtkWidget *page;
625 	gint i;
626 
627 	page = gtk_notebook_get_nth_page(notebook, page_num);
628 
629 	for (i = 0; i < MAX_XARCHIVES; i++)
630 	{
631 		if (archive[i] != NULL && archive[i]->page == page)
632 			return i;
633 	}
634 
635 	return -1;
636 }
637 
xa_get_new_archive_idx()638 gint xa_get_new_archive_idx()
639 {
640 	gint i;
641 
642 	for (i = 0; i < MAX_XARCHIVES; i++)
643 	{
644 		if (archive[i] == NULL)
645 			return i;
646 	}
647 	return -1;
648 }
649 
xa_free_entry(XArchive * archive,XEntry * entry)650 void xa_free_entry (XArchive *archive, XEntry *entry)
651 {
652 	gpointer current_column;
653 	guint i;
654 
655 	if (entry->child)
656 		xa_free_entry(archive, entry->child);
657 
658 	if (entry->next)
659 		xa_free_entry(archive, entry->next);
660 
661 	if (entry->columns)
662 	{
663 		current_column = entry->columns;
664 
665 		if (*entry->filename)
666 		{
667 			for (i = 2; i < archive->columns - 1; i++)
668 			{
669 				switch (archive->column_types[i])
670 				{
671 					case G_TYPE_STRING:
672 						g_free(*(gchar **) current_column);
673 						current_column += sizeof(gchar *);
674 						break;
675 
676 					case G_TYPE_UINT64:
677 						current_column += sizeof(guint64);
678 						break;
679 				}
680 			}
681 
682 			g_free(entry->columns);
683 			g_free(entry->filename);
684 		}
685 	}
686 
687 	g_free(entry);
688 }
689 
xa_set_archive_entries_for_each_row(XArchive * archive,const gchar * filename,gpointer * items)690 XEntry *xa_set_archive_entries_for_each_row (XArchive *archive, const gchar *filename, gpointer *items)
691 {
692 	XEntry *entry = NULL, *last = archive->root_entry;
693 	gchar **components;
694 	guint n = 0;
695 
696 	components = g_strsplit(filename, "/", -1);
697 
698 	if (*filename == '/')
699 	{
700 		gchar *slashdir;
701 
702 		n = 1;
703 		slashdir = g_strconcat("/", components[n], NULL);
704 		g_free(components[n]);
705 		components[n] = slashdir;
706 	}
707 
708 	while (components[n] && *components[n])
709 	{
710 		entry = xa_find_directory_entry(last->child, components[n]);
711 
712 		if (entry == NULL)
713 		{
714 			entry = xa_alloc_memory_for_each_row(archive->columns, archive->column_types);
715 
716 			if (entry == NULL)
717 				return NULL;
718 
719 			entry->filename = g_strdup(components[n]);
720 
721 			if (components[n + 1])
722 				entry->is_dir = TRUE;
723 
724 			entry->next = last->child;
725 			last->child = entry;
726 			entry->prev = last;
727 		}
728 
729 		if (components[n + 1] == NULL || *components[n + 1] == 0)
730 			entry->columns = xa_fill_archive_entry_columns_for_each_row(archive, entry, items);
731 
732 		last = entry;
733 		n++;
734 	}
735 
736 	g_strfreev(components);
737 
738 	return entry;
739 }
740 
xa_find_entry_from_dirpath(XArchive * archive,const gchar * dirpath)741 XEntry* xa_find_entry_from_dirpath (XArchive *archive, const gchar *dirpath)
742 {
743 	XEntry *root = archive->root_entry, *entry = NULL;
744 	gchar **components;
745 	guint n = 0;
746 
747 	components = g_strsplit(dirpath, "/", -1);
748 
749 	while (components[n] && *components[n])
750 	{
751 		entry = xa_find_directory_entry(root->child, components[n]);
752 		root = entry;
753 		n++;
754 	}
755 
756 	g_strfreev(components);
757 
758 	return entry;
759 }
760 
xa_build_full_path_name_from_entry(XEntry * entry)761 gchar *xa_build_full_path_name_from_entry (XEntry *entry)
762 {
763 	GString *path;
764 	gchar *path_local;
765 
766 	path = g_string_new("");
767 
768 	while (entry)
769 	{
770 		if (entry->is_dir)
771 			path = g_string_prepend_c(path, '/');
772 
773 		path = g_string_prepend(path, entry->filename);
774 
775 		entry = entry->prev;
776 	}
777 
778 	if (g_utf8_validate(path->str, -1, NULL))
779 		path_local = g_filename_from_utf8(path->str, -1, NULL, NULL, NULL);
780 	else
781 		path_local = g_strdup(path->str);
782 
783 	g_string_free(path, TRUE);
784 
785 	return path_local;
786 }
787 
xa_fill_list_with_recursed_entries(XEntry * entry,GSList ** p_file_list)788 void xa_fill_list_with_recursed_entries(XEntry *entry,GSList **p_file_list)
789 {
790 	if (entry == NULL)
791 		return;
792 
793 	xa_fill_list_with_recursed_entries(entry->next ,p_file_list);
794 	xa_fill_list_with_recursed_entries(entry->child,p_file_list);
795 	*p_file_list = g_slist_prepend(*p_file_list, xa_build_full_path_name_from_entry(entry));
796 }
797 
xa_detect_encrypted_archive(XArchive * archive)798 void xa_detect_encrypted_archive (XArchive *archive)
799 {
800 	archive->status = XARCHIVESTATUS_LIST;
801 	(*archive->archiver->list)(archive);
802 
803 	do
804 	{
805 		while (gtk_events_pending())
806 			gtk_main_iteration();
807 	}
808 	while (archive->child_ref);
809 }
810 
xa_fill_dir_sidebar(XArchive * archive,gboolean force_reload)811 void xa_fill_dir_sidebar(XArchive *archive,gboolean force_reload)
812 {
813 	GtkTreeIter iter;
814 
815 	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(archive_dir_treestore), &iter) && force_reload == FALSE)
816 		return;
817 
818 	gtk_tree_store_clear(archive_dir_treestore);
819 	xa_build_dir_sidebar(archive->root_entry, archive_dir_treestore, NULL, NULL);
820 }
821 
xa_dir_sidebar_row_selected(GtkTreeSelection * selection,gpointer user_data)822 void xa_dir_sidebar_row_selected (GtkTreeSelection *selection, gpointer user_data)
823 {
824 	XEntry *entry;
825 	GtkTreeIter iter;
826 	GtkTreeIter parent;
827 	GtkTreePath *path;
828 	GtkTreeModel *model;
829 	GString *string = g_string_new("");
830 	gchar *dir;
831 	gint idx;
832 
833 	idx = xa_find_archive_index(gtk_notebook_get_current_page(notebook));
834 
835 	if ((idx >= 0) && gtk_tree_selection_get_selected(selection, &model, &iter))
836 	{
837 		path = gtk_tree_model_get_path(model,&iter);
838 		if ( ! gtk_tree_view_row_expanded(GTK_TREE_VIEW(archive_dir_treeview),path))
839 			gtk_tree_view_expand_to_path(GTK_TREE_VIEW(archive_dir_treeview),path);
840 		gtk_tree_path_free(path);
841 		/* Let get the last selected dir */
842 		gtk_tree_model_get(model,&iter,1,&dir,-1);
843 		g_string_prepend_c(string,'/');
844 		g_string_prepend(string,dir);
845 		/* Get the memory address of entry so to update the main listview */
846 		gtk_tree_model_get(model,&iter,2,&entry,-1);
847 		while (gtk_tree_model_iter_parent(model,&parent,&iter))
848 		{
849 			gtk_tree_model_get(model,&parent,1,&dir,-1);
850 			g_string_prepend_c(string,'/');
851 			g_string_prepend(string,dir);
852 			iter = parent;
853 		}
854 		gtk_entry_set_text(GTK_ENTRY(location_entry),string->str);
855 		g_string_free(string,TRUE);
856 
857 		xa_update_window_with_archive_entries(archive[idx],entry);
858 		xa_set_statusbar_message_for_displayed_rows(archive[idx]);
859 	}
860 }
861 
xa_dir_sidebar_select_row(XEntry * entry)862 void xa_dir_sidebar_select_row (XEntry *entry)
863 {
864 	gtk_tree_model_foreach(GTK_TREE_MODEL(archive_dir_treestore), xa_dir_sidebar_find_row, entry);
865 }
866 
xa_sort_dirs_before_files(GtkTreeModel * model,GtkTreeIter * a,GtkTreeIter * b,XArchive * archive)867 gint xa_sort_dirs_before_files (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, XArchive *archive)
868 {
869 	XEntry *entry1, *entry2;
870 
871 	gtk_tree_model_get(model, a, archive->columns - 1, &entry1, -1);
872 	gtk_tree_model_get(model, b, archive->columns - 1, &entry2, -1);
873 	if (entry1->is_dir != entry2->is_dir)
874 	{
875 		if (entry1->is_dir)
876 			return -1;
877 		else
878 			return 1;
879 	}
880 	/* This for sorting the files */
881 	return strcasecmp (entry1->filename,entry2->filename);
882 }
883