1 /* -*- Mode: C; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 3 -*- */
2
3 /*
4 * File-Roller
5 *
6 * Copyright (C) 2001 The Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
21 *
22 * $Id: fr-archive.c,v 1.19 2004/09/21 08:44:31 makeinu Exp $
23 */
24
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <glib.h>
29 #include "gimv_image_info.h"
30 #include "fileutil.h"
31 #include "fr-archive.h"
32 #include "fr-command.h"
33 #include "fr-process.h"
34 #include "gtk2-compat.h"
35
36
37 #define MAX_CHUNK_LEN 16000 /* FIXME : what is the max length of a command
38 * line ? */
39
40
41 enum {
42 START,
43 DONE,
44 LAST_SIGNAL
45 };
46
47
48 static GHashTable *ext_archivers = NULL;
49 static GList *archive_ext_list = NULL;
50
51
52 static GtkObjectClass *parent_class;
53 static guint fr_archive_signals[LAST_SIGNAL] = { 0 };
54
55
56 static void
fr_archive_destroy(GtkObject * object)57 fr_archive_destroy (GtkObject *object)
58 {
59 FRArchive *archive;
60
61 g_return_if_fail (object != NULL);
62 g_return_if_fail (FR_IS_ARCHIVE (object));
63
64 archive = FR_ARCHIVE (object);
65
66 if (archive->command != NULL)
67 gtk_object_unref (GTK_OBJECT (archive->command));
68
69 gtk_object_unref (GTK_OBJECT (archive->process));
70
71 g_print (_("archive \"%s\" has been finalized.\n"), archive->filename);
72
73 if (archive->filename != NULL)
74 g_free (archive->filename);
75
76 /* Chain up */
77 if (GTK_OBJECT_CLASS (parent_class)->destroy)
78 (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
79 }
80
81
82 static void
fr_archive_class_init(FRArchiveClass * class)83 fr_archive_class_init (FRArchiveClass *class)
84 {
85 GtkObjectClass *object_class;
86
87 object_class = (GtkObjectClass *) class;
88 parent_class = gtk_type_class (gtk_object_get_type ());
89
90 fr_archive_signals[START] =
91 gtk_signal_new ("start",
92 GTK_RUN_LAST,
93 GTK_CLASS_TYPE (object_class),
94 GTK_SIGNAL_OFFSET (FRArchiveClass, start),
95 gtk_marshal_NONE__INT,
96 GTK_TYPE_NONE, 1,
97 GTK_TYPE_INT);
98
99 fr_archive_signals[DONE] =
100 gtk_signal_new ("done",
101 GTK_RUN_LAST,
102 GTK_CLASS_TYPE (object_class),
103 GTK_SIGNAL_OFFSET (FRArchiveClass, done),
104 gtk_marshal_NONE__INT_INT,
105 GTK_TYPE_NONE, 2,
106 GTK_TYPE_INT,
107 GTK_TYPE_INT);
108 gtk_object_class_add_signals (object_class, fr_archive_signals,
109 LAST_SIGNAL);
110
111 object_class->destroy = fr_archive_destroy;
112 class->start = NULL;
113 class->done = NULL;
114 }
115
116
117 static void
fr_archive_init(FRArchive * archive)118 fr_archive_init (FRArchive *archive)
119 {
120 archive->filename = NULL;
121 archive->command = NULL;
122 archive->process = fr_process_new ();
123
124 #ifdef USE_GTK2
125 gtk_object_ref (GTK_OBJECT (archive));
126 gtk_object_sink (GTK_OBJECT (archive));
127 #endif
128 }
129
130
131 GtkType
fr_archive_get_type(void)132 fr_archive_get_type (void)
133 {
134 static GtkType fr_archive_type = 0;
135
136 if (! fr_archive_type) {
137 GtkTypeInfo fr_archive_info = {
138 "FRArchive",
139 sizeof (FRArchive),
140 sizeof (FRArchiveClass),
141 (GtkClassInitFunc) fr_archive_class_init,
142 (GtkObjectInitFunc) fr_archive_init,
143 NULL, /* reserved_1 */
144 NULL, /* reserved_2 */
145 (GtkClassInitFunc) NULL
146 };
147
148 fr_archive_type = gtk_type_unique (gtk_object_get_type (),
149 &fr_archive_info);
150 }
151
152 return fr_archive_type;
153 }
154
155
156 FRArchive *
fr_archive_new(void)157 fr_archive_new (void)
158 {
159 FRArchive *archive;
160 archive = FR_ARCHIVE (gtk_type_new (fr_archive_get_type ()));
161 return archive;
162 }
163
164
165 FRArchive *
fr_archive_ref(FRArchive * archive)166 fr_archive_ref (FRArchive *archive)
167 {
168 g_return_val_if_fail (archive != NULL, NULL);
169 g_return_val_if_fail (FR_IS_ARCHIVE (archive), NULL);
170
171 gtk_object_ref (GTK_OBJECT (archive));
172
173 return archive;
174 }
175
176
177 void
fr_archive_unref(FRArchive * archive)178 fr_archive_unref (FRArchive *archive)
179 {
180 g_return_if_fail (archive != NULL);
181 g_return_if_fail (FR_IS_ARCHIVE (archive));
182
183 gtk_object_unref (GTK_OBJECT (archive));
184 }
185
186
187 ExtArchiverPlugin *found_archiver = NULL;
188
189 static void
archiver_lookup(gpointer key,gpointer value,gpointer user_data)190 archiver_lookup (gpointer key, gpointer value, gpointer user_data)
191 {
192 char *filename = user_data;
193
194 if (filename && key && fileutil_extension_is (filename, key))
195 found_archiver = value;
196 }
197
198
199 static gboolean
create_command_from_filename(FRArchive * archive,const char * filename)200 create_command_from_filename (FRArchive * archive, const char *filename)
201 {
202 ExtArchiverPlugin *archiver = NULL;
203
204 g_hash_table_foreach (ext_archivers,
205 archiver_lookup,
206 (gpointer) filename);
207
208 if (!found_archiver) return FALSE;
209
210 archiver = found_archiver;
211 found_archiver = NULL;
212
213 archive->command = archiver->archiver_new (archive->process,
214 filename, archive);
215 archive->is_compressed = archiver->is_compressed;
216
217 return TRUE;
218 }
219
220
221 static void
action_started(FRCommand * command,FRAction action,FRArchive * archive)222 action_started (FRCommand *command,
223 FRAction action,
224 FRArchive *archive)
225 {
226 gtk_signal_emit (GTK_OBJECT (archive),
227 fr_archive_signals[START],
228 action);
229 }
230
231
232 static void
action_performed(FRCommand * command,FRAction action,FRProcError error,FRArchive * archive)233 action_performed (FRCommand *command,
234 FRAction action,
235 FRProcError error,
236 FRArchive *archive)
237 {
238 #ifdef DEBUG
239 char *s_action = NULL;
240
241 switch (action) {
242 case FR_ACTION_LIST:
243 s_action = "List";
244 break;
245 case FR_ACTION_ADD:
246 s_action = "Add";
247 break;
248 case FR_ACTION_DELETE:
249 s_action = "Delete";
250 break;
251 case FR_ACTION_EXTRACT:
252 s_action = "Extract";
253 break;
254 }
255 g_print ("%s [DONE]\n", s_action);
256 #endif
257
258 gtk_signal_emit (GTK_OBJECT (archive),
259 fr_archive_signals[DONE],
260 action,
261 error);
262 }
263
264
265 void
fr_archive_new_file(FRArchive * archive,char * filename)266 fr_archive_new_file (FRArchive *archive, char *filename)
267 {
268 FRCommand *tmp_command;
269
270 if (filename == NULL)
271 return;
272
273 archive->read_only = FALSE;
274
275 if (archive->filename != NULL)
276 g_free (archive->filename);
277 archive->filename = g_strdup (filename);
278
279 tmp_command = archive->command;
280 if (! create_command_from_filename (archive, filename))
281 return;
282 if (tmp_command != NULL)
283 gtk_object_unref (GTK_OBJECT (tmp_command));
284
285 gtk_signal_connect (GTK_OBJECT (archive->command), "start",
286 GTK_SIGNAL_FUNC (action_started),
287 archive);
288 gtk_signal_connect (GTK_OBJECT (archive->command), "done",
289 GTK_SIGNAL_FUNC (action_performed),
290 archive);
291 }
292
293
294 gboolean
fr_archive_load(FRArchive * archive,const char * filename)295 fr_archive_load (FRArchive *archive,
296 const char *filename)
297 {
298 FRCommand *tmp_command;
299
300 g_return_val_if_fail (archive != NULL, FALSE);
301
302 if (access (filename, F_OK) != 0) /* file must exists. */
303 return FALSE;
304
305 archive->read_only = access (filename, W_OK) != 0;
306
307 /* without this check you can't call
308 * archive_load (archive, archive->filename) */
309
310 if (archive->filename != filename) {
311 if (archive->filename != NULL)
312 g_free (archive->filename);
313 archive->filename = g_strdup (filename);
314 }
315
316 tmp_command = archive->command;
317 if (!create_command_from_filename (archive, filename))
318 return FALSE;
319 if (tmp_command != NULL)
320 gtk_object_unref (GTK_OBJECT (tmp_command));
321
322 gtk_signal_connect (GTK_OBJECT (archive->command), "start",
323 GTK_SIGNAL_FUNC (action_started),
324 archive);
325
326 gtk_signal_connect (GTK_OBJECT (archive->command), "done",
327 GTK_SIGNAL_FUNC (action_performed),
328 archive);
329
330 fr_command_list (archive->command);
331
332 return TRUE;
333 }
334
335
336 void
fr_archive_reload(FRArchive * archive)337 fr_archive_reload (FRArchive *archive)
338 {
339 g_return_if_fail (archive != NULL);
340 g_return_if_fail (archive->filename != NULL);
341
342 fr_command_list (archive->command);
343 }
344
345
346 void
fr_archive_rename(FRArchive * archive,char * filename)347 fr_archive_rename (FRArchive *archive,
348 char *filename)
349 {
350 g_return_if_fail (archive != NULL);
351
352 if (archive->filename != NULL)
353 g_free (archive->filename);
354 archive->filename = g_strdup (filename);
355
356 fr_command_set_filename (archive->command, filename);
357 }
358
359
360 /* -- add -- */
361
362
363 static GimvImageInfo *
find_file_in_archive(FRArchive * archive,char * path)364 find_file_in_archive (FRArchive *archive, char *path)
365 {
366 GList *scan;
367
368 for (scan = archive->command->file_list; scan; scan = scan->next) {
369 GimvImageInfo *fdata = scan->data;
370
371 if (strcmp (path, fdata->filename) == 0)
372 return fdata;
373 }
374
375 return NULL;
376 }
377
378
379 #if 0
380 GList *
381 get_files_in_archive (FRArchive *archive,
382 GList *file_list,
383 gchar *base_dir,
384 gboolean skip_newer)
385 {
386 GList *files_in_arch = NULL;
387 GList *scan;
388
389 for (scan = file_list; scan; scan = scan->next) {
390 gchar *filename = scan->data;
391 gchar *fullpath;
392 GimvImageInfo *fdata;
393
394
395 fdata = find_file_in_archive (archive, filename);
396
397 if (fdata == NULL)
398 continue;
399
400 fullpath = g_strconcat (base_dir, "/", filename, NULL);
401
402 if (skip_newer
403 && isfile (fullpath)
404 && (fdata->st.st_mtime > get_file_mtime (fullpath))) {
405 g_free (fullpath);
406 continue;
407 }
408
409 files_in_arch = g_list_prepend (files_in_arch,
410 g_strdup (fdata->filename));
411 g_free (fullpath);
412 }
413
414 return files_in_arch;
415 }
416 #endif
417
418
419 void
fr_archive_add(FRArchive * archive,GList * file_list,gchar * base_dir,gboolean update)420 fr_archive_add (FRArchive *archive,
421 GList *file_list,
422 gchar *base_dir,
423 gboolean update)
424 {
425 if (archive->read_only)
426 return;
427
428 fr_process_clear (archive->process);
429
430 if (file_list == NULL) {
431 fr_command_add (archive->command, file_list, base_dir, update);
432 } else {
433 GList *scan;
434
435 for (scan = file_list; scan != NULL; ) {
436 GList *prev = scan->prev;
437 GList *chunk_list;
438 int l;
439
440 chunk_list = scan;
441 l = 0;
442 while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
443 if (l == 0)
444 l = strlen (scan->data);
445 prev = scan;
446 scan = scan->next;
447 if (scan != NULL)
448 l += strlen (scan->data);
449 }
450
451 prev->next = NULL;
452 fr_command_add (archive->command,
453 chunk_list,
454 base_dir,
455 update);
456 prev->next = scan;
457 }
458 }
459
460 fr_process_start (archive->process, FALSE);
461 }
462
463
464 #if 0
465 void
466 fr_archive_add_with_wildcard (FRArchive *archive,
467 const char *files,
468 gchar *base_dir,
469 gboolean update,
470 gboolean recursive,
471 gboolean follow_links,
472 gboolean same_fs,
473 gboolean no_backup_files,
474 gboolean no_dot_files,
475 gboolean ignore_case)
476 {
477 GList *file_list;
478
479 if (archive->read_only)
480 return;
481
482 file_list = get_wildcard_file_list (base_dir,
483 files,
484 recursive,
485 follow_links,
486 same_fs,
487 no_backup_files,
488 no_dot_files,
489 ignore_case);
490 fr_archive_add (archive,
491 file_list,
492 base_dir,
493 update);
494 g_list_foreach (file_list, (GFunc) g_free, NULL);
495 g_free (file_list);
496 }
497
498
499 void
500 fr_archive_add_directory (FRArchive *archive,
501 const char *directory,
502 gchar *base_dir,
503 gboolean update)
504
505 {
506 GList *file_list;
507
508 if (archive->read_only)
509 return;
510
511 file_list = get_directory_file_list (directory, base_dir);
512 fr_archive_add (archive,
513 file_list,
514 base_dir,
515 update);
516 g_list_foreach (file_list, (GFunc) g_free, NULL);
517 g_list_free (file_list);
518 }
519 #endif
520
521
522 /* -- remove -- */
523
524
525 void
fr_archive_remove(FRArchive * archive,GList * file_list)526 fr_archive_remove (FRArchive *archive,
527 GList *file_list)
528 {
529 g_return_if_fail (archive != NULL);
530
531 if (archive->read_only)
532 return;
533
534 fr_process_clear (archive->process);
535
536 if (file_list == NULL) {
537 fr_command_delete (archive->command, file_list);
538 } else {
539 GList *scan;
540
541 for (scan = file_list; scan != NULL; ) {
542 GList *prev = scan->prev;
543 GList *chunk_list;
544 int l;
545
546 chunk_list = scan;
547 l = 0;
548 while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
549 if (l == 0)
550 l = strlen (scan->data);
551 prev = scan;
552 scan = scan->next;
553 if (scan != NULL)
554 l += strlen (scan->data);
555 }
556
557 prev->next = NULL;
558 fr_command_delete (archive->command, chunk_list);
559 prev->next = scan;
560 }
561 }
562
563 fr_process_start (archive->process, FALSE);
564 }
565
566
567 /* -- extract -- */
568
569
570 static void
move_files_to_dir(FRArchive * archive,GList * file_list,char * source_dir,char * dest_dir)571 move_files_to_dir (FRArchive *archive,
572 GList *file_list,
573 char *source_dir,
574 char *dest_dir)
575 {
576 GList *scan;
577
578 fr_process_begin_command (archive->process, "mv");
579 fr_process_add_arg (archive->process, "-f");
580 for (scan = file_list; scan; scan = scan->next) {
581 char path[4096];
582 char *filename = scan->data;
583
584 if (filename[0] == '/')
585 sprintf (path, "%s%s", source_dir, filename);
586 else
587 sprintf (path, "%s/%s", source_dir, filename);
588
589 fr_process_add_arg (archive->process, path);
590 }
591 fr_process_add_arg (archive->process, dest_dir);
592 fr_process_end_command (archive->process);
593 }
594
595
596 static void
move_files_in_chunks(FRArchive * archive,GList * file_list,char * temp_dir,char * dest_dir)597 move_files_in_chunks (FRArchive *archive,
598 GList *file_list,
599 char *temp_dir,
600 char *dest_dir)
601 {
602 GList *scan;
603 int temp_dir_l = strlen (temp_dir);
604
605 for (scan = file_list; scan != NULL; ) {
606 GList *prev = scan->prev;
607 GList *chunk_list;
608 int l;
609
610 chunk_list = scan;
611 l = 0;
612 while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
613 if (l == 0)
614 l = temp_dir_l + 1 + strlen (scan->data);
615 prev = scan;
616 scan = scan->next;
617 if (scan != NULL)
618 l += temp_dir_l + 1 + strlen (scan->data);
619 }
620
621 prev->next = NULL;
622 move_files_to_dir (archive,
623 chunk_list,
624 temp_dir, dest_dir);
625 prev->next = scan;
626 }
627 }
628
629
630 static void
extract_in_chunks(FRCommand * command,GList * file_list,char * dest_dir,gboolean overwrite,gboolean skip_older,gboolean junk_paths)631 extract_in_chunks (FRCommand *command,
632 GList *file_list,
633 char *dest_dir,
634 gboolean overwrite,
635 gboolean skip_older,
636 gboolean junk_paths)
637 {
638 GList *scan;
639
640 if (file_list == NULL) {
641 fr_command_extract (command,
642 file_list,
643 dest_dir,
644 overwrite,
645 skip_older,
646 junk_paths);
647 return;
648 }
649
650 for (scan = file_list; scan != NULL; ) {
651 GList *prev = scan->prev;
652 GList *chunk_list;
653 int l;
654
655 chunk_list = scan;
656 l = 0;
657 while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
658 if (l == 0)
659 l = strlen (scan->data);
660 prev = scan;
661 scan = scan->next;
662 if (scan != NULL)
663 l += strlen (scan->data);
664 }
665
666 prev->next = NULL;
667 fr_command_extract (command,
668 chunk_list,
669 dest_dir,
670 overwrite,
671 skip_older,
672 junk_paths);
673 prev->next = scan;
674 }
675 }
676
677
678 void
fr_archive_extract(FRArchive * archive,GList * file_list,char * dest_dir,gboolean skip_older,gboolean overwrite,gboolean junk_paths)679 fr_archive_extract (FRArchive *archive,
680 GList * file_list,
681 char * dest_dir,
682 gboolean skip_older,
683 gboolean overwrite,
684 gboolean junk_paths)
685 {
686 GList *filtered;
687 GList *scan;
688 gboolean extract_all;
689 gboolean move_to_dest_dir;
690
691 g_return_if_fail (archive != NULL);
692
693 /* if a command supports all the requested options... */
694
695 if (! (! archive->command->propExtractCanAvoidOverwrite && ! overwrite)
696 && ! (! archive->command->propExtractCanSkipOlder && skip_older)
697 && ! (! archive->command->propExtractCanJunkPaths && junk_paths)) {
698
699 fr_process_clear (archive->process);
700 extract_in_chunks (archive->command,
701 file_list,
702 dest_dir,
703 overwrite,
704 skip_older,
705 junk_paths);
706
707 fr_process_start (archive->process, FALSE);
708 return;
709 }
710
711 /* .. else we have to implement the unsupported ones. */
712
713 fr_process_clear (archive->process);
714
715 move_to_dest_dir = (junk_paths
716 && ! archive->command->propExtractCanJunkPaths);
717
718 extract_all = (file_list == NULL);
719 if (extract_all) {
720 GList *scan;
721
722 scan = archive->command->file_list;
723 for (; scan; scan = scan->next) {
724 GimvImageInfo *fdata = scan->data;
725 file_list = g_list_prepend (file_list, g_strdup (fdata->filename));
726 }
727 }
728
729 filtered = NULL;
730 for (scan = file_list; scan; scan = scan->next) {
731 GimvImageInfo *fdata;
732 char *filename = scan->data;
733 char full_name[4096];
734
735 if (filename[0] == '/')
736 sprintf (full_name, "%s%s", dest_dir, filename);
737 else
738 sprintf (full_name, "%s/%s", dest_dir, filename);
739 fdata = find_file_in_archive (archive, filename);
740
741 if (fdata == NULL)
742 continue;
743
744 if (skip_older
745 && isfile (full_name)
746 && (fdata->st.st_mtime < get_file_mtime (full_name)))
747 continue;
748
749 if (! overwrite && isfile (full_name))
750 continue;
751
752 filtered = g_list_prepend (filtered, fdata->filename);
753 }
754
755 if (filtered != NULL)
756 filtered = g_list_reverse (filtered);
757 else {
758 /* all files got filtered, do nothing. */
759 g_list_foreach (file_list, (GFunc) g_free, NULL);
760 g_list_free (file_list);
761 return;
762 }
763
764 if (move_to_dest_dir) {
765 char *temp_dir;
766
767 temp_dir = g_strdup_printf ("%s%s%d",
768 g_get_tmp_dir (),
769 "/gimv.",
770 getpid ());
771 ensure_dir_exists (temp_dir);
772 extract_in_chunks (archive->command,
773 filtered,
774 temp_dir,
775 overwrite,
776 skip_older,
777 junk_paths);
778
779 move_files_in_chunks (archive, filtered, temp_dir, dest_dir);
780
781 /* remove the temp dir. */
782 fr_process_begin_command (archive->process, "rm");
783 fr_process_add_arg (archive->process, "-rf");
784 fr_process_add_arg (archive->process, temp_dir);
785 fr_process_end_command (archive->process);
786
787 g_free (temp_dir);
788 } else {
789 extract_in_chunks (archive->command,
790 filtered,
791 dest_dir,
792 overwrite,
793 skip_older,
794 junk_paths);
795 }
796
797 if (filtered != NULL)
798 g_list_free (filtered);
799
800 if (extract_all) {
801 /* the list has been created in this function. */
802 g_list_foreach (file_list, (GFunc) g_free, NULL);
803 g_list_free (file_list);
804 }
805
806 fr_process_start (archive->process, FALSE);
807 }
808
809
810 gchar *
fr_archive_utils_get_file_name_ext(const char * filename)811 fr_archive_utils_get_file_name_ext (const char *filename)
812 {
813 GList *node;
814
815 node = archive_ext_list;
816 while (node) {
817 gchar *ext = node->data;
818
819 if (ext && *ext && fileutil_extension_is (filename, ext)) {
820 return ext;
821 }
822 node = g_list_next (node);
823 }
824
825 return NULL;
826 }
827
828
829 gboolean
fr_archive_plugin_regist(const gchar * plugin_name,const gchar * module_name,gpointer impl,gint size)830 fr_archive_plugin_regist (const gchar *plugin_name,
831 const gchar *module_name,
832 gpointer impl,
833 gint size)
834 {
835 ExtArchiverPlugin *archiver = impl;
836
837 g_return_val_if_fail (module_name, FALSE);
838 g_return_val_if_fail (archiver, FALSE);
839 g_return_val_if_fail (size > 0, FALSE);
840 g_return_val_if_fail (archiver->if_version == GIMV_ARCHIVER_IF_VERSION, FALSE);
841 g_return_val_if_fail (archiver->format && archiver->archiver_new, FALSE);
842
843 if (!ext_archivers)
844 ext_archivers = g_hash_table_new (g_str_hash, g_str_equal);
845
846 g_hash_table_insert (ext_archivers, archiver->format, archiver);
847 archive_ext_list = g_list_append (archive_ext_list,
848 g_strdup (archiver->format));
849
850 return TRUE;
851 }
852