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