EFL 1.7 svn doobies
[profile/ivi/eina.git] / src / lib / eina_file_win32.c
1 /* EINA - EFL data type library
2  * Copyright (C) 2010 Vincent Torri
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library;
16  * if not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #ifdef HAVE_ALLOCA_H
24 # include <alloca.h>
25 #elif defined __GNUC__
26 # define alloca __builtin_alloca
27 #elif defined _AIX
28 # define alloca __alloca
29 #elif defined _MSC_VER
30 # include <malloc.h>
31 # define alloca _alloca
32 #else
33 # include <stddef.h>
34 # ifdef  __cplusplus
35 extern "C"
36 # endif
37 void *alloca (size_t);
38 #endif
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42
43 #define WIN32_LEAN_AND_MEAN
44 #include <windows.h>
45 #undef WIN32_LEAN_AND_MEAN
46
47 #include <Evil.h>
48
49 #include "eina_config.h"
50 #include "eina_private.h"
51
52 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
53 #include "eina_safety_checks.h"
54 #include "eina_file.h"
55 #include "eina_stringshare.h"
56 #include "eina_hash.h"
57 #include "eina_list.h"
58 #include "eina_lock.h"
59 #include "eina_log.h"
60
61 /*============================================================================*
62  *                                  Local                                     *
63  *============================================================================*/
64
65 /**
66  * @cond LOCAL
67  */
68
69 #ifndef EINA_LOG_COLOR_DEFAULT
70 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
71 #endif
72
73 #ifdef ERR
74 #undef ERR
75 #endif
76 #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
77
78 #ifdef WRN
79 #undef WRN
80 #endif
81 #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
82
83 #ifdef DBG
84 #undef DBG
85 #endif
86 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
87
88 #ifdef MAP_FAILED
89 # undef MAP_FAILED
90 #endif
91 #define MAP_FAILED ((void *)-1)
92
93 typedef struct _Eina_File_Iterator        Eina_File_Iterator;
94 typedef struct _Eina_Lines_Iterator       Eina_Lines_Iterator;
95 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
96 typedef struct _Eina_File_Map             Eina_File_Map;
97
98 struct _Eina_File_Iterator
99 {
100    Eina_Iterator   iterator;
101
102    WIN32_FIND_DATA data;
103    HANDLE          handle;
104    size_t          length;
105    Eina_Bool       is_last : 1;
106
107    char            dir[1];
108 };
109
110 struct _Eina_Lines_Iterator
111 {
112    Eina_Iterator iterator;
113
114    Eina_File *fp;
115    const char *map;
116    const char *end;
117
118    int boundary;
119
120    Eina_File_Line current;
121 };
122
123 struct _Eina_File_Direct_Iterator
124 {
125    Eina_Iterator         iterator;
126
127    WIN32_FIND_DATA       data;
128    HANDLE                handle;
129    size_t                length;
130    Eina_Bool             is_last : 1;
131
132    Eina_File_Direct_Info info;
133
134    char                  dir[1];
135 };
136
137 struct _Eina_File
138 {
139    const char *filename;
140
141    Eina_Hash *map;
142    Eina_Hash *rmap;
143    void *global_map;
144
145    Eina_Lock lock;
146
147    ULONGLONG length;
148    ULONGLONG mtime;
149
150    int refcount;
151    int global_refcount;
152
153    HANDLE handle;
154    HANDLE fm;
155
156    Eina_Bool shared : 1;
157    Eina_Bool delete_me : 1;
158 };
159
160 struct _Eina_File_Map
161 {
162    void *map;
163
164    unsigned long int offset;
165    unsigned long int length;
166
167    int refcount;
168 };
169
170 static Eina_Hash *_eina_file_cache = NULL;
171 static Eina_Lock _eina_file_lock_cache;
172
173 static int _eina_file_log_dom = -1;
174
175 static Eina_Bool
176 _eina_file_win32_is_dir(const char *dir)
177 {
178 #ifdef UNICODE
179    wchar_t *wdir = NULL;
180 #endif
181    DWORD    attr;
182
183    /* check if it's a directory */
184 #ifdef UNICODE
185    wdir = evil_char_to_wchar(dir);
186    if (!wdir)
187      return EINA_FALSE;
188
189    attr = GetFileAttributes(wdir);
190    free(wdir);
191 #else
192    attr = GetFileAttributes(dir);
193 #endif
194
195    if (attr == 0xFFFFFFFF)
196      return EINA_FALSE;
197
198    if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
199      return EINA_FALSE;
200
201    return EINA_TRUE;
202 }
203
204 static char *
205 _eina_file_win32_dir_new(const char *dir)
206 {
207    char *new_dir;
208    size_t length;
209
210    length = strlen(dir);
211
212    new_dir = (char *)malloc(sizeof(char) * length + 5);
213    if (!new_dir)
214      return NULL;
215
216    memcpy(new_dir, dir, length);
217    memcpy(new_dir + length, "\\*.*", 5);
218
219    return new_dir;
220 }
221
222 static HANDLE
223 _eina_file_win32_first_file(const char *dir, WIN32_FIND_DATA *fd)
224 {
225   HANDLE h;
226 #ifdef UNICODE
227    wchar_t *wdir = NULL;
228
229    wdir = evil_char_to_wchar(dir);
230    if (!wdir)
231      return NULL;
232
233    h = FindFirstFile(wdir, fd);
234    free(wdir);
235 #else
236    h = FindFirstFile(dir, fd);
237 #endif
238
239    if (!h)
240      return NULL;
241
242    while ((fd->cFileName[0] == '.') &&
243           ((fd->cFileName[1] == '\0') ||
244            ((fd->cFileName[1] == '.') && (fd->cFileName[2] == '\0'))))
245      {
246         if (!FindNextFile(h, fd))
247           return NULL;
248      }
249
250    return h;
251 }
252
253 static Eina_Bool
254 _eina_file_win32_ls_iterator_next(Eina_File_Iterator *it, void **data)
255 {
256 #ifdef UNICODE
257    wchar_t  *old_name;
258 #else
259    char     *old_name;
260 #endif
261    char     *name;
262    char     *cname;
263    size_t    length;
264    Eina_Bool is_last;
265    Eina_Bool res = EINA_TRUE;
266
267    if (it->handle == INVALID_HANDLE_VALUE)
268      return EINA_FALSE;
269
270    is_last = it->is_last;
271 #ifdef UNICODE
272    old_name = _wcsdup(it->data.cFileName);
273 #else
274    old_name = _strdup(it->data.cFileName);
275 #endif
276    if (!old_name)
277      return EINA_FALSE;
278
279    do {
280       if (!FindNextFile(it->handle, &it->data))
281         {
282            if (GetLastError() == ERROR_NO_MORE_FILES)
283              it->is_last = EINA_TRUE;
284            else
285              res = EINA_FALSE;
286         }
287    } while ((it->data.cFileName[0] == '.') &&
288             ((it->data.cFileName[1] == '\0') ||
289              ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
290
291 #ifdef UNICODE
292    cname = evil_wchar_to_char(old_name);
293    if (!cname)
294      return EINA_FALSE;
295 #else
296      cname = old_name;
297 #endif
298
299    length = strlen(cname);
300    name = alloca(length + 2 + it->length);
301
302    memcpy(name,                  it->dir, it->length);
303    memcpy(name + it->length,     "\\",    1);
304    memcpy(name + it->length + 1, cname,   length + 1);
305
306    *data = (char *)eina_stringshare_add(name);
307
308 #ifdef UNICODE
309    free(cname);
310 #endif
311    free(old_name);
312
313    if (is_last)
314      res = EINA_FALSE;
315
316    return res;
317 }
318
319 static HANDLE
320 _eina_file_win32_ls_iterator_container(Eina_File_Iterator *it)
321 {
322    return it->handle;
323 }
324
325 static void
326 _eina_file_win32_ls_iterator_free(Eina_File_Iterator *it)
327 {
328    if (it->handle != INVALID_HANDLE_VALUE)
329      FindClose(it->handle);
330
331    EINA_MAGIC_SET(&it->iterator, 0);
332    free(it);
333 }
334
335 static Eina_Bool
336 _eina_file_win32_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
337 {
338 #ifdef UNICODE
339    wchar_t  *old_name;
340 #else
341    char     *old_name;
342 #endif
343    char     *cname;
344    size_t    length;
345    DWORD     attr;
346    Eina_Bool is_last;
347    Eina_Bool res = EINA_TRUE;
348
349    if (it->handle == INVALID_HANDLE_VALUE)
350      return EINA_FALSE;
351
352    attr = it->data.dwFileAttributes;
353    is_last = it->is_last;
354 #ifdef UNICODE
355    old_name = _wcsdup(it->data.cFileName);
356 #else
357    old_name = _strdup(it->data.cFileName);
358 #endif
359    if (!old_name)
360      return EINA_FALSE;
361
362    do {
363       if (!FindNextFile(it->handle, &it->data))
364         {
365            if (GetLastError() == ERROR_NO_MORE_FILES)
366              it->is_last = EINA_TRUE;
367            else
368              res = EINA_FALSE;
369         }
370
371 #ifdef UNICODE
372      length = wcslen(old_name);
373 #else
374      length = strlen(old_name);
375 #endif
376      if (it->info.name_start + length + 1 >= PATH_MAX)
377        {
378           free(old_name);
379 #ifdef UNICODE
380           old_name = _wcsdup(it->data.cFileName);
381 #else
382           old_name = _strdup(it->data.cFileName);
383 #endif
384           continue;
385        }
386
387    } while ((it->data.cFileName[0] == '.') &&
388             ((it->data.cFileName[1] == '\0') ||
389              ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
390
391 #ifdef UNICODE
392    cname = evil_wchar_to_char(old_name);
393    if (!cname)
394      return EINA_FALSE;
395 #else
396      cname = old_name;
397 #endif
398
399    memcpy(it->info.path + it->info.name_start, cname, length);
400    it->info.name_length = length;
401    it->info.path_length = it->info.name_start + length;
402    it->info.path[it->info.path_length] = '\0';
403
404    if (attr & FILE_ATTRIBUTE_DIRECTORY)
405      it->info.type = EINA_FILE_DIR;
406    else if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
407      it->info.type = EINA_FILE_LNK;
408    else if (attr & (FILE_ATTRIBUTE_ARCHIVE |
409                     FILE_ATTRIBUTE_COMPRESSED |
410                     FILE_ATTRIBUTE_COMPRESSED |
411                     FILE_ATTRIBUTE_HIDDEN |
412                     FILE_ATTRIBUTE_NORMAL |
413                     FILE_ATTRIBUTE_SPARSE_FILE |
414                     FILE_ATTRIBUTE_TEMPORARY))
415      it->info.type = EINA_FILE_REG;
416    else
417      it->info.type = EINA_FILE_UNKNOWN;
418
419    *data = &it->info;
420
421 #ifdef UNICODE
422    free(cname);
423 #endif
424
425    free(old_name);
426
427    if (is_last)
428      res = EINA_FALSE;
429
430    return res;
431 }
432
433 static HANDLE
434 _eina_file_win32_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
435 {
436    return it->handle;
437 }
438
439 static void
440 _eina_file_win32_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
441 {
442    if (it->handle != INVALID_HANDLE_VALUE)
443      FindClose(it->handle);
444
445    EINA_MAGIC_SET(&it->iterator, 0);
446    free(it);
447 }
448
449 static void
450 _eina_file_real_close(Eina_File *file)
451 {
452    eina_hash_free(file->rmap);
453    eina_hash_free(file->map);
454
455    if (file->global_map != MAP_FAILED)
456      UnmapViewOfFile(file->global_map);
457
458    CloseHandle(file->fm);
459    CloseHandle(file->handle);
460
461    free(file);
462 }
463
464 static void
465 _eina_file_map_close(Eina_File_Map *map)
466 {
467    if (map->map != MAP_FAILED)
468      UnmapViewOfFile(map->map);
469    free(map);
470 }
471
472 static unsigned int
473 _eina_file_map_key_length(const void *key __UNUSED__)
474 {
475    return sizeof (unsigned long int) * 2;
476 }
477
478 static int
479 _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
480                        const unsigned long int *key2, int key2_length __UNUSED__)
481 {
482    if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
483    return key1[0] - key2[0];
484 }
485
486 static int
487 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
488 {
489    return eina_hash_int64(&key[0], sizeof (unsigned long int))
490      ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
491 }
492
493 static char *
494 _eina_file_win32_escape(const char *path, size_t *length)
495 {
496    char *result;
497    char *p;
498    char *q;
499    size_t len;
500
501    result = strdup(path ? path : "");
502    if (!result)
503      return NULL;
504
505    p = result;
506    while (*p)
507      {
508        if (*p == '\\') *p = '/';
509        p++;
510      }
511    p = result;
512    q = result;
513
514    if (!result)
515      return NULL;
516
517    if (length) len = *length;
518    else len = strlen(result);
519
520    while ((p = strchr(p, '/')))
521      {
522         // remove double `/'
523         if (p[1] == '/')
524           {
525              memmove(p, p + 1, --len - (p - result));
526              result[len] = '\0';
527           }
528         else
529           if (p[1] == '.'
530               && p[2] == '.')
531             {
532                // remove `/../'
533                if (p[3] == '/')
534                  {
535                     char tmp;
536
537                     len -= p + 3 - q;
538                     memmove(q, p + 3, len - (q - result));
539                     result[len] = '\0';
540                     p = q;
541
542                     /* Update q correctly. */
543                     tmp = *p;
544                     *p = '\0';
545                     q = strrchr(result, '/');
546                     if (!q) q = result;
547                     *p = tmp;
548                  }
549                else
550                  // remove '/..$'
551                  if (p[3] == '\0')
552                    {
553                       len -= p + 2 - q;
554                       result[len] = '\0';
555                       q = p;
556                       ++p;
557                    }
558                  else
559                    {
560                       q = p;
561                       ++p;
562                    }
563             }
564           else
565             {
566                q = p;
567                ++p;
568             }
569      }
570
571    if (length)
572      *length = len;
573
574    return result;
575 }
576
577 /* search '\r' and '\n' by preserving cache locality and page locality
578    in doing a search inside 4K boundary.
579  */
580 static inline const char *
581 _eina_fine_eol(const char *start, int boundary, const char *end)
582 {
583    const char *cr;
584    const char *lf;
585    unsigned long long chunk;
586
587    while (start < end)
588      {
589         chunk = start + boundary < end ? boundary : end - start;
590         cr = memchr(start, '\r', chunk);
591         lf = memchr(start, '\n', chunk);
592         if (cr)
593           {
594              if (lf && lf < cr)
595                return lf + 1;
596              return cr + 1;
597           }
598         else if (lf)
599            return lf + 1;
600
601         start += chunk;
602         boundary = 4096;
603      }
604
605    return end;
606 }
607
608 static Eina_Bool
609 _eina_file_map_lines_iterator_next(Eina_Lines_Iterator *it, void **data)
610 {
611    const char *eol;
612    unsigned char match;
613
614    if (it->current.end >= it->end)
615      return EINA_FALSE;
616
617    match = *it->current.end;
618    while ((*it->current.end == '\n' || *it->current.end == '\r')
619           && it->current.end < it->end)
620      {
621         if (match == *it->current.end)
622           it->current.index++;
623         it->current.end++;
624      }
625    it->current.index++;
626
627    if (it->current.end == it->end)
628      return EINA_FALSE;
629
630    eol = _eina_fine_eol(it->current.end,
631                         it->boundary,
632                         it->end);
633    it->boundary = (uintptr_t) eol & 0x3FF;
634    if (it->boundary == 0) it->boundary = 4096;
635
636    it->current.start = it->current.end;
637
638    it->current.end = eol;
639    it->current.length = eol - it->current.start - 1;
640
641    *data = &it->current;
642    return EINA_TRUE;
643 }
644
645 static Eina_File *
646 _eina_file_map_lines_iterator_container(Eina_Lines_Iterator *it)
647 {
648    return it->fp;
649 }
650
651 static void
652 _eina_file_map_lines_iterator_free(Eina_Lines_Iterator *it)
653 {
654    eina_file_map_free(it->fp, (void*) it->map);
655    eina_file_close(it->fp);
656
657    EINA_MAGIC_SET(&it->iterator, 0);
658    free(it);
659 }
660
661
662 /**
663  * @endcond
664  */
665
666 /*============================================================================*
667  *                                 Global                                     *
668  *============================================================================*/
669
670 Eina_Bool
671 eina_file_init(void)
672 {
673    _eina_file_log_dom = eina_log_domain_register("eina_file",
674                                                  EINA_LOG_COLOR_DEFAULT);
675    if (_eina_file_log_dom < 0)
676      {
677         EINA_LOG_ERR("Could not register log domain: eina_file");
678         return EINA_FALSE;
679      }
680
681    _eina_file_cache = eina_hash_string_djb2_new(NULL);
682    if (!_eina_file_cache)
683      {
684         ERR("Could not create cache.");
685         eina_log_domain_unregister(_eina_file_log_dom);
686         _eina_file_log_dom = -1;
687         return EINA_FALSE;
688      }
689
690    eina_lock_new(&_eina_file_lock_cache);
691
692    return EINA_TRUE;
693 }
694
695 Eina_Bool
696 eina_file_shutdown(void)
697 {
698    if (eina_hash_population(_eina_file_cache) > 0)
699      {
700         Eina_Iterator *it;
701         const char *key;
702
703         it = eina_hash_iterator_key_new(_eina_file_cache);
704         EINA_ITERATOR_FOREACH(it, key)
705           ERR("File [%s] still open !", key);
706         eina_iterator_free(it);
707      }
708
709    eina_hash_free(_eina_file_cache);
710
711    eina_lock_free(&_eina_file_lock_cache);
712
713    eina_log_domain_unregister(_eina_file_log_dom);
714    _eina_file_log_dom = -1;
715    return EINA_TRUE;
716 }
717
718 /*============================================================================*
719  *                                   API                                      *
720  *============================================================================*/
721
722
723 EAPI char *
724 eina_file_path_sanitize(const char *path)
725 {
726    char *result = NULL;
727    size_t len;
728
729    if (!path) return NULL;
730
731    len = strlen(path);
732
733    if (!evil_path_is_absolute(path))
734      {
735         DWORD l;
736
737         l = GetCurrentDirectory(0, NULL);
738         if (l > 0)
739           {
740              char *cwd;
741              char *tmp;
742
743              cwd = alloca(sizeof(char) * (l + 1));
744              GetCurrentDirectory(l + 1, cwd);
745              len += l + 2;
746              tmp = alloca(sizeof (char) * len);
747              snprintf(tmp, len, "%s\\%s", cwd, path);
748              tmp[len - 1] = '\0';
749              result = tmp;
750           }
751      }
752
753    return _eina_file_win32_escape(result ? result : path, &len);
754 }
755
756 EAPI Eina_Bool
757 eina_file_dir_list(const char *dir,
758                    Eina_Bool recursive,
759                    Eina_File_Dir_List_Cb cb,
760                    void *data)
761 {
762    WIN32_FIND_DATA file;
763    HANDLE h;
764    char *new_dir;
765
766    EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
767    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
768    EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
769
770    if (!_eina_file_win32_is_dir(dir))
771      return EINA_FALSE;
772
773    new_dir = _eina_file_win32_dir_new(dir);
774    if (!new_dir)
775       return EINA_FALSE;
776
777    h = _eina_file_win32_first_file(new_dir, &file);
778
779    if (h == INVALID_HANDLE_VALUE)
780       return EINA_FALSE;
781
782    do
783      {
784         char *filename;
785
786 # ifdef UNICODE
787         filename = evil_wchar_to_char(file.cFileName);
788 # else
789         filename = file.cFileName;
790 # endif /* ! UNICODE */
791         if (!strcmp(filename, ".") || !strcmp(filename, ".."))
792            continue;
793
794         cb(filename, dir, data);
795
796         if (recursive == EINA_TRUE)
797           {
798              char *path;
799
800              path = alloca(strlen(dir) + strlen(filename) + 2);
801              strcpy(path, dir);
802              strcat(path, "/");
803              strcat(path, filename);
804
805              if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
806                 continue;
807
808              eina_file_dir_list(path, recursive, cb, data);
809           }
810
811 # ifdef UNICODE
812         free(filename);
813 # endif /* UNICODE */
814
815      } while (FindNextFile(h, &file));
816    FindClose(h);
817
818    return EINA_TRUE;
819 }
820
821 EAPI Eina_Array *
822 eina_file_split(char *path)
823 {
824    Eina_Array *ea;
825    char *current;
826    size_t length;
827
828    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
829
830    ea = eina_array_new(16);
831
832    if (!ea)
833       return NULL;
834
835    current = path;
836    while (*current)
837      {
838         if ((*current == '\\') || (*current == '/'))
839           {
840              if (((*current == '\\') && (current[1] == '\\')) ||
841                  ((*current == '/') && (current[1] == '/')))
842                {
843                   *current = '\0';
844                   goto next_char;
845                }
846
847              length = current - path;
848              if (length <= 0)
849                goto next_char;
850
851              eina_array_push(ea, path);
852              *current = '\0';
853              path = current + 1;
854           }
855      next_char:
856         current++;
857      }
858
859    if (*path != '\0')
860         eina_array_push(ea, path);
861
862    return ea;
863 }
864
865 EAPI Eina_Iterator *
866 eina_file_ls(const char *dir)
867 {
868    Eina_File_Iterator *it;
869    char               *new_dir;
870    size_t              length;
871
872    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
873
874    if (!dir || !*dir)
875       return NULL;
876
877    if (!_eina_file_win32_is_dir(dir))
878      return NULL;
879
880    length = strlen(dir);
881
882    it = calloc(1, sizeof (Eina_File_Iterator) + length);
883    if (!it)
884       return NULL;
885
886    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
887
888    new_dir = _eina_file_win32_dir_new(dir);
889    if (!new_dir)
890       goto free_it;
891
892    it->handle = _eina_file_win32_first_file(new_dir, &it->data);
893    free(new_dir);
894    if (it->handle == INVALID_HANDLE_VALUE)
895      goto free_it;
896
897    memcpy(it->dir, dir, length + 1);
898    if ((dir[length - 1] != '\\') && (dir[length - 1] != '/'))
899       it->length = length;
900    else
901       it->length = length - 1;
902
903    it->iterator.version = EINA_ITERATOR_VERSION;
904    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_ls_iterator_next);
905    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_ls_iterator_container);
906    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_ls_iterator_free);
907
908    return &it->iterator;
909
910  free_it:
911    free(it);
912
913    return NULL;
914 }
915
916 EAPI Eina_Iterator *
917 eina_file_direct_ls(const char *dir)
918 {
919    Eina_File_Direct_Iterator *it;
920    char                      *new_dir;
921    size_t                     length;
922
923    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
924
925    if (!dir || !*dir)
926       return NULL;
927
928    length = strlen(dir);
929
930    if (length + 12 + 2 >= MAX_PATH)
931       return NULL;
932
933    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
934    if (!it)
935       return NULL;
936
937    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
938
939    new_dir = _eina_file_win32_dir_new(dir);
940    if (!new_dir)
941       goto free_it;
942
943    it->handle = _eina_file_win32_first_file(new_dir, &it->data);
944    free(new_dir);
945    if (it->handle == INVALID_HANDLE_VALUE)
946      goto free_it;
947
948    memcpy(it->dir, dir, length + 1);
949    it->length = length;
950
951    memcpy(it->info.path, dir, length);
952    if ((dir[length - 1] == '\\') || (dir[length - 1] == '/'))
953       it->info.name_start = length;
954    else
955      {
956         it->info.path[length] = '\\';
957         it->info.name_start = length + 1;
958      }
959
960    it->iterator.version = EINA_ITERATOR_VERSION;
961    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_direct_ls_iterator_next);
962    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_direct_ls_iterator_container);
963    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_direct_ls_iterator_free);
964
965    return &it->iterator;
966
967  free_it:
968    free(it);
969
970    return NULL;
971 }
972
973 EAPI Eina_Iterator *
974 eina_file_stat_ls(const char *dir)
975 {
976    return eina_file_direct_ls(dir);
977 }
978
979 EAPI Eina_File *
980 eina_file_open(const char *path, Eina_Bool shared)
981 {
982    Eina_File *file;
983    Eina_File *n;
984    char *filename;
985    HANDLE handle;
986    HANDLE fm;
987    WIN32_FILE_ATTRIBUTE_DATA fad;
988    ULARGE_INTEGER length;
989    ULARGE_INTEGER mtime;
990
991    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
992
993    filename = eina_file_path_sanitize(path);
994    if (!filename) return NULL;
995
996    /* FIXME: how to emulate shm_open ? Just OpenFileMapping ? */
997 #if 0
998    if (shared)
999      handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
1000                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
1001                          NULL);
1002    else
1003 #endif
1004      handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
1005                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
1006                          NULL);
1007
1008    if (handle == INVALID_HANDLE_VALUE)
1009      return NULL;
1010
1011    fm = CreateFileMapping(handle, NULL, PAGE_READONLY, 0, 0, NULL);
1012    if (!fm)
1013      goto close_handle;
1014
1015    if (!GetFileAttributesEx(filename, GetFileExInfoStandard, &fad))
1016      goto close_fm;
1017
1018    length.u.LowPart = fad.nFileSizeLow;
1019    length.u.HighPart = fad.nFileSizeHigh;
1020    mtime.u.LowPart = fad.ftLastWriteTime.dwLowDateTime;
1021    mtime.u.HighPart = fad.ftLastWriteTime.dwHighDateTime;
1022
1023    eina_lock_take(&_eina_file_lock_cache);
1024
1025    file = eina_hash_find(_eina_file_cache, filename);
1026    if (file &&
1027        (file->mtime != mtime.QuadPart || file->length != length.QuadPart))
1028      {
1029         file->delete_me = EINA_TRUE;
1030         eina_hash_del(_eina_file_cache, file->filename, file);
1031         _eina_file_real_close(file);
1032         file = NULL;
1033      }
1034
1035    if (!file)
1036      {
1037         n = malloc(sizeof(Eina_File) + strlen(filename) + 1);
1038         if (!n)
1039           {
1040              eina_lock_release(&_eina_file_lock_cache);
1041              goto close_fm;
1042           }
1043
1044         memset(n, 0, sizeof(Eina_File));
1045         n->filename = (char*) (n + 1);
1046         strcpy((char*) n->filename, filename);
1047         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
1048                                EINA_KEY_CMP(_eina_file_map_key_cmp),
1049                                EINA_KEY_HASH(_eina_file_map_key_hash),
1050                                EINA_FREE_CB(_eina_file_map_close),
1051                                3);
1052         n->rmap = eina_hash_pointer_new(NULL);
1053         n->global_map = MAP_FAILED;
1054         n->length = length.QuadPart;
1055         n->mtime = mtime.QuadPart;
1056         n->handle = handle;
1057         n->fm = fm;
1058         n->shared = shared;
1059         eina_lock_new(&n->lock);
1060         eina_hash_direct_add(_eina_file_cache, n->filename, n);
1061      }
1062    else
1063      {
1064         CloseHandle(fm);
1065         CloseHandle(handle);
1066
1067         n = file;
1068      }
1069    eina_lock_take(&n->lock);
1070    n->refcount++;
1071    eina_lock_release(&n->lock);
1072
1073    eina_lock_release(&_eina_file_lock_cache);
1074
1075    free(filename);
1076
1077    return n;
1078
1079  close_fm:
1080    CloseHandle(fm);
1081  close_handle:
1082    CloseHandle(handle);
1083
1084    return NULL;
1085 }
1086
1087 EAPI void
1088 eina_file_close(Eina_File *file)
1089 {
1090    EINA_SAFETY_ON_NULL_RETURN(file);
1091
1092    eina_lock_take(&file->lock);
1093    file->refcount--;
1094    eina_lock_release(&file->lock);
1095
1096    if (file->refcount != 0) return ;
1097    eina_lock_take(&_eina_file_lock_cache);
1098
1099    eina_hash_del(_eina_file_cache, file->filename, file);
1100    _eina_file_real_close(file);
1101
1102    eina_lock_release(&_eina_file_lock_cache);
1103 }
1104
1105 EAPI size_t
1106 eina_file_size_get(Eina_File *file)
1107 {
1108    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1109    return file->length;
1110 }
1111
1112 EAPI time_t
1113 eina_file_mtime_get(Eina_File *file)
1114 {
1115    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1116   return file->mtime;
1117 }
1118
1119 EAPI const char *
1120 eina_file_filename_get(Eina_File *file)
1121 {
1122    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1123    return file->filename;
1124 }
1125
1126 EAPI Eina_Iterator *eina_file_xattr_get(Eina_File *file __UNUSED__)
1127 {
1128    return NULL;
1129 }
1130
1131 EAPI Eina_Iterator *eina_file_xattr_value_get(Eina_File *file __UNUSED__)
1132 {
1133    return NULL;
1134 }
1135
1136 EAPI void *
1137 eina_file_map_all(Eina_File *file, Eina_File_Populate rule __UNUSED__)
1138 {
1139    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1140
1141    eina_lock_take(&file->lock);
1142    if (file->global_map == MAP_FAILED)
1143      {
1144         void  *data;
1145
1146         data = MapViewOfFile(file->fm, FILE_MAP_READ,
1147                              0, 0, file->length);
1148         if (!data)
1149           file->global_map = MAP_FAILED;
1150         else
1151           file->global_map = data;
1152      }
1153
1154    if (file->global_map != MAP_FAILED)
1155      {
1156         file->global_refcount++;
1157         return file->global_map;
1158      }
1159
1160    eina_lock_release(&file->lock);
1161    return NULL;
1162 }
1163
1164 EAPI Eina_Iterator *
1165 eina_file_map_lines(Eina_File *file)
1166 {
1167    Eina_Lines_Iterator *it;
1168
1169    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1170
1171    if (file->length == 0) return NULL;
1172
1173    it = calloc(1, sizeof (Eina_Lines_Iterator));
1174    if (!it) return NULL;
1175
1176    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
1177
1178    it->map = eina_file_map_all(file, EINA_FILE_SEQUENTIAL);
1179    if (!it->map)
1180      {
1181         free(it);
1182         return NULL;
1183      }
1184
1185    eina_lock_take(&file->lock);
1186    file->refcount++;
1187    eina_lock_release(&file->lock);
1188
1189    it->fp = file;
1190    it->boundary = 4096;
1191    it->current.start = it->map;
1192    it->current.end = it->current.start;
1193    it->current.index = 0;
1194    it->current.length = 0;
1195    it->end = it->map + it->fp->length;
1196
1197    it->iterator.version = EINA_ITERATOR_VERSION;
1198    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_map_lines_iterator_next);
1199    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_map_lines_iterator_container);
1200    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_map_lines_iterator_free);
1201
1202    return &it->iterator;
1203 }
1204
1205 EAPI void *
1206 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
1207                   unsigned long int offset, unsigned long int length)
1208 {
1209    Eina_File_Map *map;
1210    unsigned long int key[2];
1211
1212    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1213
1214    if (offset > file->length)
1215      return NULL;
1216    if (offset + length > file->length)
1217      return NULL;
1218
1219    if (offset == 0 && length == file->length)
1220      return eina_file_map_all(file, rule);
1221
1222    key[0] = offset;
1223    key[1] = length;
1224
1225    eina_lock_take(&file->lock);
1226
1227    map = eina_hash_find(file->map, &key);
1228    if (!map)
1229      {
1230         void  *data;
1231
1232         map = malloc(sizeof (Eina_File_Map));
1233         if (!map)
1234           {
1235              eina_lock_release(&file->lock);
1236              return NULL;
1237           }
1238
1239         data = MapViewOfFile(file->fm, FILE_MAP_READ,
1240                              offset & 0xffff0000,
1241                              offset & 0x0000ffff,
1242                              length);
1243         if (!data)
1244           map->map = MAP_FAILED;
1245         else
1246           map->map = data;
1247
1248         map->offset = offset;
1249         map->length = length;
1250         map->refcount = 0;
1251
1252         if (map->map == MAP_FAILED)
1253           {
1254              free(map);
1255              eina_lock_release(&file->lock);
1256              return NULL;
1257           }
1258
1259         eina_hash_add(file->map, &key, map);
1260         eina_hash_direct_add(file->rmap, map->map, map);
1261      }
1262
1263    map->refcount++;
1264
1265    eina_lock_release(&file->lock);
1266
1267    return map->map;
1268 }
1269
1270 EAPI void
1271 eina_file_map_free(Eina_File *file, void *map)
1272 {
1273    EINA_SAFETY_ON_NULL_RETURN(file);
1274
1275    eina_lock_take(&file->lock);
1276
1277    if (file->global_map == map)
1278      {
1279         file->global_refcount--;
1280
1281         if (file->global_refcount > 0) goto on_exit;
1282
1283         UnmapViewOfFile(file->global_map);
1284         file->global_map = MAP_FAILED;
1285      }
1286    else
1287      {
1288         Eina_File_Map *em;
1289         unsigned long int key[2];
1290
1291         em = eina_hash_find(file->rmap, &map);
1292         if (!em) goto on_exit;
1293
1294         em->refcount--;
1295
1296         if (em->refcount > 0) goto on_exit;
1297
1298         key[0] = em->offset;
1299         key[1] = em->length;
1300
1301         eina_hash_del(file->rmap, &map, em);
1302         eina_hash_del(file->map, &key, em);
1303      }
1304
1305  on_exit:
1306    eina_lock_release(&file->lock);
1307 }
1308
1309 EAPI Eina_Bool
1310 eina_file_map_faulted(Eina_File *file, void *map)
1311 {
1312   /*
1313    * FIXME:
1314    * vc++ : http://msdn.microsoft.com/en-us/library/windows/desktop/aa366801%28v=vs.85%29.aspx
1315    *
1316    * mingw-w64 :
1317    * - 32 bits : there is a way to implement __try/__except/__final in C.
1318    *   see excpt.h header for 32-bits
1319    * - 64 bits : some inline assembly required for it.  See as example our
1320    *   startup-code in WinMainCRTStartup() in crtexe.c :
1321 {
1322   int ret = 255;
1323 #ifdef __SEH__
1324   asm ("\t.l_startw:\n"
1325     "\t.seh_handler __C_specific_handler, @except\n"
1326     "\t.seh_handlerdata\n"
1327     "\t.long 1\n"
1328     "\t.rva .l_startw, .l_endw, _gnu_exception_handler ,.l_endw\n"
1329     "\t.text"
1330     );
1331 #endif
1332   mingw_app_type = 1;
1333   __security_init_cookie ();
1334   ret = __tmainCRTStartup ();
1335 #ifdef __SEH__
1336   asm ("\tnop\n"
1337     "\t.l_endw: nop\n");
1338 #endif
1339   return ret;
1340 }
1341    */
1342    return EINA_FALSE;
1343 }
1344
1345 EAPI int
1346 eina_file_statat(void *container __UNUSED__, Eina_File_Direct_Info *info, Eina_Stat *st)
1347 {
1348    struct __stat64 buf;
1349
1350    EINA_SAFETY_ON_NULL_RETURN_VAL(info, -1);
1351    EINA_SAFETY_ON_NULL_RETURN_VAL(st, -1);
1352
1353    if (stat64(info->path, &buf))
1354      {
1355         if (info->type != EINA_FILE_LNK)
1356           info->type = EINA_FILE_UNKNOWN;
1357         return -1;
1358      }
1359
1360    if (info->type == EINA_FILE_UNKNOWN)
1361      {
1362         if (S_ISREG(buf.st_mode))
1363           info->type = EINA_FILE_REG;
1364         else if (S_ISDIR(buf.st_mode))
1365           info->type = EINA_FILE_DIR;
1366         else
1367           info->type = EINA_FILE_UNKNOWN;
1368      }
1369
1370    st->dev = buf.st_dev;
1371    st->ino = buf.st_ino;
1372    st->mode = buf.st_mode;
1373    st->nlink = buf.st_nlink;
1374    st->uid = buf.st_uid;
1375    st->gid = buf.st_gid;
1376    st->rdev = buf.st_rdev;
1377    st->size = buf.st_size;
1378    st->blksize = 0;
1379    st->blocks = 0;
1380    st->atime = buf.st_atime;
1381    st->mtime = buf.st_mtime;
1382    st->ctime = buf.st_ctime;
1383    st->atimensec = 0;
1384    st->mtimensec = 0;
1385    st->ctimensec = 0;
1386
1387    return 0;
1388 }