diff options
author | David Walter Seikel | 2012-01-04 18:41:13 +1000 |
---|---|---|
committer | David Walter Seikel | 2012-01-04 18:41:13 +1000 |
commit | dd7595a3475407a7fa96a97393bae8c5220e8762 (patch) | |
tree | e341e911d7eb911a51684a7412ef7f7c7605d28e /libraries/ecore/src/lib/ecore_file/ecore_file.c | |
parent | Add the skeleton. (diff) | |
download | SledjHamr-dd7595a3475407a7fa96a97393bae8c5220e8762.zip SledjHamr-dd7595a3475407a7fa96a97393bae8c5220e8762.tar.gz SledjHamr-dd7595a3475407a7fa96a97393bae8c5220e8762.tar.bz2 SledjHamr-dd7595a3475407a7fa96a97393bae8c5220e8762.tar.xz |
Add the base Enlightenment Foundation Libraries - eina, eet, evas, ecore, embryo, and edje.
Note that embryo wont be used, but I'm not sure yet if you can build edje without it.
Diffstat (limited to '')
-rw-r--r-- | libraries/ecore/src/lib/ecore_file/ecore_file.c | 1110 |
1 files changed, 1110 insertions, 0 deletions
diff --git a/libraries/ecore/src/lib/ecore_file/ecore_file.c b/libraries/ecore/src/lib/ecore_file/ecore_file.c new file mode 100644 index 0000000..2546f2f --- /dev/null +++ b/libraries/ecore/src/lib/ecore_file/ecore_file.c | |||
@@ -0,0 +1,1110 @@ | |||
1 | #ifdef HAVE_CONFIG_H | ||
2 | # include <config.h> | ||
3 | #endif | ||
4 | |||
5 | #include <stdio.h> | ||
6 | #include <string.h> | ||
7 | |||
8 | #ifndef _MSC_VER | ||
9 | # include <unistd.h> | ||
10 | # include <libgen.h> | ||
11 | #endif | ||
12 | |||
13 | #ifdef _WIN32 | ||
14 | # include <direct.h> | ||
15 | #endif | ||
16 | |||
17 | #ifdef HAVE_FEATURES_H | ||
18 | # include <features.h> | ||
19 | #endif | ||
20 | #include <ctype.h> | ||
21 | #include <errno.h> | ||
22 | |||
23 | #ifdef HAVE_ATFILE_SOURCE | ||
24 | # include <dirent.h> | ||
25 | #endif | ||
26 | |||
27 | #include "ecore_file_private.h" | ||
28 | |||
29 | int _ecore_file_log_dom = -1; | ||
30 | static int _ecore_file_init_count = 0; | ||
31 | |||
32 | /* externally accessible functions */ | ||
33 | |||
34 | /** | ||
35 | * @addtogroup Ecore_File_Group Ecore_File - Files and directories convenience functions | ||
36 | * | ||
37 | * @{ | ||
38 | */ | ||
39 | |||
40 | /** | ||
41 | * @brief Initialize the Ecore_File library. | ||
42 | * | ||
43 | * @return 1 or greater on success, 0 on error. | ||
44 | * | ||
45 | * This function sets up Ecore_File and the services it will use | ||
46 | * (monitoring, downloading, PATH related feature). It returns 0 on | ||
47 | * failure, otherwise it returns the number of times it has already | ||
48 | * been called. | ||
49 | * | ||
50 | * When Ecore_File is not used anymore, call ecore_file_shutdown() | ||
51 | * to shut down the Ecore_File library. | ||
52 | */ | ||
53 | EAPI int | ||
54 | ecore_file_init() | ||
55 | { | ||
56 | if (++_ecore_file_init_count != 1) | ||
57 | return _ecore_file_init_count; | ||
58 | |||
59 | if (!ecore_init()) | ||
60 | return --_ecore_file_init_count; | ||
61 | |||
62 | _ecore_file_log_dom = eina_log_domain_register | ||
63 | ("ecore_file", ECORE_FILE_DEFAULT_LOG_COLOR); | ||
64 | if(_ecore_file_log_dom < 0) | ||
65 | { | ||
66 | EINA_LOG_ERR("Impossible to create a log domain for the ecore file module."); | ||
67 | return --_ecore_file_init_count; | ||
68 | } | ||
69 | ecore_file_path_init(); | ||
70 | ecore_file_monitor_init(); | ||
71 | ecore_file_download_init(); | ||
72 | |||
73 | /* FIXME: were the tests disabled for a good reason ? */ | ||
74 | |||
75 | /* | ||
76 | if (!ecore_file_monitor_init()) | ||
77 | goto shutdown_ecore_file_path; | ||
78 | |||
79 | if (!ecore_file_download_init()) | ||
80 | goto shutdown_ecore_file_monitor; | ||
81 | */ | ||
82 | |||
83 | return _ecore_file_init_count; | ||
84 | |||
85 | /* | ||
86 | shutdown_ecore_file_monitor: | ||
87 | ecore_file_monitor_shutdown(); | ||
88 | shutdown_ecore_file_path: | ||
89 | ecore_file_path_shutdown(); | ||
90 | |||
91 | return --_ecore_file_init_count; | ||
92 | */ | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * @brief Shut down the Ecore_File library. | ||
97 | * | ||
98 | * @return 0 when the library is completely shut down, 1 or | ||
99 | * greater otherwise. | ||
100 | * | ||
101 | * This function shuts down the Ecore_File library. It returns 0 when it has | ||
102 | * been called the same number of times than ecore_file_init(). In that case | ||
103 | * it shuts down all the services it uses. | ||
104 | */ | ||
105 | EAPI int | ||
106 | ecore_file_shutdown() | ||
107 | { | ||
108 | if (--_ecore_file_init_count != 0) | ||
109 | return _ecore_file_init_count; | ||
110 | |||
111 | ecore_file_download_shutdown(); | ||
112 | ecore_file_monitor_shutdown(); | ||
113 | ecore_file_path_shutdown(); | ||
114 | |||
115 | eina_log_domain_unregister(_ecore_file_log_dom); | ||
116 | _ecore_file_log_dom = -1; | ||
117 | |||
118 | ecore_shutdown(); | ||
119 | |||
120 | return _ecore_file_init_count; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * @brief Get the time of the last modification to the given file. | ||
125 | * | ||
126 | * @param file The name of the file. | ||
127 | * @return Return the time of the last data modification, or 0 on | ||
128 | * failure. | ||
129 | * | ||
130 | * This function returns the time of the last modification of | ||
131 | * @p file. On failure, it returns 0. | ||
132 | */ | ||
133 | EAPI long long | ||
134 | ecore_file_mod_time(const char *file) | ||
135 | { | ||
136 | struct stat st; | ||
137 | |||
138 | if (stat(file, &st) < 0) return 0; | ||
139 | return st.st_mtime; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * @brief Get the size of the given file. | ||
144 | * | ||
145 | * @param file The name of the file. | ||
146 | * @return Return the size of the file in bytes, or 0 on failure. | ||
147 | * | ||
148 | * This function returns the size of @p file in bytes. On failure, it | ||
149 | * returns 0. | ||
150 | */ | ||
151 | EAPI long long | ||
152 | ecore_file_size(const char *file) | ||
153 | { | ||
154 | struct stat st; | ||
155 | |||
156 | if (stat(file, &st) < 0) return 0; | ||
157 | return st.st_size; | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * @brief Check if the given file exists. | ||
162 | * | ||
163 | * @param file The name of the file. | ||
164 | * @return Return EINA_TRUE if the file exists, EINA_FALSE otherwise. | ||
165 | * | ||
166 | * This function returns EINA_TRUE if @p file exists on local filesystem, | ||
167 | * EINA_FALSE otherwise. | ||
168 | */ | ||
169 | EAPI Eina_Bool | ||
170 | ecore_file_exists(const char *file) | ||
171 | { | ||
172 | struct stat st; | ||
173 | if (!file) return EINA_FALSE; | ||
174 | |||
175 | /*Workaround so that "/" returns a true, otherwise we can't monitor "/" in ecore_file_monitor*/ | ||
176 | if (stat(file, &st) < 0 && strcmp(file, "/")) return EINA_FALSE; | ||
177 | return EINA_TRUE; | ||
178 | } | ||
179 | |||
180 | /** | ||
181 | * @brief Check if the given file is a directory. | ||
182 | * | ||
183 | * @param file The name of the file. | ||
184 | * @return Return EINA_TRUE if the file exists and is a directory, | ||
185 | * EINA_FALSE otherwise. | ||
186 | * | ||
187 | * This function returns EINA_TRUE if @p file exists exists and is a | ||
188 | * directory on local filesystem, EINA_FALSE otherwise. | ||
189 | */ | ||
190 | EAPI Eina_Bool | ||
191 | ecore_file_is_dir(const char *file) | ||
192 | { | ||
193 | struct stat st; | ||
194 | |||
195 | if (stat(file, &st) < 0) return EINA_FALSE; | ||
196 | if (S_ISDIR(st.st_mode)) return EINA_TRUE; | ||
197 | return EINA_FALSE; | ||
198 | } | ||
199 | |||
200 | static mode_t default_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; | ||
201 | |||
202 | /** | ||
203 | * @brief Create a new directory. | ||
204 | * | ||
205 | * @param dir The name of the directory to create | ||
206 | * @return EINA_TRUE on successful creation, EINA_FALSE otherwise. | ||
207 | * | ||
208 | * This function creates the directory @p dir with the mode S_IRUSR | | ||
209 | * S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH. On | ||
210 | * success, it returns EINA_TRUE, EINA_FALSE otherwise. | ||
211 | */ | ||
212 | EAPI Eina_Bool | ||
213 | ecore_file_mkdir(const char *dir) | ||
214 | { | ||
215 | if (mkdir(dir, default_mode) < 0) return EINA_FALSE; | ||
216 | return EINA_TRUE; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * @brief Create complete directory in a batch. | ||
221 | * | ||
222 | * @param dirs The list of directories, null terminated. | ||
223 | * @return The number of successful directories created, -1 if dirs is | ||
224 | * @c NULL. | ||
225 | * | ||
226 | * This function creates all the directories that are in the null | ||
227 | * terminated array @p dirs. The function loops over the directories | ||
228 | * and call ecore_file_mkdir(). This function returns -1 if @p dirs is | ||
229 | * @c NULL, otherwise if returns the number of suceesfully created | ||
230 | * directories. | ||
231 | */ | ||
232 | EAPI int | ||
233 | ecore_file_mkdirs(const char **dirs) | ||
234 | { | ||
235 | int i = 0; | ||
236 | |||
237 | if (!dirs) return -1; | ||
238 | |||
239 | for (; *dirs; dirs++) | ||
240 | if (ecore_file_mkdir(*dirs)) | ||
241 | i++; | ||
242 | return i; | ||
243 | } | ||
244 | |||
245 | /** | ||
246 | * @brief Create complete list of sub-directories in a batch (optimized). | ||
247 | * | ||
248 | * @param base The base directory to act on. | ||
249 | * @param subdirs The list of directories, null terminated. | ||
250 | * @return number of successful directories created, -1 on failure. | ||
251 | * | ||
252 | * This function creates all the directories that are in the null | ||
253 | * terminated array @p dirs in the @p base directory. If @p base does | ||
254 | * not exist, it will be created. The function loops over the directories | ||
255 | * and call ecore_file_mkdir(). The whole path of the directories must | ||
256 | * exist. So if base/a/b/c wants to be created, @p subdirs must | ||
257 | * contain "a", "a/b" and "a/b/c", in that order. This function | ||
258 | * returns -1 if @p dirs or @p base are @c NULL, or if @p base is | ||
259 | * empty ("\0"). It returns 0 is @p base is not a directory or | ||
260 | * invalid, or if it can't be created. Otherwise if returns the number | ||
261 | * of suceesfully created directories. | ||
262 | */ | ||
263 | EAPI int | ||
264 | ecore_file_mksubdirs(const char *base, const char **subdirs) | ||
265 | { | ||
266 | #ifndef HAVE_ATFILE_SOURCE | ||
267 | char buf[PATH_MAX]; | ||
268 | int baselen; | ||
269 | #else | ||
270 | int fd; | ||
271 | DIR *dir; | ||
272 | #endif | ||
273 | int i; | ||
274 | |||
275 | if (!subdirs) return -1; | ||
276 | if ((!base) || (base[0] == '\0')) return -1; | ||
277 | |||
278 | if ((!ecore_file_is_dir(base)) && (!ecore_file_mkpath(base))) | ||
279 | return 0; | ||
280 | |||
281 | #ifndef HAVE_ATFILE_SOURCE | ||
282 | baselen = eina_strlcpy(buf, base, sizeof(buf)); | ||
283 | if ((baselen < 1) || (baselen + 1 >= (int)sizeof(buf))) | ||
284 | return 0; | ||
285 | |||
286 | if (buf[baselen - 1] != '/') | ||
287 | { | ||
288 | buf[baselen] = '/'; | ||
289 | baselen++; | ||
290 | } | ||
291 | #else | ||
292 | dir = opendir(base); | ||
293 | if (!dir) | ||
294 | return 0; | ||
295 | fd = dirfd(dir); | ||
296 | #endif | ||
297 | |||
298 | i = 0; | ||
299 | for (; *subdirs; subdirs++) | ||
300 | { | ||
301 | struct stat st; | ||
302 | |||
303 | #ifndef HAVE_ATFILE_SOURCE | ||
304 | eina_strlcpy(buf + baselen, *subdirs, sizeof(buf) - baselen); | ||
305 | if (stat(buf, &st) == 0) | ||
306 | #else | ||
307 | if (fstatat(fd, *subdirs, &st, 0) == 0) | ||
308 | #endif | ||
309 | { | ||
310 | if (S_ISDIR(st.st_mode)) | ||
311 | { | ||
312 | i++; | ||
313 | continue; | ||
314 | } | ||
315 | } | ||
316 | else | ||
317 | { | ||
318 | if (errno == ENOENT) | ||
319 | { | ||
320 | #ifndef HAVE_ATFILE_SOURCE | ||
321 | if (mkdir(buf, default_mode) == 0) | ||
322 | #else | ||
323 | if (mkdirat(fd, *subdirs, default_mode) == 0) | ||
324 | #endif | ||
325 | { | ||
326 | i++; | ||
327 | continue; | ||
328 | } | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | |||
333 | #ifdef HAVE_ATFILE_SOURCE | ||
334 | closedir(dir); | ||
335 | #endif | ||
336 | |||
337 | return i; | ||
338 | } | ||
339 | |||
340 | /** | ||
341 | * @brief Delete the given directory. | ||
342 | * | ||
343 | * @param dir The name of the directory to delete. | ||
344 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
345 | * | ||
346 | * This function deletes @p dir. It returns EINA_TRUE on success, | ||
347 | * EINA_FALSE otherwise. | ||
348 | */ | ||
349 | EAPI Eina_Bool | ||
350 | ecore_file_rmdir(const char *dir) | ||
351 | { | ||
352 | if (rmdir(dir) < 0) return EINA_FALSE; | ||
353 | return EINA_TRUE; | ||
354 | } | ||
355 | |||
356 | /** | ||
357 | * @brief Delete the given file. | ||
358 | * | ||
359 | * @param file The name of the file to delete. | ||
360 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
361 | * | ||
362 | * This function deletes @p file. It returns EINA_TRUE on success, | ||
363 | * EINA_FALSE otherwise. | ||
364 | */ | ||
365 | EAPI Eina_Bool | ||
366 | ecore_file_unlink(const char *file) | ||
367 | { | ||
368 | if (unlink(file) < 0) return EINA_FALSE; | ||
369 | return EINA_TRUE; | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * @brief Remove the given file or directory. | ||
374 | * | ||
375 | * @param file The name of the file or directory to delete. | ||
376 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
377 | * | ||
378 | * This function removes @p file. It returns EINA_TRUE on success, | ||
379 | * EINA_FALSE otherwise. | ||
380 | */ | ||
381 | EAPI Eina_Bool | ||
382 | ecore_file_remove(const char *file) | ||
383 | { | ||
384 | if (remove(file) < 0) return EINA_FALSE; | ||
385 | return EINA_TRUE; | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * @brief Delete the given directory and all its contents. | ||
390 | * | ||
391 | * @param dir The name of the directory to delete. | ||
392 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
393 | * | ||
394 | * This function delete @p dir and all its contents. If @p dir is a | ||
395 | * link only the link is removed. It returns EINA_TRUE on success, | ||
396 | * EINA_FALSE otherwise. | ||
397 | */ | ||
398 | EAPI Eina_Bool | ||
399 | ecore_file_recursive_rm(const char *dir) | ||
400 | { | ||
401 | Eina_Iterator *it; | ||
402 | char buf[PATH_MAX]; | ||
403 | struct stat st; | ||
404 | int ret; | ||
405 | |||
406 | if (readlink(dir, buf, sizeof(buf)) > 0) | ||
407 | return ecore_file_unlink(dir); | ||
408 | |||
409 | ret = stat(dir, &st); | ||
410 | if ((ret == 0) && (S_ISDIR(st.st_mode))) | ||
411 | { | ||
412 | Eina_File_Direct_Info *info; | ||
413 | |||
414 | ret = 1; | ||
415 | if (stat(dir, &st) == -1) return EINA_FALSE; /* WOOT: WHY ARE WE CALLING STAT TWO TIMES ??? */ | ||
416 | |||
417 | it = eina_file_direct_ls(dir); | ||
418 | EINA_ITERATOR_FOREACH(it, info) | ||
419 | { | ||
420 | if (!ecore_file_recursive_rm(info->path)) | ||
421 | ret = 0; | ||
422 | } | ||
423 | eina_iterator_free(it); | ||
424 | |||
425 | if (!ecore_file_rmdir(dir)) ret = 0; | ||
426 | if (ret) | ||
427 | return EINA_TRUE; | ||
428 | else | ||
429 | return EINA_FALSE; | ||
430 | } | ||
431 | else | ||
432 | { | ||
433 | if (ret == -1) return EINA_FALSE; | ||
434 | return ecore_file_unlink(dir); | ||
435 | } | ||
436 | } | ||
437 | |||
438 | static inline Eina_Bool | ||
439 | _ecore_file_mkpath_if_not_exists(const char *path) | ||
440 | { | ||
441 | struct stat st; | ||
442 | |||
443 | /* Windows: path like C: or D: etc are valid, but stat() returns an error */ | ||
444 | #ifdef _WIN32 | ||
445 | if ((strlen(path) == 2) && | ||
446 | ((path[0] >= 'a' && path[0] <= 'z') || | ||
447 | (path[0] >= 'A' && path[0] <= 'Z')) && | ||
448 | (path[1] == ':')) | ||
449 | return EINA_TRUE; | ||
450 | #endif | ||
451 | |||
452 | if (stat(path, &st) < 0) | ||
453 | return ecore_file_mkdir(path); | ||
454 | else if (!S_ISDIR(st.st_mode)) | ||
455 | return EINA_FALSE; | ||
456 | else | ||
457 | return EINA_TRUE; | ||
458 | } | ||
459 | |||
460 | /** | ||
461 | * @brief Create a complete path. | ||
462 | * | ||
463 | * @param path The path to create | ||
464 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
465 | * | ||
466 | * This function creates @p path and all the subdirectories it | ||
467 | * contains. The separator is '/' or '\'. If @p path exists, this | ||
468 | * function returns EINA_TRUE immediately. It returns EINA_TRUE on | ||
469 | * success, EINA_FALSE otherwise. | ||
470 | */ | ||
471 | EAPI Eina_Bool | ||
472 | ecore_file_mkpath(const char *path) | ||
473 | { | ||
474 | char ss[PATH_MAX]; | ||
475 | unsigned int i; | ||
476 | |||
477 | if (ecore_file_is_dir(path)) | ||
478 | return EINA_TRUE; | ||
479 | |||
480 | for (i = 0; path[i] != '\0'; ss[i] = path[i], i++) | ||
481 | { | ||
482 | if (i == sizeof(ss) - 1) return EINA_FALSE; | ||
483 | if (((path[i] == '/') || (path[i] == '\\')) && (i > 0)) | ||
484 | { | ||
485 | ss[i] = '\0'; | ||
486 | if (!_ecore_file_mkpath_if_not_exists(ss)) | ||
487 | return EINA_FALSE; | ||
488 | } | ||
489 | } | ||
490 | ss[i] = '\0'; | ||
491 | return _ecore_file_mkpath_if_not_exists(ss); | ||
492 | } | ||
493 | |||
494 | /** | ||
495 | * @brief Create complete paths in a batch. | ||
496 | * | ||
497 | * @param paths list of paths, null terminated. | ||
498 | * @return number of successful paths created, -1 if paths is NULL. | ||
499 | * | ||
500 | * This function creates all the directories that are in the null | ||
501 | * terminated array @p paths. The function loops over the directories | ||
502 | * and call ecore_file_mkpath(), hence on Windows, '\' must be | ||
503 | * replaced by '/' before calling that function. This function | ||
504 | * returns -1 if @p paths is @c NULL. Otherwise if returns the number | ||
505 | * of suceesfully created directories. | ||
506 | */ | ||
507 | EAPI int | ||
508 | ecore_file_mkpaths(const char **paths) | ||
509 | { | ||
510 | int i = 0; | ||
511 | |||
512 | if (!paths) return -1; | ||
513 | |||
514 | for (; *paths; paths++) | ||
515 | if (ecore_file_mkpath(*paths)) | ||
516 | i++; | ||
517 | return i; | ||
518 | } | ||
519 | |||
520 | /** | ||
521 | * @brief Copy the given file to the given destination. | ||
522 | * | ||
523 | * @param src The name of the source file. | ||
524 | * @param dst The name of the destination file. | ||
525 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
526 | * | ||
527 | * This function copies @p src to @p dst. If the absolute path name of | ||
528 | * @p src and @p dst can not be computed, or if they are equal, or if | ||
529 | * the copy fails, the function returns EINA_FALSE, otherwise it | ||
530 | * returns EINA_TRUE. | ||
531 | */ | ||
532 | EAPI Eina_Bool | ||
533 | ecore_file_cp(const char *src, const char *dst) | ||
534 | { | ||
535 | FILE *f1, *f2; | ||
536 | char buf[16384]; | ||
537 | char realpath1[PATH_MAX], realpath2[PATH_MAX]; | ||
538 | size_t num; | ||
539 | Eina_Bool ret = EINA_TRUE; | ||
540 | |||
541 | if (!realpath(src, realpath1)) return EINA_FALSE; | ||
542 | if (realpath(dst, realpath2) && !strcmp(realpath1, realpath2)) return EINA_FALSE; | ||
543 | |||
544 | f1 = fopen(src, "rb"); | ||
545 | if (!f1) return EINA_FALSE; | ||
546 | f2 = fopen(dst, "wb"); | ||
547 | if (!f2) | ||
548 | { | ||
549 | fclose(f1); | ||
550 | return EINA_FALSE; | ||
551 | } | ||
552 | while ((num = fread(buf, 1, sizeof(buf), f1)) > 0) | ||
553 | { | ||
554 | if (fwrite(buf, 1, num, f2) != num) ret = EINA_FALSE; | ||
555 | } | ||
556 | fclose(f1); | ||
557 | fclose(f2); | ||
558 | return ret; | ||
559 | } | ||
560 | |||
561 | /** | ||
562 | * @brief Move the given file to the given destination. | ||
563 | * | ||
564 | * @param src The name of the source file. | ||
565 | * @param dst The name of the destination file. | ||
566 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
567 | * | ||
568 | * This function moves @p src to @p dst. It returns EINA_TRUE on | ||
569 | * success, EINA_FALSE otherwise. | ||
570 | */ | ||
571 | EAPI Eina_Bool | ||
572 | ecore_file_mv(const char *src, const char *dst) | ||
573 | { | ||
574 | char buf[PATH_MAX]; | ||
575 | int fd; | ||
576 | |||
577 | if (rename(src, dst)) | ||
578 | { | ||
579 | // File cannot be moved directly because | ||
580 | // it resides on a different mount point. | ||
581 | if (errno == EXDEV) | ||
582 | { | ||
583 | struct stat st; | ||
584 | |||
585 | // Make sure this is a regular file before | ||
586 | // we do anything fancy. | ||
587 | stat(src, &st); | ||
588 | if (S_ISREG(st.st_mode)) | ||
589 | { | ||
590 | char *dir; | ||
591 | |||
592 | dir = ecore_file_dir_get(dst); | ||
593 | // Since we can't directly rename, try to | ||
594 | // copy to temp file in the dst directory | ||
595 | // and then rename. | ||
596 | snprintf(buf, sizeof(buf), "%s/.%s.tmp.XXXXXX", | ||
597 | dir, ecore_file_file_get(dst)); | ||
598 | free(dir); | ||
599 | fd = mkstemp(buf); | ||
600 | if (fd < 0) | ||
601 | { | ||
602 | perror("mkstemp"); | ||
603 | goto FAIL; | ||
604 | } | ||
605 | close(fd); | ||
606 | |||
607 | // Copy to temp file | ||
608 | if (!ecore_file_cp(src, buf)) | ||
609 | goto FAIL; | ||
610 | |||
611 | // Set file permissions of temp file to match src | ||
612 | chmod(buf, st.st_mode); | ||
613 | |||
614 | // Try to atomically move temp file to dst | ||
615 | if (rename(buf, dst)) | ||
616 | { | ||
617 | // If we still cannot atomically move | ||
618 | // do a normal copy and hope for the best. | ||
619 | if (!ecore_file_cp(buf, dst)) | ||
620 | goto FAIL; | ||
621 | } | ||
622 | |||
623 | // Delete temporary file and src | ||
624 | ecore_file_unlink(buf); | ||
625 | ecore_file_unlink(src); | ||
626 | goto PASS; | ||
627 | } | ||
628 | } | ||
629 | goto FAIL; | ||
630 | } | ||
631 | |||
632 | PASS: | ||
633 | return EINA_TRUE; | ||
634 | |||
635 | FAIL: | ||
636 | return EINA_FALSE; | ||
637 | } | ||
638 | |||
639 | /** | ||
640 | * @brief Create a symbolic link. | ||
641 | * | ||
642 | * @param src The name of the file to link. | ||
643 | * @param dest The name of link. | ||
644 | * @return EINA_TRUE on success, EINA_FALSE otherwise. | ||
645 | * | ||
646 | * This function create the symbolic link @p dest of @p src. This | ||
647 | * function does not work on Windows. It returns EINA_TRUE on success, | ||
648 | * EINA_FALSE otherwise. | ||
649 | */ | ||
650 | EAPI Eina_Bool | ||
651 | ecore_file_symlink(const char *src, const char *dest) | ||
652 | { | ||
653 | if (!symlink(src, dest)) return EINA_TRUE; | ||
654 | |||
655 | return EINA_FALSE; | ||
656 | } | ||
657 | |||
658 | /** | ||
659 | * @brief Get the canonicalized absolute path name. | ||
660 | * | ||
661 | * @param file The file path. | ||
662 | * @return The canonicalized absolute pathname or an empty string on | ||
663 | * failure. | ||
664 | * | ||
665 | * This function returns the absolute path name of @p file as a newly | ||
666 | * allocated string. If @p file is @c NULL, or on error, this function | ||
667 | * returns an empty string. Otherwise, it returns the absolute path | ||
668 | * name. When not needed anymore, the returned value must be freed. | ||
669 | */ | ||
670 | EAPI char * | ||
671 | ecore_file_realpath(const char *file) | ||
672 | { | ||
673 | char buf[PATH_MAX]; | ||
674 | |||
675 | /* | ||
676 | * Some implementations of realpath do not conform to the SUS. | ||
677 | * And as a result we must prevent a null arg from being passed. | ||
678 | */ | ||
679 | if (!file) return strdup(""); | ||
680 | if (!realpath(file, buf)) return strdup(""); | ||
681 | |||
682 | return strdup(buf); | ||
683 | } | ||
684 | |||
685 | /** | ||
686 | * Get the filename from a given path. | ||
687 | * | ||
688 | * @param path The complete path. | ||
689 | * @return The file name. | ||
690 | * | ||
691 | * This function returns the file name of @p path. If @p path is | ||
692 | * @c NULL, the functions returns @c NULL. | ||
693 | */ | ||
694 | EAPI const char * | ||
695 | ecore_file_file_get(const char *path) | ||
696 | { | ||
697 | char *result = NULL; | ||
698 | |||
699 | if (!path) return NULL; | ||
700 | if ((result = strrchr(path, '/'))) result++; | ||
701 | else result = (char *)path; | ||
702 | return result; | ||
703 | } | ||
704 | |||
705 | /** | ||
706 | * @brief Get the directory where the given file resides. | ||
707 | * | ||
708 | * @param file The name of the file. | ||
709 | * @return The directory name. | ||
710 | * | ||
711 | * This function returns the directory where @p file resides as anewly | ||
712 | * allocated string. If @p file is @c NULL or on error, this function | ||
713 | * returns @c NULL. When not needed anymore, the returned value must | ||
714 | * be freed. | ||
715 | */ | ||
716 | EAPI char * | ||
717 | ecore_file_dir_get(const char *file) | ||
718 | { | ||
719 | char *p; | ||
720 | char buf[PATH_MAX]; | ||
721 | |||
722 | if (!file) return NULL; | ||
723 | strncpy(buf, file, PATH_MAX); | ||
724 | buf[PATH_MAX - 1] = 0; | ||
725 | p = dirname(buf); | ||
726 | return strdup(p); | ||
727 | } | ||
728 | |||
729 | /** | ||
730 | * @brief Check if the given file can be read. | ||
731 | * | ||
732 | * @param file The name of the file. | ||
733 | * @return EINA_TRUE if the file is readable, EINA_FALSE otherwise. | ||
734 | * | ||
735 | * This function returns EINA_TRUE if @p file can be read, EINA_FALSE | ||
736 | * otherwise. | ||
737 | */ | ||
738 | EAPI Eina_Bool | ||
739 | ecore_file_can_read(const char *file) | ||
740 | { | ||
741 | if (!file) return EINA_FALSE; | ||
742 | if (!access(file, R_OK)) return EINA_TRUE; | ||
743 | return EINA_FALSE; | ||
744 | } | ||
745 | |||
746 | /** | ||
747 | * @brief Check if the given file can be written. | ||
748 | * | ||
749 | * @param file The name of the file. | ||
750 | * @return EINA_TRUE if the file is writable, EINA_FALSE otherwise. | ||
751 | * | ||
752 | * This function returns EINA_TRUE if @p file can be written, EINA_FALSE | ||
753 | * otherwise. | ||
754 | */ | ||
755 | EAPI Eina_Bool | ||
756 | ecore_file_can_write(const char *file) | ||
757 | { | ||
758 | if (!file) return EINA_FALSE; | ||
759 | if (!access(file, W_OK)) return EINA_TRUE; | ||
760 | return EINA_FALSE; | ||
761 | } | ||
762 | |||
763 | /** | ||
764 | * @bbrief Check if the given file can be executed. | ||
765 | * | ||
766 | * @param file The name of the file. | ||
767 | * @return EINA_TRUE if the file can be executed, EINA_FALSE otherwise. | ||
768 | * | ||
769 | * This function returns EINA_TRUE if @p file can be executed, EINA_FALSE | ||
770 | * otherwise. | ||
771 | */ | ||
772 | EAPI Eina_Bool | ||
773 | ecore_file_can_exec(const char *file) | ||
774 | { | ||
775 | if (!file) return EINA_FALSE; | ||
776 | if (!access(file, X_OK)) return EINA_TRUE; | ||
777 | return EINA_FALSE; | ||
778 | } | ||
779 | |||
780 | /** | ||
781 | * @brief Get the path pointed by the given link. | ||
782 | * | ||
783 | * @param lnk The name of the link. | ||
784 | * @return The path pointed by link or NULL. | ||
785 | * | ||
786 | * This function returns the path pointed by @p link as a newly | ||
787 | * allocated string. This function does not work on Windows. On | ||
788 | * failure, the function returns @c NULL. When not needed anymore, the | ||
789 | * returned value must be freed. | ||
790 | */ | ||
791 | EAPI char * | ||
792 | ecore_file_readlink(const char *lnk) | ||
793 | { | ||
794 | char buf[PATH_MAX]; | ||
795 | int count; | ||
796 | |||
797 | if ((count = readlink(lnk, buf, sizeof(buf) - 1)) < 0) return NULL; | ||
798 | buf[count] = 0; | ||
799 | return strdup(buf); | ||
800 | } | ||
801 | |||
802 | /** | ||
803 | * @brief Get the list of the files and directories in the given | ||
804 | * directory. | ||
805 | * | ||
806 | * @param dir The name of the directory to list | ||
807 | * @return Return an Eina_List containing all the files in the directory; | ||
808 | * on failure it returns NULL. | ||
809 | * | ||
810 | * This function returns a list of allocated strings of all the files | ||
811 | * and directories contained in @p dir. The list will be sorted with | ||
812 | * strcoll as compare function. That means that you may want to set | ||
813 | * the current locale for the category LC_COLLATE with | ||
814 | * setlocale(). For more information see the manual pages of strcoll | ||
815 | * and setlocale. The list will not contain the directory entries for | ||
816 | * '.' and '..'. On failure, @c NULL is returned. When not needed | ||
817 | * anymore, the list elements must be freed. | ||
818 | */ | ||
819 | EAPI Eina_List * | ||
820 | ecore_file_ls(const char *dir) | ||
821 | { | ||
822 | Eina_File_Direct_Info *info; | ||
823 | Eina_Iterator *ls; | ||
824 | Eina_List *list = NULL; | ||
825 | |||
826 | ls = eina_file_direct_ls(dir); | ||
827 | EINA_ITERATOR_FOREACH(ls, info) | ||
828 | { | ||
829 | char *f; | ||
830 | |||
831 | f = strdup(info->path + info->name_start); | ||
832 | list = eina_list_append(list, f); | ||
833 | } | ||
834 | eina_iterator_free(ls); | ||
835 | |||
836 | list = eina_list_sort(list, eina_list_count(list), EINA_COMPARE_CB(strcoll)); | ||
837 | |||
838 | return list; | ||
839 | } | ||
840 | |||
841 | /** | ||
842 | * @brief Return the executable from the given command. | ||
843 | * | ||
844 | * @param app The application command, with parameters. | ||
845 | * | ||
846 | * This function returns the executable from @p app as a newly | ||
847 | * allocated string. Arguments are removed and escae characters are | ||
848 | * handled. If @p app is @c NULL, or on failure, the function returns | ||
849 | * @c NULL. When not needed anymore, the returned value must be freed. | ||
850 | */ | ||
851 | EAPI char * | ||
852 | ecore_file_app_exe_get(const char *app) | ||
853 | { | ||
854 | char *p, *pp, *exe1 = NULL, *exe2 = NULL; | ||
855 | char *exe = NULL; | ||
856 | int in_quot_dbl = 0, in_quot_sing = 0, restart = 0; | ||
857 | |||
858 | if (!app) return NULL; | ||
859 | |||
860 | p = (char *)app; | ||
861 | restart: | ||
862 | while ((*p) && (isspace(*p))) p++; | ||
863 | exe1 = p; | ||
864 | while (*p) | ||
865 | { | ||
866 | if (in_quot_sing) | ||
867 | { | ||
868 | if (*p == '\'') | ||
869 | in_quot_sing = 0; | ||
870 | } | ||
871 | else if (in_quot_dbl) | ||
872 | { | ||
873 | if (*p == '\"') | ||
874 | in_quot_dbl = 0; | ||
875 | } | ||
876 | else | ||
877 | { | ||
878 | if (*p == '\'') | ||
879 | in_quot_sing = 1; | ||
880 | else if (*p == '\"') | ||
881 | in_quot_dbl = 1; | ||
882 | if ((isspace(*p)) && (!((p > app) && (p[-1] != '\\')))) | ||
883 | break; | ||
884 | } | ||
885 | p++; | ||
886 | } | ||
887 | exe2 = p; | ||
888 | if (exe2 == exe1) return NULL; | ||
889 | if (*exe1 == '~') | ||
890 | { | ||
891 | char *homedir; | ||
892 | int len; | ||
893 | |||
894 | /* Skip ~ */ | ||
895 | exe1++; | ||
896 | |||
897 | homedir = getenv("HOME"); | ||
898 | if (!homedir) return NULL; | ||
899 | len = strlen(homedir); | ||
900 | if (exe) free(exe); | ||
901 | exe = malloc(len + exe2 - exe1 + 2); | ||
902 | if (!exe) return NULL; | ||
903 | pp = exe; | ||
904 | if (len) | ||
905 | { | ||
906 | strcpy(exe, homedir); | ||
907 | pp += len; | ||
908 | if (*(pp - 1) != '/') | ||
909 | { | ||
910 | *pp = '/'; | ||
911 | pp++; | ||
912 | } | ||
913 | } | ||
914 | } | ||
915 | else | ||
916 | { | ||
917 | if (exe) free(exe); | ||
918 | exe = malloc(exe2 - exe1 + 1); | ||
919 | if (!exe) return NULL; | ||
920 | pp = exe; | ||
921 | } | ||
922 | p = exe1; | ||
923 | restart = 0; | ||
924 | in_quot_dbl = 0; | ||
925 | in_quot_sing = 0; | ||
926 | while (*p) | ||
927 | { | ||
928 | if (in_quot_sing) | ||
929 | { | ||
930 | if (*p == '\'') | ||
931 | in_quot_sing = 0; | ||
932 | else | ||
933 | { | ||
934 | *pp = *p; | ||
935 | pp++; | ||
936 | } | ||
937 | } | ||
938 | else if (in_quot_dbl) | ||
939 | { | ||
940 | if (*p == '\"') | ||
941 | in_quot_dbl = 0; | ||
942 | else | ||
943 | { | ||
944 | /* technically this is wrong. double quotes also accept | ||
945 | * special chars: | ||
946 | * | ||
947 | * $, `, \ | ||
948 | */ | ||
949 | *pp = *p; | ||
950 | pp++; | ||
951 | } | ||
952 | } | ||
953 | else | ||
954 | { | ||
955 | /* technically we should handle special chars: | ||
956 | * | ||
957 | * $, `, \, etc. | ||
958 | */ | ||
959 | if ((p > exe1) && (p[-1] == '\\')) | ||
960 | { | ||
961 | if (*p != '\n') | ||
962 | { | ||
963 | *pp = *p; | ||
964 | pp++; | ||
965 | } | ||
966 | } | ||
967 | else if ((p > exe1) && (*p == '=')) | ||
968 | { | ||
969 | restart = 1; | ||
970 | *pp = *p; | ||
971 | pp++; | ||
972 | } | ||
973 | else if (*p == '\'') | ||
974 | in_quot_sing = 1; | ||
975 | else if (*p == '\"') | ||
976 | in_quot_dbl = 1; | ||
977 | else if (isspace(*p)) | ||
978 | { | ||
979 | if (restart) | ||
980 | goto restart; | ||
981 | else | ||
982 | break; | ||
983 | } | ||
984 | else | ||
985 | { | ||
986 | *pp = *p; | ||
987 | pp++; | ||
988 | } | ||
989 | } | ||
990 | p++; | ||
991 | } | ||
992 | *pp = 0; | ||
993 | return exe; | ||
994 | } | ||
995 | |||
996 | /** | ||
997 | * @brief Add the escape sequence ('\\') to the given file name. | ||
998 | * | ||
999 | * @param filename The file name. | ||
1000 | * @return The file name with special characters escaped. | ||
1001 | * | ||
1002 | * This function adds the escape sequence ('\\') to the given file | ||
1003 | * name and returns the result as a newly allocated string. If the | ||
1004 | * length of the returned string is longer than PATH_MAX, or on | ||
1005 | * failure, @c NULL is returned. When not needed anymore, the returned | ||
1006 | * value must be freed. | ||
1007 | */ | ||
1008 | EAPI char * | ||
1009 | ecore_file_escape_name(const char *filename) | ||
1010 | { | ||
1011 | const char *p; | ||
1012 | char *q; | ||
1013 | char buf[PATH_MAX]; | ||
1014 | |||
1015 | p = filename; | ||
1016 | q = buf; | ||
1017 | while (*p) | ||
1018 | { | ||
1019 | if ((q - buf) > (PATH_MAX - 6)) return NULL; | ||
1020 | if ( | ||
1021 | (*p == ' ') || (*p == '\t') || (*p == '\n') || | ||
1022 | (*p == '\\') || (*p == '\'') || (*p == '\"') || | ||
1023 | (*p == ';') || (*p == '!') || (*p == '#') || | ||
1024 | (*p == '$') || (*p == '%') || (*p == '&') || | ||
1025 | (*p == '*') || (*p == '(') || (*p == ')') || | ||
1026 | (*p == '[') || (*p == ']') || (*p == '{') || | ||
1027 | (*p == '}') || (*p == '|') || (*p == '<') || | ||
1028 | (*p == '>') || (*p == '?') | ||
1029 | ) | ||
1030 | { | ||
1031 | *q = '\\'; | ||
1032 | q++; | ||
1033 | } | ||
1034 | *q = *p; | ||
1035 | q++; | ||
1036 | p++; | ||
1037 | } | ||
1038 | *q = 0; | ||
1039 | return strdup(buf); | ||
1040 | } | ||
1041 | |||
1042 | /** | ||
1043 | * @bried Remove the extension from the given file name. | ||
1044 | * | ||
1045 | * @param path The name of the file. | ||
1046 | * @return A newly allocated string with the extension stripped out or | ||
1047 | * NULL on errors. | ||
1048 | * | ||
1049 | * This function removes the extension from @p path and returns the | ||
1050 | * result as a newly allocated string. If @p path is @c NULL, or on | ||
1051 | * failure, the function returns @c NULL. When not needed anymore, the | ||
1052 | * returned value must be freed. | ||
1053 | */ | ||
1054 | EAPI char * | ||
1055 | ecore_file_strip_ext(const char *path) | ||
1056 | { | ||
1057 | char *p, *file = NULL; | ||
1058 | |||
1059 | if (!path) | ||
1060 | return NULL; | ||
1061 | |||
1062 | p = strrchr(path, '.'); | ||
1063 | if (!p) | ||
1064 | file = strdup(path); | ||
1065 | else if (p != path) | ||
1066 | { | ||
1067 | file = malloc(((p - path) + 1) * sizeof(char)); | ||
1068 | if (file) | ||
1069 | { | ||
1070 | memcpy(file, path, (p - path)); | ||
1071 | file[p - path] = 0; | ||
1072 | } | ||
1073 | } | ||
1074 | |||
1075 | return file; | ||
1076 | } | ||
1077 | |||
1078 | /** | ||
1079 | * @brief Check if the given directory is empty. | ||
1080 | * | ||
1081 | * @param dir The name of the directory to check. | ||
1082 | * @return 1 if directory is empty, 0 if it has at least one file or | ||
1083 | * -1 in case of errors. | ||
1084 | * | ||
1085 | * This functions checks if @p dir is empty. The '.' and '..' files | ||
1086 | * will be ignored. If @p dir is empty, 1 is returned, if it contains | ||
1087 | * at least 1 file, 0 is returned. On failure, -1 is returned. | ||
1088 | */ | ||
1089 | EAPI int | ||
1090 | ecore_file_dir_is_empty(const char *dir) | ||
1091 | { | ||
1092 | Eina_File_Direct_Info *info; | ||
1093 | Eina_Iterator *it; | ||
1094 | |||
1095 | it = eina_file_direct_ls(dir); | ||
1096 | if (!it) return -1; | ||
1097 | |||
1098 | EINA_ITERATOR_FOREACH(it, info) | ||
1099 | { | ||
1100 | eina_iterator_free(it); | ||
1101 | return 0; | ||
1102 | } | ||
1103 | |||
1104 | eina_iterator_free(it); | ||
1105 | return 1; | ||
1106 | } | ||
1107 | |||
1108 | /** | ||
1109 | * @} | ||
1110 | */ | ||