fix logic
[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 #define WIN32_LEAN_AND_MEAN
41 #include <windows.h>
42 #undef WIN32_LEAN_AND_MEAN
43
44 #include <Evil.h>
45
46 #include "eina_config.h"
47 #include "eina_private.h"
48
49 /* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
50 #include "eina_safety_checks.h"
51 #include "eina_file.h"
52 #include "eina_stringshare.h"
53 #include "eina_hash.h"
54 #include "eina_list.h"
55 #include "eina_lock.h"
56 #include "eina_log.h"
57
58 /*============================================================================*
59  *                                  Local                                     *
60  *============================================================================*/
61
62 /**
63  * @cond LOCAL
64  */
65
66 #ifndef EINA_LOG_COLOR_DEFAULT
67 #define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
68 #endif
69
70 #ifdef ERR
71 #undef ERR
72 #endif
73 #define ERR(...) EINA_LOG_DOM_ERR(_eina_file_log_dom, __VA_ARGS__)
74
75 #ifdef WRN
76 #undef WRN
77 #endif
78 #define WRN(...) EINA_LOG_DOM_WARN(_eina_file_log_dom, __VA_ARGS__)
79
80 #ifdef DBG
81 #undef DBG
82 #endif
83 #define DBG(...) EINA_LOG_DOM_DBG(_eina_file_log_dom, __VA_ARGS__)
84
85 #ifdef MAP_FAILED
86 # undef MAP_FAILED
87 #endif
88 #define MAP_FAILED ((void *)-1)
89
90 typedef struct _Eina_File_Iterator        Eina_File_Iterator;
91 typedef struct _Eina_File_Direct_Iterator Eina_File_Direct_Iterator;
92 typedef struct _Eina_File_Map             Eina_File_Map;
93
94 struct _Eina_File_Iterator
95 {
96    Eina_Iterator   iterator;
97
98    WIN32_FIND_DATA data;
99    HANDLE          handle;
100    size_t          length;
101    Eina_Bool       is_last : 1;
102
103    char            dir[1];
104 };
105
106 struct _Eina_File_Direct_Iterator
107 {
108    Eina_Iterator         iterator;
109
110    WIN32_FIND_DATA       data;
111    HANDLE                handle;
112    size_t                length;
113    Eina_Bool             is_last : 1;
114
115    Eina_File_Direct_Info info;
116
117    char                  dir[1];
118 };
119
120 struct _Eina_File
121 {
122    const char *filename;
123
124    Eina_Hash *map;
125    Eina_Hash *rmap;
126    void *global_map;
127
128    Eina_Lock lock;
129
130    ULONGLONG length;
131    ULONGLONG mtime;
132
133    int refcount;
134    int global_refcount;
135
136    HANDLE handle;
137    HANDLE fm;
138
139    Eina_Bool shared : 1;
140    Eina_Bool delete_me : 1;
141 };
142
143 struct _Eina_File_Map
144 {
145    void *map;
146
147    unsigned long int offset;
148    unsigned long int length;
149
150    int refcount;
151 };
152
153 static Eina_Hash *_eina_file_cache = NULL;
154 static Eina_Lock _eina_file_lock_cache;
155
156 static int _eina_file_log_dom = -1;
157
158 static void
159 _eina_file_win32_backslash_change(char *dir)
160 {
161    char *tmp;
162
163    tmp = dir;
164    while (*tmp)
165      {
166         if (*tmp == '/') *tmp = '\\';
167         tmp++;
168      }
169 }
170
171 static Eina_Bool
172 _eina_file_win32_is_dir(const char *dir)
173 {
174 #ifdef UNICODE
175    wchar_t *wdir = NULL;
176 #endif
177    DWORD    attr;
178
179    /* check if it's a directory */
180 #ifdef UNICODE
181    wdir = evil_char_to_wchar(dir);
182    if (!wdir)
183      return EINA_FALSE;
184
185    attr = GetFileAttributes(wdir);
186    free(wdir);
187 #else
188    attr = GetFileAttributes(dir);
189 #endif
190
191    if (attr == 0xFFFFFFFF)
192      return EINA_FALSE;
193
194    if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
195      return EINA_FALSE;
196
197    return EINA_TRUE;
198 }
199
200 static char *
201 _eina_file_win32_dir_new(const char *dir)
202 {
203    char *new_dir;
204    size_t length;
205
206    length = strlen(dir);
207
208    new_dir = (char *)malloc(sizeof(char) * length + 5);
209    if (!new_dir)
210      return NULL;
211
212    memcpy(new_dir, dir, length);
213    memcpy(new_dir + length, "\\*.*", 5);
214    _eina_file_win32_backslash_change(new_dir);
215
216    return new_dir;
217 }
218
219 static HANDLE
220 _eina_file_win32_first_file(const char *dir, WIN32_FIND_DATA *fd)
221 {
222   HANDLE h;
223 #ifdef UNICODE
224    wchar_t *wdir = NULL;
225
226    wdir = evil_char_to_wchar(dir);
227    if (!wdir)
228      return NULL;
229
230    h = FindFirstFile(wdir, fd);
231    free(wdir);
232 #else
233    h = FindFirstFile(dir, fd);
234 #endif
235
236    if (!h)
237      return NULL;
238
239    while ((fd->cFileName[0] == '.') &&
240           ((fd->cFileName[1] == '\0') ||
241            ((fd->cFileName[1] == '.') && (fd->cFileName[2] == '\0'))))
242      {
243         if (!FindNextFile(h, fd))
244           return NULL;
245      }
246
247    return h;
248 }
249
250 static Eina_Bool
251 _eina_file_win32_ls_iterator_next(Eina_File_Iterator *it, void **data)
252 {
253 #ifdef UNICODE
254    wchar_t  *old_name;
255 #else
256    char     *old_name;
257 #endif
258    char     *name;
259    char     *cname;
260    size_t    length;
261    Eina_Bool is_last;
262    Eina_Bool res = EINA_TRUE;
263
264    if (it->handle == INVALID_HANDLE_VALUE)
265      return EINA_FALSE;
266
267    is_last = it->is_last;
268 #ifdef UNICODE
269    old_name = _wcsdup(it->data.cFileName);
270 #else
271    old_name = _strdup(it->data.cFileName);
272 #endif
273    if (!old_name)
274      return EINA_FALSE;
275
276    do {
277       if (!FindNextFile(it->handle, &it->data))
278         {
279            if (GetLastError() == ERROR_NO_MORE_FILES)
280              it->is_last = EINA_TRUE;
281            else
282              res = EINA_FALSE;
283         }
284    } while ((it->data.cFileName[0] == '.') &&
285             ((it->data.cFileName[1] == '\0') ||
286              ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
287
288 #ifdef UNICODE
289    cname = evil_wchar_to_char(old_name);
290    if (!cname)
291      return EINA_FALSE;
292 #else
293      cname = old_name;
294 #endif
295
296    length = strlen(cname);
297    name = alloca(length + 2 + it->length);
298
299    memcpy(name,                  it->dir, it->length);
300    memcpy(name + it->length,     "\\",    1);
301    memcpy(name + it->length + 1, cname,   length + 1);
302
303    *data = (char *)eina_stringshare_add(name);
304
305 #ifdef UNICODE
306    free(cname);
307 #endif
308    free(old_name);
309
310    if (is_last)
311      res = EINA_FALSE;
312
313    return res;
314 }
315
316 static HANDLE
317 _eina_file_win32_ls_iterator_container(Eina_File_Iterator *it)
318 {
319    return it->handle;
320 }
321
322 static void
323 _eina_file_win32_ls_iterator_free(Eina_File_Iterator *it)
324 {
325    if (it->handle != INVALID_HANDLE_VALUE)
326      FindClose(it->handle);
327
328    EINA_MAGIC_SET(&it->iterator, 0);
329    free(it);
330 }
331
332 static Eina_Bool
333 _eina_file_win32_direct_ls_iterator_next(Eina_File_Direct_Iterator *it, void **data)
334 {
335 #ifdef UNICODE
336    wchar_t  *old_name;
337 #else
338    char     *old_name;
339 #endif
340    char     *cname;
341    size_t    length;
342    DWORD     attr;
343    Eina_Bool is_last;
344    Eina_Bool res = EINA_TRUE;
345
346    if (it->handle == INVALID_HANDLE_VALUE)
347      return EINA_FALSE;
348
349    attr = it->data.dwFileAttributes;
350    is_last = it->is_last;
351 #ifdef UNICODE
352    old_name = _wcsdup(it->data.cFileName);
353 #else
354    old_name = _strdup(it->data.cFileName);
355 #endif
356    if (!old_name)
357      return EINA_FALSE;
358
359    do {
360       if (!FindNextFile(it->handle, &it->data))
361         {
362            if (GetLastError() == ERROR_NO_MORE_FILES)
363              it->is_last = EINA_TRUE;
364            else
365              res = EINA_FALSE;
366         }
367
368 #ifdef UNICODE
369      length = wcslen(old_name);
370 #else
371      length = strlen(old_name);
372 #endif
373      if (it->info.name_start + length + 1 >= PATH_MAX)
374        {
375           free(old_name);
376 #ifdef UNICODE
377           old_name = _wcsdup(it->data.cFileName);
378 #else
379           old_name = _strdup(it->data.cFileName);
380 #endif
381           continue;
382        }
383
384    } while ((it->data.cFileName[0] == '.') &&
385             ((it->data.cFileName[1] == '\0') ||
386              ((it->data.cFileName[1] == '.') && (it->data.cFileName[2] == '\0')))); /* FIXME: what about UNICODE ? */
387
388 #ifdef UNICODE
389    cname = evil_wchar_to_char(old_name);
390    if (!cname)
391      return EINA_FALSE;
392 #else
393      cname = old_name;
394 #endif
395
396    memcpy(it->info.path + it->info.name_start, cname, length);
397    it->info.name_length = length;
398    it->info.path_length = it->info.name_start + length;
399    it->info.path[it->info.path_length] = '\0';
400
401    if (attr & FILE_ATTRIBUTE_DIRECTORY)
402      it->info.type = EINA_FILE_DIR;
403    else if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
404      it->info.type = EINA_FILE_LNK;
405    else if (attr & (FILE_ATTRIBUTE_ARCHIVE |
406                     FILE_ATTRIBUTE_COMPRESSED |
407                     FILE_ATTRIBUTE_COMPRESSED |
408                     FILE_ATTRIBUTE_HIDDEN |
409                     FILE_ATTRIBUTE_NORMAL |
410                     FILE_ATTRIBUTE_SPARSE_FILE |
411                     FILE_ATTRIBUTE_TEMPORARY))
412      it->info.type = EINA_FILE_REG;
413    else
414      it->info.type = EINA_FILE_UNKNOWN;
415
416    *data = &it->info;
417
418 #ifdef UNICODE
419    free(cname);
420 #endif
421
422    free(old_name);
423
424    if (is_last)
425      res = EINA_FALSE;
426
427    return res;
428 }
429
430 static HANDLE
431 _eina_file_win32_direct_ls_iterator_container(Eina_File_Direct_Iterator *it)
432 {
433    return it->handle;
434 }
435
436 static void
437 _eina_file_win32_direct_ls_iterator_free(Eina_File_Direct_Iterator *it)
438 {
439    if (it->handle != INVALID_HANDLE_VALUE)
440      FindClose(it->handle);
441
442    EINA_MAGIC_SET(&it->iterator, 0);
443    free(it);
444 }
445
446 static void
447 _eina_file_real_close(Eina_File *file)
448 {
449    eina_hash_free(file->rmap);
450    eina_hash_free(file->map);
451
452    if (file->global_map != MAP_FAILED)
453      UnmapViewOfFile(file->global_map);
454
455    CloseHandle(file->fm);
456    CloseHandle(file->handle);
457
458    free(file);
459 }
460
461 static void
462 _eina_file_map_close(Eina_File_Map *map)
463 {
464    if (map->map != MAP_FAILED)
465      UnmapViewOfFile(map->map);
466    free(map);
467 }
468
469 static unsigned int
470 _eina_file_map_key_length(const void *key __UNUSED__)
471 {
472    return sizeof (unsigned long int) * 2;
473 }
474
475 static int
476 _eina_file_map_key_cmp(const unsigned long int *key1, int key1_length __UNUSED__,
477                        const unsigned long int *key2, int key2_length __UNUSED__)
478 {
479    if (key1[0] - key2[0] == 0) return key1[1] - key2[1];
480    return key1[0] - key2[0];
481 }
482
483 static int
484 _eina_file_map_key_hash(const unsigned long int *key, int key_length __UNUSED__)
485 {
486    return eina_hash_int64(&key[0], sizeof (unsigned long int))
487      ^ eina_hash_int64(&key[1], sizeof (unsigned long int));
488 }
489
490 static char *
491 _eina_file_win32_escape(const char *path, size_t *length)
492 {
493    char *result = strdup(path ? path : "");
494    char *p = result;
495    char *q = result;
496    size_t len;
497
498    if (!result)
499      return NULL;
500
501    if (length) len = *length;
502    else len = strlen(result);
503
504    while ((p = strchr(p, '/')))
505      {
506         // remove double `/'
507         if (p[1] == '/')
508           {
509              memmove(p, p + 1, --len - (p - result));
510              result[len] = '\0';
511           }
512         else
513           if (p[1] == '.'
514               && p[2] == '.')
515             {
516                // remove `/../'
517                if (p[3] == '/')
518                  {
519                     char tmp;
520
521                     len -= p + 3 - q;
522                     memmove(q, p + 3, len - (q - result));
523                     result[len] = '\0';
524                     p = q;
525
526                     /* Update q correctly. */
527                     tmp = *p;
528                     *p = '\0';
529                     q = strrchr(result, '/');
530                     if (!q) q = result;
531                     *p = tmp;
532                  }
533                else
534                  // remove '/..$'
535                  if (p[3] == '\0')
536                    {
537                       len -= p + 2 - q;
538                       result[len] = '\0';
539                       q = p;
540                       ++p;
541                    }
542                  else
543                    {
544                       q = p;
545                       ++p;
546                    }
547             }
548           else
549             {
550                q = p;
551                ++p;
552             }
553      }
554
555    if (length)
556      *length = len;
557
558    return result;
559 }
560
561
562 /**
563  * @endcond
564  */
565
566 /*============================================================================*
567  *                                 Global                                     *
568  *============================================================================*/
569
570 Eina_Bool
571 eina_file_init(void)
572 {
573    _eina_file_log_dom = eina_log_domain_register("eina_file",
574                                                  EINA_LOG_COLOR_DEFAULT);
575    if (_eina_file_log_dom < 0)
576      {
577         EINA_LOG_ERR("Could not register log domain: eina_file");
578         return EINA_FALSE;
579      }
580
581    _eina_file_cache = eina_hash_string_djb2_new(NULL);
582    if (!_eina_file_cache)
583      {
584         ERR("Could not create cache.");
585         eina_log_domain_unregister(_eina_file_log_dom);
586         _eina_file_log_dom = -1;
587         return EINA_FALSE;
588      }
589
590    eina_lock_new(&_eina_file_lock_cache);
591
592    return EINA_TRUE;
593 }
594
595 Eina_Bool
596 eina_file_shutdown(void)
597 {
598    if (eina_hash_population(_eina_file_cache) > 0)
599      {
600         Eina_Iterator *it;
601         const char *key;
602
603         it = eina_hash_iterator_key_new(_eina_file_cache);
604         EINA_ITERATOR_FOREACH(it, key)
605           ERR("File [%s] still open !", key);
606         eina_iterator_free(it);
607      }
608
609    eina_hash_free(_eina_file_cache);
610
611    eina_lock_free(&_eina_file_lock_cache);
612
613    eina_log_domain_unregister(_eina_file_log_dom);
614    _eina_file_log_dom = -1;
615    return EINA_TRUE;
616 }
617
618 /*============================================================================*
619  *                                   API                                      *
620  *============================================================================*/
621
622
623 EAPI char *
624 eina_file_path_sanitize(const char *path)
625 {
626    char *result = NULL;
627    size_t len;
628
629    if (!path) return NULL;
630
631    len = strlen(path);
632    if (len < 3) return NULL;
633
634    if (!evil_path_is_absolute(path))
635      {
636         DWORD l;
637
638         l = GetCurrentDirectory(0, NULL);
639         if (l > 0)
640           {
641              char *cwd;
642              DWORD l2;
643
644              cwd = alloca(sizeof(char) * (l + 1));
645              l2 = GetCurrentDirectory(l + 1, cwd);
646              if (l2 == l)
647                {
648                   char *tmp;
649
650                   len += l + 2;
651                   tmp = alloca(sizeof (char) * len);
652                   snprintf(tmp, len, "%s/%s", cwd, path);
653                   tmp[len - 1] = '\0';
654                   result = tmp;
655                }
656           }
657      }
658
659    return _eina_file_win32_escape(result ? result : path, &len);
660 }
661
662 EAPI Eina_Bool
663 eina_file_dir_list(const char *dir,
664                    Eina_Bool recursive,
665                    Eina_File_Dir_List_Cb cb,
666                    void *data)
667 {
668    WIN32_FIND_DATA file;
669    HANDLE h;
670    char *new_dir;
671
672    EINA_SAFETY_ON_NULL_RETURN_VAL(cb,  EINA_FALSE);
673    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, EINA_FALSE);
674    EINA_SAFETY_ON_TRUE_RETURN_VAL(dir[0] == '\0', EINA_FALSE);
675
676    if (!_eina_file_win32_is_dir(dir))
677      return EINA_FALSE;
678
679    new_dir = _eina_file_win32_dir_new(dir);
680    if (!new_dir)
681       return EINA_FALSE;
682
683    h = _eina_file_win32_first_file(new_dir, &file);
684
685    if (h == INVALID_HANDLE_VALUE)
686       return EINA_FALSE;
687
688    do
689      {
690         char *filename;
691
692 # ifdef UNICODE
693         filename = evil_wchar_to_char(file.cFileName);
694 # else
695         filename = file.cFileName;
696 # endif /* ! UNICODE */
697         if (!strcmp(filename, ".") || !strcmp(filename, ".."))
698            continue;
699
700         cb(filename, dir, data);
701
702         if (recursive == EINA_TRUE)
703           {
704              char *path;
705
706              path = alloca(strlen(dir) + strlen(filename) + 2);
707              strcpy(path, dir);
708              strcat(path, "/");
709              strcat(path, filename);
710
711              if (!(file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
712                 continue;
713
714              eina_file_dir_list(path, recursive, cb, data);
715           }
716
717 # ifdef UNICODE
718         free(filename);
719 # endif /* UNICODE */
720
721      } while (FindNextFile(h, &file));
722    FindClose(h);
723
724    return EINA_TRUE;
725 }
726
727 EAPI Eina_Array *
728 eina_file_split(char *path)
729 {
730    Eina_Array *ea;
731    char *current;
732    size_t length;
733
734    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
735
736    ea = eina_array_new(16);
737
738    if (!ea)
739       return NULL;
740
741    for (current = strchr(path, '\\');
742         current;
743         path = current + 1, current = strchr(path, '\\'))
744      {
745         length = current - path;
746
747         if (length <= 0)
748            continue;
749
750         eina_array_push(ea, path);
751         *current = '\0';
752      }
753
754    if (*path != '\0')
755         eina_array_push(ea, path);
756
757    return ea;
758 }
759
760 EAPI Eina_Iterator *
761 eina_file_ls(const char *dir)
762 {
763    Eina_File_Iterator *it;
764    char               *new_dir;
765    size_t              length;
766
767    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
768
769    if (!dir || !*dir)
770       return NULL;
771
772    if (!_eina_file_win32_is_dir(dir))
773      return NULL;
774
775    length = strlen(dir);
776
777    it = calloc(1, sizeof (Eina_File_Iterator) + length);
778    if (!it)
779       return NULL;
780
781    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
782
783    new_dir = _eina_file_win32_dir_new(dir);
784    if (!new_dir)
785       goto free_it;
786
787    it->handle = _eina_file_win32_first_file(new_dir, &it->data);
788    free(new_dir);
789    if (it->handle == INVALID_HANDLE_VALUE)
790      goto free_it;
791
792    memcpy(it->dir, dir, length + 1);
793    if (dir[length - 1] != '\\')
794       it->length = length;
795    else
796       it->length = length - 1;
797    _eina_file_win32_backslash_change(it->dir);
798
799    it->iterator.version = EINA_ITERATOR_VERSION;
800    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_ls_iterator_next);
801    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_ls_iterator_container);
802    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_ls_iterator_free);
803
804    return &it->iterator;
805
806  free_it:
807    free(it);
808
809    return NULL;
810 }
811
812 EAPI Eina_Iterator *
813 eina_file_direct_ls(const char *dir)
814 {
815    Eina_File_Direct_Iterator *it;
816    char                      *new_dir;
817    size_t                     length;
818
819    EINA_SAFETY_ON_NULL_RETURN_VAL(dir, NULL);
820
821    if (!dir || !*dir)
822       return NULL;
823
824    length = strlen(dir);
825
826    if (length + 12 + 2 >= MAX_PATH)
827       return NULL;
828
829    it = calloc(1, sizeof(Eina_File_Direct_Iterator) + length);
830    if (!it)
831       return NULL;
832
833    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
834
835    new_dir = _eina_file_win32_dir_new(dir);
836    if (!new_dir)
837       goto free_it;
838
839    it->handle = _eina_file_win32_first_file(new_dir, &it->data);
840    free(new_dir);
841    if (it->handle == INVALID_HANDLE_VALUE)
842      goto free_it;
843
844    memcpy(it->dir, dir, length + 1);
845    it->length = length;
846    _eina_file_win32_backslash_change(it->dir);
847
848    memcpy(it->info.path, dir, length);
849    if (dir[length - 1] == '\\')
850       it->info.name_start = length;
851    else
852      {
853         it->info.path[length] = '\\';
854         it->info.name_start = length + 1;
855      }
856    _eina_file_win32_backslash_change(it->info.path);
857
858    it->iterator.version = EINA_ITERATOR_VERSION;
859    it->iterator.next = FUNC_ITERATOR_NEXT(_eina_file_win32_direct_ls_iterator_next);
860    it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_eina_file_win32_direct_ls_iterator_container);
861    it->iterator.free = FUNC_ITERATOR_FREE(_eina_file_win32_direct_ls_iterator_free);
862
863    return &it->iterator;
864
865  free_it:
866    free(it);
867
868    return NULL;
869 }
870
871 EAPI Eina_Iterator *
872 eina_file_stat_ls(const char *dir)
873 {
874    return eina_file_direct_ls(dir);
875 }
876
877 EAPI Eina_File *
878 eina_file_open(const char *path, Eina_Bool shared)
879 {
880    Eina_File *file;
881    Eina_File *n;
882    char *filename;
883    HANDLE handle;
884    HANDLE fm;
885    WIN32_FILE_ATTRIBUTE_DATA fad;
886    ULARGE_INTEGER length;
887    ULARGE_INTEGER mtime;
888
889    EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
890
891    filename = eina_file_path_sanitize(path);
892    if (!filename) return NULL;
893
894    /* FIXME: how to emulate shm_open ? Just OpenFileMapping ? */
895 #if 0
896    if (shared)
897      handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
898                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
899                          NULL);
900    else
901 #endif
902      handle = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
903                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY,
904                          NULL);
905
906    if (handle == INVALID_HANDLE_VALUE)
907      return NULL;
908
909    fm = CreateFileMapping(handle, NULL, PAGE_READONLY, 0, 0, NULL);
910    if (!fm)
911      goto close_handle;
912
913    if (!GetFileAttributesEx(filename, GetFileExInfoStandard, &fad))
914      goto close_fm;
915
916    length.u.LowPart = fad.nFileSizeLow;
917    length.u.HighPart = fad.nFileSizeHigh;
918    mtime.u.LowPart = fad.ftLastWriteTime.dwLowDateTime;
919    mtime.u.HighPart = fad.ftLastWriteTime.dwHighDateTime;
920
921    eina_lock_take(&_eina_file_lock_cache);
922
923    file = eina_hash_find(_eina_file_cache, filename);
924    if (file &&
925        (file->mtime != mtime.QuadPart || file->length != length.QuadPart))
926      {
927         file->delete_me = EINA_TRUE;
928         eina_hash_del(_eina_file_cache, file->filename, file);
929         _eina_file_real_close(file);
930         file = NULL;
931      }
932
933    if (!file)
934      {
935         n = malloc(sizeof (Eina_File) + strlen(filename) + 1);
936         if (!n)
937           {
938              eina_lock_release(&_eina_file_lock_cache);
939              goto close_fm;
940           }
941
942         n->filename = (char*) (n + 1);
943         strcpy((char*) n->filename, filename);
944         n->map = eina_hash_new(EINA_KEY_LENGTH(_eina_file_map_key_length),
945                                EINA_KEY_CMP(_eina_file_map_key_cmp),
946                                EINA_KEY_HASH(_eina_file_map_key_hash),
947                                EINA_FREE_CB(_eina_file_map_close),
948                                3);
949         n->rmap = eina_hash_pointer_new(NULL);
950         n->global_map = MAP_FAILED;
951         n->global_refcount = 0;
952         n->length = length.QuadPart;
953         n->mtime = mtime.QuadPart;
954         n->refcount = 0;
955         n->handle = handle;
956         n->fm = fm;
957         n->shared = shared;
958         n->delete_me = EINA_FALSE;
959         eina_lock_new(&n->lock);
960         eina_hash_direct_add(_eina_file_cache, n->filename, n);
961      }
962    else
963      {
964         CloseHandle(fm);
965         CloseHandle(handle);
966
967         n = file;
968      }
969    eina_lock_take(&n->lock);
970    n->refcount++;
971    eina_lock_release(&n->lock);
972
973    eina_lock_release(&_eina_file_lock_cache);
974
975    free(filename);
976
977    return n;
978
979  close_fm:
980    CloseHandle(fm);
981  close_handle:
982    CloseHandle(handle);
983
984    return NULL;
985 }
986
987 EAPI void
988 eina_file_close(Eina_File *file)
989 {
990    EINA_SAFETY_ON_NULL_RETURN(file);
991
992    eina_lock_take(&file->lock);
993    file->refcount--;
994    eina_lock_release(&file->lock);
995
996    if (file->refcount != 0) return ;
997    eina_lock_take(&_eina_file_lock_cache);
998
999    eina_hash_del(_eina_file_cache, file->filename, file);
1000    _eina_file_real_close(file);
1001    
1002    eina_lock_release(&_eina_file_lock_cache);
1003 }
1004
1005 EAPI size_t
1006 eina_file_size_get(Eina_File *file)
1007 {
1008    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1009    return file->length;
1010 }
1011
1012 EAPI time_t
1013 eina_file_mtime_get(Eina_File *file)
1014 {
1015    EINA_SAFETY_ON_NULL_RETURN_VAL(file, 0);
1016   return file->mtime;
1017 }
1018
1019 EAPI const char *
1020 eina_file_filename_get(Eina_File *file)
1021 {
1022    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1023    return file->filename;
1024 }
1025
1026 EAPI void *
1027 eina_file_map_all(Eina_File *file, Eina_File_Populate rule __UNUSED__)
1028 {
1029    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1030
1031    eina_lock_take(&file->lock);
1032    if (file->global_map == MAP_FAILED)
1033      {
1034         void  *data;
1035
1036         data = MapViewOfFile(file->fm, FILE_MAP_READ,
1037                              0, 0, file->length);
1038         if (!data)
1039           file->global_map = MAP_FAILED;
1040         else
1041           file->global_map = data;
1042      }
1043
1044    if (file->global_map != MAP_FAILED)
1045      {
1046         file->global_refcount++;
1047         return file->global_map;
1048      }
1049
1050    eina_lock_release(&file->lock);
1051    return NULL;
1052 }
1053
1054 EAPI void *
1055 eina_file_map_new(Eina_File *file, Eina_File_Populate rule,
1056                   unsigned long int offset, unsigned long int length)
1057 {
1058    Eina_File_Map *map;
1059    unsigned long int key[2];
1060
1061    EINA_SAFETY_ON_NULL_RETURN_VAL(file, NULL);
1062
1063    if (offset > file->length)
1064      return NULL;
1065    if (offset + length > file->length)
1066      return NULL;
1067
1068    if (offset == 0 && length == file->length)
1069      return eina_file_map_all(file, rule);
1070
1071    key[0] = offset;
1072    key[1] = length;
1073
1074    eina_lock_take(&file->lock);
1075
1076    map = eina_hash_find(file->map, &key);
1077    if (!map)
1078      {
1079         void  *data;
1080
1081         map = malloc(sizeof (Eina_File_Map));
1082         if (!map)
1083           {
1084              eina_lock_release(&file->lock);
1085              return NULL;
1086           }
1087
1088         data = MapViewOfFile(file->fm, FILE_MAP_READ,
1089                              offset & 0xffff0000,
1090                              offset & 0x0000ffff,
1091                              length);
1092         if (!data)
1093           map->map = MAP_FAILED;
1094         else
1095           map->map = data;
1096
1097         map->offset = offset;
1098         map->length = length;
1099         map->refcount = 0;
1100
1101         if (map->map == MAP_FAILED)
1102           {
1103              free(map);
1104              eina_lock_release(&file->lock);
1105              return NULL;
1106           }
1107
1108         eina_hash_add(file->map, &key, map);
1109         eina_hash_direct_add(file->rmap, map->map, map);
1110      }
1111
1112    map->refcount++;
1113
1114    eina_lock_release(&file->lock);
1115
1116    return map->map;
1117 }
1118
1119 EAPI void
1120 eina_file_map_free(Eina_File *file, void *map)
1121 {
1122    EINA_SAFETY_ON_NULL_RETURN(file);
1123
1124    eina_lock_take(&file->lock);
1125
1126    if (file->global_map == map)
1127      {
1128         file->global_refcount--;
1129
1130         if (file->global_refcount > 0) goto on_exit;
1131
1132         UnmapViewOfFile(file->global_map);
1133         file->global_map = MAP_FAILED;
1134      }
1135    else
1136      {
1137         Eina_File_Map *em;
1138         unsigned long int key[2];
1139
1140         em = eina_hash_find(file->rmap, &map);
1141         if (!em) goto on_exit;
1142
1143         em->refcount--;
1144
1145         if (em->refcount > 0) goto on_exit;
1146
1147         key[0] = em->offset;
1148         key[1] = em->length;
1149
1150         eina_hash_del(file->rmap, &map, em);
1151         eina_hash_del(file->map, &key, em);
1152      }
1153
1154  on_exit:
1155    eina_lock_release(&file->lock);
1156 }