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