1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * Engrampa
5 *
6 * Copyright (C) 2008 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 02110-1301, USA.
21 */
22
23 #include <string.h>
24 #include <glib.h>
25 #include <gio/gio.h>
26 #include "glib-utils.h"
27 #include "file-utils.h"
28 #include "gio-utils.h"
29
30
31 #define N_FILES_PER_REQUEST 128
32
33
34 /* -- filter -- */
35
36
37 typedef enum {
38 FILTER_DEFAULT = 0,
39 FILTER_NODOTFILES = 1 << 1,
40 FILTER_IGNORECASE = 1 << 2,
41 FILTER_NOBACKUPFILES = 1 << 3
42 } FilterOptions;
43
44
45 typedef struct {
46 char *pattern;
47 FilterOptions options;
48 GRegex **regexps;
49 } Filter;
50
51
52 static Filter *
filter_new(const char * pattern,FilterOptions options)53 filter_new (const char *pattern,
54 FilterOptions options)
55 {
56 Filter *filter;
57 GRegexCompileFlags flags;
58
59 filter = g_new0 (Filter, 1);
60
61 if ((pattern != NULL) && (strcmp (pattern, "*") != 0))
62 filter->pattern = g_strdup (pattern);
63
64 filter->options = options;
65 if (filter->options & FILTER_IGNORECASE)
66 flags = G_REGEX_CASELESS;
67 else
68 flags = 0;
69 filter->regexps = search_util_get_regexps (pattern, flags);
70
71 return filter;
72 }
73
74
75 static void
filter_destroy(Filter * filter)76 filter_destroy (Filter *filter)
77 {
78 if (filter == NULL)
79 return;
80
81 g_free (filter->pattern);
82 if (filter->regexps != NULL)
83 free_regexps (filter->regexps);
84 g_free (filter);
85 }
86
87
88 static gboolean
filter_matches(Filter * filter,const char * name)89 filter_matches (Filter *filter,
90 const char *name)
91 {
92 const char *file_name;
93 char *utf8_name;
94 gboolean matched;
95
96 g_return_val_if_fail (name != NULL, FALSE);
97
98 file_name = file_name_from_path (name);
99
100 if ((filter->options & FILTER_NODOTFILES)
101 && ((file_name[0] == '.') || (strstr (file_name, "/.") != NULL)))
102 return FALSE;
103
104 if ((filter->options & FILTER_NOBACKUPFILES)
105 && (file_name[strlen (file_name) - 1] == '~'))
106 return FALSE;
107
108 if (filter->pattern == NULL)
109 return TRUE;
110
111 utf8_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
112 matched = match_regexps (filter->regexps, utf8_name, 0);
113 g_free (utf8_name);
114
115 return matched;
116 }
117
118
119 static gboolean
filter_empty(Filter * filter)120 filter_empty (Filter *filter)
121 {
122 return ((filter->pattern == NULL) || (strcmp (filter->pattern, "*") == 0));
123 }
124
125
126 /* -- g_directory_foreach_child -- */
127
128
129 typedef struct {
130 GFile *base_directory;
131 gboolean recursive;
132 gboolean follow_links;
133 StartDirCallback start_dir_func;
134 ForEachChildCallback for_each_file_func;
135 ForEachDoneCallback done_func;
136 gpointer user_data;
137
138 /* private */
139
140 GFile *current;
141 GHashTable *already_visited;
142 GList *to_visit;
143 GCancellable *cancellable;
144 GFileEnumerator *enumerator;
145 GError *error;
146 guint source_id;
147 } ForEachChildData;
148
149
150 static void
for_each_child_data_free(ForEachChildData * fec)151 for_each_child_data_free (ForEachChildData *fec)
152 {
153 if (fec == NULL)
154 return;
155
156 if (fec->base_directory != NULL)
157 g_object_unref (fec->base_directory);
158 if (fec->current != NULL)
159 g_object_unref (fec->current);
160 if (fec->already_visited)
161 g_hash_table_destroy (fec->already_visited);
162 if (fec->to_visit != NULL)
163 g_list_free (fec->to_visit);
164 g_free (fec);
165 }
166
167
168 static gboolean
for_each_child_done_cb(gpointer user_data)169 for_each_child_done_cb (gpointer user_data)
170 {
171 ForEachChildData *fec = user_data;
172
173 g_source_remove (fec->source_id);
174 if (fec->current != NULL) {
175 g_object_unref (fec->current);
176 fec->current = NULL;
177 }
178 if (fec->done_func)
179 fec->done_func (fec->error, fec->user_data);
180 for_each_child_data_free (fec);
181
182 return FALSE;
183 }
184
185
186 static void
for_each_child_done(ForEachChildData * fec)187 for_each_child_done (ForEachChildData *fec)
188 {
189 fec->source_id = g_idle_add (for_each_child_done_cb, fec);
190 }
191
192
193 static void for_each_child_start_current (ForEachChildData *fec);
194
195
196 static gboolean
for_each_child_start_cb(gpointer user_data)197 for_each_child_start_cb (gpointer user_data)
198 {
199 ForEachChildData *fec = user_data;
200
201 g_source_remove (fec->source_id);
202 for_each_child_start_current (fec);
203
204 return FALSE;
205 }
206
207
208 static void
for_each_child_start(ForEachChildData * fec)209 for_each_child_start (ForEachChildData *fec)
210 {
211 fec->source_id = g_idle_add (for_each_child_start_cb, fec);
212 }
213
214
215 static void
for_each_child_set_current_uri(ForEachChildData * fec,const char * directory)216 for_each_child_set_current_uri (ForEachChildData *fec,
217 const char *directory)
218 {
219 if (fec->current != NULL)
220 g_object_unref (fec->current);
221 fec->current = g_file_new_for_uri (directory);
222 }
223
224
225 static void
for_each_child_set_current(ForEachChildData * fec,GFile * directory)226 for_each_child_set_current (ForEachChildData *fec,
227 GFile *directory)
228 {
229 if (fec->current != NULL)
230 g_object_unref (fec->current);
231 fec->current = g_file_dup (directory);
232 }
233
234 static void
for_each_child_start_next_sub_directory(ForEachChildData * fec)235 for_each_child_start_next_sub_directory (ForEachChildData *fec)
236 {
237 char *sub_directory = NULL;
238
239 if (fec->to_visit != NULL) {
240 GList *tmp;
241
242 sub_directory = (char*) fec->to_visit->data;
243 tmp = fec->to_visit;
244 fec->to_visit = g_list_remove_link (fec->to_visit, tmp);
245 g_list_free (tmp);
246 }
247
248 if (sub_directory != NULL) {
249 for_each_child_set_current_uri (fec, sub_directory);
250 for_each_child_start (fec);
251 }
252 else
253 for_each_child_done (fec);
254 }
255
256
257 static void
for_each_child_close_enumerator(GObject * source_object,GAsyncResult * result,gpointer user_data)258 for_each_child_close_enumerator (GObject *source_object,
259 GAsyncResult *result,
260 gpointer user_data)
261 {
262 ForEachChildData *fec = user_data;
263 GError *error = NULL;
264
265 if (! g_file_enumerator_close_finish (fec->enumerator,
266 result,
267 &error))
268 {
269 if (fec->error == NULL)
270 fec->error = g_error_copy (error);
271 else
272 g_clear_error (&error);
273 }
274
275 if ((fec->error == NULL) && fec->recursive)
276 for_each_child_start_next_sub_directory (fec);
277 else
278 for_each_child_done (fec);
279 }
280
281
282 static void
for_each_child_next_files_ready(GObject * source_object,GAsyncResult * result,gpointer user_data)283 for_each_child_next_files_ready (GObject *source_object,
284 GAsyncResult *result,
285 gpointer user_data)
286 {
287 ForEachChildData *fec = user_data;
288 GList *children, *scan;
289
290 children = g_file_enumerator_next_files_finish (fec->enumerator,
291 result,
292 &(fec->error));
293
294 if (children == NULL) {
295 g_file_enumerator_close_async (fec->enumerator,
296 G_PRIORITY_DEFAULT,
297 fec->cancellable,
298 for_each_child_close_enumerator,
299 fec);
300 return;
301 }
302
303 for (scan = children; scan; scan = scan->next) {
304 GFileInfo *child_info = scan->data;
305 GFile *f;
306 char *uri;
307
308 f = g_file_get_child (fec->current, g_file_info_get_name (child_info));
309 uri = g_file_get_uri (f);
310
311 if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) {
312 /* avoid to visit a directory more than once */
313
314 if (g_hash_table_lookup (fec->already_visited, uri) == NULL) {
315 char *sub_directory;
316
317 sub_directory = g_strdup (uri);
318 g_hash_table_insert (fec->already_visited, sub_directory, GINT_TO_POINTER (1));
319 fec->to_visit = g_list_append (fec->to_visit, sub_directory);
320 }
321 }
322
323 fec->for_each_file_func (uri, child_info, fec->user_data);
324
325 g_free (uri);
326 g_object_unref (f);
327 }
328
329 g_file_enumerator_next_files_async (fec->enumerator,
330 N_FILES_PER_REQUEST,
331 G_PRIORITY_DEFAULT,
332 fec->cancellable,
333 for_each_child_next_files_ready,
334 fec);
335 }
336
337 static void
for_each_child_ready(GObject * source_object,GAsyncResult * result,gpointer user_data)338 for_each_child_ready (GObject *source_object,
339 GAsyncResult *result,
340 gpointer user_data)
341 {
342 ForEachChildData *fec = user_data;
343
344 fec->enumerator = g_file_enumerate_children_finish (fec->current, result, &(fec->error));
345 if (fec->enumerator == NULL) {
346 for_each_child_done (fec);
347 return;
348 }
349
350 g_file_enumerator_next_files_async (fec->enumerator,
351 N_FILES_PER_REQUEST,
352 G_PRIORITY_DEFAULT,
353 fec->cancellable,
354 for_each_child_next_files_ready,
355 fec);
356 }
357
358
359 static void
for_each_child_start_current(ForEachChildData * fec)360 for_each_child_start_current (ForEachChildData *fec)
361 {
362 if (fec->start_dir_func != NULL) {
363 char *directory;
364 DirOp op;
365
366 directory = g_file_get_uri (fec->current);
367 op = fec->start_dir_func (directory, &(fec->error), fec->user_data);
368 g_free (directory);
369
370 switch (op) {
371 case DIR_OP_SKIP:
372 for_each_child_start_next_sub_directory (fec);
373 return;
374 case DIR_OP_STOP:
375 for_each_child_done (fec);
376 return;
377 case DIR_OP_CONTINUE:
378 break;
379 }
380 }
381
382 g_file_enumerate_children_async (fec->current,
383 "standard::name,standard::type",
384 fec->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
385 G_PRIORITY_DEFAULT,
386 fec->cancellable,
387 for_each_child_ready,
388 fec);
389 }
390
391 /**
392 * g_directory_foreach_child:
393 * @directory: The directory to visit.
394 * @recursive: Whether to traverse the @directory recursively.
395 * @follow_links: Whether to dereference the symbolic links.
396 * @cancellable: An optional @GCancellable object, used to cancel the process.
397 * @start_dir_func: the function called for each sub-directory, or %NULL if
398 * not needed.
399 * @for_each_file_func: the function called for each file. Can't be %NULL.
400 * @done_func: the function called at the end of the traversing process.
401 * Can't be %NULL.
402 * @user_data: data to pass to @done_func
403 *
404 * Traverse the @directory's filesystem structure calling the
405 * @for_each_file_func function for each file in the directory; the
406 * @start_dir_func function on each directory before it's going to be
407 * traversed, this includes @directory too; the @done_func function is
408 * called at the end of the process.
409 * Some traversing options are available: if @recursive is TRUE the
410 * directory is traversed recursively; if @follow_links is TRUE, symbolic
411 * links are dereferenced, otherwise they are returned as links.
412 * Each callback uses the same @user_data additional parameter.
413 */
414 void
g_directory_foreach_child(GFile * directory,gboolean recursive,gboolean follow_links,GCancellable * cancellable,StartDirCallback start_dir_func,ForEachChildCallback for_each_file_func,ForEachDoneCallback done_func,gpointer user_data)415 g_directory_foreach_child (GFile *directory,
416 gboolean recursive,
417 gboolean follow_links,
418 GCancellable *cancellable,
419 StartDirCallback start_dir_func,
420 ForEachChildCallback for_each_file_func,
421 ForEachDoneCallback done_func,
422 gpointer user_data)
423 {
424 ForEachChildData *fec;
425
426 g_return_if_fail (for_each_file_func != NULL);
427
428 fec = g_new0 (ForEachChildData, 1);
429
430 fec->base_directory = g_object_ref (directory);
431 fec->recursive = recursive;
432 fec->follow_links = follow_links;
433 fec->cancellable = cancellable;
434 fec->start_dir_func = start_dir_func;
435 fec->for_each_file_func = for_each_file_func;
436 fec->done_func = done_func;
437 fec->user_data = user_data;
438 fec->already_visited = g_hash_table_new_full (g_str_hash,
439 g_str_equal,
440 g_free,
441 NULL);
442
443 for_each_child_set_current (fec, fec->base_directory);
444 for_each_child_start_current (fec);
445 }
446
447
448 /* -- get_file_list_data -- */
449
450
451 typedef struct {
452 GList *files;
453 GList *dirs;
454 GFile *directory;
455 GFile *base_dir;
456 GCancellable *cancellable;
457 ListReadyCallback done_func;
458 gpointer done_data;
459 GList *to_visit;
460 GList *current_dir;
461 Filter *include_filter;
462 Filter *exclude_filter;
463 Filter *exclude_folders_filter;
464 guint visit_timeout;
465 } GetFileListData;
466
467
468 static void
get_file_list_data_free(GetFileListData * gfl)469 get_file_list_data_free (GetFileListData *gfl)
470 {
471 if (gfl == NULL)
472 return;
473
474 filter_destroy (gfl->include_filter);
475 filter_destroy (gfl->exclude_filter);
476 filter_destroy (gfl->exclude_folders_filter);
477 path_list_free (gfl->files);
478 path_list_free (gfl->dirs);
479 path_list_free (gfl->to_visit);
480 if (gfl->directory != NULL)
481 g_object_unref (gfl->directory);
482 if (gfl->base_dir != NULL)
483 g_object_unref (gfl->base_dir);
484 g_free (gfl);
485 }
486
487
488 /* -- g_directory_list_async -- */
489
490
491 static GList*
get_relative_file_list(GList * rel_list,GList * file_list,GFile * base_dir)492 get_relative_file_list (GList *rel_list,
493 GList *file_list,
494 GFile *base_dir)
495 {
496 GList *scan;
497
498 if (base_dir == NULL)
499 return NULL;
500
501 for (scan = file_list; scan; scan = scan->next) {
502 char *full_path = scan->data;
503 GFile *f;
504 char *relative_path;
505
506 f = g_file_new_for_commandline_arg (full_path);
507 relative_path = g_file_get_relative_path (base_dir, f);
508 if (relative_path != NULL)
509 rel_list = g_list_prepend (rel_list, relative_path);
510 g_object_unref (f);
511 }
512
513 return rel_list;
514 }
515
516
517 static GList*
get_dir_list_from_file_list(GHashTable * h_dirs,const char * base_dir,GList * files,gboolean is_dir_list)518 get_dir_list_from_file_list (GHashTable *h_dirs,
519 const char *base_dir,
520 GList *files,
521 gboolean is_dir_list)
522 {
523 GList *scan;
524 GList *dir_list = NULL;
525 size_t base_dir_len;
526
527 if (base_dir == NULL)
528 base_dir = "";
529 base_dir_len = strlen (base_dir);
530
531 for (scan = files; scan; scan = scan->next) {
532 char *filename = scan->data;
533 char *dir_name;
534
535 if (strlen (filename) <= base_dir_len)
536 continue;
537
538 if (is_dir_list)
539 dir_name = g_strdup (filename + base_dir_len + 1);
540 else
541 dir_name = remove_level_from_path (filename + base_dir_len + 1);
542
543 while ((dir_name != NULL) && (dir_name[0] != '\0') && (strcmp (dir_name, "/") != 0)) {
544 char *tmp;
545 char *dir;
546
547 /* avoid to insert duplicated folders */
548
549 dir = g_strconcat (base_dir, "/", dir_name, NULL);
550 if (g_hash_table_lookup (h_dirs, dir) == NULL) {
551 g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
552 dir_list = g_list_prepend (dir_list, dir);
553 }
554 else
555 g_free (dir);
556
557 tmp = dir_name;
558 dir_name = remove_level_from_path (tmp);
559 g_free (tmp);
560 }
561
562 g_free (dir_name);
563 }
564
565 return dir_list;
566 }
567
568
569 static void
get_file_list_done(GError * error,gpointer user_data)570 get_file_list_done (GError *error,
571 gpointer user_data)
572 {
573 GetFileListData *gfl = user_data;
574 GHashTable *h_dirs;
575 GList *scan;
576 char *uri;
577
578 gfl->files = g_list_reverse (gfl->files);
579 gfl->dirs = g_list_reverse (gfl->dirs);
580
581 if (! filter_empty (gfl->include_filter) || (gfl->exclude_filter->pattern != NULL)) {
582 path_list_free (gfl->dirs);
583 gfl->dirs = NULL;
584 }
585
586 h_dirs = g_hash_table_new (g_str_hash, g_str_equal);
587
588 /* Always include the base directory, this way empty base
589 * directories are added to the archive as well. */
590
591 if (gfl->base_dir != NULL) {
592 char *dir;
593
594 dir = g_file_get_uri (gfl->base_dir);
595 gfl->dirs = g_list_prepend (gfl->dirs, dir);
596 g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
597 }
598
599 /* Add all the parent directories in gfl->files/gfl->dirs to the
600 * gfl->dirs list, the hash table is used to avoid duplicated
601 * entries. */
602
603 for (scan = gfl->dirs; scan; scan = scan->next)
604 g_hash_table_insert (h_dirs, (char*)scan->data, GINT_TO_POINTER (1));
605
606 uri = g_file_get_uri (gfl->base_dir);
607 gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->files, FALSE));
608
609 if (filter_empty (gfl->include_filter))
610 gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->dirs, TRUE));
611
612 g_free (uri);
613 /**/
614
615 if (error == NULL) {
616 GList *rel_files, *rel_dirs;
617
618 if (gfl->base_dir != NULL) {
619 rel_files = get_relative_file_list (NULL, gfl->files, gfl->base_dir);
620 rel_dirs = get_relative_file_list (NULL, gfl->dirs, gfl->base_dir);
621 }
622 else {
623 rel_files = gfl->files;
624 rel_dirs = gfl->dirs;
625 gfl->files = NULL;
626 gfl->dirs = NULL;
627 }
628
629 /* rel_files/rel_dirs must be deallocated in done_func */
630 gfl->done_func (rel_files, rel_dirs, NULL, gfl->done_data);
631 }
632 else
633 gfl->done_func (NULL, NULL, error, gfl->done_data);
634
635 g_hash_table_destroy (h_dirs);
636 get_file_list_data_free (gfl);
637 }
638
639
640 static void
get_file_list_for_each_file(const char * uri,GFileInfo * info,gpointer user_data)641 get_file_list_for_each_file (const char *uri,
642 GFileInfo *info,
643 gpointer user_data)
644 {
645 GetFileListData *gfl = user_data;
646
647 switch (g_file_info_get_file_type (info)) {
648 case G_FILE_TYPE_REGULAR:
649 if (filter_matches (gfl->include_filter, uri))
650 if ((gfl->exclude_filter->pattern == NULL) || ! filter_matches (gfl->exclude_filter, uri))
651 gfl->files = g_list_prepend (gfl->files, g_strdup (uri));
652 break;
653 default:
654 break;
655 }
656 }
657
658
659 static DirOp
get_file_list_start_dir(const char * uri,GError ** error,gpointer user_data)660 get_file_list_start_dir (const char *uri,
661 GError **error,
662 gpointer user_data)
663 {
664 GetFileListData *gfl = user_data;
665
666 if ((gfl->exclude_folders_filter->pattern == NULL) || ! filter_matches (gfl->exclude_folders_filter, uri)) {
667 gfl->dirs = g_list_prepend (gfl->dirs, g_strdup (uri));
668 return DIR_OP_CONTINUE;
669 }
670 else
671 return DIR_OP_SKIP;
672 }
673
674
675 void
g_directory_list_async(const char * directory,const char * base_dir,gboolean recursive,gboolean follow_links,gboolean no_backup_files,gboolean no_dot_files,const char * include_files,const char * exclude_files,const char * exclude_folders,gboolean ignorecase,GCancellable * cancellable,ListReadyCallback done_func,gpointer done_data)676 g_directory_list_async (const char *directory,
677 const char *base_dir,
678 gboolean recursive,
679 gboolean follow_links,
680 gboolean no_backup_files,
681 gboolean no_dot_files,
682 const char *include_files,
683 const char *exclude_files,
684 const char *exclude_folders,
685 gboolean ignorecase,
686 GCancellable *cancellable,
687 ListReadyCallback done_func,
688 gpointer done_data)
689 {
690 GetFileListData *gfl;
691 FilterOptions filter_options;
692
693 gfl = g_new0 (GetFileListData, 1);
694 gfl->directory = g_file_new_for_commandline_arg (directory);
695 gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
696 gfl->done_func = done_func;
697 gfl->done_data = done_data;
698
699 filter_options = FILTER_DEFAULT;
700 if (no_backup_files)
701 filter_options |= FILTER_NOBACKUPFILES;
702 if (no_dot_files)
703 filter_options |= FILTER_NODOTFILES;
704 if (ignorecase)
705 filter_options |= FILTER_IGNORECASE;
706 gfl->include_filter = filter_new (include_files, filter_options);
707 gfl->exclude_filter = filter_new (exclude_files, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
708 gfl->exclude_folders_filter = filter_new (exclude_folders, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
709
710 g_directory_foreach_child (gfl->directory,
711 recursive,
712 follow_links,
713 cancellable,
714 get_file_list_start_dir,
715 get_file_list_for_each_file,
716 get_file_list_done,
717 gfl);
718 }
719
720
721 /* -- g_list_items_async -- */
722
723
724 static void get_items_for_current_dir (GetFileListData *gfl);
725
726
727 static gboolean
get_items_for_next_dir_idle_cb(gpointer data)728 get_items_for_next_dir_idle_cb (gpointer data)
729 {
730 GetFileListData *gfl = data;
731
732 g_source_remove (gfl->visit_timeout);
733 gfl->visit_timeout = 0;
734
735 gfl->current_dir = g_list_next (gfl->current_dir);
736 get_items_for_current_dir (gfl);
737
738 return FALSE;
739 }
740
741
742 static void
get_items_for_current_dir_done(GList * files,GList * dirs,GError * error,gpointer data)743 get_items_for_current_dir_done (GList *files,
744 GList *dirs,
745 GError *error,
746 gpointer data)
747 {
748 GetFileListData *gfl = data;
749
750 if (error != NULL) {
751 if (gfl->done_func)
752 gfl->done_func (NULL, NULL, error, gfl->done_data);
753 path_list_free (files);
754 path_list_free (dirs);
755 get_file_list_data_free (gfl);
756 return;
757 }
758
759 gfl->files = g_list_concat (gfl->files, files);
760 gfl->dirs = g_list_concat (gfl->dirs, dirs);
761
762 gfl->visit_timeout = g_idle_add (get_items_for_next_dir_idle_cb, gfl);
763 }
764
765
766 static void
get_items_for_current_dir(GetFileListData * gfl)767 get_items_for_current_dir (GetFileListData *gfl)
768 {
769 GFile *current_dir;
770 char *directory_name;
771 GFile *directory_file;
772 char *directory_uri;
773 char *base_dir_uri;
774
775 if (gfl->current_dir == NULL) {
776 if (gfl->done_func) {
777 /* gfl->files/gfl->dirs must be deallocated in gfl->done_func */
778 gfl->done_func (gfl->files, gfl->dirs, NULL, gfl->done_data);
779 gfl->files = NULL;
780 gfl->dirs = NULL;
781 }
782 get_file_list_data_free (gfl);
783 return;
784 }
785
786 current_dir = g_file_new_for_uri ((char*) gfl->current_dir->data);
787 directory_name = g_file_get_basename (current_dir);
788 directory_file = g_file_get_child (gfl->base_dir, directory_name);
789 directory_uri = g_file_get_uri (directory_file);
790 base_dir_uri = g_file_get_uri (gfl->base_dir);
791
792 g_directory_list_all_async (directory_uri,
793 base_dir_uri,
794 TRUE,
795 gfl->cancellable,
796 get_items_for_current_dir_done,
797 gfl);
798
799 g_free (base_dir_uri);
800 g_free (directory_uri);
801 g_object_unref (directory_file);
802 g_free (directory_name);
803 g_object_unref (current_dir);
804 }
805
806
807 void
g_list_items_async(GList * items,const char * base_dir,GCancellable * cancellable,ListReadyCallback done_func,gpointer done_data)808 g_list_items_async (GList *items,
809 const char *base_dir,
810 GCancellable *cancellable,
811 ListReadyCallback done_func,
812 gpointer done_data)
813 {
814 GetFileListData *gfl;
815 int base_len;
816 GList *scan;
817
818 g_return_if_fail (base_dir != NULL);
819
820 gfl = g_new0 (GetFileListData, 1);
821 gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
822 gfl->cancellable = cancellable;
823 gfl->done_func = done_func;
824 gfl->done_data = done_data;
825
826 base_len = 0;
827 if (strcmp (base_dir, "/") != 0)
828 base_len = strlen (base_dir);
829
830 for (scan = items; scan; scan = scan->next) {
831 char *uri = scan->data;
832
833 /* FIXME: this is not async */
834 if (uri_is_dir (uri)) {
835 gfl->to_visit = g_list_prepend (gfl->to_visit, g_strdup (uri));
836 }
837 else {
838 char *rel_path = g_uri_unescape_string (uri + base_len + 1, NULL);
839 gfl->files = g_list_prepend (gfl->files, rel_path);
840 }
841 }
842
843 gfl->current_dir = gfl->to_visit;
844 get_items_for_current_dir (gfl);
845 }
846
847
848 /* -- g_copy_files_async -- */
849
850
851 typedef struct {
852 GList *sources;
853 GList *destinations;
854 GFileCopyFlags flags;
855 int io_priority;
856 GCancellable *cancellable;
857 CopyProgressCallback progress_callback;
858 gpointer progress_callback_data;
859 CopyDoneCallback callback;
860 gpointer user_data;
861
862 GList *source;
863 GList *destination;
864 int n_file;
865 int tot_files;
866 } CopyFilesData;
867
868
869 static CopyFilesData*
copy_files_data_new(GList * sources,GList * destinations,GFileCopyFlags flags,int io_priority,GCancellable * cancellable,CopyProgressCallback progress_callback,gpointer progress_callback_data,CopyDoneCallback callback,gpointer user_data)870 copy_files_data_new (GList *sources,
871 GList *destinations,
872 GFileCopyFlags flags,
873 int io_priority,
874 GCancellable *cancellable,
875 CopyProgressCallback progress_callback,
876 gpointer progress_callback_data,
877 CopyDoneCallback callback,
878 gpointer user_data)
879 {
880 CopyFilesData *cfd;
881
882 cfd = g_new0 (CopyFilesData, 1);
883 cfd->sources = gio_file_list_dup (sources);
884 cfd->destinations = gio_file_list_dup (destinations);
885 cfd->flags = flags;
886 cfd->io_priority = io_priority;
887 cfd->cancellable = cancellable;
888 cfd->progress_callback = progress_callback;
889 cfd->progress_callback_data = progress_callback_data;
890 cfd->callback = callback;
891 cfd->user_data = user_data;
892
893 cfd->source = cfd->sources;
894 cfd->destination = cfd->destinations;
895 cfd->n_file = 1;
896 cfd->tot_files = g_list_length (cfd->sources);
897
898 return cfd;
899 }
900
901
902 static void
copy_files_data_free(CopyFilesData * cfd)903 copy_files_data_free (CopyFilesData *cfd)
904 {
905 if (cfd == NULL)
906 return;
907 gio_file_list_free (cfd->sources);
908 gio_file_list_free (cfd->destinations);
909 g_free (cfd);
910 }
911
912
913 static void g_copy_current_file (CopyFilesData *cfd);
914
915
916 static void
g_copy_next_file(CopyFilesData * cfd)917 g_copy_next_file (CopyFilesData *cfd)
918 {
919 cfd->source = g_list_next (cfd->source);
920 cfd->destination = g_list_next (cfd->destination);
921 cfd->n_file++;
922
923 g_copy_current_file (cfd);
924 }
925
926
927 static void
g_copy_files_ready_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)928 g_copy_files_ready_cb (GObject *source_object,
929 GAsyncResult *result,
930 gpointer user_data)
931 {
932 CopyFilesData *cfd = user_data;
933 GFile *source = cfd->source->data;
934 GError *error = NULL;
935
936 if (! g_file_copy_finish (source, result, &error)) {
937 /* source and target are directories, ignore the error */
938 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE))
939 g_clear_error (&error);
940 /* source is directory, create target directory */
941 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE)) {
942 g_clear_error (&error);
943 g_file_make_directory ((GFile*) cfd->destination->data,
944 cfd->cancellable,
945 &error);
946 }
947 }
948
949 if (error) {
950 if (cfd->callback)
951 cfd->callback (error, cfd->user_data);
952 g_clear_error (&error);
953 copy_files_data_free (cfd);
954 return;
955 }
956
957 g_copy_next_file (cfd);
958 }
959
960
961 static void
g_copy_files_progress_cb(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)962 g_copy_files_progress_cb (goffset current_num_bytes,
963 goffset total_num_bytes,
964 gpointer user_data)
965 {
966 CopyFilesData *cfd = user_data;
967
968 if (cfd->progress_callback)
969 cfd->progress_callback (cfd->n_file,
970 cfd->tot_files,
971 (GFile*) cfd->source->data,
972 (GFile*) cfd->destination->data,
973 current_num_bytes,
974 total_num_bytes,
975 cfd->progress_callback_data);
976 }
977
978
979 static void
g_copy_current_file(CopyFilesData * cfd)980 g_copy_current_file (CopyFilesData *cfd)
981 {
982 if ((cfd->source == NULL) || (cfd->destination == NULL)) {
983 if (cfd->callback)
984 cfd->callback (NULL, cfd->user_data);
985 copy_files_data_free (cfd);
986 return;
987 }
988
989 g_file_copy_async ((GFile*) cfd->source->data,
990 (GFile*) cfd->destination->data,
991 cfd->flags,
992 cfd->io_priority,
993 cfd->cancellable,
994 g_copy_files_progress_cb,
995 cfd,
996 g_copy_files_ready_cb,
997 cfd);
998 }
999
1000
1001 void
g_copy_files_async(GList * sources,GList * destinations,GFileCopyFlags flags,int io_priority,GCancellable * cancellable,CopyProgressCallback progress_callback,gpointer progress_callback_data,CopyDoneCallback callback,gpointer user_data)1002 g_copy_files_async (GList *sources,
1003 GList *destinations,
1004 GFileCopyFlags flags,
1005 int io_priority,
1006 GCancellable *cancellable,
1007 CopyProgressCallback progress_callback,
1008 gpointer progress_callback_data,
1009 CopyDoneCallback callback,
1010 gpointer user_data)
1011 {
1012 CopyFilesData *cfd;
1013
1014 cfd = copy_files_data_new (sources,
1015 destinations,
1016 flags,
1017 io_priority,
1018 cancellable,
1019 progress_callback,
1020 progress_callback_data,
1021 callback,
1022 user_data);
1023 g_copy_current_file (cfd);
1024 }
1025
1026
1027 void
g_copy_file_async(GFile * source,GFile * destination,GFileCopyFlags flags,int io_priority,GCancellable * cancellable,CopyProgressCallback progress_callback,gpointer progress_callback_data,CopyDoneCallback callback,gpointer user_data)1028 g_copy_file_async (GFile *source,
1029 GFile *destination,
1030 GFileCopyFlags flags,
1031 int io_priority,
1032 GCancellable *cancellable,
1033 CopyProgressCallback progress_callback,
1034 gpointer progress_callback_data,
1035 CopyDoneCallback callback,
1036 gpointer user_data)
1037 {
1038 GList *source_files;
1039 GList *destination_files;
1040
1041 source_files = g_list_append (NULL, (gpointer) source);
1042 destination_files = g_list_append (NULL, (gpointer) destination);
1043
1044 g_copy_files_async (source_files,
1045 destination_files,
1046 flags,
1047 io_priority,
1048 cancellable,
1049 progress_callback,
1050 progress_callback_data,
1051 callback,
1052 user_data);
1053
1054 g_list_free (source_files);
1055 g_list_free (destination_files);
1056 }
1057
1058
1059 /*void
1060 g_copy_uris_async (GList *sources,
1061 GList *destinations,
1062 GFileCopyFlags flags,
1063 int io_priority,
1064 GCancellable *cancellable,
1065 CopyProgressCallback progress_callback,
1066 gpointer progress_callback_data,
1067 CopyDoneCallback callback,
1068 gpointer user_data)
1069 {
1070 GList *source_files, *destination_files;
1071
1072 source_files = gio_file_list_new_from_uri_list (sources);
1073 destination_files = gio_file_list_new_from_uri_list (destinations);
1074
1075 g_copy_files_async (source_files,
1076 destination_files,
1077 flags,
1078 io_priority,
1079 cancellable,
1080 progress_callback,
1081 progress_callback_data,
1082 callback,
1083 user_data);
1084
1085 gio_file_list_free (source_files);
1086 gio_file_list_free (destination_files);
1087 }
1088
1089
1090 void
1091 g_copy_uri_async (const char *source,
1092 const char *destination,
1093 GFileCopyFlags flags,
1094 int io_priority,
1095 GCancellable *cancellable,
1096 CopyProgressCallback progress_callback,
1097 gpointer progress_callback_data,
1098 CopyDoneCallback callback,
1099 gpointer user_data)
1100 {
1101 GList *source_list;
1102 GList *destination_list;
1103
1104 source_list = g_list_append (NULL, (gpointer)source);
1105 destination_list = g_list_append (NULL, (gpointer)destination);
1106
1107 g_copy_uris_async (source_list,
1108 destination_list,
1109 flags,
1110 io_priority,
1111 cancellable,
1112 progress_callback,
1113 progress_callback_data,
1114 callback,
1115 user_data);
1116
1117 g_list_free (source_list);
1118 g_list_free (destination_list);
1119 }*/
1120
1121
1122 /* -- g_directory_copy_async -- */
1123
1124
1125 typedef struct {
1126 char *uri;
1127 GFileInfo *info;
1128 } ChildData;
1129
1130
1131 static ChildData*
child_data_new(const char * uri,GFileInfo * info)1132 child_data_new (const char *uri,
1133 GFileInfo *info)
1134 {
1135 ChildData *data;
1136
1137 data = g_new0 (ChildData, 1);
1138 data->uri = g_strdup (uri);
1139 data->info = g_file_info_dup (info);
1140
1141 return data;
1142 }
1143
1144
1145 static void
child_data_free(ChildData * child)1146 child_data_free (ChildData *child)
1147 {
1148 if (child == NULL)
1149 return;
1150 g_free (child->uri);
1151 g_object_unref (child->info);
1152 g_free (child);
1153 }
1154
1155
1156 typedef struct {
1157 GFile *source;
1158 GFile *destination;
1159 GFileCopyFlags flags;
1160 int io_priority;
1161 GCancellable *cancellable;
1162 CopyProgressCallback progress_callback;
1163 gpointer progress_callback_data;
1164 CopyDoneCallback callback;
1165 gpointer user_data;
1166 GError *error;
1167
1168 GList *to_copy;
1169 GList *current;
1170 GFile *current_source;
1171 GFile *current_destination;
1172 int n_file, tot_files;
1173 guint source_id;
1174 } DirectoryCopyData;
1175
1176
1177 static void
directory_copy_data_free(DirectoryCopyData * dcd)1178 directory_copy_data_free (DirectoryCopyData *dcd)
1179 {
1180 if (dcd == NULL)
1181 return;
1182
1183 if (dcd->source != NULL)
1184 g_object_unref (dcd->source);
1185 if (dcd->destination != NULL)
1186 g_object_unref (dcd->destination);
1187 if (dcd->current_source != NULL) {
1188 g_object_unref (dcd->current_source);
1189 dcd->current_source = NULL;
1190 }
1191 if (dcd->current_destination != NULL) {
1192 g_object_unref (dcd->current_destination);
1193 dcd->current_destination = NULL;
1194 }
1195 g_list_foreach (dcd->to_copy, (GFunc) child_data_free, NULL);
1196 g_list_free (dcd->to_copy);
1197 g_free (dcd);
1198 }
1199
1200
1201 static gboolean
g_directory_copy_done(gpointer user_data)1202 g_directory_copy_done (gpointer user_data)
1203 {
1204 DirectoryCopyData *dcd = user_data;
1205
1206 g_source_remove (dcd->source_id);
1207
1208 if (dcd->callback)
1209 dcd->callback (dcd->error, dcd->user_data);
1210 if (dcd->error != NULL)
1211 g_clear_error (&(dcd->error));
1212 directory_copy_data_free (dcd);
1213
1214 return FALSE;
1215 }
1216
1217
1218 static GFile *
get_destination_for_uri(DirectoryCopyData * dcd,const char * uri)1219 get_destination_for_uri (DirectoryCopyData *dcd,
1220 const char *uri)
1221 {
1222 GFile *f_uri;
1223 GFile *destination_file;
1224 char *relative_path;
1225
1226 f_uri = g_file_new_for_uri (uri);
1227 relative_path = g_file_get_relative_path (dcd->source, f_uri);
1228 if (relative_path != NULL)
1229 destination_file = g_file_resolve_relative_path (dcd->destination, relative_path);
1230 else
1231 destination_file = g_file_dup (dcd->destination);
1232
1233 g_free (relative_path);
1234 g_object_unref (f_uri);
1235
1236 return destination_file;
1237 }
1238
1239
1240 static void g_directory_copy_current_child (DirectoryCopyData *dcd);
1241
1242
1243 static gboolean
g_directory_copy_next_child(gpointer user_data)1244 g_directory_copy_next_child (gpointer user_data)
1245 {
1246 DirectoryCopyData *dcd = user_data;
1247
1248 g_source_remove (dcd->source_id);
1249
1250 dcd->current = g_list_next (dcd->current);
1251 dcd->n_file++;
1252 g_directory_copy_current_child (dcd);
1253
1254 return FALSE;
1255 }
1256
1257
1258 static void
g_directory_copy_child_done_cb(GObject * source_object,GAsyncResult * result,gpointer user_data)1259 g_directory_copy_child_done_cb (GObject *source_object,
1260 GAsyncResult *result,
1261 gpointer user_data)
1262 {
1263 DirectoryCopyData *dcd = user_data;
1264
1265 if (! g_file_copy_finish ((GFile*)source_object, result, &(dcd->error))) {
1266 dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
1267 return;
1268 }
1269
1270 dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
1271 }
1272
1273
1274 static void
g_directory_copy_child_progress_cb(goffset current_num_bytes,goffset total_num_bytes,gpointer user_data)1275 g_directory_copy_child_progress_cb (goffset current_num_bytes,
1276 goffset total_num_bytes,
1277 gpointer user_data)
1278 {
1279 DirectoryCopyData *dcd = user_data;
1280
1281 if (dcd->progress_callback)
1282 dcd->progress_callback (dcd->n_file,
1283 dcd->tot_files,
1284 dcd->current_source,
1285 dcd->current_destination,
1286 current_num_bytes,
1287 total_num_bytes,
1288 dcd->progress_callback_data);
1289 }
1290
1291
1292 static void
g_directory_copy_current_child(DirectoryCopyData * dcd)1293 g_directory_copy_current_child (DirectoryCopyData *dcd)
1294 {
1295 ChildData *child;
1296 gboolean async_op = FALSE;
1297
1298 if (dcd->current == NULL) {
1299 dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
1300 return;
1301 }
1302
1303 if (dcd->current_source != NULL) {
1304 g_object_unref (dcd->current_source);
1305 dcd->current_source = NULL;
1306 }
1307 if (dcd->current_destination != NULL) {
1308 g_object_unref (dcd->current_destination);
1309 dcd->current_destination = NULL;
1310 }
1311
1312 child = dcd->current->data;
1313 dcd->current_source = g_file_new_for_uri (child->uri);
1314 dcd->current_destination = get_destination_for_uri (dcd, child->uri);
1315 if (dcd->current_destination == NULL) {
1316 dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
1317 return;
1318 }
1319
1320 switch (g_file_info_get_file_type (child->info)) {
1321 case G_FILE_TYPE_DIRECTORY:
1322 /* FIXME: how to make a directory asynchronously ? */
1323
1324 /* doesn't check the returned error for now, because when an
1325 * error occurs the code is not returned (for example when
1326 * a directory already exists the G_IO_ERROR_EXISTS code is
1327 * *not* returned), so we cannot discriminate between warnings
1328 * and fatal errors. (see bug #525155) */
1329
1330 g_file_make_directory (dcd->current_destination,
1331 NULL,
1332 NULL);
1333
1334 /*if (! g_file_make_directory (dcd->current_destination,
1335 dcd->cancellable,
1336 &(dcd->error)))
1337 {
1338 dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
1339 return;
1340 }*/
1341 break;
1342 case G_FILE_TYPE_SYMBOLIC_LINK:
1343 /* FIXME: how to make a link asynchronously ? */
1344
1345 g_file_make_symbolic_link (dcd->current_destination,
1346 g_file_info_get_symlink_target (child->info),
1347 NULL,
1348 NULL);
1349
1350 /*if (! g_file_make_symbolic_link (dcd->current_destination,
1351 g_file_info_get_symlink_target (child->info),
1352 dcd->cancellable,
1353 &(dcd->error)))
1354 {
1355 dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
1356 return;
1357 }*/
1358 break;
1359 case G_FILE_TYPE_REGULAR:
1360 g_file_copy_async (dcd->current_source,
1361 dcd->current_destination,
1362 dcd->flags,
1363 dcd->io_priority,
1364 dcd->cancellable,
1365 g_directory_copy_child_progress_cb,
1366 dcd,
1367 g_directory_copy_child_done_cb,
1368 dcd);
1369 async_op = TRUE;
1370 break;
1371 default:
1372 break;
1373 }
1374
1375 if (! async_op)
1376 dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
1377 }
1378
1379
1380 static gboolean
g_directory_copy_start_copying(gpointer user_data)1381 g_directory_copy_start_copying (gpointer user_data)
1382 {
1383 DirectoryCopyData *dcd = user_data;
1384
1385 g_source_remove (dcd->source_id);
1386
1387 dcd->to_copy = g_list_reverse (dcd->to_copy);
1388 dcd->current = dcd->to_copy;
1389 dcd->n_file = 1;
1390 g_directory_copy_current_child (dcd);
1391
1392 return FALSE;
1393 }
1394
1395
1396 static void
g_directory_copy_list_ready(GError * error,gpointer user_data)1397 g_directory_copy_list_ready (GError *error,
1398 gpointer user_data)
1399 {
1400 DirectoryCopyData *dcd = user_data;
1401
1402 if (error != NULL) {
1403 dcd->error = g_error_copy (error);
1404 dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
1405 return;
1406 }
1407
1408 dcd->source_id = g_idle_add (g_directory_copy_start_copying, dcd);
1409 }
1410
1411
1412 static void
g_directory_copy_for_each_file(const char * uri,GFileInfo * info,gpointer user_data)1413 g_directory_copy_for_each_file (const char *uri,
1414 GFileInfo *info,
1415 gpointer user_data)
1416 {
1417 DirectoryCopyData *dcd = user_data;
1418
1419 dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info));
1420 dcd->tot_files++;
1421 }
1422
1423
1424 static DirOp
g_directory_copy_start_dir(const char * uri,GError ** error,gpointer user_data)1425 g_directory_copy_start_dir (const char *uri,
1426 GError **error,
1427 gpointer user_data)
1428 {
1429 DirectoryCopyData *dcd = user_data;
1430 GFileInfo *info;
1431
1432 info = g_file_info_new ();
1433 g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
1434 dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info));
1435 g_object_unref (info);
1436
1437 dcd->tot_files++;
1438
1439 return DIR_OP_CONTINUE;
1440 }
1441
1442
1443 void
g_directory_copy_async(const char * source,const char * destination,GFileCopyFlags flags,int io_priority,GCancellable * cancellable,CopyProgressCallback progress_callback,gpointer progress_callback_data,CopyDoneCallback callback,gpointer user_data)1444 g_directory_copy_async (const char *source,
1445 const char *destination,
1446 GFileCopyFlags flags,
1447 int io_priority,
1448 GCancellable *cancellable,
1449 CopyProgressCallback progress_callback,
1450 gpointer progress_callback_data,
1451 CopyDoneCallback callback,
1452 gpointer user_data)
1453 {
1454 DirectoryCopyData *dcd;
1455
1456 /* Creating GFile objects here will save us lot of effort in path construction */
1457 dcd = g_new0 (DirectoryCopyData, 1);
1458 dcd->source = g_file_new_for_commandline_arg (source);
1459 dcd->destination = g_file_new_for_commandline_arg (destination);
1460 dcd->flags = flags;
1461 dcd->io_priority = io_priority;
1462 dcd->cancellable = cancellable;
1463 dcd->progress_callback = progress_callback;
1464 dcd->progress_callback_data = progress_callback_data;
1465 dcd->callback = callback;
1466 dcd->user_data = user_data;
1467
1468 g_directory_foreach_child (dcd->source,
1469 TRUE,
1470 TRUE,
1471 dcd->cancellable,
1472 g_directory_copy_start_dir,
1473 g_directory_copy_for_each_file,
1474 g_directory_copy_list_ready,
1475 dcd);
1476 }
1477
1478
1479 gboolean
g_load_file_in_buffer(GFile * file,void * buffer,gsize size,GError ** error)1480 g_load_file_in_buffer (GFile *file,
1481 void *buffer,
1482 gsize size,
1483 GError **error)
1484 {
1485 GFileInputStream *istream;
1486 int n;
1487
1488 istream = g_file_read (file, NULL, error);
1489 if (istream == NULL)
1490 return FALSE;
1491
1492 n = g_input_stream_read (G_INPUT_STREAM (istream), buffer, size, NULL, error);
1493 g_object_unref (istream);
1494
1495 return (n >= 0);
1496 }
1497
1498