process_events() update, the number of processed events is bounded.
[gliv.git] / src / foreach_file.c
blob5c7f90d2779d48fdaee4d931ac7ea7d16ee573fa
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /*****************************************
22 * A simplified ftw(3), specific to GLiv *
23 *****************************************/
25 #include <sys/types.h> /* dev_t, ino_t */
26 #include <sys/stat.h> /* struct stat, stat() */
27 #include <dirent.h> /* struct dirent, *dir() */
28 #include <stdio.h> /* perror() */
30 #include "gliv.h"
31 #include "foreach_file.h"
32 #include "str_utils.h"
34 /* Used to detect insertions of already present elements. */
35 static gboolean destroyed_node = FALSE;
38 * Directories informations saved into
39 * the AVL to avoid infinite loops.
41 typedef struct {
42 dev_t dev;
43 ino_t ino;
44 } dir_info;
46 /* Called when we insert an element that was already there. */
47 static void value_destroy_notify(gpointer data)
49 g_free(data);
50 destroyed_node = TRUE;
53 /* To build and search the AVL. */
54 static gint cmp_func(const dir_info * dir1, const dir_info * dir2)
56 if (dir1->ino < dir2->ino)
57 return -1;
59 if (dir1->ino > dir2->ino)
60 return 1;
62 if (dir1->dev < dir2->dev)
63 return -1;
65 return dir1->dev > dir2->dev;
68 /* Push directories and handle files. */
69 static GSList *process_dir(GSList * stack, const gchar * dirname,
70 foreach_file_func func, gint * res)
72 DIR *dir;
73 struct dirent *dir_ent;
74 struct stat st;
75 gchar *name, *fullname;
77 dir = opendir(dirname);
78 if (dir == NULL) {
79 perror(dirname);
80 return stack;
83 for (dir_ent = readdir(dir); dir_ent != NULL; dir_ent = readdir(dir)) {
84 name = dir_ent->d_name;
86 if (name[0] == '.' &&
87 (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
88 /* Skip "." and "..". */
89 continue;
91 fullname = g_build_filename(dirname, name, NULL);
93 if (!stat(fullname, &st)) {
95 if (S_ISDIR(st.st_mode))
96 /* A directory. */
97 stack = g_slist_prepend(stack, fullname);
98 else {
99 /* A file. */
100 *res += (*func) (fullname);
101 g_free(fullname);
106 closedir(dir);
107 return stack;
110 /* Return TRUE if the dir has been correctly inserted. */
111 static gboolean tree_insert_dir(GTree * tree, struct stat *st)
113 dir_info *dir;
115 dir = g_new(dir_info, 1);
117 dir->dev = st->st_dev;
118 dir->ino = st->st_ino;
120 g_tree_insert(tree, dir, NULL);
122 if (destroyed_node) {
123 /* It was already there. */
124 destroyed_node = FALSE;
125 return FALSE;
128 return TRUE;
132 * We know that func is add_file_to_list and
133 * we return the number of inserted files.
135 gint foreach_file(const gchar * path, foreach_file_func func)
137 GSList *stack = NULL;
138 GTree *tree;
139 gchar *current;
140 struct stat st;
141 gint res = 0;
143 if (stat(path, &st) < 0) {
144 /* The path is not usable. */
145 perror(path);
146 return 0;
149 if (S_ISDIR(st.st_mode) == FALSE)
150 /* The path is a file, not a directory. */
151 return (*func) (path);
153 /* The path is a valid directory. */
155 stack = g_slist_prepend(stack, g_strdup(path)); /* push */
157 tree = g_tree_new_full((GCompareDataFunc) cmp_func, NULL,
158 g_free, value_destroy_notify);
160 while (stack) {
161 current = stack->data;
162 stack = g_slist_remove_link(stack, stack); /* pop */
164 if (!stat(current, &st) && tree_insert_dir(tree, &st))
165 /* Not already scanned. */
166 stack = process_dir(stack, current, func, &res);
168 g_free(current);
171 g_tree_destroy(tree);
172 destroyed_node = FALSE;
174 return res;