2 This file is part of mktorrent
3 Copyright (C) 2009 Emil Renner Berthing
5 mktorrent is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 mktorrent is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 #include <sys/types.h>
30 #define DIRSEP_CHAR '\\'
32 #define DIRSEP_CHAR '/'
41 struct dir_state
*next
;
42 struct dir_state
*prev
;
48 static struct dir_state
*dir_state_new(struct dir_state
*prev
,
49 struct dir_state
*next
)
51 struct dir_state
*ds
= malloc(sizeof(struct dir_state
));
54 fprintf(stderr
, "Out of memory.\n");
64 static unsigned int dir_state_open(struct dir_state
*ds
, const char *name
,
67 ds
->dir
= opendir(name
);
68 if (ds
->dir
== NULL
) {
69 fprintf(stderr
, "Error opening '%s': %s\n",
70 name
, strerror(errno
));
79 static unsigned int dir_state_reopen(struct dir_state
*ds
, char *name
)
81 name
[ds
->length
] = '\0';
82 ds
->dir
= opendir(name
);
83 if (ds
->dir
== NULL
) {
84 fprintf(stderr
, "Error opening '%s': %s\n",
85 name
, strerror(errno
));
89 name
[ds
->length
] = DIRSEP_CHAR
;
91 seekdir(ds
->dir
, ds
->offset
);
96 static unsigned int dir_state_close(struct dir_state
*ds
)
98 ds
->offset
= telldir(ds
->dir
);
100 fprintf(stderr
, "Error getting dir offset: %s\n",
105 if (closedir(ds
->dir
)) {
106 fprintf(stderr
, "Error closing directory: %s\n",
116 static unsigned int cleanup(struct dir_state
*ds
, char *path
, int ret
)
122 struct dir_state
*next
= ds
->next
;
133 EXPORT
int file_tree_walk(const char *dirname
, unsigned int nfds
,
134 file_tree_walk_cb callback
, void *data
)
136 size_t path_size
= 256;
140 struct dir_state
*ds
= dir_state_new(NULL
, NULL
);
141 struct dir_state
*first_open
;
147 path
= malloc(path_size
);
149 fprintf(stderr
, "Out of memory.\n");
150 return cleanup(ds
, NULL
, -1);
152 path_max
= path
+ path_size
;
154 /* copy dirname to path */
156 while ((*end
= *dirname
)) {
159 if (end
== path_max
) {
162 new_path
= realloc(path
, 2*path_size
);
163 if (new_path
== NULL
) {
164 fprintf(stderr
, "Out of memory.\n");
165 return cleanup(ds
, path
, -1);
167 end
= new_path
+ path_size
;
170 path_max
= path
+ path_size
;
174 /* strip ending directory separators */
175 while (end
> path
&& *(end
-1) == DIRSEP_CHAR
) {
180 if (dir_state_open(ds
, path
, end
- path
))
181 return cleanup(ds
, path
, -1);
189 struct dirent
*de
= readdir(ds
->dir
);
196 if (de
->d_name
[0] == '.'
197 && (de
->d_name
[1] == '\0'
198 || (de
->d_name
[1] == '.'
199 && de
->d_name
[2] == '\0'))) {
203 end
= path
+ ds
->length
+ 1;
205 while ((*end
= *p
)) {
208 if (end
== path_max
) {
211 new_path
= realloc(path
, 2*path_size
);
212 if (new_path
== NULL
) {
213 fprintf(stderr
, "Out of memory.\n");
214 return cleanup(ds
, path
, -1);
216 end
= new_path
+ path_size
;
219 path_max
= path
+ path_size
;
223 if (stat(path
, &sbuf
)) {
224 fprintf(stderr
, "Error stat'ing '%s': %s\n",
225 path
, strerror(errno
));
226 return cleanup(ds
, path
, -1);
229 r
= callback(path
, &sbuf
, data
);
231 return cleanup(ds
, path
, r
);
233 if (S_ISDIR(sbuf
.st_mode
)) {
234 if (ds
->next
== NULL
&&
235 (ds
->next
= dir_state_new(ds
, NULL
)) == NULL
)
236 return cleanup(ds
, path
, -1);
241 if (dir_state_close(first_open
))
242 return cleanup(ds
, path
, -1);
243 first_open
= first_open
->next
;
247 if (dir_state_open(ds
, path
, end
- path
))
248 return cleanup(ds
, path
, -1);
255 if (closedir(ds
->dir
)) {
256 path
[ds
->length
] = '\0';
257 fprintf(stderr
, "Error closing '%s': %s\n",
258 path
, strerror(errno
));
259 return cleanup(ds
, path
, -1);
262 if (ds
->prev
== NULL
)
268 if (ds
->dir
== NULL
) {
269 if (dir_state_reopen(ds
, path
))
270 return cleanup(ds
, path
, -1);
277 return cleanup(ds
, path
, 0);