summaryrefslogtreecommitdiff
path: root/src/lib/ecore_file
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2012-12-02 22:35:45 +0000
committerVincent Torri <vincent.torri@gmail.com>2012-12-02 22:35:45 +0000
commit7d6010b12c47a20e492da808e3192c3f87dab619 (patch)
tree26c6fd189e046a76560c0bc740b85f4d767ae399 /src/lib/ecore_file
parent53fc441d5475155965d92da89502fe4634a561b2 (diff)
merge: add escape ecore, fix several bugs
SVN revision: 79995
Diffstat (limited to 'src/lib/ecore_file')
-rw-r--r--src/lib/ecore_file/Ecore_File.h190
-rw-r--r--src/lib/ecore_file/ecore_file.c1137
-rw-r--r--src/lib/ecore_file/ecore_file_download.c455
-rw-r--r--src/lib/ecore_file/ecore_file_monitor.c180
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_inotify.c331
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_poll.c340
-rw-r--r--src/lib/ecore_file/ecore_file_monitor_win32.c310
-rw-r--r--src/lib/ecore_file/ecore_file_path.c192
-rw-r--r--src/lib/ecore_file/ecore_file_private.h129
9 files changed, 3264 insertions, 0 deletions
diff --git a/src/lib/ecore_file/Ecore_File.h b/src/lib/ecore_file/Ecore_File.h
new file mode 100644
index 0000000000..30f3bd7d9e
--- /dev/null
+++ b/src/lib/ecore_file/Ecore_File.h
@@ -0,0 +1,190 @@
1#ifndef ECORE_FILE_H
2#define ECORE_FILE_H
3
4/*
5 * TODO:
6 * - More events, move/rename of directory file
7 */
8
9#include <Eina.h>
10
11#ifdef EAPI
12# undef EAPI
13#endif
14
15#ifdef _WIN32
16# ifdef EFL_ECORE_FILE_BUILD
17# ifdef DLL_EXPORT
18# define EAPI __declspec(dllexport)
19# else
20# define EAPI
21# endif /* ! DLL_EXPORT */
22# else
23# define EAPI __declspec(dllimport)
24# endif /* ! EFL_ECORE_FILE_BUILD */
25#else
26# ifdef __GNUC__
27# if __GNUC__ >= 4
28# define EAPI __attribute__ ((visibility("default")))
29# else
30# define EAPI
31# endif
32# else
33# define EAPI
34# endif
35#endif /* ! _WIN32 */
36
37/**
38 * @file Ecore_File.h
39 * @brief Files utility functions
40 */
41
42#ifdef __cplusplus
43extern "C" {
44#endif
45
46/**
47 * @defgroup Ecore_File_Group Ecore_File - Files and directories convenience functions
48 *
49 * @{
50 */
51
52/**
53 * @typedef Ecore_File_Monitor
54 * Abstract type used when monitoring a directory.
55 */
56typedef struct _Ecore_File_Monitor Ecore_File_Monitor;
57
58/**
59 * @typedef Ecore_File_Download_Job
60 * Abstract type used when aborting a download.
61 */
62typedef struct _Ecore_File_Download_Job Ecore_File_Download_Job;
63
64/**
65 * @typedef _Ecore_File_Event
66 * The event type returned when a file or directory is monitored.
67 */
68typedef enum _Ecore_File_Event
69{
70 ECORE_FILE_EVENT_NONE, /**< No event. */
71 ECORE_FILE_EVENT_CREATED_FILE, /**< Created file event. */
72 ECORE_FILE_EVENT_CREATED_DIRECTORY, /**< Created directory event. */
73 ECORE_FILE_EVENT_DELETED_FILE, /**< Deleted file event. */
74 ECORE_FILE_EVENT_DELETED_DIRECTORY, /**< Deleted directory event. */
75 ECORE_FILE_EVENT_DELETED_SELF, /**< Deleted monitored directory event. */
76 ECORE_FILE_EVENT_MODIFIED, /**< Modified file or directory event. */
77 ECORE_FILE_EVENT_CLOSED /**< Closed file event */
78} Ecore_File_Event;
79
80/**
81 * @typedef Ecore_File_Monitor_Cb
82 * Callback type used when a monitored directory has changes.
83 */
84typedef void (*Ecore_File_Monitor_Cb)(void *data, Ecore_File_Monitor *em, Ecore_File_Event event, const char *path);
85
86/**
87 * @typedef Ecore_File_Download_Completion_Cb
88 * Callback type used when a download is finished.
89 */
90typedef void (*Ecore_File_Download_Completion_Cb)(void *data, const char *file, int status);
91
92/**
93 * @typedef _Ecore_File_Progress_Return
94 * What to do with the download as a return from the
95 * Ecore_File_Download_Progress_Cb function, if provided.
96 */
97typedef enum _Ecore_File_Progress_Return
98{
99 ECORE_FILE_PROGRESS_CONTINUE = 0, /**< Continue the download. */
100 ECORE_FILE_PROGRESS_ABORT = 1 /**< Abort the download. */
101} Ecore_File_Progress_Return;
102
103/**
104 * @typedef Ecore_File_Download_Progress_Cb
105 * Callback type used while a download is in progress.
106 */
107typedef int (*Ecore_File_Download_Progress_Cb)(void *data,
108 const char *file,
109 long int dltotal,
110 long int dlnow,
111 long int ultotal,
112 long int ulnow);
113
114/* File operations */
115
116EAPI int ecore_file_init (void);
117EAPI int ecore_file_shutdown (void);
118EAPI long long ecore_file_mod_time (const char *file);
119EAPI long long ecore_file_size (const char *file);
120EAPI Eina_Bool ecore_file_exists (const char *file);
121EAPI Eina_Bool ecore_file_is_dir (const char *file);
122EAPI Eina_Bool ecore_file_mkdir (const char *dir);
123EAPI int ecore_file_mkdirs (const char **dirs);
124EAPI int ecore_file_mksubdirs (const char *base, const char **subdirs);
125EAPI Eina_Bool ecore_file_rmdir (const char *dir);
126EAPI Eina_Bool ecore_file_recursive_rm (const char *dir);
127EAPI Eina_Bool ecore_file_mkpath (const char *path);
128EAPI int ecore_file_mkpaths (const char **paths);
129EAPI Eina_Bool ecore_file_cp (const char *src, const char *dst);
130EAPI Eina_Bool ecore_file_mv (const char *src, const char *dst);
131EAPI Eina_Bool ecore_file_symlink (const char *src, const char *dest);
132EAPI char *ecore_file_realpath (const char *file);
133EAPI Eina_Bool ecore_file_unlink (const char *file);
134EAPI Eina_Bool ecore_file_remove (const char *file);
135EAPI const char *ecore_file_file_get (const char *path);
136EAPI char *ecore_file_dir_get (const char *path);
137EAPI Eina_Bool ecore_file_can_read (const char *file);
138EAPI Eina_Bool ecore_file_can_write (const char *file);
139EAPI Eina_Bool ecore_file_can_exec (const char *file);
140EAPI char *ecore_file_readlink (const char *link);
141EAPI Eina_List *ecore_file_ls (const char *dir);
142EAPI Eina_Iterator *ecore_file_ls_iterator (const char *dir);
143EAPI char *ecore_file_app_exe_get (const char *app);
144EAPI char *ecore_file_escape_name (const char *filename);
145EAPI char *ecore_file_strip_ext (const char *file);
146EAPI int ecore_file_dir_is_empty (const char *dir);
147
148/* Monitoring */
149
150EAPI Ecore_File_Monitor *ecore_file_monitor_add(const char *path,
151 Ecore_File_Monitor_Cb func,
152 void *data);
153EAPI void ecore_file_monitor_del(Ecore_File_Monitor *ecore_file_monitor);
154EAPI const char *ecore_file_monitor_path_get(Ecore_File_Monitor *ecore_file_monitor);
155
156/* Path */
157
158EAPI Eina_Bool ecore_file_path_dir_exists(const char *in_dir);
159EAPI Eina_Bool ecore_file_app_installed(const char *exe);
160EAPI Eina_List *ecore_file_app_list(void);
161
162/* Download */
163
164EAPI Eina_Bool ecore_file_download(const char *url,
165 const char *dst,
166 Ecore_File_Download_Completion_Cb completion_cb,
167 Ecore_File_Download_Progress_Cb progress_cb,
168 void *data,
169 Ecore_File_Download_Job **job_ret);
170EAPI Eina_Bool ecore_file_download_full(const char *url,
171 const char *dst,
172 Ecore_File_Download_Completion_Cb completion_cb,
173 Ecore_File_Download_Progress_Cb progress_cb,
174 void *data,
175 Ecore_File_Download_Job **job_ret,
176 Eina_Hash *headers);
177
178EAPI void ecore_file_download_abort_all(void);
179EAPI void ecore_file_download_abort(Ecore_File_Download_Job *job);
180EAPI Eina_Bool ecore_file_download_protocol_available(const char *protocol);
181
182/**
183 * @}
184 */
185
186#ifdef __cplusplus
187}
188#endif
189
190#endif
diff --git a/src/lib/ecore_file/ecore_file.c b/src/lib/ecore_file/ecore_file.c
new file mode 100644
index 0000000000..6876511fc6
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file.c
@@ -0,0 +1,1137 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8
9#ifndef _MSC_VER
10# include <unistd.h>
11# include <libgen.h>
12#endif
13
14#ifdef _WIN32
15# include <direct.h>
16#endif
17
18#ifdef HAVE_FEATURES_H
19# include <features.h>
20#endif
21#include <ctype.h>
22#include <errno.h>
23
24#ifdef HAVE_ATFILE_SOURCE
25# include <dirent.h>
26#endif
27
28#include "ecore_file_private.h"
29
30int _ecore_file_log_dom = -1;
31static int _ecore_file_init_count = 0;
32
33/* externally accessible functions */
34
35/**
36 * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions
37 *
38 * @{
39 */
40
41/**
42 * @brief Initialize the Ecore_File library.
43 *
44 * @return 1 or greater on success, 0 on error.
45 *
46 * This function sets up Ecore_File and the services it will use
47 * (monitoring, downloading, PATH related feature). It returns 0 on
48 * failure, otherwise it returns the number of times it has already
49 * been called.
50 *
51 * When Ecore_File is not used anymore, call ecore_file_shutdown()
52 * to shut down the Ecore_File library.
53 */
54EAPI int
55ecore_file_init()
56{
57 if (++_ecore_file_init_count != 1)
58 return _ecore_file_init_count;
59
60 if (!ecore_init())
61 return --_ecore_file_init_count;
62
63 _ecore_file_log_dom = eina_log_domain_register
64 ("ecore_file", ECORE_FILE_DEFAULT_LOG_COLOR);
65 if(_ecore_file_log_dom < 0)
66 {
67 EINA_LOG_ERR("Impossible to create a log domain for the ecore file module.");
68 return --_ecore_file_init_count;
69 }
70 ecore_file_path_init();
71 ecore_file_monitor_init();
72 ecore_file_download_init();
73
74 /* FIXME: were the tests disabled for a good reason ? */
75
76 /*
77 if (!ecore_file_monitor_init())
78 goto shutdown_ecore_file_path;
79
80 if (!ecore_file_download_init())
81 goto shutdown_ecore_file_monitor;
82 */
83
84 return _ecore_file_init_count;
85
86 /*
87 shutdown_ecore_file_monitor:
88 ecore_file_monitor_shutdown();
89 shutdown_ecore_file_path:
90 ecore_file_path_shutdown();
91
92 return --_ecore_file_init_count;
93 */
94}
95
96/**
97 * @brief Shut down the Ecore_File library.
98 *
99 * @return 0 when the library is completely shut down, 1 or
100 * greater otherwise.
101 *
102 * This function shuts down the Ecore_File library. It returns 0 when it has
103 * been called the same number of times than ecore_file_init(). In that case
104 * it shuts down all the services it uses.
105 */
106EAPI int
107ecore_file_shutdown()
108{
109 if (--_ecore_file_init_count != 0)
110 return _ecore_file_init_count;
111
112 ecore_file_download_shutdown();
113 ecore_file_monitor_shutdown();
114 ecore_file_path_shutdown();
115
116 eina_log_domain_unregister(_ecore_file_log_dom);
117 _ecore_file_log_dom = -1;
118
119 ecore_shutdown();
120
121 return _ecore_file_init_count;
122}
123
124/**
125 * @brief Get the time of the last modification to the given file.
126 *
127 * @param file The name of the file.
128 * @return Return the time of the last data modification, or 0 on
129 * failure.
130 *
131 * This function returns the time of the last modification of
132 * @p file. On failure, it returns 0.
133 */
134EAPI long long
135ecore_file_mod_time(const char *file)
136{
137 struct stat st;
138
139 if (stat(file, &st) < 0) return 0;
140 return st.st_mtime;
141}
142
143/**
144 * @brief Get the size of the given file.
145 *
146 * @param file The name of the file.
147 * @return Return the size of the file in bytes, or 0 on failure.
148 *
149 * This function returns the size of @p file in bytes. On failure, it
150 * returns 0.
151 */
152EAPI long long
153ecore_file_size(const char *file)
154{
155 struct stat st;
156
157 if (stat(file, &st) < 0) return 0;
158 return st.st_size;
159}
160
161/**
162 * @brief Check if the given file exists.
163 *
164 * @param file The name of the file.
165 * @return @c EINA_TRUE if the @p file exists, @c EINA_FALSE otherwise.
166 *
167 * This function returns @c EINA_TRUE if @p file exists on local filesystem,
168 * @c EINA_FALSE otherwise.
169 */
170EAPI Eina_Bool
171ecore_file_exists(const char *file)
172{
173 struct stat st;
174 if (!file) return EINA_FALSE;
175
176 /*Workaround so that "/" returns a true, otherwise we can't monitor "/" in ecore_file_monitor*/
177 if (stat(file, &st) < 0 && strcmp(file, "/")) return EINA_FALSE;
178 return EINA_TRUE;
179}
180
181/**
182 * @brief Check if the given file is a directory.
183 *
184 * @param file The name of the file.
185 * @return @c EINA_TRUE if the file exists and is a directory, @c EINA_FALSE
186 * otherwise.
187 *
188 * This function returns @c EINA_TRUE if @p file exists exists and is a
189 * directory on local filesystem, @c EINA_FALSE otherwise.
190 */
191EAPI Eina_Bool
192ecore_file_is_dir(const char *file)
193{
194 struct stat st;
195
196 if (stat(file, &st) < 0) return EINA_FALSE;
197 if (S_ISDIR(st.st_mode)) return EINA_TRUE;
198 return EINA_FALSE;
199}
200
201static mode_t default_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
202
203/**
204 * @brief Create a new directory.
205 *
206 * @param dir The name of the directory to create
207 * @return @c EINA_TRUE on successful creation, @c EINA_FALSE otherwise.
208 *
209 * This function creates the directory @p dir with the mode S_IRUSR |
210 * S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH. On
211 * success, it returns @c EINA_TRUE, @c EINA_FALSE otherwise.
212 */
213EAPI Eina_Bool
214ecore_file_mkdir(const char *dir)
215{
216 if (mkdir(dir, default_mode) < 0) return EINA_FALSE;
217 return EINA_TRUE;
218}
219
220/**
221 * @brief Create complete directory in a batch.
222 *
223 * @param dirs The list of directories, null terminated.
224 * @return The number of successful directories created, -1 if dirs is
225 * @c NULL.
226 *
227 * This function creates all the directories that are in the null
228 * terminated array @p dirs. The function loops over the directories
229 * and call ecore_file_mkdir(). This function returns -1 if @p dirs is
230 * @c NULL, otherwise if returns the number of suceesfully created
231 * directories.
232 */
233EAPI int
234ecore_file_mkdirs(const char **dirs)
235{
236 int i = 0;
237
238 if (!dirs) return -1;
239
240 for (; *dirs; dirs++)
241 if (ecore_file_mkdir(*dirs))
242 i++;
243 return i;
244}
245
246/**
247 * @brief Create complete list of sub-directories in a batch (optimized).
248 *
249 * @param base The base directory to act on.
250 * @param subdirs The list of directories, null terminated.
251 * @return number of successful directories created, -1 on failure.
252 *
253 * This function creates all the directories that are in the null
254 * terminated array @p dirs in the @p base directory. If @p base does
255 * not exist, it will be created. The function loops over the directories
256 * and call ecore_file_mkdir(). The whole path of the directories must
257 * exist. So if base/a/b/c wants to be created, @p subdirs must
258 * contain "a", "a/b" and "a/b/c", in that order. This function
259 * returns -1 if @p dirs or @p base are @c NULL, or if @p base is
260 * empty ("\0"). It returns 0 is @p base is not a directory or
261 * invalid, or if it can't be created. Otherwise if returns the number
262 * of suceesfully created directories.
263 */
264EAPI int
265ecore_file_mksubdirs(const char *base, const char **subdirs)
266{
267#ifndef HAVE_ATFILE_SOURCE
268 char buf[PATH_MAX];
269 int baselen;
270#else
271 int fd;
272 DIR *dir;
273#endif
274 int i;
275
276 if (!subdirs) return -1;
277 if ((!base) || (base[0] == '\0')) return -1;
278
279 if ((!ecore_file_is_dir(base)) && (!ecore_file_mkpath(base)))
280 return 0;
281
282#ifndef HAVE_ATFILE_SOURCE
283 baselen = eina_strlcpy(buf, base, sizeof(buf));
284 if ((baselen < 1) || (baselen + 1 >= (int)sizeof(buf)))
285 return 0;
286
287 if (buf[baselen - 1] != '/')
288 {
289 buf[baselen] = '/';
290 baselen++;
291 }
292#else
293 dir = opendir(base);
294 if (!dir)
295 return 0;
296 fd = dirfd(dir);
297#endif
298
299 i = 0;
300 for (; *subdirs; subdirs++)
301 {
302 struct stat st;
303
304#ifndef HAVE_ATFILE_SOURCE
305 eina_strlcpy(buf + baselen, *subdirs, sizeof(buf) - baselen);
306 if (stat(buf, &st) == 0)
307#else
308 if (fstatat(fd, *subdirs, &st, 0) == 0)
309#endif
310 {
311 if (S_ISDIR(st.st_mode))
312 {
313 i++;
314 continue;
315 }
316 }
317 else
318 {
319 if (errno == ENOENT)
320 {
321#ifndef HAVE_ATFILE_SOURCE
322 if (mkdir(buf, default_mode) == 0)
323#else
324 if (mkdirat(fd, *subdirs, default_mode) == 0)
325#endif
326 {
327 i++;
328 continue;
329 }
330 }
331 }
332 }
333
334#ifdef HAVE_ATFILE_SOURCE
335 closedir(dir);
336#endif
337
338 return i;
339}
340
341/**
342 * @brief Delete the given directory.
343 *
344 * @param dir The name of the directory to delete.
345 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
346 *
347 * This function deletes @p dir. It returns @c EINA_TRUE on success,
348 * @c EINA_FALSE otherwise.
349 */
350EAPI Eina_Bool
351ecore_file_rmdir(const char *dir)
352{
353 if (rmdir(dir) < 0) return EINA_FALSE;
354 return EINA_TRUE;
355}
356
357/**
358 * @brief Delete the given file.
359 *
360 * @param file The name of the file to delete.
361 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
362 *
363 * This function deletes @p file. It returns @c EINA_TRUE on success,
364 * @c EINA_FALSE otherwise.
365 */
366EAPI Eina_Bool
367ecore_file_unlink(const char *file)
368{
369 if (unlink(file) < 0) return EINA_FALSE;
370 return EINA_TRUE;
371}
372
373/**
374 * @brief Remove the given file or directory.
375 *
376 * @param file The name of the file or directory to delete.
377 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
378 *
379 * This function removes @p file. It returns @c EINA_TRUE on success,
380 * @c EINA_FALSE otherwise.
381 */
382EAPI Eina_Bool
383ecore_file_remove(const char *file)
384{
385 if (remove(file) < 0) return EINA_FALSE;
386 return EINA_TRUE;
387}
388
389/**
390 * @brief Delete the given directory and all its contents.
391 *
392 * @param dir The name of the directory to delete.
393 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
394 *
395 * This function delete @p dir and all its contents. If @p dir is a
396 * link only the link is removed. It returns @c EINA_TRUE on success,
397 * @c EINA_FALSE otherwise.
398 */
399EAPI Eina_Bool
400ecore_file_recursive_rm(const char *dir)
401{
402 struct stat st;
403
404#ifdef _WIN32
405 char buf[PATH_MAX];
406
407 if (readlink(dir, buf, sizeof(buf) - 1) > 0)
408 return ecore_file_unlink(dir);
409 if (stat(dir, &st) == -1)
410 return EINA_FALSE;
411#else
412 if (lstat(dir, &st) == -1)
413 return EINA_FALSE;
414#endif
415
416 if (S_ISDIR(st.st_mode))
417 {
418 Eina_File_Direct_Info *info;
419 Eina_Iterator *it;
420 int ret;
421
422 ret = 1;
423 it = eina_file_direct_ls(dir);
424 EINA_ITERATOR_FOREACH(it, info)
425 {
426 if (!ecore_file_recursive_rm(info->path))
427 ret = 0;
428 }
429 eina_iterator_free(it);
430
431 if (!ecore_file_rmdir(dir)) ret = 0;
432 if (ret)
433 return EINA_TRUE;
434 else
435 return EINA_FALSE;
436 }
437 else
438 {
439 return ecore_file_unlink(dir);
440 }
441}
442
443static inline Eina_Bool
444_ecore_file_mkpath_if_not_exists(const char *path)
445{
446 struct stat st;
447
448 /* Windows: path like C: or D: etc are valid, but stat() returns an error */
449#ifdef _WIN32
450 if ((strlen(path) == 2) &&
451 ((path[0] >= 'a' && path[0] <= 'z') ||
452 (path[0] >= 'A' && path[0] <= 'Z')) &&
453 (path[1] == ':'))
454 return EINA_TRUE;
455#endif
456
457 if (stat(path, &st) < 0)
458 return ecore_file_mkdir(path);
459 else if (!S_ISDIR(st.st_mode))
460 return EINA_FALSE;
461 else
462 return EINA_TRUE;
463}
464
465/**
466 * @brief Create a complete path.
467 *
468 * @param path The path to create
469 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
470 *
471 * This function creates @p path and all the subdirectories it
472 * contains. The separator is '/' or '\'. If @p path exists, this
473 * function returns @c EINA_TRUE immediately. It returns @c EINA_TRUE on
474 * success, @c EINA_FALSE otherwise.
475 */
476EAPI Eina_Bool
477ecore_file_mkpath(const char *path)
478{
479 char ss[PATH_MAX];
480 unsigned int i;
481
482 if (ecore_file_is_dir(path))
483 return EINA_TRUE;
484
485 for (i = 0; path[i] != '\0'; ss[i] = path[i], i++)
486 {
487 if (i == sizeof(ss) - 1) return EINA_FALSE;
488 if (((path[i] == '/') || (path[i] == '\\')) && (i > 0))
489 {
490 ss[i] = '\0';
491 if (!_ecore_file_mkpath_if_not_exists(ss))
492 return EINA_FALSE;
493 }
494 }
495 ss[i] = '\0';
496 return _ecore_file_mkpath_if_not_exists(ss);
497}
498
499/**
500 * @brief Create complete paths in a batch.
501 *
502 * @param paths list of paths, null terminated.
503 * @return number of successful paths created, -1 if paths is NULL.
504 *
505 * This function creates all the directories that are in the null
506 * terminated array @p paths. The function loops over the directories
507 * and call ecore_file_mkpath(), hence on Windows, '\' must be
508 * replaced by '/' before calling that function. This function
509 * returns -1 if @p paths is @c NULL. Otherwise if returns the number
510 * of suceesfully created directories.
511 */
512EAPI int
513ecore_file_mkpaths(const char **paths)
514{
515 int i = 0;
516
517 if (!paths) return -1;
518
519 for (; *paths; paths++)
520 if (ecore_file_mkpath(*paths))
521 i++;
522 return i;
523}
524
525/**
526 * @brief Copy the given file to the given destination.
527 *
528 * @param src The name of the source file.
529 * @param dst The name of the destination file.
530 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
531 *
532 * This function copies @p src to @p dst. If the absolute path name of
533 * @p src and @p dst can not be computed, or if they are equal, or if
534 * the copy fails, the function returns @c EINA_FALSE, otherwise it
535 * returns @c EINA_TRUE.
536 */
537EAPI Eina_Bool
538ecore_file_cp(const char *src, const char *dst)
539{
540 FILE *f1, *f2;
541 char buf[16384];
542 char realpath1[PATH_MAX], realpath2[PATH_MAX];
543 size_t num;
544 Eina_Bool ret = EINA_TRUE;
545
546 if (!realpath(src, realpath1)) return EINA_FALSE;
547 if (realpath(dst, realpath2) && !strcmp(realpath1, realpath2)) return EINA_FALSE;
548
549 f1 = fopen(src, "rb");
550 if (!f1) return EINA_FALSE;
551 f2 = fopen(dst, "wb");
552 if (!f2)
553 {
554 fclose(f1);
555 return EINA_FALSE;
556 }
557 while ((num = fread(buf, 1, sizeof(buf), f1)) > 0)
558 {
559 if (fwrite(buf, 1, num, f2) != num) ret = EINA_FALSE;
560 }
561 fclose(f1);
562 fclose(f2);
563 return ret;
564}
565
566/**
567 * @brief Move the given file to the given destination.
568 *
569 * @param src The name of the source file.
570 * @param dst The name of the destination file.
571 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
572 *
573 * This function moves @p src to @p dst. It returns @c EINA_TRUE on
574 * success, @c EINA_FALSE otherwise.
575 */
576EAPI Eina_Bool
577ecore_file_mv(const char *src, const char *dst)
578{
579 char buf[PATH_MAX];
580 int fd;
581
582 if (rename(src, dst))
583 {
584 // File cannot be moved directly because
585 // it resides on a different mount point.
586 if (errno == EXDEV)
587 {
588 struct stat st;
589
590 // Make sure this is a regular file before
591 // we do anything fancy.
592 stat(src, &st);
593 if (S_ISREG(st.st_mode))
594 {
595 char *dir;
596
597 dir = ecore_file_dir_get(dst);
598 // Since we can't directly rename, try to
599 // copy to temp file in the dst directory
600 // and then rename.
601 snprintf(buf, sizeof(buf), "%s/.%s.tmp.XXXXXX",
602 dir, ecore_file_file_get(dst));
603 free(dir);
604 fd = mkstemp(buf);
605 if (fd < 0) goto FAIL;
606 close(fd);
607
608 // Copy to temp file
609 if (!ecore_file_cp(src, buf))
610 goto FAIL;
611
612 // Set file permissions of temp file to match src
613 chmod(buf, st.st_mode);
614
615 // Try to atomically move temp file to dst
616 if (rename(buf, dst))
617 {
618 // If we still cannot atomically move
619 // do a normal copy and hope for the best.
620 if (!ecore_file_cp(buf, dst))
621 goto FAIL;
622 }
623
624 // Delete temporary file and src
625 ecore_file_unlink(buf);
626 ecore_file_unlink(src);
627 goto PASS;
628 }
629 }
630 goto FAIL;
631 }
632
633PASS:
634 return EINA_TRUE;
635
636FAIL:
637 return EINA_FALSE;
638}
639
640/**
641 * @brief Create a symbolic link.
642 *
643 * @param src The name of the file to link.
644 * @param dest The name of link.
645 * @return @c EINA_TRUE on success, @c EINA_FALSE otherwise.
646 *
647 * This function create the symbolic link @p dest of @p src. This
648 * function does not work on Windows. It returns @c EINA_TRUE on success,
649 * @c EINA_FALSE otherwise.
650 */
651EAPI Eina_Bool
652ecore_file_symlink(const char *src, const char *dest)
653{
654 if (!symlink(src, dest)) return EINA_TRUE;
655
656 return EINA_FALSE;
657}
658
659/**
660 * @brief Get the canonicalized absolute path name.
661 *
662 * @param file The file path.
663 * @return The canonicalized absolute pathname or an empty string on
664 * failure.
665 *
666 * This function returns the absolute path name of @p file as a newly
667 * allocated string. If @p file is @c NULL, or on error, this function
668 * returns an empty string. Otherwise, it returns the absolute path
669 * name. When not needed anymore, the returned value must be freed.
670 */
671EAPI char *
672ecore_file_realpath(const char *file)
673{
674 char buf[PATH_MAX];
675
676 /*
677 * Some implementations of realpath do not conform to the SUS.
678 * And as a result we must prevent a null arg from being passed.
679 */
680 if (!file) return strdup("");
681 if (!realpath(file, buf)) return strdup("");
682
683 return strdup(buf);
684}
685
686/**
687 * Get the filename from a given path.
688 *
689 * @param path The complete path.
690 * @return The file name.
691 *
692 * This function returns the file name of @p path. If @p path is
693 * @c NULL, the functions returns @c NULL.
694 */
695EAPI const char *
696ecore_file_file_get(const char *path)
697{
698 char *result = NULL;
699
700 if (!path) return NULL;
701 if ((result = strrchr(path, '/'))) result++;
702 else result = (char *)path;
703 return result;
704}
705
706/**
707 * @brief Get the directory where the given file resides.
708 *
709 * @param file The name of the file.
710 * @return The directory name.
711 *
712 * This function returns the directory where @p file resides as anewly
713 * allocated string. If @p file is @c NULL or on error, this function
714 * returns @c NULL. When not needed anymore, the returned value must
715 * be freed.
716 */
717EAPI char *
718ecore_file_dir_get(const char *file)
719{
720 char *p;
721 char buf[PATH_MAX];
722
723 if (!file) return NULL;
724 strncpy(buf, file, PATH_MAX);
725 buf[PATH_MAX - 1] = 0;
726 p = dirname(buf);
727 return strdup(p);
728}
729
730/**
731 * @brief Check if the given file can be read.
732 *
733 * @param file The name of the file.
734 * @return @c EINA_TRUE if the @p file is readable, @c EINA_FALSE otherwise.
735 *
736 * This function returns @c EINA_TRUE if @p file can be read, @c EINA_FALSE
737 * otherwise.
738 */
739EAPI Eina_Bool
740ecore_file_can_read(const char *file)
741{
742 if (!file) return EINA_FALSE;
743 if (!access(file, R_OK)) return EINA_TRUE;
744 return EINA_FALSE;
745}
746
747/**
748 * @brief Check if the given file can be written.
749 *
750 * @param file The name of the file.
751 * @return @c EINA_TRUE if the @p file is writable, @c EINA_FALSE otherwise.
752 *
753 * This function returns @c EINA_TRUE if @p file can be written, @c EINA_FALSE
754 * otherwise.
755 */
756EAPI Eina_Bool
757ecore_file_can_write(const char *file)
758{
759 if (!file) return EINA_FALSE;
760 if (!access(file, W_OK)) return EINA_TRUE;
761 return EINA_FALSE;
762}
763
764/**
765 * @brief Check if the given file can be executed.
766 *
767 * @param file The name of the file.
768 * @return @c EINA_TRUE if the @p file can be executed, @c EINA_FALSE
769 * otherwise.
770 *
771 * This function returns @c EINA_TRUE if @p file can be executed, @c EINA_FALSE
772 * otherwise.
773 */
774EAPI Eina_Bool
775ecore_file_can_exec(const char *file)
776{
777 if (!file) return EINA_FALSE;
778 if (!access(file, X_OK)) return EINA_TRUE;
779 return EINA_FALSE;
780}
781
782/**
783 * @brief Get the path pointed by the given link.
784 *
785 * @param lnk The name of the link.
786 * @return The path pointed by link or NULL.
787 *
788 * This function returns the path pointed by @p link as a newly
789 * allocated string. This function does not work on Windows. On
790 * failure, the function returns @c NULL. When not needed anymore, the
791 * returned value must be freed.
792 */
793EAPI char *
794ecore_file_readlink(const char *lnk)
795{
796 char buf[PATH_MAX];
797 int count;
798
799 if ((count = readlink(lnk, buf, sizeof(buf) - 1)) < 0) return NULL;
800 buf[count] = 0;
801 return strdup(buf);
802}
803
804/**
805 * @brief Get the list of the files and directories in the given
806 * directory.
807 *
808 * @param dir The name of the directory to list
809 * @return Return an Eina_List containing all the files in the directory;
810 * on failure it returns NULL.
811 *
812 * This function returns a list of allocated strings of all the files
813 * and directories contained in @p dir. The list will be sorted with
814 * strcoll as compare function. That means that you may want to set
815 * the current locale for the category LC_COLLATE with
816 * setlocale(). For more information see the manual pages of strcoll
817 * and setlocale. The list will not contain the directory entries for
818 * '.' and '..'. On failure, @c NULL is returned. When not needed
819 * anymore, the list elements must be freed.
820 */
821EAPI Eina_List *
822ecore_file_ls(const char *dir)
823{
824 Eina_File_Direct_Info *info;
825 Eina_Iterator *ls;
826 Eina_List *list = NULL;
827
828 ls = eina_file_direct_ls(dir);
829 if (!ls) return NULL;
830
831 EINA_ITERATOR_FOREACH(ls, info)
832 {
833 char *f;
834
835 f = strdup(info->path + info->name_start);
836 list = eina_list_append(list, f);
837 }
838 eina_iterator_free(ls);
839
840 list = eina_list_sort(list, eina_list_count(list), EINA_COMPARE_CB(strcoll));
841
842 return list;
843}
844
845/**
846 * @brief Return the executable from the given command.
847 *
848 * @param app The application command, with parameters.
849 * @return The executable from @p app as a newly allocated string. Arguments
850 * are removed and escape characters are handled. If @p app is @c NULL, or
851 * on failure, the function returns @c NULL. When not needed anymore, the
852 * returned value must be freed.
853 */
854EAPI char *
855ecore_file_app_exe_get(const char *app)
856{
857 char *p, *pp, *exe1 = NULL, *exe2 = NULL;
858 char *exe = NULL;
859 int in_quot_dbl = 0, in_quot_sing = 0, restart = 0;
860
861 if (!app) return NULL;
862
863 p = (char *)app;
864restart:
865 while ((*p) && (isspace((unsigned char)*p))) p++;
866 exe1 = p;
867 while (*p)
868 {
869 if (in_quot_sing)
870 {
871 if (*p == '\'')
872 in_quot_sing = 0;
873 }
874 else if (in_quot_dbl)
875 {
876 if (*p == '\"')
877 in_quot_dbl = 0;
878 }
879 else
880 {
881 if (*p == '\'')
882 in_quot_sing = 1;
883 else if (*p == '\"')
884 in_quot_dbl = 1;
885 if ((isspace((unsigned char)*p)) && ((p <= app) || (p[-1] == '\\')))
886 break;
887 }
888 p++;
889 }
890 exe2 = p;
891 if (exe2 == exe1) return NULL;
892 if (*exe1 == '~')
893 {
894 char *homedir;
895 int len;
896
897 /* Skip ~ */
898 exe1++;
899
900 homedir = getenv("HOME");
901 if (!homedir) return NULL;
902 len = strlen(homedir);
903 if (exe) free(exe);
904 exe = malloc(len + exe2 - exe1 + 2);
905 if (!exe) return NULL;
906 pp = exe;
907 if (len)
908 {
909 strcpy(exe, homedir);
910 pp += len;
911 if (*(pp - 1) != '/')
912 {
913 *pp = '/';
914 pp++;
915 }
916 }
917 }
918 else
919 {
920 if (exe) free(exe);
921 exe = malloc(exe2 - exe1 + 1);
922 if (!exe) return NULL;
923 pp = exe;
924 }
925 p = exe1;
926 restart = 0;
927 in_quot_dbl = 0;
928 in_quot_sing = 0;
929 while (*p)
930 {
931 if (in_quot_sing)
932 {
933 if (*p == '\'')
934 in_quot_sing = 0;
935 else
936 {
937 *pp = *p;
938 pp++;
939 }
940 }
941 else if (in_quot_dbl)
942 {
943 if (*p == '\"')
944 in_quot_dbl = 0;
945 else
946 {
947 /* technically this is wrong. double quotes also accept
948 * special chars:
949 *
950 * $, `, \
951 */
952 *pp = *p;
953 pp++;
954 }
955 }
956 else
957 {
958 /* technically we should handle special chars:
959 *
960 * $, `, \, etc.
961 */
962 if ((p > exe1) && (p[-1] == '\\'))
963 {
964 if (*p != '\n')
965 {
966 *pp = *p;
967 pp++;
968 }
969 }
970 else if ((p > exe1) && (*p == '='))
971 {
972 restart = 1;
973 *pp = *p;
974 pp++;
975 }
976 else if (*p == '\'')
977 in_quot_sing = 1;
978 else if (*p == '\"')
979 in_quot_dbl = 1;
980 else if (isspace((unsigned char)*p))
981 {
982 if (restart)
983 goto restart;
984 else
985 break;
986 }
987 else
988 {
989 *pp = *p;
990 pp++;
991 }
992 }
993 p++;
994 }
995 *pp = 0;
996 return exe;
997}
998
999/**
1000 * @brief Add the escape sequence ('\\') to the given file name.
1001 *
1002 * @param filename The file name.
1003 * @return The file name with special characters escaped.
1004 *
1005 * This function adds the escape sequence ('\\') to the given file
1006 * name and returns the result as a newly allocated string. If the
1007 * length of the returned string is longer than PATH_MAX, or on
1008 * failure, @c NULL is returned. When not needed anymore, the returned
1009 * value must be freed.
1010 */
1011EAPI char *
1012ecore_file_escape_name(const char *filename)
1013{
1014 const char *p;
1015 char *q;
1016 char buf[PATH_MAX];
1017
1018 EINA_SAFETY_ON_NULL_RETURN_VAL(filename, NULL);
1019
1020 p = filename;
1021 q = buf;
1022 while (*p)
1023 {
1024 if ((q - buf) > (PATH_MAX - 6)) return NULL;
1025 if (
1026 (*p == ' ') || (*p == '\\') || (*p == '\'') ||
1027 (*p == '\"') || (*p == ';') || (*p == '!') ||
1028 (*p == '#') || (*p == '$') || (*p == '%') ||
1029 (*p == '&') || (*p == '*') || (*p == '(') ||
1030 (*p == ')') || (*p == '[') || (*p == ']') ||
1031 (*p == '{') || (*p == '}') || (*p == '|') ||
1032 (*p == '<') || (*p == '>') || (*p == '?')
1033 )
1034 {
1035 *q = '\\';
1036 q++;
1037 }
1038 else if (*p == '\t')
1039 {
1040 *q = '\\';
1041 q++;
1042 *q = '\\';
1043 q++;
1044 *q = 't';
1045 q++;
1046 p++;
1047 continue;
1048 }
1049 else if (*p == '\n')
1050 {
1051 *q = '\\';
1052 q++;
1053 *q = '\\';
1054 q++;
1055 *q = 'n';
1056 q++;
1057 p++;
1058 continue;
1059 }
1060
1061 *q = *p;
1062 q++;
1063 p++;
1064 }
1065 *q = 0;
1066 return strdup(buf);
1067}
1068
1069/**
1070 * @brief Remove the extension from the given file name.
1071 *
1072 * @param path The name of the file.
1073 * @return A newly allocated string with the extension stripped out or
1074 * @c NULL on errors.
1075 *
1076 * This function removes the extension from @p path and returns the
1077 * result as a newly allocated string. If @p path is @c NULL, or on
1078 * failure, the function returns @c NULL. When not needed anymore, the
1079 * returned value must be freed.
1080 */
1081EAPI char *
1082ecore_file_strip_ext(const char *path)
1083{
1084 char *p, *file = NULL;
1085
1086 if (!path)
1087 return NULL;
1088
1089 p = strrchr(path, '.');
1090 if (!p)
1091 file = strdup(path);
1092 else if (p != path)
1093 {
1094 file = malloc(((p - path) + 1) * sizeof(char));
1095 if (file)
1096 {
1097 memcpy(file, path, (p - path));
1098 file[p - path] = 0;
1099 }
1100 }
1101
1102 return file;
1103}
1104
1105/**
1106 * @brief Check if the given directory is empty.
1107 *
1108 * @param dir The name of the directory to check.
1109 * @return @c 1 if directory is empty, @c 0 if it has at least one file or
1110 * @c -1 in case of errors.
1111 *
1112 * This functions checks if @p dir is empty. The '.' and '..' files
1113 * will be ignored. If @p dir is empty, 1 is returned, if it contains
1114 * at least one file, @c 0 is returned. On failure, @c -1 is returned.
1115 */
1116EAPI int
1117ecore_file_dir_is_empty(const char *dir)
1118{
1119 Eina_File_Direct_Info *info;
1120 Eina_Iterator *it;
1121
1122 it = eina_file_direct_ls(dir);
1123 if (!it) return -1;
1124
1125 EINA_ITERATOR_FOREACH(it, info)
1126 {
1127 eina_iterator_free(it);
1128 return 0;
1129 }
1130
1131 eina_iterator_free(it);
1132 return 1;
1133}
1134
1135/**
1136 * @}
1137 */
diff --git a/src/lib/ecore_file/ecore_file_download.c b/src/lib/ecore_file/ecore_file_download.c
new file mode 100644
index 0000000000..98d4a1f972
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_download.c
@@ -0,0 +1,455 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8
9#ifdef BUILD_ECORE_CON
10# include "Ecore_Con.h"
11#endif
12
13#include "ecore_file_private.h"
14
15#ifdef BUILD_ECORE_CON
16
17#define ECORE_MAGIC_FILE_DOWNLOAD_JOB 0xf7427cb8
18#define ECORE_FILE_DOWNLOAD_TIMEOUT 30
19
20struct _Ecore_File_Download_Job
21{
22 ECORE_MAGIC;
23
24 Ecore_Con_Url *url_con;
25 FILE *file;
26
27 char *dst;
28
29 Ecore_File_Download_Completion_Cb completion_cb;
30 Ecore_File_Download_Progress_Cb progress_cb;
31};
32
33#ifdef HAVE_CURL
34Ecore_File_Download_Job *_ecore_file_download_curl(const char *url, const char *dst,
35 Ecore_File_Download_Completion_Cb completion_cb,
36 Ecore_File_Download_Progress_Cb progress_cb,
37 void *data,
38 Eina_Hash *headers);
39
40static Eina_Bool _ecore_file_download_url_complete_cb(void *data, int type, void *event);
41static Eina_Bool _ecore_file_download_url_progress_cb(void *data, int type, void *event);
42#endif
43
44static Ecore_Event_Handler *_url_complete_handler = NULL;
45static Ecore_Event_Handler *_url_progress_download = NULL;
46static Eina_List *_job_list;
47
48static int download_init = 0;
49
50#endif /* BUILD_ECORE_CON */
51
52int
53ecore_file_download_init(void)
54{
55#ifdef BUILD_ECORE_CON
56 download_init++;
57 if (download_init > 1) return 1;
58 if (!ecore_con_init()) return 0;
59 if (!ecore_con_url_init())
60 {
61 ecore_con_shutdown();
62 return 0;
63 }
64# ifdef HAVE_CURL
65 _url_complete_handler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _ecore_file_download_url_complete_cb, NULL);
66 _url_progress_download = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, _ecore_file_download_url_progress_cb, NULL);
67# endif
68#endif /* BUILD_ECORE_CON */
69 return 1;
70}
71
72void
73ecore_file_download_shutdown(void)
74{
75#ifdef BUILD_ECORE_CON
76 download_init--;
77 if (download_init > 0) return;
78 if (_url_complete_handler)
79 ecore_event_handler_del(_url_complete_handler);
80 if (_url_progress_download)
81 ecore_event_handler_del(_url_progress_download);
82 _url_complete_handler = NULL;
83 _url_progress_download = NULL;
84 ecore_file_download_abort_all();
85 ecore_con_url_shutdown();
86 ecore_con_shutdown();
87#endif /* BUILD_ECORE_CON */
88}
89
90#ifdef BUILD_ECORE_CON
91# ifdef HAVE_CURL
92static Eina_Bool
93_ecore_file_download_headers_foreach_cb(const Eina_Hash *hash EINA_UNUSED, const void *key, void *data, void *fdata)
94{
95 Ecore_File_Download_Job *job = fdata;
96 ecore_con_url_additional_header_add(job->url_con, key, data);
97
98 return EINA_TRUE;
99}
100# endif
101#endif
102
103static Eina_Bool
104_ecore_file_download(const char *url,
105 const char *dst,
106 Ecore_File_Download_Completion_Cb completion_cb,
107 Ecore_File_Download_Progress_Cb progress_cb,
108 void *data,
109 Ecore_File_Download_Job **job_ret,
110 Eina_Hash *headers)
111{
112#ifdef BUILD_ECORE_CON
113 if (!url)
114 {
115 CRIT("Download URL is null");
116 return EINA_FALSE;
117 }
118
119 char *dir = ecore_file_dir_get(dst);
120
121 if (!ecore_file_is_dir(dir))
122 {
123 ERR("%s is not a directory", dir);
124 free(dir);
125 return EINA_FALSE;
126 }
127 free(dir);
128 if (ecore_file_exists(dst))
129 {
130 WRN("%s already exists", dst);
131 return EINA_FALSE;
132 }
133
134 if (!strncmp(url, "file://", 7))
135 {
136 /* FIXME: Maybe fork? Might take a while to copy.
137 * Check filesize? */
138 /* Just copy it */
139
140 url += 7;
141 /* skip hostname */
142 url = strchr(url, '/');
143 return ecore_file_cp(url, dst);
144 }
145# ifdef HAVE_CURL
146 else if ((!strncmp(url, "http://", 7)) || (!strncmp(url, "https://", 8)) ||
147 (!strncmp(url, "ftp://", 6)))
148 {
149 /* download */
150 Ecore_File_Download_Job *job;
151
152 job = _ecore_file_download_curl(url, dst, completion_cb, progress_cb, data, headers);
153 if(job_ret) *job_ret = job;
154 if(job)
155 return EINA_TRUE;
156 else
157 {
158 ERR("no job returned\n");
159 return EINA_FALSE;
160 }
161 return job ? EINA_TRUE : EINA_FALSE;
162 }
163# else
164 else if ((!strncmp(url, "http://", 7)) || (!strncmp(url, "https://", 8)) ||
165 (!strncmp(url, "ftp://", 6)))
166 {
167 (void)completion_cb;
168 (void)progress_cb;
169 (void)data;
170 (void)job_ret;
171 (void)headers;
172 return EINA_FALSE;
173 }
174# endif
175 else
176 {
177 return EINA_FALSE;
178 }
179#else
180 (void)url;
181 (void)dst;
182 (void)completion_cb;
183 (void)progress_cb;
184 (void)data;
185 (void)job_ret;
186 (void)headers;
187 return EINA_FALSE;
188#endif /* BUILD_ECORE_CON */
189}
190
191/**
192 * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions
193 *
194 * @{
195 */
196
197/**
198 * @brief Download the given url to the given destination.
199 *
200 * @param url The complete url to download.
201 * @param dst The local file to save the downloaded to.
202 * @param completion_cb A callback called on download complete.
203 * @param progress_cb A callback called during the download operation.
204 * @param data User data passed to both callbacks.
205 * @param job_ret Job used to abort the download.
206 * @return @c EINA_TRUE if the download start or @c EINA_FALSE on failure.
207 *
208 * This function starts the download of the URL @p url and saves it to
209 * @p dst. @p url must provide the protocol, including 'http://',
210 * 'ftp://' or 'file://'. Ecore_File must be compiled with CURL to
211 * download using http and ftp protocols. If @p dst is ill-formed, or
212 * if it already exists, the function returns @c EINA_FALSE. When the
213 * download is complete, the callback @p completion_cb is called and
214 * @p data is passed to it. The @p status parameter of @p completion_cb
215 * will be filled with the status of the download (200, 404,...). The
216 * @p progress_cb is called during the download operation, each time a
217 * packet is received or when CURL wants. It can be used to display the
218 * percentage of the downloaded file. Return 0 from this callback, if provided,
219 * to continue the operation or anything else to abort the download. The only
220 * operations that can be aborted are those with protocol 'http' or 'ftp'. In
221 * that case @p job_ret can be filled. It can be used with
222 * ecore_file_download_abort() or ecore_file_download_abort_all() to
223 * respectively abort one or all download operations. This function returns
224 * @c EINA_TRUE if the download starts, @c EINA_FALSE otherwise.
225 */
226EAPI Eina_Bool
227ecore_file_download(const char *url,
228 const char *dst,
229 Ecore_File_Download_Completion_Cb completion_cb,
230 Ecore_File_Download_Progress_Cb progress_cb,
231 void *data,
232 Ecore_File_Download_Job **job_ret)
233{
234 return _ecore_file_download(url, dst, completion_cb, progress_cb, data, job_ret, NULL);
235}
236
237/**
238 * @brief Download the given url to the given destination with additional headers.
239 *
240 * @param url The complete url to download.
241 * @param dst The local file to save the downloaded to.
242 * @param completion_cb A callback called on download complete.
243 * @param progress_cb A callback called during the download operation.
244 * @param data User data passed to both callbacks.
245 * @param job_ret Job used to abort the download.
246 * @param headers pointer of header lists.
247 * @return @c EINA_TRUE if the download start or @c EINA_FALSE on failure.
248 */
249EAPI Eina_Bool
250ecore_file_download_full(const char *url,
251 const char *dst,
252 Ecore_File_Download_Completion_Cb completion_cb,
253 Ecore_File_Download_Progress_Cb progress_cb,
254 void *data,
255 Ecore_File_Download_Job **job_ret,
256 Eina_Hash *headers)
257{
258 return _ecore_file_download(url, dst, completion_cb, progress_cb, data, job_ret, headers);
259}
260
261/**
262 * @brief Check if the given protocol is available.
263 *
264 * @param protocol The protocol to check.
265 * @return @c EINA_TRUE if protocol is handled, @c EINA_FALSE otherwise.
266 *
267 * This function returns @c EINA_TRUE if @p protocol is supported,
268 * @c EINA_FALSE otherwise. @p protocol can be 'http://', 'ftp://' or
269 * 'file://'. Ecore_FILE must be compiled with CURL to handle http and
270 * ftp protocols.
271 */
272EAPI Eina_Bool
273ecore_file_download_protocol_available(const char *protocol)
274{
275#ifdef BUILD_ECORE_CON
276 if (!strncmp(protocol, "file://", 7)) return EINA_TRUE;
277# ifdef HAVE_CURL
278 else if (!strncmp(protocol, "http://", 7)) return EINA_TRUE;
279 else if (!strncmp(protocol, "ftp://", 6)) return EINA_TRUE;
280# endif
281#else
282 (void)protocol;
283#endif /* BUILD_ECORE_CON */
284
285 return EINA_FALSE;
286}
287
288#ifdef BUILD_ECORE_CON
289
290# ifdef HAVE_CURL
291static int
292_ecore_file_download_url_compare_job(const void *data1, const void *data2)
293{
294 const Ecore_File_Download_Job *job = data1;
295 const Ecore_Con_Url *url = data2;
296
297 if (job->url_con == url) return 0;
298 return -1;
299}
300
301static Eina_Bool
302_ecore_file_download_url_complete_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
303{
304 Ecore_Con_Event_Url_Complete *ev = event;
305 Ecore_File_Download_Job *job;
306
307 job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
308 if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return ECORE_CALLBACK_PASS_ON;
309
310 fclose(job->file);
311 if (job->completion_cb)
312 job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, ev->status);
313
314 _job_list = eina_list_remove(_job_list, job);
315 free(job->dst);
316 ecore_con_url_free(job->url_con);
317 free(job);
318
319 return ECORE_CALLBACK_DONE;
320}
321
322static Eina_Bool
323_ecore_file_download_url_progress_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
324{
325/* this reports the downloads progress. if we return 0, then download
326 * continues, if we return anything else, then the download stops */
327 Ecore_Con_Event_Url_Progress *ev = event;
328 Ecore_File_Download_Job *job;
329
330 job = eina_list_search_unsorted(_job_list, _ecore_file_download_url_compare_job, ev->url_con);
331 if (!ECORE_MAGIC_CHECK(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB)) return ECORE_CALLBACK_PASS_ON;
332
333 if (job->progress_cb)
334 if (job->progress_cb(ecore_con_url_data_get(job->url_con), job->dst,
335 (long int) ev->down.total, (long int) ev->down.now,
336 (long int) ev->up.total, (long int) ev->up.now) != 0)
337 {
338 _job_list = eina_list_remove(_job_list, job);
339 fclose(job->file);
340 free(job->dst);
341 free(job);
342
343 return ECORE_CALLBACK_PASS_ON;
344 }
345
346 return ECORE_CALLBACK_DONE;
347}
348
349Ecore_File_Download_Job *
350_ecore_file_download_curl(const char *url, const char *dst,
351 Ecore_File_Download_Completion_Cb completion_cb,
352 Ecore_File_Download_Progress_Cb progress_cb,
353 void *data,
354 Eina_Hash *headers)
355{
356 Ecore_File_Download_Job *job;
357
358 job = calloc(1, sizeof(Ecore_File_Download_Job));
359 if (!job) return NULL;
360
361 ECORE_MAGIC_SET(job, ECORE_MAGIC_FILE_DOWNLOAD_JOB);
362
363 job->file = fopen(dst, "wb");
364 if (!job->file)
365 {
366 free(job);
367 return NULL;
368 }
369 job->url_con = ecore_con_url_new(url);
370 if (!job->url_con)
371 {
372 fclose(job->file);
373 free(job);
374 return NULL;
375 }
376
377 if (headers) eina_hash_foreach(headers, _ecore_file_download_headers_foreach_cb, job);
378 ecore_con_url_fd_set(job->url_con, fileno(job->file));
379 ecore_con_url_data_set(job->url_con, data);
380
381 job->dst = strdup(dst);
382
383 job->completion_cb = completion_cb;
384 job->progress_cb = progress_cb;
385 _job_list = eina_list_append(_job_list, job);
386
387 if (!ecore_con_url_get(job->url_con))
388 {
389 ecore_con_url_free(job->url_con);
390 _job_list = eina_list_remove(_job_list, job);
391 fclose(job->file);
392 ecore_file_remove(job->dst);
393 free(job->dst);
394 free(job);
395 return NULL;
396 }
397
398 return job;
399}
400# endif
401#endif
402
403/**
404 * @brief Abort the given download job and call the completion_cb
405 * callbck with a status of 1 (error).
406 *
407 * @param job The download job to abort.
408 *
409 * This function aborts a download operation started by
410 * ecore_file_download(). @p job is the #Ecore_File_Download_Job
411 * structure filled by ecore_file_download(). If it is @c NULL, this
412 * function does nothing. To abort all the currently downloading
413 * operations, call ecore_file_download_abort_all().
414 */
415EAPI void
416ecore_file_download_abort(Ecore_File_Download_Job *job)
417{
418 if (!job)
419 return;
420
421#ifdef BUILD_ECORE_CON
422 if (job->completion_cb)
423 job->completion_cb(ecore_con_url_data_get(job->url_con), job->dst, 1);
424# ifdef HAVE_CURL
425 ecore_con_url_free(job->url_con);
426# endif
427 _job_list = eina_list_remove(_job_list, job);
428 fclose(job->file);
429 free(job->dst);
430 free(job);
431#endif /* BUILD_ECORE_CON */
432}
433
434/**
435 * @brief Abort all downloads.
436 *
437 * This function aborts all the downloads that have been started by
438 * ecore_file_download(). It loops over the started downloads and call
439 * ecore_file_download_abort() for each of them. To abort only one
440 * specific download operation, call ecore_file_download_abort().
441 */
442EAPI void
443ecore_file_download_abort_all(void)
444{
445#ifdef BUILD_ECORE_CON
446 Ecore_File_Download_Job *job;
447
448 EINA_LIST_FREE(_job_list, job)
449 ecore_file_download_abort(job);
450#endif /* BUILD_ECORE_CON */
451}
452
453/**
454 * @}
455 */
diff --git a/src/lib/ecore_file/ecore_file_monitor.c b/src/lib/ecore_file/ecore_file_monitor.c
new file mode 100644
index 0000000000..8b07589a9e
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_monitor.c
@@ -0,0 +1,180 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include "ecore_file_private.h"
6
7typedef enum {
8 ECORE_FILE_MONITOR_TYPE_NONE,
9#ifdef HAVE_INOTIFY
10 ECORE_FILE_MONITOR_TYPE_INOTIFY,
11#endif
12#ifdef HAVE_NOTIFY_WIN32
13 ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32,
14#endif
15#ifdef HAVE_POLL
16 ECORE_FILE_MONITOR_TYPE_POLL
17#endif
18} Ecore_File_Monitor_Type;
19
20static Ecore_File_Monitor_Type monitor_type = ECORE_FILE_MONITOR_TYPE_NONE;
21
22int
23ecore_file_monitor_init(void)
24{
25#ifdef HAVE_INOTIFY
26 monitor_type = ECORE_FILE_MONITOR_TYPE_INOTIFY;
27 if (ecore_file_monitor_inotify_init())
28 return 1;
29#endif
30#ifdef HAVE_NOTIFY_WIN32
31 monitor_type = ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32;
32 if (ecore_file_monitor_win32_init())
33 return 1;
34#endif
35#ifdef HAVE_POLL
36 monitor_type = ECORE_FILE_MONITOR_TYPE_POLL;
37 if (ecore_file_monitor_poll_init())
38 return 1;
39#endif
40 monitor_type = ECORE_FILE_MONITOR_TYPE_NONE;
41 return 0;
42}
43
44void
45ecore_file_monitor_shutdown(void)
46{
47 switch (monitor_type)
48 {
49 case ECORE_FILE_MONITOR_TYPE_NONE:
50 break;
51#ifdef HAVE_INOTIFY
52 case ECORE_FILE_MONITOR_TYPE_INOTIFY:
53 ecore_file_monitor_inotify_shutdown();
54 break;
55#endif
56#ifdef HAVE_NOTIFY_WIN32
57 case ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32:
58 ecore_file_monitor_win32_shutdown();
59 break;
60#endif
61#ifdef HAVE_POLL
62 case ECORE_FILE_MONITOR_TYPE_POLL:
63 ecore_file_monitor_poll_shutdown();
64 break;
65#endif
66 }
67}
68
69/**
70 * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions
71 *
72 * @{
73 */
74
75/**
76 * @brief Monitor the given path using inotify, Windows notification, or polling.
77 *
78 * @param path The path to monitor.
79 * @param func The function to call on changes.
80 * @param data The data passed to func.
81 * @return An Ecore_File_Monitor pointer or NULL on failure.
82 *
83 * This function monitors @p path. If @p path is @c NULL, or is an
84 * empty string, or none of the notify methods (Inotify, Windows
85 * notification or polling) is available, or if @p path is not a file,
86 * the function returns @c NULL. Otherwise, it returns a newly
87 * allocated Ecore_File_Monitor object and the monitoring begins. When
88 * one of the Ecore_File_Event event is notified, @p func is called
89 * and @p data is passed to @p func. Call ecore_file_monitor_del() to
90 * stop the monitoring.
91 */
92EAPI Ecore_File_Monitor *
93ecore_file_monitor_add(const char *path,
94 Ecore_File_Monitor_Cb func,
95 void *data)
96{
97 if (!path || !*path)
98 return NULL;
99
100 switch (monitor_type)
101 {
102 case ECORE_FILE_MONITOR_TYPE_NONE:
103 return NULL;
104#ifdef HAVE_INOTIFY
105 case ECORE_FILE_MONITOR_TYPE_INOTIFY:
106 return ecore_file_monitor_inotify_add(path, func, data);
107#endif
108#ifdef HAVE_NOTIFY_WIN32
109 case ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32:
110 return ecore_file_monitor_win32_add(path, func, data);
111#endif
112#ifdef HAVE_POLL
113 case ECORE_FILE_MONITOR_TYPE_POLL:
114 return ecore_file_monitor_poll_add(path, func, data);
115#endif
116 }
117 return NULL;
118}
119
120/**
121 * @brief Stop the monitoring of the given path.
122 *
123 * @param em The Ecore_File_Monitor to stop.
124 *
125 * This function stops the the monitoring of the path that has been
126 * monitored by ecore_file_monitor_add(). @p em must be the value
127 * returned by ecore_file_monitor_add(). If @p em is @c NULL, or none
128 * of the notify methods (Inotify, Windows notification or polling) is
129 * availablethis function does nothing.
130 */
131EAPI void
132ecore_file_monitor_del(Ecore_File_Monitor *em)
133{
134 if (!em)
135 return;
136
137 switch (monitor_type)
138 {
139 case ECORE_FILE_MONITOR_TYPE_NONE:
140 break;
141#ifdef HAVE_INOTIFY
142 case ECORE_FILE_MONITOR_TYPE_INOTIFY:
143 ecore_file_monitor_inotify_del(em);
144 break;
145#endif
146#ifdef HAVE_NOTIFY_WIN32
147 case ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32:
148 ecore_file_monitor_win32_del(em);
149 break;
150#endif
151#ifdef HAVE_POLL
152 case ECORE_FILE_MONITOR_TYPE_POLL:
153 ecore_file_monitor_poll_del(em);
154 break;
155#endif
156 }
157}
158
159/**
160 * @brief Get the monitored path.
161 *
162 * @param em The Ecore_File_Monitor to query.
163 * @return The path that is monitored by @p em.
164 *
165 * This function returns the monitored path that has been
166 * monitored by ecore_file_monitor_add(). @p em must be the value
167 * returned by ecore_file_monitor_add(). If @p em is @c NULL, the
168 * function returns @c NULL.
169 */
170EAPI const char *
171ecore_file_monitor_path_get(Ecore_File_Monitor *em)
172{
173 if (!em)
174 return NULL;
175 return em->path;
176}
177
178/**
179 * @}
180 */
diff --git a/src/lib/ecore_file/ecore_file_monitor_inotify.c b/src/lib/ecore_file/ecore_file_monitor_inotify.c
new file mode 100644
index 0000000000..1b682fc4a0
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_monitor_inotify.c
@@ -0,0 +1,331 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8#include <sys/types.h>
9#include <unistd.h>
10
11#include "ecore_file_private.h"
12
13/*
14 * TODO:
15 *
16 * - Listen to these events:
17 * IN_ACCESS, IN_ATTRIB, IN_CLOSE_WRITE, IN_CLOSE_NOWRITE, IN_OPEN
18 * - Read all events first, then call the callbacks. This will prevent several
19 * callbacks with the typic save cycle (delete file, new file)
20 * - Listen to IN_IGNORED, emitted when the watch is removed
21 */
22
23#ifdef HAVE_INOTIFY
24
25#include <sys/inotify.h>
26
27
28typedef struct _Ecore_File_Monitor_Inotify Ecore_File_Monitor_Inotify;
29
30#define ECORE_FILE_MONITOR_INOTIFY(x) ((Ecore_File_Monitor_Inotify *)(x))
31
32struct _Ecore_File_Monitor_Inotify
33{
34 Ecore_File_Monitor monitor;
35 int wd;
36};
37
38static Ecore_Fd_Handler *_fdh = NULL;
39static Ecore_File_Monitor *_monitors = NULL;
40static pid_t _inotify_fd_pid = -1;
41
42static Eina_Bool _ecore_file_monitor_inotify_handler(void *data, Ecore_Fd_Handler *fdh);
43static Ecore_File_Monitor *_ecore_file_monitor_inotify_monitor_find(int wd);
44static void _ecore_file_monitor_inotify_events(Ecore_File_Monitor *em, char *file, int mask);
45static int _ecore_file_monitor_inotify_monitor(Ecore_File_Monitor *em, const char *path);
46#if 0
47static void _ecore_file_monitor_inotify_print(char *file, int mask);
48#endif
49
50int
51ecore_file_monitor_inotify_init(void)
52{
53 int fd;
54
55 fd = inotify_init();
56 if (fd < 0)
57 return 0;
58
59 _fdh = ecore_main_fd_handler_add(fd, ECORE_FD_READ, _ecore_file_monitor_inotify_handler,
60 NULL, NULL, NULL);
61 if (!_fdh)
62 {
63 close(fd);
64 return 0;
65 }
66
67 _inotify_fd_pid = getpid();
68 return 1;
69}
70
71int
72ecore_file_monitor_inotify_shutdown(void)
73{
74 int fd;
75
76 while(_monitors)
77 ecore_file_monitor_inotify_del(_monitors);
78
79 if (_fdh)
80 {
81 fd = ecore_main_fd_handler_fd_get(_fdh);
82 ecore_main_fd_handler_del(_fdh);
83 close(fd);
84 }
85 _inotify_fd_pid = -1;
86 return 1;
87}
88
89Ecore_File_Monitor *
90ecore_file_monitor_inotify_add(const char *path,
91 void (*func) (void *data, Ecore_File_Monitor *em,
92 Ecore_File_Event event,
93 const char *path),
94 void *data)
95{
96 Ecore_File_Monitor *em;
97 int len;
98
99 if (_inotify_fd_pid == -1) return NULL;
100
101 if (_inotify_fd_pid != getpid())
102 {
103 ecore_file_monitor_inotify_shutdown();
104 ecore_file_monitor_inotify_init();
105 }
106
107 em = calloc(1, sizeof(Ecore_File_Monitor_Inotify));
108 if (!em) return NULL;
109
110 em->func = func;
111 em->data = data;
112
113 em->path = strdup(path);
114 len = strlen(em->path);
115 if (em->path[len - 1] == '/' && strcmp(em->path, "/"))
116 em->path[len - 1] = 0;
117
118 _monitors = ECORE_FILE_MONITOR(eina_inlist_append(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
119
120 if (!_ecore_file_monitor_inotify_monitor(em, em->path))
121 return NULL;
122
123 return em;
124}
125
126void
127ecore_file_monitor_inotify_del(Ecore_File_Monitor *em)
128{
129 int fd;
130
131 if (_monitors)
132 _monitors = ECORE_FILE_MONITOR(eina_inlist_remove(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
133
134 fd = ecore_main_fd_handler_fd_get(_fdh);
135 if (ECORE_FILE_MONITOR_INOTIFY(em)->wd)
136 inotify_rm_watch(fd, ECORE_FILE_MONITOR_INOTIFY(em)->wd);
137 free(em->path);
138 free(em);
139}
140
141static Eina_Bool
142_ecore_file_monitor_inotify_handler(void *data EINA_UNUSED, Ecore_Fd_Handler *fdh)
143{
144 Ecore_File_Monitor *em;
145 char buffer[16384];
146 struct inotify_event *event;
147 int i = 0;
148 int event_size;
149 ssize_t size;
150
151 size = read(ecore_main_fd_handler_fd_get(fdh), buffer, sizeof(buffer));
152 while (i < size)
153 {
154 event = (struct inotify_event *)&buffer[i];
155 event_size = sizeof(struct inotify_event) + event->len;
156 i += event_size;
157
158 em = _ecore_file_monitor_inotify_monitor_find(event->wd);
159 if (!em) continue;
160
161 _ecore_file_monitor_inotify_events(em, (event->len ? event->name : NULL), event->mask);
162 }
163
164 return ECORE_CALLBACK_RENEW;
165}
166
167static Ecore_File_Monitor *
168_ecore_file_monitor_inotify_monitor_find(int wd)
169{
170 Ecore_File_Monitor *l;
171
172 EINA_INLIST_FOREACH(_monitors, l)
173 {
174 if (ECORE_FILE_MONITOR_INOTIFY(l)->wd == wd)
175 return l;
176 }
177 return NULL;
178}
179
180static void
181_ecore_file_monitor_inotify_events(Ecore_File_Monitor *em, char *file, int mask)
182{
183 char buf[PATH_MAX];
184 int isdir;
185
186 if ((file) && (file[0]))
187 snprintf(buf, sizeof(buf), "%s/%s", em->path, file);
188 else
189 strcpy(buf, em->path);
190 isdir = mask & IN_ISDIR;
191
192#if 0
193 _ecore_file_monitor_inotify_print(buf, mask);
194#endif
195
196 if (mask & IN_ATTRIB)
197 {
198 em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, buf);
199 }
200 if (mask & IN_CLOSE_WRITE)
201 {
202 if (!isdir)
203 em->func(em->data, em, ECORE_FILE_EVENT_CLOSED, buf);
204 }
205 if (mask & IN_MODIFY)
206 {
207 if (!isdir)
208 em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, buf);
209 }
210 if (mask & IN_MOVED_FROM)
211 {
212 if (isdir)
213 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_DIRECTORY, buf);
214 else
215 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_FILE, buf);
216 }
217 if (mask & IN_MOVED_TO)
218 {
219 if (isdir)
220 em->func(em->data, em, ECORE_FILE_EVENT_CREATED_DIRECTORY, buf);
221 else
222 em->func(em->data, em, ECORE_FILE_EVENT_CREATED_FILE, buf);
223 }
224 if (mask & IN_DELETE)
225 {
226 if (isdir)
227 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_DIRECTORY, buf);
228 else
229 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_FILE, buf);
230 }
231 if (mask & IN_CREATE)
232 {
233 if (isdir)
234 em->func(em->data, em, ECORE_FILE_EVENT_CREATED_DIRECTORY, buf);
235 else
236 em->func(em->data, em, ECORE_FILE_EVENT_CREATED_FILE, buf);
237 }
238 if (mask & IN_DELETE_SELF)
239 {
240 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
241 }
242 if (mask & IN_MOVE_SELF)
243 {
244 /* We just call delete. The dir is gone... */
245 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
246 }
247 if (mask & IN_UNMOUNT)
248 {
249 /* We just call delete. The dir is gone... */
250 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
251 }
252 if (mask & IN_IGNORED)
253 {
254 /* The watch is removed. If the file name still exists monitor the new one,
255 * else delete it */
256 if (ecore_file_exists(em->path))
257 {
258 if (_ecore_file_monitor_inotify_monitor(em, em->path))
259 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
260 }
261 else
262 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
263 }
264}
265
266static int
267_ecore_file_monitor_inotify_monitor(Ecore_File_Monitor *em, const char *path)
268{
269 int mask =
270 IN_ATTRIB |
271 IN_CLOSE_WRITE |
272 IN_MOVED_FROM |
273 IN_MOVED_TO |
274 IN_DELETE |
275 IN_CREATE |
276 IN_MODIFY |
277 IN_DELETE_SELF |
278 IN_MOVE_SELF |
279 IN_UNMOUNT;
280
281 ECORE_FILE_MONITOR_INOTIFY(em)->wd =
282 inotify_add_watch(ecore_main_fd_handler_fd_get(_fdh), path, mask);
283 if (ECORE_FILE_MONITOR_INOTIFY(em)->wd < 0)
284 {
285 INF("inotify_add_watch failed, file was deleted");
286 ecore_file_monitor_inotify_del(em);
287 return 0;
288 }
289 return 1;
290}
291
292#if 0
293static void
294_ecore_file_monitor_inotify_print(char *file, int mask)
295{
296 const char *type;
297
298 if (mask & IN_ISDIR)
299 type = "dir";
300 else
301 type = "file";
302
303 if (mask & IN_ACCESS)
304 INF("Inotify accessed %s: %s", type, file);
305 if (mask & IN_MODIFY)
306 INF("Inotify modified %s: %s", type, file);
307 if (mask & IN_ATTRIB)
308 INF("Inotify attributes %s: %s", type, file);
309 if (mask & IN_CLOSE_WRITE)
310 INF("Inotify close write %s: %s", type, file);
311 if (mask & IN_CLOSE_NOWRITE)
312 INF("Inotify close write %s: %s", type, file);
313 if (mask & IN_OPEN)
314 INF("Inotify open %s: %s", type, file);
315 if (mask & IN_MOVED_FROM)
316 INF("Inotify moved from %s: %s", type, file);
317 if (mask & IN_MOVED_TO)
318 INF("Inotify moved to %s: %s", type, file);
319 if (mask & IN_DELETE)
320 INF("Inotify delete %s: %s", type, file);
321 if (mask & IN_CREATE)
322 INF("Inotify create %s: %s", type, file);
323 if (mask & IN_DELETE_SELF)
324 INF("Inotify delete self %s: %s", type, file);
325 if (mask & IN_MOVE_SELF)
326 INF("Inotify move self %s: %s", type, file);
327 if (mask & IN_UNMOUNT)
328 INF("Inotify unmount %s: %s", type, file);
329}
330#endif
331#endif /* HAVE_INOTIFY */
diff --git a/src/lib/ecore_file/ecore_file_monitor_poll.c b/src/lib/ecore_file/ecore_file_monitor_poll.c
new file mode 100644
index 0000000000..68889a74e8
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_monitor_poll.c
@@ -0,0 +1,340 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8
9#include "ecore_file_private.h"
10
11#ifdef HAVE_POLL
12
13/*
14 * TODO:
15 * - Implement recursive as an option!
16 * - Keep whole path or just name of file? (Memory or CPU...)
17 * - Remove requests without files?
18 * - Change poll time
19 */
20
21typedef struct _Ecore_File_Monitor_Poll Ecore_File_Monitor_Poll;
22
23#define ECORE_FILE_MONITOR_POLL(x) ((Ecore_File_Monitor_Poll *)(x))
24
25struct _Ecore_File_Monitor_Poll
26{
27 Ecore_File_Monitor monitor;
28 int mtime;
29 unsigned char deleted;
30};
31
32#define ECORE_FILE_INTERVAL_MIN 1.0
33#define ECORE_FILE_INTERVAL_STEP 0.5
34#define ECORE_FILE_INTERVAL_MAX 5.0
35
36static double _interval = ECORE_FILE_INTERVAL_MIN;
37static Ecore_Timer *_timer = NULL;
38static Ecore_File_Monitor *_monitors = NULL;
39static int _lock = 0;
40
41static Eina_Bool _ecore_file_monitor_poll_handler(void *data);
42static void _ecore_file_monitor_poll_check(Ecore_File_Monitor *em);
43static int _ecore_file_monitor_poll_checking(Ecore_File_Monitor *em, char *name);
44
45int
46ecore_file_monitor_poll_init(void)
47{
48 return 1;
49}
50
51int
52ecore_file_monitor_poll_shutdown(void)
53{
54 while(_monitors)
55 ecore_file_monitor_poll_del(_monitors);
56
57 if (_timer)
58 {
59 ecore_timer_del(_timer);
60 _timer = NULL;
61 }
62 return 1;
63}
64
65Ecore_File_Monitor *
66ecore_file_monitor_poll_add(const char *path,
67 void (*func) (void *data, Ecore_File_Monitor *em,
68 Ecore_File_Event event,
69 const char *path),
70 void *data)
71{
72 Ecore_File_Monitor *em;
73 size_t len;
74
75 if (!path) return NULL;
76 if (!func) return NULL;
77
78 em = calloc(1, sizeof(Ecore_File_Monitor_Poll));
79 if (!em) return NULL;
80
81 if (!_timer)
82 _timer = ecore_timer_add(_interval, _ecore_file_monitor_poll_handler, NULL);
83 else
84 ecore_timer_interval_set(_timer, ECORE_FILE_INTERVAL_MIN);
85
86 em->path = strdup(path);
87 len = strlen(em->path);
88 if (em->path[len - 1] == '/' && strcmp(em->path, "/"))
89 em->path[len - 1] = 0;
90
91 em->func = func;
92 em->data = data;
93
94 ECORE_FILE_MONITOR_POLL(em)->mtime = ecore_file_mod_time(em->path);
95 _monitors = ECORE_FILE_MONITOR(eina_inlist_append(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
96
97 if (ecore_file_exists(em->path))
98 {
99 if (ecore_file_is_dir(em->path))
100 {
101 /* Check for subdirs */
102 Eina_List *files;
103 char *file;
104
105 files = ecore_file_ls(em->path);
106 EINA_LIST_FREE(files, file)
107 {
108 Ecore_File *f;
109 char buf[PATH_MAX];
110
111 f = calloc(1, sizeof(Ecore_File));
112 if (!f)
113 {
114 free(file);
115 continue;
116 }
117
118 snprintf(buf, sizeof(buf), "%s/%s", em->path, file);
119 f->name = file;
120 f->mtime = ecore_file_mod_time(buf);
121 f->is_dir = ecore_file_is_dir(buf);
122 em->files = (Ecore_File *) eina_inlist_append(EINA_INLIST_GET(em->files), EINA_INLIST_GET(f));
123 }
124 }
125 }
126 else
127 {
128 ecore_file_monitor_poll_del(em);
129 return NULL;
130 }
131
132 return em;
133}
134
135void
136ecore_file_monitor_poll_del(Ecore_File_Monitor *em)
137{
138 Ecore_File *l;
139
140 if (_lock)
141 {
142 ECORE_FILE_MONITOR_POLL(em)->deleted = 1;
143 return;
144 }
145
146 /* Remove files */
147 /*It's possible there weren't any files to monitor, so check if the list is init*/
148 if (em->files)
149 {
150 for (l = em->files; l;)
151 {
152 Ecore_File *file = l;
153
154 l = (Ecore_File *) EINA_INLIST_GET(l)->next;
155 free(file->name);
156 free(file);
157 }
158 }
159
160 if (_monitors)
161 _monitors = ECORE_FILE_MONITOR(eina_inlist_remove(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
162
163 free(em->path);
164 free(em);
165
166 if (_timer)
167 {
168 if (!_monitors)
169 {
170 ecore_timer_del(_timer);
171 _timer = NULL;
172 }
173 else
174 ecore_timer_interval_set(_timer, ECORE_FILE_INTERVAL_MIN);
175 }
176}
177
178static Eina_Bool
179_ecore_file_monitor_poll_handler(void *data EINA_UNUSED)
180{
181 Ecore_File_Monitor *l;
182
183 _interval += ECORE_FILE_INTERVAL_STEP;
184
185 _lock = 1;
186 EINA_INLIST_FOREACH(_monitors, l)
187 _ecore_file_monitor_poll_check(l);
188 _lock = 0;
189
190 if (_interval > ECORE_FILE_INTERVAL_MAX)
191 _interval = ECORE_FILE_INTERVAL_MAX;
192 ecore_timer_interval_set(_timer, _interval);
193
194 for (l = _monitors; l;)
195 {
196 Ecore_File_Monitor *em = l;
197
198 l = ECORE_FILE_MONITOR(EINA_INLIST_GET(l)->next);
199 if (ECORE_FILE_MONITOR_POLL(em)->deleted)
200 ecore_file_monitor_del(em);
201 }
202 return ECORE_CALLBACK_RENEW;
203}
204
205static void
206_ecore_file_monitor_poll_check(Ecore_File_Monitor *em)
207{
208 int mtime;
209
210 mtime = ecore_file_mod_time(em->path);
211 if (mtime < ECORE_FILE_MONITOR_POLL(em)->mtime)
212 {
213 Ecore_File *l;
214 Ecore_File_Event event;
215
216 /* Notify all files deleted */
217 for (l = em->files; l;)
218 {
219 Ecore_File *f = l;
220 char buf[PATH_MAX];
221
222 l = (Ecore_File *) EINA_INLIST_GET(l)->next;
223
224 snprintf(buf, sizeof(buf), "%s/%s", em->path, f->name);
225 if (f->is_dir)
226 event = ECORE_FILE_EVENT_DELETED_DIRECTORY;
227 else
228 event = ECORE_FILE_EVENT_DELETED_FILE;
229 em->func(em->data, em, event, buf);
230 free(f->name);
231 free(f);
232 }
233 em->files = NULL;
234 em->func(em->data, em, ECORE_FILE_EVENT_DELETED_SELF, em->path);
235 _interval = ECORE_FILE_INTERVAL_MIN;
236 }
237 else
238 {
239 Ecore_File *l;
240
241 /* Check for changed files */
242 for (l = em->files; l;)
243 {
244 Ecore_File *f = l;
245 char buf[PATH_MAX];
246 int mt;
247 Ecore_File_Event event;
248
249 l = (Ecore_File *) EINA_INLIST_GET(l)->next;
250
251 snprintf(buf, sizeof(buf), "%s/%s", em->path, f->name);
252 mt = ecore_file_mod_time(buf);
253 if (mt < f->mtime)
254 {
255 if (f->is_dir)
256 event = ECORE_FILE_EVENT_DELETED_DIRECTORY;
257 else
258 event = ECORE_FILE_EVENT_DELETED_FILE;
259
260 em->func(em->data, em, event, buf);
261 em->files = (Ecore_File *) eina_inlist_remove(EINA_INLIST_GET(em->files), EINA_INLIST_GET(f));
262 free(f->name);
263 free(f);
264 _interval = ECORE_FILE_INTERVAL_MIN;
265 }
266 else if ((mt > f->mtime) && !(f->is_dir))
267 {
268 em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, buf);
269 _interval = ECORE_FILE_INTERVAL_MIN;
270 f->mtime = mt;
271 }
272 else
273 f->mtime = mt;
274 }
275
276 /* Check for new files */
277 if (ECORE_FILE_MONITOR_POLL(em)->mtime < mtime)
278 {
279 Eina_List *files;
280 Eina_List *fl;
281 char *file;
282
283 /* Files have been added or removed */
284 files = ecore_file_ls(em->path);
285 if (files)
286 {
287 /* Are we a directory? We should check first, rather than rely on null here*/
288 EINA_LIST_FOREACH(files, fl, file)
289 {
290 Ecore_File *f;
291 char buf[PATH_MAX];
292 Ecore_File_Event event;
293
294 if (_ecore_file_monitor_poll_checking(em, file))
295 continue;
296
297 snprintf(buf, sizeof(buf), "%s/%s", em->path, file);
298 f = calloc(1, sizeof(Ecore_File));
299 if (!f)
300 continue;
301
302 f->name = strdup(file);
303 f->mtime = ecore_file_mod_time(buf);
304 f->is_dir = ecore_file_is_dir(buf);
305 if (f->is_dir)
306 event = ECORE_FILE_EVENT_CREATED_DIRECTORY;
307 else
308 event = ECORE_FILE_EVENT_CREATED_FILE;
309 em->func(em->data, em, event, buf);
310 em->files = (Ecore_File *) eina_inlist_append(EINA_INLIST_GET(em->files), EINA_INLIST_GET(f));
311 }
312 while (files)
313 {
314 file = eina_list_data_get(files);
315 free(file);
316 files = eina_list_remove_list(files, files);
317 }
318 }
319
320 if (!ecore_file_is_dir(em->path))
321 em->func(em->data, em, ECORE_FILE_EVENT_MODIFIED, em->path);
322 _interval = ECORE_FILE_INTERVAL_MIN;
323 }
324 }
325 ECORE_FILE_MONITOR_POLL(em)->mtime = mtime;
326}
327
328static int
329_ecore_file_monitor_poll_checking(Ecore_File_Monitor *em, char *name)
330{
331 Ecore_File *l;
332
333 EINA_INLIST_FOREACH(em->files, l)
334 {
335 if (!strcmp(l->name, name))
336 return 1;
337 }
338 return 0;
339}
340#endif
diff --git a/src/lib/ecore_file/ecore_file_monitor_win32.c b/src/lib/ecore_file/ecore_file_monitor_win32.c
new file mode 100644
index 0000000000..7f3af0907b
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_monitor_win32.c
@@ -0,0 +1,310 @@
1/*
2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3 */
4
5#ifdef HAVE_CONFIG_H
6# include <config.h>
7#endif
8
9#ifdef HAVE_NOTIFY_WIN32
10
11# define WIN32_LEAN_AND_MEAN
12# include <windows.h>
13# undef WIN32_LEAN_AND_MEAN
14# include <process.h>
15
16# include "ecore_file_private.h"
17
18
19typedef struct _Ecore_File_Monitor_Win32 Ecore_File_Monitor_Win32;
20typedef struct _Ecore_File_Monitor_Win32_Data Ecore_File_Monitor_Win32_Data;
21
22/* 4096 = 256 * sizeof(FILE_NOTIFY_INFORMATION) */
23# define ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE 4096
24# define ECORE_FILE_MONITOR_WIN32(x) ((Ecore_File_Monitor_Win32 *)(x))
25
26struct _Ecore_File_Monitor_Win32_Data
27{
28 char buffer[ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE];
29 OVERLAPPED overlapped;
30 HANDLE handle;
31 HANDLE event;
32 Ecore_File_Monitor *monitor;
33 Ecore_Win32_Handler *h;
34 DWORD buf_length;
35 int is_dir;
36};
37
38struct _Ecore_File_Monitor_Win32
39{
40 Ecore_File_Monitor monitor;
41 Ecore_File_Monitor_Win32_Data *file;
42 Ecore_File_Monitor_Win32_Data *dir;
43};
44
45static Ecore_File_Monitor *_monitors = NULL;
46
47static Eina_Bool _ecore_file_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh);
48
49
50static Ecore_File_Monitor_Win32_Data *
51_ecore_file_monitor_win32_data_new(Ecore_File_Monitor *monitor, int type)
52{
53 Ecore_File_Monitor_Win32_Data *md;
54 DWORD filter;
55
56 md = (Ecore_File_Monitor_Win32_Data *)calloc(1, sizeof(Ecore_File_Monitor_Win32_Data));
57 if (!md) return NULL;
58
59 md->handle = CreateFile(monitor->path,
60 FILE_LIST_DIRECTORY,
61 FILE_SHARE_READ |
62 FILE_SHARE_WRITE,
63 NULL,
64 OPEN_EXISTING,
65 FILE_FLAG_BACKUP_SEMANTICS |
66 FILE_FLAG_OVERLAPPED,
67 NULL);
68 if (md->handle == INVALID_HANDLE_VALUE)
69 goto free_md;
70
71 md->event = CreateEvent(NULL, FALSE, FALSE, NULL);
72 if (!md->event)
73 goto close_handle;
74
75 ZeroMemory (&md->overlapped, sizeof(md->overlapped));
76 md->overlapped.hEvent = md->event;
77
78 filter = (type == 0) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME;
79 filter |=
80 FILE_NOTIFY_CHANGE_ATTRIBUTES |
81 FILE_NOTIFY_CHANGE_SIZE |
82 FILE_NOTIFY_CHANGE_LAST_WRITE |
83 FILE_NOTIFY_CHANGE_LAST_ACCESS |
84 FILE_NOTIFY_CHANGE_CREATION |
85 FILE_NOTIFY_CHANGE_SECURITY;
86
87 if (!ReadDirectoryChangesW(md->handle,
88 md->buffer,
89 ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE,
90 FALSE,
91 filter,
92 &md->buf_length,
93 &md->overlapped,
94 NULL))
95 goto close_event;
96
97 md->h = ecore_main_win32_handler_add(md->event,
98 _ecore_file_monitor_win32_cb,
99 md);
100 if (!md->h)
101 goto close_event;
102
103 md->monitor = monitor;
104 md->is_dir = type;
105
106 return md;
107
108 close_event:
109 CloseHandle(md->event);
110 close_handle:
111 CloseHandle(md->handle);
112 free_md:
113 free(md);
114
115 return NULL;
116}
117
118static void
119_ecore_file_monitor_win32_data_free(Ecore_File_Monitor_Win32_Data *md)
120{
121 if (!md) return;
122
123 CloseHandle(md->event);
124 CloseHandle (md->handle);
125 free (md);
126}
127
128static Eina_Bool
129_ecore_file_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh)
130{
131 char filename[PATH_MAX];
132 PFILE_NOTIFY_INFORMATION fni;
133 Ecore_File_Monitor_Win32_Data *md;
134 wchar_t *wname;
135 char *name;
136 DWORD filter;
137 DWORD offset;
138 DWORD buf_length;
139 Ecore_File_Event event = ECORE_FILE_EVENT_NONE;
140
141 md = (Ecore_File_Monitor_Win32_Data *)data;
142
143 if (!GetOverlappedResult (md->handle, &md->overlapped, &buf_length, TRUE))
144 return 1;
145
146 fni = (PFILE_NOTIFY_INFORMATION)md->buffer;
147 do {
148 if (!fni)
149 break;
150 offset = fni->NextEntryOffset;
151
152 wname = (wchar_t *)malloc(sizeof(wchar_t) * (fni->FileNameLength + 1));
153 if (!wname)
154 return 0;
155
156 memcpy(wname, fni->FileName, fni->FileNameLength);
157 wname[fni->FileNameLength]='\0';
158 name = evil_wchar_to_char(wname);
159 free(wname);
160 if (!name)
161 return 0;
162
163 _snprintf(filename, PATH_MAX, "%s\\%s", md->monitor->path, name);
164 free(name);
165
166 switch (fni->Action)
167 {
168 case FILE_ACTION_ADDED:
169 if (md->is_dir)
170 event = ECORE_FILE_EVENT_CREATED_DIRECTORY;
171 else
172 event = ECORE_FILE_EVENT_CREATED_FILE;
173 break;
174 case FILE_ACTION_REMOVED:
175 if (md->is_dir)
176 event = ECORE_FILE_EVENT_DELETED_DIRECTORY;
177 else
178 event = ECORE_FILE_EVENT_DELETED_FILE;
179 break;
180 case FILE_ACTION_MODIFIED:
181 if (!md->is_dir)
182 event = ECORE_FILE_EVENT_MODIFIED;
183 break;
184 case FILE_ACTION_RENAMED_OLD_NAME:
185 if (md->is_dir)
186 event = ECORE_FILE_EVENT_DELETED_DIRECTORY;
187 else
188 event = ECORE_FILE_EVENT_DELETED_FILE;
189 break;
190 case FILE_ACTION_RENAMED_NEW_NAME:
191 if (md->is_dir)
192 event = ECORE_FILE_EVENT_CREATED_DIRECTORY;
193 else
194 event = ECORE_FILE_EVENT_CREATED_FILE;
195 break;
196 default:
197 fprintf(stderr, "unknown event\n");
198 event = ECORE_FILE_EVENT_NONE;
199 break;
200 }
201 if (event != ECORE_FILE_EVENT_NONE)
202 md->monitor->func(md->monitor->data, md->monitor, event, filename);
203
204 fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni + offset);
205 } while (offset);
206
207 filter = (md->is_dir == 0) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME;
208 filter |=
209 FILE_NOTIFY_CHANGE_ATTRIBUTES |
210 FILE_NOTIFY_CHANGE_SIZE |
211 FILE_NOTIFY_CHANGE_LAST_WRITE |
212 FILE_NOTIFY_CHANGE_LAST_ACCESS |
213 FILE_NOTIFY_CHANGE_CREATION |
214 FILE_NOTIFY_CHANGE_SECURITY;
215
216 ReadDirectoryChangesW(md->handle,
217 md->buffer,
218 ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE,
219 FALSE,
220 filter,
221 &md->buf_length,
222 &md->overlapped,
223 NULL);
224 return 1;
225}
226
227int
228ecore_file_monitor_win32_init(void)
229{
230 return 1;
231}
232
233int
234ecore_file_monitor_win32_shutdown(void)
235{
236 return 1;
237}
238
239Ecore_File_Monitor *
240ecore_file_monitor_win32_add(const char *path,
241 void (*func) (void *data, Ecore_File_Monitor *em,
242 Ecore_File_Event event,
243 const char *path),
244 void *data)
245{
246 Ecore_File_Monitor_Win32 *m;
247 Ecore_File_Monitor *em;
248 size_t len;
249
250 if (!path || (*path == '\0')) return NULL;
251 if (!ecore_file_exists(path) || !ecore_file_is_dir(path))
252 return NULL;
253 if (!func) return NULL;
254
255 em = (Ecore_File_Monitor *)calloc(1, sizeof(Ecore_File_Monitor_Win32));
256 if (!em) return NULL;
257
258 em->func = func;
259 em->data = data;
260
261 em->path = strdup(path);
262 if (!em->path)
263 {
264 free(em);
265 return NULL;
266 }
267 len = strlen(em->path);
268 if (em->path[len - 1] == '/' || em->path[len - 1] == '\\')
269 em->path[len - 1] = '\0';
270
271 m = ECORE_FILE_MONITOR_WIN32(em);
272
273 m->file = _ecore_file_monitor_win32_data_new(em, 0);
274 if (!m->file)
275 {
276 free(em->path);
277 free(em);
278 return NULL;
279 }
280
281 m->dir = _ecore_file_monitor_win32_data_new(em, 1);
282 if (!m->dir)
283 {
284 _ecore_file_monitor_win32_data_free(m->file);
285 free(em->path);
286 free(em);
287 return NULL;
288 }
289
290 _monitors = ECORE_FILE_MONITOR(eina_inlist_append(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em)));
291
292 return em;
293}
294
295void
296ecore_file_monitor_win32_del(Ecore_File_Monitor *em)
297{
298 Ecore_File_Monitor_Win32 *m;
299
300 if (!em)
301 return;
302
303 m = ECORE_FILE_MONITOR_WIN32(em);
304 _ecore_file_monitor_win32_data_free(m->dir);
305 _ecore_file_monitor_win32_data_free(m->file);
306 free(em->path);
307 free(em);
308}
309
310#endif
diff --git a/src/lib/ecore_file/ecore_file_path.c b/src/lib/ecore_file/ecore_file_path.c
new file mode 100644
index 0000000000..c1c54b7c0f
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_path.c
@@ -0,0 +1,192 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#ifdef STDC_HEADERS
6# include <stdlib.h>
7# include <stddef.h>
8#else
9# ifdef HAVE_STDLIB_H
10# include <stdlib.h>
11# endif
12#endif
13#ifdef HAVE_ALLOCA_H
14# include <alloca.h>
15#elif !defined alloca
16# ifdef __GNUC__
17# define alloca __builtin_alloca
18# elif defined _AIX
19# define alloca __alloca
20# elif defined _MSC_VER
21# include <malloc.h>
22# define alloca _alloca
23# elif !defined HAVE_ALLOCA
24# ifdef __cplusplus
25extern "C"
26# endif
27void *alloca (size_t);
28# endif
29#endif
30
31#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34
35#include "ecore_file_private.h"
36
37static Eina_List *__ecore_file_path_bin = NULL;
38
39static Eina_List *_ecore_file_path_from_env(const char *env);
40
41void
42ecore_file_path_init(void)
43{
44 __ecore_file_path_bin = _ecore_file_path_from_env("PATH");
45}
46
47void
48ecore_file_path_shutdown(void)
49{
50 char *dir;
51
52 EINA_LIST_FREE(__ecore_file_path_bin, dir)
53 eina_stringshare_del(dir);
54}
55
56Eina_List *
57_ecore_file_path_from_env(const char *env)
58{
59 Eina_List *path = NULL;
60 char *env_tmp, *env_path, *p, *last;
61
62 env_tmp = getenv(env);
63 if (!env_tmp)
64 return path;
65
66 env_path = alloca(sizeof(char) * strlen(env_tmp) + 1);
67 memset(env_path, 0, strlen(env_tmp));
68 strcpy(env_path, env_tmp);
69 last = env_path;
70 for (p = env_path; *p; p++)
71 {
72 if (*p == ':')
73 *p = '\0';
74
75 if (!*p)
76 {
77 if (!ecore_file_path_dir_exists(last))
78 path = eina_list_append(path, eina_stringshare_add(last));
79 last = p + 1;
80 }
81 }
82 if (p > last)
83 path = eina_list_append(path, eina_stringshare_add(last));
84
85 return path;
86}
87
88/**
89 * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions
90 *
91 * @{
92 */
93
94/**
95 * @brief Check if the given directory is in PATH.
96 *
97 * @param in_dir The name of the directory to search in PATH.
98 * @return @c EINA_TRUE if the directory exist in PATH, @c EINA_FALSE otherwise.
99 *
100 * This function checks if @p in_dir is in the environment variable
101 * PATH. If @p in_dir is @c NULL, or if PATH is empty, or @p in_dir is
102 * not in PATH, the function returns @c EINA_FALSE, otherwise it returns
103 * @c EINA_TRUE.
104 */
105EAPI Eina_Bool
106ecore_file_path_dir_exists(const char *in_dir)
107{
108 Eina_List *l;
109 char *dir;
110
111 if (!in_dir)
112 return EINA_FALSE;
113
114 if (!__ecore_file_path_bin) return EINA_FALSE;
115 EINA_LIST_FOREACH(__ecore_file_path_bin, l, dir)
116 {
117 if (strcmp(dir, in_dir))
118 return EINA_TRUE;
119 }
120
121 return EINA_FALSE;
122}
123
124/**
125 * @brief Check if the given application is installed.
126 *
127 * @param exe The name of the application
128 * @return @c EINA_TRUE if the @p exe is in PATH and is executable,
129 * @c EINA_FALSE otherwise.
130 *
131 * This function checks if @p exe exists in PATH and is executable. If
132 * @p exe is @c NULL or is not executable, the function returns
133 * @c EINA_FALSE, otherwise it returns @c EINA_TRUE.
134 */
135EAPI Eina_Bool
136ecore_file_app_installed(const char *exe)
137{
138 Eina_List *l;
139 char *dir;
140 char buf[PATH_MAX];
141
142 if (!exe) return EINA_FALSE;
143 if (ecore_file_can_exec(exe)) return EINA_TRUE;
144
145 EINA_LIST_FOREACH(__ecore_file_path_bin, l, dir)
146 {
147 snprintf(buf, sizeof(buf), "%s/%s", dir, exe);
148 if (ecore_file_can_exec(buf))
149 return EINA_TRUE;
150 }
151
152 return EINA_FALSE;
153}
154
155/**
156 * @brief Get a list of all the applications installed on the system.
157 *
158 * @return An Eina_List containing all the executable files in the
159 * system.
160 *
161 * This function returns a list of allocated strings of all the
162 * executable files. If no files are found, the function returns
163 * @c NULL. When not needed anymore, the element of the list must be
164 * freed.
165 */
166EAPI Eina_List *
167ecore_file_app_list(void)
168{
169 Eina_List *list = NULL;
170 Eina_List *files;
171 Eina_List *l;
172 char buf[PATH_MAX], *dir, *exe;
173
174 EINA_LIST_FOREACH(__ecore_file_path_bin, l, dir)
175 {
176 files = ecore_file_ls(dir);
177 EINA_LIST_FREE(files, exe)
178 {
179 snprintf(buf, sizeof(buf), "%s/%s", dir, exe);
180 if ((ecore_file_can_exec(buf)) &&
181 (!ecore_file_is_dir(buf)))
182 list = eina_list_append(list, strdup(buf));
183 free(exe);
184 }
185 }
186
187 return list;
188}
189
190/**
191 * @}
192 */
diff --git a/src/lib/ecore_file/ecore_file_private.h b/src/lib/ecore_file/ecore_file_private.h
new file mode 100644
index 0000000000..45d2cbd65c
--- /dev/null
+++ b/src/lib/ecore_file/ecore_file_private.h
@@ -0,0 +1,129 @@
1#ifndef ECORE_FILE_PRIVATE_H_
2#define ECORE_FILE_PRIVATE_H_
3
4#ifdef __linux__
5# include <features.h>
6#endif
7
8#ifdef HAVE_EVIL
9# include <Evil.h>
10#endif
11
12#ifdef HAVE_ESCAPE
13# include <Escape.h>
14#endif
15
16#include <sys/types.h>
17#include <sys/stat.h>
18
19#include "Ecore.h"
20#include "ecore_private.h"
21
22#include "Ecore_File.h"
23
24extern int _ecore_file_log_dom;
25
26#ifdef ECORE_FILE_DEFAULT_LOG_COLOR
27#undef ECORE_FILE_DEFAULT_LOG_COLOR
28#endif
29#define ECORE_FILE_DEFAULT_LOG_COLOR EINA_COLOR_BLUE
30
31#ifdef ERR
32# undef ERR
33#endif
34#define ERR(...) EINA_LOG_DOM_ERR(_ecore_file_log_dom, __VA_ARGS__)
35
36#ifdef DBG
37# undef DBG
38#endif
39#define DBG(...) EINA_LOG_DOM_DBG(_ecore_file_log_dom, __VA_ARGS__)
40
41#ifdef INF
42# undef INF
43#endif
44#define INF(...) EINA_LOG_DOM_INFO(_ecore_file_log_dom, __VA_ARGS__)
45
46#ifdef WRN
47# undef WRN
48#endif
49#define WRN(...) EINA_LOG_DOM_WARN(_ecore_file_log_dom, __VA_ARGS__)
50
51#ifdef CRIT
52# undef CRIT
53#endif
54#define CRIT(...) EINA_LOG_DOM_CRIT(_ecore_file_log_dom, __VA_ARGS__)
55
56/* ecore_file_monitor */
57int ecore_file_monitor_init(void);
58void ecore_file_monitor_shutdown(void);
59
60#define ECORE_FILE_MONITOR(x) ((Ecore_File_Monitor *)(x))
61
62typedef struct _Ecore_File Ecore_File;
63struct _Ecore_File
64{
65 EINA_INLIST;
66 char *name;
67 int mtime;
68 unsigned char is_dir;
69};
70
71struct _Ecore_File_Monitor
72{
73 EINA_INLIST;
74 void (*func) (void *data,
75 Ecore_File_Monitor *ecore_file_monitor,
76 Ecore_File_Event event,
77 const char *path);
78
79 char *path;
80 void *data;
81 Ecore_File *files;
82};
83
84#ifdef HAVE_INOTIFY
85int ecore_file_monitor_inotify_init(void);
86int ecore_file_monitor_inotify_shutdown(void);
87Ecore_File_Monitor *ecore_file_monitor_inotify_add(const char *path,
88 void (*func) (void *data,
89 Ecore_File_Monitor *ecore_file_monitor,
90 Ecore_File_Event event,
91 const char *path),
92 void *data);
93void ecore_file_monitor_inotify_del(Ecore_File_Monitor *ecore_file_monitor);
94#endif
95
96#ifdef HAVE_NOTIFY_WIN32
97int ecore_file_monitor_win32_init(void);
98int ecore_file_monitor_win32_shutdown(void);
99Ecore_File_Monitor *ecore_file_monitor_win32_add(const char *path,
100 void (*func) (void *data,
101 Ecore_File_Monitor *ecore_file_monitor,
102 Ecore_File_Event event,
103 const char *path),
104 void *data);
105void ecore_file_monitor_win32_del(Ecore_File_Monitor *ecore_file_monitor);
106#endif
107
108#ifdef HAVE_POLL
109int ecore_file_monitor_poll_init(void);
110int ecore_file_monitor_poll_shutdown(void);
111Ecore_File_Monitor *ecore_file_monitor_poll_add(const char *path,
112 void (*func) (void *data,
113 Ecore_File_Monitor *ecore_file_monitor,
114 Ecore_File_Event event,
115 const char *path),
116 void *data);
117void ecore_file_monitor_poll_del(Ecore_File_Monitor *ecore_file_monitor);
118
119#endif
120
121/* ecore_file_path */
122void ecore_file_path_init(void);
123void ecore_file_path_shutdown(void);
124
125/* ecore_file_download */
126int ecore_file_download_init(void);
127void ecore_file_download_shutdown(void);
128
129#endif