10b9d581d40b054e1ef02ad4908d357432ec93e3
[framework/uifw/ecore.git] / src / lib / ecore_file / ecore_file.c
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 #ifndef _FILE_OFFSET_BITS
10 # define _FILE_OFFSET_BITS  64
11 #endif
12
13 #ifdef HAVE_FEATURES_H
14 # include <features.h>
15 #endif
16 #include <ctype.h>
17 #include <errno.h>
18
19 #include "ecore_file_private.h"
20
21
22 static int init = 0;
23
24 /* externally accessible functions */
25 /**
26  * Initialize Ecore_File and the services it will use. Call this function
27  * once before you use any of the ecore file functions.
28  * @return Return the number howoften ecore_file_init() was call succesfully;
29  *         0 if it failed.
30  */
31 EAPI int
32 ecore_file_init()
33 {
34    if (++init != 1) return init;
35
36    if (!ecore_file_monitor_init())
37      goto error;
38    if (!ecore_file_path_init())
39      goto error;
40    if (!ecore_file_download_init())
41      goto error;
42    return init;
43
44 error:
45
46    ecore_file_monitor_shutdown();
47    ecore_file_path_shutdown();
48    ecore_file_download_shutdown();
49
50    return --init;
51 }
52
53 /**
54  * Shutdown the Ecore_File
55  * @return returns the number of libraries that still uses Ecore_File
56  */
57 EAPI int
58 ecore_file_shutdown()
59 {
60    if (--init != 0) return init;
61
62    ecore_file_monitor_shutdown();
63    ecore_file_path_shutdown();
64    ecore_file_download_shutdown();
65
66    return init;
67 }
68
69 /**
70  * Get the time of the last modification to the give file
71  * @param file The name of the file
72  * @return Return the time of the last data modification, if an error should
73  *         occur it will return 0
74  */
75 EAPI long long
76 ecore_file_mod_time(const char *file)
77 {
78    struct stat st;
79
80    if (stat(file, &st) < 0) return 0;
81    return st.st_mtime;
82 }
83
84 /**
85  * Get the size of the given file
86  * @param  file The name of the file
87  * @return The size of the file in byte
88  */
89 EAPI long long
90 ecore_file_size(const char *file)
91 {
92    struct stat st;
93
94    if (stat(file, &st) < 0) return 0;
95    return st.st_size;
96 }
97
98 /**
99  * Check if file exists
100  * @param  file The name of the file
101  * @return 1 if file exists on local filesystem, 0 otherwise
102  */
103 EAPI int
104 ecore_file_exists(const char *file)
105 {
106    struct stat st;
107
108    /*Workaround so that "/" returns a true, otherwise we can't monitor "/" in ecore_file_monitor*/
109    if (stat(file, &st) < 0 && strcmp(file, "/")) return 0;
110    return 1;
111 }
112
113 /**
114  * Check if file is a directory
115  * @param  file The name of the file
116  * @return 1 if file exist and is a directory, 0 otherwise
117  */
118 EAPI int
119 ecore_file_is_dir(const char *file)
120 {
121    struct stat st;
122
123    if (stat(file, &st) < 0) return 0;
124    if (S_ISDIR(st.st_mode)) return 1;
125    return 0;
126 }
127
128 static mode_t default_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
129
130 /**
131  * Create a new directory
132  * @param  dir The name of the directory to create
133  * @return 1 on successfull creation, 0 on failure
134  *
135  * The directory is created with the mode: S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH
136  */
137 EAPI int
138 ecore_file_mkdir(const char *dir)
139 {
140    if (mkdir(dir, default_mode) < 0) return 0;
141    return 1;
142 }
143
144 /**
145  * Delete the given dir
146  * @param  dir The name of the directory to delete
147  * @return 1 on success, 0 on failure
148  */
149 EAPI int
150 ecore_file_rmdir(const char *dir)
151 {
152    if (rmdir(dir) < 0) return 0;
153    return 1;
154 }
155
156 /**
157  * Delete the given file
158  * @param  file The name of the file to delete
159  * @return 1 on success, 0 on failure
160  */
161 EAPI int
162 ecore_file_unlink(const char *file)
163 {
164    if (unlink(file) < 0) return 0;
165    return 1;
166 }
167
168 /**
169  * Delete a directory and all its contents
170  * @param  dir The name of the directory to delete
171  * @return 1 on success, 0 on failure
172  *
173  * If dir is a link only the link is removed
174  */
175 EAPI int
176 ecore_file_recursive_rm(const char *dir)
177 {
178    DIR                *dirp;
179    struct dirent      *dp;
180    char               path[PATH_MAX];
181    char               buf[PATH_MAX];
182    struct             stat st;
183    int                ret;
184
185    if (readlink(dir, buf, sizeof(buf)) > 0)
186      {
187         return ecore_file_unlink(dir);
188      }
189
190    ret = stat(dir, &st);
191    if ((ret == 0) && (S_ISDIR(st.st_mode)))
192      {
193         ret = 1;
194         if (stat(dir, &st) == -1) return 0;
195         dirp = opendir(dir);
196         if (dirp)
197           {
198              while ((dp = readdir(dirp)))
199                {
200                   if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, "..")))
201                     {
202                        snprintf(path, PATH_MAX, "%s/%s", dir, dp->d_name);
203                        if (!ecore_file_recursive_rm(path))
204                          ret = 0;
205                     }
206                }
207              closedir(dirp);
208           }
209         if (!ecore_file_rmdir(dir)) ret = 0;
210         return ret;
211      }
212    else
213      {
214         if (ret == -1) return 0;
215         return ecore_file_unlink(dir);
216      }
217
218    return 1;
219 }
220
221 /**
222  * Create a complete path
223  * @param  path The path to create
224  * @return 1 on success, 0 on failure
225  */
226 EAPI int
227 ecore_file_mkpath(const char *path)
228 {
229    char ss[PATH_MAX];
230    int  i;
231
232    ss[0] = 0;
233    i = 0;
234    while (path[i])
235      {
236         if (i == sizeof(ss) - 1) return 0;
237         ss[i] = path[i];
238         ss[i + 1] = 0;
239         if (path[i] == '/')
240           {
241              ss[i] = 0;
242              if ((ecore_file_exists(ss)) && (!ecore_file_is_dir(ss))) return 0;
243              else if (!ecore_file_exists(ss)) ecore_file_mkdir(ss);
244              ss[i] = '/';
245           }
246         i++;
247      }
248    if ((ecore_file_exists(ss)) && (!ecore_file_is_dir(ss))) return 0;
249    else if (!ecore_file_exists(ss)) ecore_file_mkdir(ss);
250    return 1;
251 }
252
253 /**
254  * Copy a file
255  * @param  src The name of the source file
256  * @param  dst The name of the destination file
257  * @return 1 on success, 0 on failure
258  */
259 EAPI int
260 ecore_file_cp(const char *src, const char *dst)
261 {
262    FILE               *f1, *f2;
263    char                buf[16384];
264    char                realpath1[PATH_MAX];
265    char                realpath2[PATH_MAX];
266    size_t              num;
267    int                 ret = 1;
268
269    if (!realpath(src, realpath1)) return 0;
270    if (realpath(dst, realpath2) && !strcmp(realpath1, realpath2)) return 0;
271
272    f1 = fopen(src, "rb");
273    if (!f1) return 0;
274    f2 = fopen(dst, "wb");
275    if (!f2)
276      {
277         fclose(f1);
278         return 0;
279      }
280    while ((num = fread(buf, 1, sizeof(buf), f1)) > 0)
281      {
282         if (fwrite(buf, 1, num, f2) != num) ret = 0;
283      }
284    fclose(f1);
285    fclose(f2);
286    return ret;
287 }
288
289 /**
290  * Move a file
291  * @param  src The name of the source file
292  * @param  dst The name of the destination file
293  * @return 1 on success, 0 on failure
294  */
295 EAPI int
296 ecore_file_mv(const char *src, const char *dst)
297 {
298    if (ecore_file_exists(dst)) return 0;
299    if (rename(src, dst))
300      {
301         if (errno == EXDEV)
302           {
303              struct stat st;
304
305              stat(src, &st);
306              if (S_ISREG(st.st_mode))
307                {
308                   ecore_file_cp(src, dst);
309                   chmod(dst, st.st_mode);
310                   ecore_file_unlink(src);
311                   return 1;
312                }
313           }
314         return 0;
315      }
316    return 1;
317 }
318
319 /**
320  * Create a symbolic link
321  * @param  src The name of the file to link
322  * @param  dest The name of link
323  * @return 1 on success, 0 on failure
324  */
325 EAPI int
326 ecore_file_symlink(const char *src, const char *dest)
327 {
328    if (!symlink(src, dest)) return 1;
329
330    return 0;
331 }
332
333 /**
334  * Get the canonicalized absolute pathname
335  * @param  file The file path
336  * @return The canonicalized absolute pathname; on failure it will return
337  *         an empty string
338  */
339 EAPI char *
340 ecore_file_realpath(const char *file)
341 {
342    char  buf[PATH_MAX];
343
344    /*
345     * Some implementations of realpath do not conform to the SUS.
346     * And as a result we must prevent a null arg from being passed.
347     */
348    if (!file) return strdup("");
349    if (!realpath(file, buf)) return strdup("");
350
351    return strdup(buf);
352 }
353
354 /**
355  * Get the filename from a give path
356  * @param  path The complete path
357  * @return Only the file name
358  */
359 EAPI const char *
360 ecore_file_file_get(const char *path)
361 {
362    char *result = NULL;
363
364    if (!path) return NULL;
365    if ((result = strrchr(path, '/'))) result++;
366    else result = (char *)path;
367    return result;
368 }
369
370 /**
371  * Get the directory where file reside
372  * @param  file The name of the file
373  * @return The directory name
374  */
375 EAPI char *
376 ecore_file_dir_get(const char *file)
377 {
378    char               *p;
379    char                buf[PATH_MAX];
380
381    strncpy(buf, file, PATH_MAX);
382    p = strrchr(buf, '/');
383    if (!p)
384      {
385         return strdup(file);
386      }
387
388    if (p == buf) return strdup("/");
389
390    *p = 0;
391    return strdup(buf);
392 }
393
394 /**
395  * Check if file can be read
396  * @param  file The name of the file
397  * @return 1 if the file is readable, 0 otherwise
398  */
399 EAPI int
400 ecore_file_can_read(const char *file)
401 {
402    if (!file) return 0;
403    if (!access(file, R_OK)) return 1;
404    return 0;
405 }
406
407 /**
408  * Check if file can be written
409  * @param  file The name of the file
410  * @return 1 if the file is writable, 0 otherwise
411  */
412 EAPI int
413 ecore_file_can_write(const char *file)
414 {
415    if (!file) return 0;
416    if (!access(file, W_OK)) return 1;
417    return 0;
418 }
419
420 /**
421  * Check if file can be executed
422  * @param  file The name of the file
423  * @return 1 if the file can be executed, 0 otherwise
424  */
425 EAPI int
426 ecore_file_can_exec(const char *file)
427 {
428    if (!file) return 0;
429    if (!access(file, X_OK)) return 1;
430    return 0;
431 }
432
433 /**
434  * Get the path pointed by link
435  * @param  link The name of the link
436  * @return The path pointed by link or NULL
437  */
438 EAPI char *
439 ecore_file_readlink(const char *link)
440 {
441    char                buf[PATH_MAX];
442    int                 count;
443
444    if ((count = readlink(link, buf, sizeof(buf))) < 0) return NULL;
445    buf[count] = 0;
446    return strdup(buf);
447 }
448
449 /**
450  * Get the list of the files and directories in a given directory. The list
451  * will be sorted with strcoll as compare function. That means that you may
452  * want to set the current locale for the category LC_COLLATE with setlocale().
453  * For more information see the manual pages of strcoll and setlocale.
454  * The list will not contain the directory entries for '.' and '..'.
455  * @param  dir The name of the directory to list
456  * @return Return an Ecore_List containing all the files in the directory;
457  *         on failure it returns NULL.
458  */
459 EAPI Ecore_List *
460 ecore_file_ls(const char *dir)
461 {
462    char               *f;
463    DIR                *dirp;
464    struct dirent      *dp;
465    Ecore_List         *list;
466
467    dirp = opendir(dir);
468    if (!dirp) return NULL;
469
470    list = ecore_list_new();
471    ecore_list_free_cb_set(list, free);
472
473    while ((dp = readdir(dirp)))
474      {
475         if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, "..")))
476           {
477                f = strdup(dp->d_name);
478                ecore_list_append(list, f);
479           }
480      }
481    closedir(dirp);
482
483    ecore_list_sort(list, ECORE_COMPARE_CB(strcoll), ECORE_SORT_MIN);
484
485    ecore_list_first_goto(list);
486    return list;
487 }
488
489 /**
490  * FIXME: To be documented.
491  */
492 EAPI char *
493 ecore_file_app_exe_get(const char *app)
494 {
495    char *p, *pp, *exe1 = NULL, *exe2 = NULL;
496    char *exe = NULL;
497    int in_quot_dbl = 0, in_quot_sing = 0, restart = 0;
498
499    if (!app) return NULL;
500
501    p = (char *)app;
502 restart:
503    while ((*p) && (isspace(*p))) p++;
504    exe1 = p;
505    while (*p)
506      {
507         if (in_quot_sing)
508           {
509              if (*p == '\'')
510                in_quot_sing = 0;
511           }
512         else if (in_quot_dbl)
513           {
514              if (*p == '\"')
515                in_quot_dbl = 0;
516           }
517         else
518           {
519              if (*p == '\'')
520                in_quot_sing = 1;
521              else if (*p == '\"')
522                in_quot_dbl = 1;
523              if ((isspace(*p)) && (!((p > app) && (p[-1] != '\\'))))
524                break;
525           }
526         p++;
527      }
528    exe2 = p;
529    if (exe2 == exe1) return NULL;
530    if (*exe1 == '~')
531      {
532         char *homedir;
533         int len;
534
535         /* Skip ~ */
536         exe1++;
537
538         homedir = getenv("HOME");
539         if (!homedir) return NULL;
540         len = strlen(homedir);
541         if (exe) free(exe);
542         exe = malloc(len + exe2 - exe1 + 2);
543         if (!exe) return NULL;
544         pp = exe;
545         if (len)
546           {
547              strcpy(exe, homedir);
548              pp += len;
549              if (*(pp - 1) != '/')
550                {
551                   *pp = '/';
552                   pp++;
553                }
554           }
555      }
556    else
557      {
558         if (exe) free(exe);
559         exe = malloc(exe2 - exe1 + 1);
560         if (!exe) return NULL;
561         pp = exe;
562      }
563    p = exe1;
564    restart = 0;
565    in_quot_dbl = 0;
566    in_quot_sing = 0;
567    while (*p)
568      {
569         if (in_quot_sing)
570           {
571              if (*p == '\'')
572                in_quot_sing = 0;
573              else
574                {
575                   *pp = *p;
576                   pp++;
577                }
578           }
579         else if (in_quot_dbl)
580           {
581              if (*p == '\"')
582                in_quot_dbl = 0;
583              else
584                {
585                   /* techcincally this is wrong. double quotes also accept
586                    * special chars:
587                    *
588                    * $, `, \
589                    */
590                   *pp = *p;
591                   pp++;
592                }
593           }
594         else
595           {
596              /* technically we should handle special chars:
597               *
598               * $, `, \, etc.
599               */
600              if ((p > exe1) && (p[-1] == '\\'))
601                {
602                   if (*p != '\n')
603                     {
604                        *pp = *p;
605                        pp++;
606                     }
607                }
608              else if ((p > exe1) && (*p == '='))
609                {
610                   restart = 1;
611                   *pp = *p;
612                   pp++;
613                }
614              else if (*p == '\'')
615                in_quot_sing = 1;
616              else if (*p == '\"')
617                in_quot_dbl = 1;
618              else if (isspace(*p))
619                {
620                   if (restart)
621                     goto restart;
622                   else
623                     break;
624                }
625              else
626                {
627                   *pp = *p;
628                   pp++;
629                }
630           }
631         p++;
632      }
633    *pp = 0;
634    return exe;
635 }
636
637 /**
638  * Add the escape sequence ('\\') to the given filename
639  * @param  filename The file name
640  * @return The file name with special characters escaped; if the length of the
641  *         resulting string is longer than PATH_MAX it will return NULL
642  */
643 EAPI char *
644 ecore_file_escape_name(const char *filename)
645 {
646    const char *p;
647    char *q;
648    char buf[PATH_MAX];
649
650    p = filename;
651    q = buf;
652    while (*p)
653      {
654         if ((q - buf) > (PATH_MAX - 6)) return NULL;
655         if (
656             (*p == ' ') || (*p == '\t') || (*p == '\n') ||
657             (*p == '\\') || (*p == '\'') || (*p == '\"') ||
658             (*p == ';') || (*p == '!') || (*p == '#') ||
659             (*p == '$') || (*p == '%') || (*p == '&') ||
660             (*p == '*') || (*p == '(') || (*p == ')') ||
661             (*p == '[') || (*p == ']') || (*p == '{') ||
662             (*p == '}') || (*p == '|') || (*p == '<') ||
663             (*p == '>') || (*p == '?')
664             )
665           {
666              *q = '\\';
667              q++;
668           }
669         *q = *p;
670         q++;
671         p++;
672      }
673    *q = 0;
674    return strdup(buf);
675 }
676
677 /**
678  * Remove the extension from a given path
679  * @param  path The name of the file
680  * @return A newly allocated string with the extension stripped out or NULL on errors
681  */
682 EAPI char *
683 ecore_file_strip_ext(const char *path)
684 {
685    char *p, *file = NULL;
686
687    p = strrchr(path, '.');
688    if (!p)
689      {
690         file = strdup(path);
691      }
692    else if (p != path)
693      {
694         file = malloc(((p - path) + 1) * sizeof(char));
695         if (file)
696           {
697              memcpy(file, path, (p - path));
698              file[p - path] = 0;
699           }
700      }
701
702    return file;
703 }
704
705 /**
706  * Check if the given directory is empty. The '.' and '..' files will be ignored.
707  * @param  dir The name of the directory to check
708  * @return 1 if directory is empty, 0 if it has at least one file or -1 in case of errors
709  */
710 EAPI int
711 ecore_file_dir_is_empty(const char *dir)
712 {
713    DIR *dirp;
714    struct dirent *dp;
715
716    dirp = opendir(dir);
717    if (!dirp) return -1;
718
719    while ((dp = readdir(dirp)))
720      {
721         if ((strcmp(dp->d_name, ".")) && (strcmp(dp->d_name, "..")))
722           {
723              closedir(dirp);
724              return 0;
725           }
726      }
727    
728    closedir(dirp);
729    return 1;
730 }