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 <unistd.h>
9 #include <libgen.h>
10 
11 #ifdef _WIN32
12 # include <direct.h>
13 # include <evil_private.h> /* mkdir realpath */
14 #endif
15 
16 #ifdef HAVE_FEATURES_H
17 # include <features.h>
18 #endif
19 #include <ctype.h>
20 #include <errno.h>
21 
22 #ifdef HAVE_ATFILE_SOURCE
23 # include <dirent.h>
24 #endif
25 
26 #include "ecore_file_private.h"
27 
28 /*
29  * FIXME: the following functions will certainly not work on Windows:
30  * ecore_file_app_exe_get()
31  * ecore_file_escape_name()
32  */
33 
34 int _ecore_file_log_dom = -1;
35 static int _ecore_file_init_count = 0;
36 
37 static Eina_Bool
_ecore_file_stat(const char * file,long long * mtime,long long * size,mode_t * mode,Eina_Bool * is_dir,Eina_Bool * is_reg)38 _ecore_file_stat(const char *file,
39                  long long *mtime,
40                  long long *size,
41                  mode_t *mode,
42                  Eina_Bool *is_dir,
43                  Eina_Bool *is_reg)
44 {
45    struct stat st;
46 #ifdef _WIN32
47    /*
48     * On Windows, stat() returns -1 is file is a path finishing with
49     * a slash or blackslash
50     * see https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx
51     * ("Return Value" section)
52     *
53     * so we ensure that file never finishes with \ or /
54     */
55    char f[MAX_PATH];
56    size_t len;
57 
58    len = strlen(file);
59    if ((len + 1) > MAX_PATH)
60      return EINA_FALSE;
61 
62    memcpy(f, file, len + 1);
63    if ((f[len - 1] == '/') || (f[len - 1] == '\\'))
64      f[len - 1] = '\0';
65 
66    if (stat(f, &st) < 0)
67      return EINA_FALSE;
68 #else
69    if (stat(file, &st) < 0)
70      return EINA_FALSE;
71 #endif
72 
73    if (mtime) *mtime = st.st_mtime;
74    if (size) *size = st.st_size;
75    if (mode) *mode = st.st_mode;
76    if (is_dir) *is_dir = S_ISDIR(st.st_mode);
77    if (is_reg) *is_reg = S_ISREG(st.st_mode);
78 
79    return EINA_TRUE;
80 }
81 
82 EAPI int
ecore_file_init()83 ecore_file_init()
84 {
85    if (++_ecore_file_init_count != 1)
86      return _ecore_file_init_count;
87 
88    if (!ecore_init())
89      return --_ecore_file_init_count;
90 
91    _ecore_file_log_dom = eina_log_domain_register
92      ("ecore_file", ECORE_FILE_DEFAULT_LOG_COLOR);
93    if(_ecore_file_log_dom < 0)
94      {
95        EINA_LOG_ERR("Impossible to create a log domain for the ecore file module.");
96        return --_ecore_file_init_count;
97      }
98    ecore_file_path_init();
99    ecore_file_monitor_init();
100    ecore_file_download_init();
101 
102    /* FIXME: were the tests disabled for a good reason ? */
103 
104    /*
105    if (!ecore_file_monitor_init())
106      goto shutdown_ecore_file_path;
107 
108    if (!ecore_file_download_init())
109      goto shutdown_ecore_file_monitor;
110    */
111 
112    return _ecore_file_init_count;
113 
114    /*
115  shutdown_ecore_file_monitor:
116    ecore_file_monitor_shutdown();
117  shutdown_ecore_file_path:
118    ecore_file_path_shutdown();
119 
120    return --_ecore_file_init_count;
121    */
122 }
123 
124 EAPI int
ecore_file_shutdown()125 ecore_file_shutdown()
126 {
127    if (--_ecore_file_init_count != 0)
128      return _ecore_file_init_count;
129 
130    ecore_file_download_shutdown();
131    ecore_file_monitor_shutdown();
132    ecore_file_path_shutdown();
133 
134    eina_log_domain_unregister(_ecore_file_log_dom);
135    _ecore_file_log_dom = -1;
136 
137    ecore_shutdown();
138 
139    return _ecore_file_init_count;
140 }
141 
142 EAPI long long
ecore_file_mod_time(const char * file)143 ecore_file_mod_time(const char *file)
144 {
145    long long time;
146 
147    if (!_ecore_file_stat(file, &time, NULL, NULL, NULL, NULL))
148      return 0;
149 
150    return time;
151 }
152 
153 EAPI long long
ecore_file_size(const char * file)154 ecore_file_size(const char *file)
155 {
156    long long size;
157 
158    if (!_ecore_file_stat(file, NULL, &size, NULL, NULL, NULL))
159      return 0;
160 
161    return size;
162 }
163 
164 EAPI Eina_Bool
ecore_file_exists(const char * file)165 ecore_file_exists(const char *file)
166 {
167 #ifdef _WIN32
168    /* I prefer not touching the specific UNIX code... */
169    return _ecore_file_stat(file, NULL, NULL, NULL, NULL, NULL);
170 #else
171    struct stat st;
172    if (!file) return EINA_FALSE;
173 
174    /*Workaround so that "/" returns a true, otherwise we can't monitor "/" in ecore_file_monitor*/
175    if (stat(file, &st) < 0 && strcmp(file, "/")) return EINA_FALSE;
176    return EINA_TRUE;
177 #endif
178 }
179 
180 EAPI Eina_Bool
ecore_file_is_dir(const char * file)181 ecore_file_is_dir(const char *file)
182 {
183    Eina_Bool is_dir;
184 
185    if (!_ecore_file_stat(file, NULL, NULL, NULL, &is_dir, NULL))
186      return EINA_FALSE;
187 
188    return is_dir;
189 }
190 
191 static mode_t default_mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
192 
193 EAPI Eina_Bool
ecore_file_mkdir(const char * dir)194 ecore_file_mkdir(const char *dir)
195 {
196    return (mkdir(dir, default_mode) == 0);
197 }
198 
199 EAPI int
ecore_file_mkdirs(const char ** dirs)200 ecore_file_mkdirs(const char **dirs)
201 {
202    int i = 0;
203 
204    if (!dirs) return -1;
205 
206    for (; *dirs; dirs++)
207      if (ecore_file_mkdir(*dirs))
208        i++;
209    return i;
210 }
211 
212 EAPI int
ecore_file_mksubdirs(const char * base,const char ** subdirs)213 ecore_file_mksubdirs(const char *base, const char **subdirs)
214 {
215 #ifndef HAVE_ATFILE_SOURCE
216    char buf[PATH_MAX];
217    int baselen;
218 #else
219    int fd;
220    DIR *dir;
221 #endif
222    int i;
223 
224    if (!subdirs) return -1;
225    if ((!base) || (base[0] == '\0')) return -1;
226 
227    if ((!ecore_file_is_dir(base)) && (!ecore_file_mkpath(base)))
228      return 0;
229 
230 #ifndef HAVE_ATFILE_SOURCE
231    baselen = eina_strlcpy(buf, base, sizeof(buf));
232    if ((baselen < 1) || (baselen + 1 >= (int)sizeof(buf)))
233      return 0;
234 
235    if (buf[baselen - 1] != '/')
236      {
237         buf[baselen] = '/';
238         baselen++;
239      }
240 #else
241    dir = opendir(base);
242    if (!dir)
243      return 0;
244    fd = dirfd(dir);
245 #endif
246 
247    i = 0;
248    for (; *subdirs; subdirs++)
249      {
250 #ifdef HAVE_ATFILE_SOURCE
251         struct stat st;
252 #endif
253         Eina_Bool is_dir;
254 
255 #ifndef HAVE_ATFILE_SOURCE
256         eina_strlcpy(buf + baselen, *subdirs, sizeof(buf) - baselen);
257         if (_ecore_file_stat(buf, NULL, NULL, NULL, &is_dir, NULL))
258           {
259 #else
260         if (fstatat(fd, *subdirs, &st, 0) == 0)
261           {
262              is_dir = S_ISDIR(st.st_mode);
263 #endif
264              if (is_dir)
265                {
266                   i++;
267                   continue;
268                }
269           }
270         else
271           {
272              if (errno == ENOENT)
273                {
274 #ifndef HAVE_ATFILE_SOURCE
275                   if (ecore_file_mkdir(buf))
276 #else
277                   if (mkdirat(fd, *subdirs, default_mode) == 0)
278 #endif
279                     {
280                        i++;
281                        continue;
282                     }
283                  }
284             }
285      }
286 
287 #ifdef HAVE_ATFILE_SOURCE
288    closedir(dir);
289 #endif
290 
291    return i;
292 }
293 
294 EAPI Eina_Bool
295 ecore_file_rmdir(const char *dir)
296 {
297    if (rmdir(dir) < 0) return EINA_FALSE;
298    return EINA_TRUE;
299 }
300 
301 EAPI Eina_Bool
302 ecore_file_unlink(const char *file)
303 {
304    if (unlink(file) < 0) return EINA_FALSE;
305    return EINA_TRUE;
306 }
307 
308 EAPI Eina_Bool
309 ecore_file_remove(const char *file)
310 {
311    if (remove(file) < 0) return EINA_FALSE;
312    return EINA_TRUE;
313 }
314 
315 EAPI Eina_Bool
316 ecore_file_recursive_rm(const char *dir)
317 {
318 #ifndef _WIN32
319    struct stat st;
320 #endif
321    Eina_Bool is_dir;
322 
323 #ifdef _WIN32
324    if (!_ecore_file_stat(dir, NULL, NULL, NULL, &is_dir, NULL))
325      return EINA_FALSE;
326 #else
327    if (lstat(dir, &st) == -1)
328      return EINA_FALSE;
329    is_dir = S_ISDIR(st.st_mode);
330 #endif
331 
332    if (is_dir)
333      {
334         Eina_File_Direct_Info *info;
335         Eina_Iterator *it;
336         int ret;
337 
338         ret = 1;
339         it = eina_file_direct_ls(dir);
340         EINA_ITERATOR_FOREACH(it, info)
341           {
342              if (!ecore_file_recursive_rm(info->path))
343                ret = 0;
344           }
345         eina_iterator_free(it);
346 
347         if (!ecore_file_rmdir(dir)) ret = 0;
348         if (ret)
349             return EINA_TRUE;
350         else
351             return EINA_FALSE;
352      }
353    else
354      {
355         return ecore_file_unlink(dir);
356      }
357 }
358 
359 static inline Eina_Bool
360 _ecore_file_mkpath_if_not_exists(const char *path)
361 {
362    Eina_Bool is_dir;
363 
364    /* Windows: path like C: or D: etc are valid, but stat() returns an error */
365 #ifdef _WIN32
366    if ((strlen(path) == 2) &&
367        ((path[0] >= 'a' && path[0] <= 'z') ||
368         (path[0] >= 'A' && path[0] <= 'Z')) &&
369        (path[1] == ':'))
370      return EINA_TRUE;
371 #endif
372 
373    if (!_ecore_file_stat(path, NULL, NULL, NULL, &is_dir, NULL))
374      return ecore_file_mkdir(path);
375    else if (!is_dir)
376      return EINA_FALSE;
377    else
378      return EINA_TRUE;
379 }
380 
381 EAPI Eina_Bool
382 ecore_file_mkpath(const char *path)
383 {
384    char ss[PATH_MAX];
385    unsigned int i;
386 
387    EINA_SAFETY_ON_NULL_RETURN_VAL(path, EINA_FALSE);
388 
389    if (ecore_file_is_dir(path))
390      return EINA_TRUE;
391 
392    for (i = 0; path[i] != '\0'; ss[i] = path[i], i++)
393      {
394         if (i == sizeof(ss) - 1) return EINA_FALSE;
395         if (((path[i] == '/') || (path[i] == '\\')) && (i > 0))
396           {
397              ss[i] = '\0';
398              if (!_ecore_file_mkpath_if_not_exists(ss))
399                return EINA_FALSE;
400           }
401      }
402    ss[i] = '\0';
403    return _ecore_file_mkpath_if_not_exists(ss);
404 }
405 
406 EAPI int
407 ecore_file_mkpaths(const char **paths)
408 {
409    int i = 0;
410 
411    if (!paths) return -1;
412 
413    for (; *paths; paths++)
414      if (ecore_file_mkpath(*paths))
415        i++;
416    return i;
417 }
418 
419 EAPI Eina_Bool
420 ecore_file_cp(const char *src, const char *dst)
421 {
422    FILE *f1, *f2;
423    char buf[16384];
424    char realpath1[PATH_MAX], realpath2[PATH_MAX];
425    size_t num;
426    Eina_Bool ret = EINA_TRUE;
427 
428    if (!realpath(src, realpath1)) return EINA_FALSE;
429    if (realpath(dst, realpath2) && !strcmp(realpath1, realpath2)) return EINA_FALSE;
430 
431    f1 = fopen(src, "rb");
432    if (!f1) return EINA_FALSE;
433    f2 = fopen(dst, "wb");
434    if (!f2)
435      {
436         fclose(f1);
437         return EINA_FALSE;
438      }
439    while ((num = fread(buf, 1, sizeof(buf), f1)) > 0)
440      {
441         if (fwrite(buf, 1, num, f2) != num) ret = EINA_FALSE;
442      }
443    fclose(f1);
444    fclose(f2);
445    return ret;
446 }
447 
448 EAPI Eina_Bool
449 ecore_file_mv(const char *src, const char *dst)
450 {
451    char buf[PATH_MAX];
452    int fd;
453 
454    if (rename(src, dst))
455      {
456         // File cannot be moved directly because
457         // it resides on a different mount point.
458         if (errno == EXDEV)
459           {
460              mode_t mode;
461              Eina_Bool is_reg;
462 
463              // Make sure this is a regular file before
464              // we do anything fancy.
465              if (!_ecore_file_stat(src, NULL, NULL, &mode, NULL, &is_reg))
466                  goto FAIL;
467              if (is_reg)
468                {
469                   char *dir;
470                   Eina_Tmpstr *tmpstr = NULL;
471 
472                   dir = ecore_file_dir_get(dst);
473                   // Since we can't directly rename, try to
474                   // copy to temp file in the dst directory
475                   // and then rename.
476                   snprintf(buf, sizeof(buf), "%s/.%s.tmp.XXXXXX",
477                            dir, ecore_file_file_get(dst));
478                   free(dir);
479                   fd = eina_file_mkstemp(buf, &tmpstr);
480                   if (fd < 0) goto FAIL;
481                   close(fd);
482 
483                   // Copy to temp file
484                   if (!ecore_file_cp(src, tmpstr))
485                     {
486                        eina_tmpstr_del(tmpstr);
487                        goto FAIL;
488                     }
489 
490                   // Set file permissions of temp file to match src
491                   if (chmod(tmpstr, mode) == -1)
492                     {
493                        eina_tmpstr_del(tmpstr);
494                        goto FAIL;
495                     }
496 
497                   // Try to atomically move temp file to dst
498                   if (rename(tmpstr, dst))
499                     {
500                        // If we still cannot atomically move
501                        // do a normal copy and hope for the best.
502                        if (!ecore_file_cp(tmpstr, dst))
503                          {
504                             eina_tmpstr_del(tmpstr);
505                             goto FAIL;
506                          }
507                     }
508 
509                   // Delete temporary file and src
510                   ecore_file_unlink(tmpstr);
511                   ecore_file_unlink(src);
512                   eina_tmpstr_del(tmpstr);
513                   goto PASS;
514                }
515           }
516 #ifdef _WIN32
517         if (errno == ENOENT)
518           {
519              struct _stat s;
520              _stat(dst, &s);
521              if (_S_IFREG & s.st_mode)
522                {
523                   ecore_file_unlink(dst);
524                   if (rename(src, dst))
525                     {
526                        return EINA_TRUE;
527                     }
528                }
529           }
530 #endif
531         goto FAIL;
532      }
533 
534 PASS:
535    return EINA_TRUE;
536 
537 FAIL:
538    return EINA_FALSE;
539 }
540 
541 EAPI Eina_Bool
542 ecore_file_symlink(const char *src, const char *dest)
543 {
544 #ifndef _WIN32
545    return !symlink(src, dest);
546 #else
547    return EINA_FALSE;
548    (void)src;
549    (void)dest;
550 #endif
551 }
552 
553 EAPI char *
554 ecore_file_realpath(const char *file)
555 {
556    char buf[PATH_MAX];
557 
558    /*
559     * Some implementations of realpath do not conform to the SUS.
560     * And as a result we must prevent a null arg from being passed.
561     */
562    if (!file) return strdup("");
563    if (!realpath(file, buf)) return strdup("");
564 
565    return strdup(buf);
566 }
567 
568 EAPI const char *
569 ecore_file_file_get(const char *path)
570 {
571    char *result = NULL;
572 
573    if (!path) return NULL;
574 
575    if ((result = strrchr(path, '/'))) result++;
576    else result = (char *)path;
577 
578 #ifdef _WIN32
579    /*
580     * Here, we know that there is no more / in the string beginning at
581     * 'result'. So just check that there is no more \ from it.
582     */
583    {
584       char *result_backslash;
585       if ((result_backslash = strrchr(result, '\\')))
586         result = ++result_backslash;
587    }
588 #endif
589 
590    return result;
591 }
592 
593 EAPI char *
594 ecore_file_dir_get(const char *file)
595 {
596    char *p;
597    char buf[PATH_MAX];
598 
599    if (!file) return NULL;
600    strncpy(buf, file, PATH_MAX);
601    buf[PATH_MAX - 1] = 0;
602    p = dirname(buf);
603    return strdup(p);
604 }
605 
606 EAPI Eina_Bool
607 ecore_file_can_read(const char *file)
608 {
609    if (!file) return EINA_FALSE;
610    if (!access(file, R_OK)) return EINA_TRUE;
611    return EINA_FALSE;
612 }
613 
614 EAPI Eina_Bool
615 ecore_file_can_write(const char *file)
616 {
617    if (!file) return EINA_FALSE;
618    if (!access(file, W_OK)) return EINA_TRUE;
619    return EINA_FALSE;
620 }
621 
622 EAPI Eina_Bool
623 ecore_file_can_exec(const char *file)
624 {
625 #ifdef _WIN32
626    HANDLE h;
627    HANDLE fm;
628    char *base;
629    char *base_nt;
630    LARGE_INTEGER sz;
631    WORD characteristics;
632 #endif
633 
634    if (!file || !*file) return EINA_FALSE;
635 
636 #ifdef _WIN32
637    /*
638     * we parse the file to check if it is a PE file (EXE or DLL)
639     * and we finally check whether it's a DLL or not.
640     * Reference :
641     * https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
642     */
643    h = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL,
644                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
645    if (h == INVALID_HANDLE_VALUE)
646      goto test_bat;
647 
648    if (!GetFileSizeEx(h, &sz))
649      goto close_h;
650 
651    /* a PE file must have at least the DOS and NT headers */
652    if (sz.QuadPart < (LONGLONG)(sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)))
653      goto close_h;
654 
655    fm = CreateFileMapping(h, NULL, PAGE_READONLY, 0, 0, NULL);
656    if (fm == NULL)
657      goto close_h;
658 
659    base = (char *)MapViewOfFile(fm, FILE_MAP_READ, 0, 0, 0);
660    CloseHandle(fm);
661    if (base == NULL)
662      goto close_h;
663 
664    /*
665     * the PE file begins with the DOS header.
666     * First magic number : the DOS header must begin with a DOS magic number,
667     * that is "MZ", that is 0x5a4d, stored in a WORD.
668     */
669    if (*((WORD *)base) != 0x5a4d)
670      goto unmap_view;
671 
672    /*
673     * The position of the NT header is located at the offset 0x3c.
674     */
675    base_nt = base + *((DWORD *)(base + 0x3c));
676    /*
677     * The NT header begins with the magic number "PE\0\0", that is
678     * 0x00004550, stored in a DWORD.
679     */
680    if (*((DWORD *)base_nt) != 0x00004550)
681      goto unmap_view;
682 
683    /*
684     * to get informations about executable (EXE or DLL), we look at
685     * the 'Characteristics' member of the NT header, located at the offset
686     * 22 (4 for the magic number, 18 for the offset) from base_nt.
687     * https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics
688     */
689    characteristics = *((WORD *)(base_nt + 4 + 18));
690 
691    UnmapViewOfFile(base);
692    CloseHandle(h);
693 
694    /*
695     * 0x0002 : if set, EXE or DLL
696     * 0x2000 : if set, DLL
697     */
698    if ((characteristics & 0x0002) && !(characteristics & 0x2000))
699      return EINA_TRUE;
700  unmap_view:
701    UnmapViewOfFile(base);
702  close_h:
703    CloseHandle(h);
704  test_bat:
705    /*
706     * a .bat file, considered as an executable, is only a text file,
707     * so we rely on the extension. Not the best but we cannot do more.
708     */
709    return eina_str_has_extension(file, ".bat");
710 #else
711    if (!access(file, X_OK)) return EINA_TRUE;
712 #endif
713    return EINA_FALSE;
714 }
715 
716 EAPI char *
717 ecore_file_readlink(const char *link)
718 {
719 #ifndef _WIN32
720    char buf[PATH_MAX];
721    int count;
722 
723    if ((count = readlink(link, buf, sizeof(buf) - 1)) < 0) return NULL;
724    buf[count] = 0;
725    return strdup(buf);
726 #else
727    return NULL;
728    (void)link;
729 #endif
730 }
731 
732 EAPI Eina_List *
733 ecore_file_ls(const char *dir)
734 {
735    Eina_File_Direct_Info *info;
736    Eina_Iterator *ls;
737    Eina_List *list = NULL;
738 
739    ls = eina_file_direct_ls(dir);
740    if (!ls) return NULL;
741 
742    EINA_ITERATOR_FOREACH(ls, info)
743      {
744         char *f;
745 
746         f = strdup(info->path + info->name_start);
747         list = eina_list_append(list, f);
748      }
749    eina_iterator_free(ls);
750 
751    list = eina_list_sort(list, eina_list_count(list), EINA_COMPARE_CB(strcoll));
752 
753    return list;
754 }
755 
756 EAPI char *
757 ecore_file_app_exe_get(const char *app)
758 {
759    Eina_Strbuf *buf;
760    char *exe;
761    const char *p;
762    Eina_Bool in_qout_double = EINA_FALSE;
763    Eina_Bool in_qout_single = EINA_FALSE;
764 
765    if (!app) return NULL;
766    buf = eina_strbuf_new();
767    if (!buf) return NULL;
768    p = app;
769    if ((p[0] == '~') && (p[1] == '/'))
770      {
771         const char *home = eina_environment_home_get();
772         if (home) eina_strbuf_append(buf, home);
773         p++;
774      }
775    for (; *p; p++)
776      {
777         if (in_qout_double)
778           {
779              if (*p == '\\')
780                {
781                   if (p[1]) p++;
782                   eina_strbuf_append_char(buf, *p);
783                }
784              else if (*p == '"') in_qout_double = EINA_FALSE;
785              else eina_strbuf_append_char(buf, *p);
786           }
787         else if (in_qout_single)
788           {
789              if (*p == '\\')
790                {
791                   if (p[1]) p++;
792                   eina_strbuf_append_char(buf, *p);
793                }
794              else if (*p == '\'') in_qout_single = EINA_FALSE;
795              else eina_strbuf_append_char(buf, *p);
796           }
797         else
798           {
799              if (*p == '\\')
800                {
801                   if (p[1]) p++;
802                   eina_strbuf_append_char(buf, *p);
803                }
804              else if (*p == '"') in_qout_double = EINA_TRUE;
805              else if (*p == '\'') in_qout_single = EINA_TRUE;
806              else
807                {
808                   if (isspace((unsigned char)(*p))) break;
809                   eina_strbuf_append_char(buf, *p);
810                }
811           }
812      }
813    exe = eina_strbuf_string_steal(buf);
814    eina_strbuf_free(buf);
815    return exe;
816 }
817 
818 EAPI char *
819 ecore_file_escape_name(const char *filename)
820 {
821    const char *p;
822    char *q;
823    char buf[PATH_MAX];
824 
825    EINA_SAFETY_ON_NULL_RETURN_VAL(filename, NULL);
826 
827    p = filename;
828    q = buf;
829    while (*p)
830      {
831         if ((q - buf) > (PATH_MAX - 6)) return NULL;
832         if (
833             (*p == ' ') || (*p == '\\') || (*p == '\'') ||
834             (*p == '\"') || (*p == ';') || (*p == '!') ||
835             (*p == '#') || (*p == '$') || (*p == '%') ||
836             (*p == '&') || (*p == '*') || (*p == '(') ||
837             (*p == ')') || (*p == '[') || (*p == ']') ||
838             (*p == '{') || (*p == '}') || (*p == '|') ||
839             (*p == '<') || (*p == '>') || (*p == '?')
840             )
841           {
842              *q = '\\';
843              q++;
844           }
845         else if (*p == '\t')
846           {
847              *q = '\\';
848              q++;
849              *q = '\\';
850              q++;
851              *q = 't';
852              q++;
853              p++;
854              continue;
855           }
856         else if (*p == '\n')
857           {
858             *q = '\\';
859             q++;
860             *q = '\\';
861             q++;
862             *q = 'n';
863             q++;
864             p++;
865             continue;
866           }
867 
868         *q = *p;
869         q++;
870         p++;
871      }
872    *q = 0;
873    return strdup(buf);
874 }
875 
876 EAPI char *
877 ecore_file_strip_ext(const char *path)
878 {
879    char *p, *file = NULL;
880 
881    if (!path)
882      return NULL;
883 
884    p = strrchr(path, '.');
885    if (!p)
886      file = strdup(path);
887    else if (p != path)
888      {
889         file = malloc(((p - path) + 1) * sizeof(char));
890         if (file)
891           {
892              memcpy(file, path, (p - path));
893              file[p - path] = 0;
894           }
895      }
896 
897    return file;
898 }
899 
900 EAPI int
901 ecore_file_dir_is_empty(const char *dir)
902 {
903    Eina_File_Direct_Info *info;
904    Eina_Iterator *it;
905 
906    it = eina_file_direct_ls(dir);
907    if (!it) return -1;
908 
909    EINA_ITERATOR_FOREACH(it, info)
910      {
911         eina_iterator_free(it);
912         return 0;
913      }
914 
915    eina_iterator_free(it);
916    return 1;
917 }
918