34da1f7b0cc7b68daa2f3b29ada373b80da25f99
[sdk] / ecere / src / sys / File.ec
1 namespace sys;
2
3 #define _Noreturn
4
5 default:
6 #define set _set
7 #define uint _uint
8 #define File _File
9 #define strlen _strlen
10 #undef __BLOCKS__
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <stdlib.h>
14
15 #define UNICODE
16
17 #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
18
19 #if defined(ECERE_BOOTSTRAP)
20 #undef __WIN32__
21 #undef __linux__
22 #undef __APPLE__
23 #undef __UNIX__
24 #endif
25
26 #ifndef ECERE_BOOTSTRAP
27 #if defined(__GNUC__) || defined(__WATCOMC__) || defined(__WIN32__)
28 #include <time.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #endif
33
34 #if defined(__unix__) || defined(__APPLE__)
35 #include <utime.h>
36 #endif
37
38 #if defined(__WIN32__) || defined(__WATCOMC__)
39 #include <direct.h>
40 #else
41 #include <dirent.h>
42 #endif
43
44 #if defined(__WIN32__)
45 #define WIN32_LEAN_AND_MEAN
46 #define String String_
47 #include <windows.h>
48 #undef String
49 #include <io.h>
50
51 BOOL __declspec(dllimport) WINAPI GetVolumePathName(LPCTSTR lpszFileName,LPTSTR lpszVolumePathName,DWORD cchBufferLength);
52
53 // Missing function...
54 /*
55 #ifndef WNetGetResourceInformation
56 DWORD APIENTRY WNetGetResourceInformationA(LPNETRESOURCE lpNetResource, LPVOID lpBuffer, LPDWORD lpcbBuffer, LPTSTR* lplpSystem);
57 #ifdef UNICODE
58 #define WNetGetResourceInformation  WNetGetResourceInformationW
59 #else
60 #define WNetGetResourceInformation  WNetGetResourceInformationA
61 #endif
62 #endif
63 */
64
65 #else
66 #include <unistd.h>
67 #endif
68
69
70 #include "zlib.h"
71
72 #endif //#ifndef ECERE_BOOTSTRAP
73 private:
74
75 #undef set
76 #undef uint
77 #undef File
78 #undef strlen
79
80 import "System"
81
82 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET) && !defined(ECERE_BOOTSTRAP)
83 import "HTTPFile"
84 #endif
85
86 import "dataTypes"
87
88 // IMPLEMENTATION OF THESE IS IN _File.c
89 default:
90
91 FILE *eC_stdin(void);
92 FILE *eC_stdout(void);
93
94 uint FILE_GetSize(FILE * input);
95 bool FILE_Lock(FILE * input, FILE * output, FileLock type, uint64 start, uint64 length, bool wait);
96 void FILE_set_buffered(FILE * input, FILE * output, bool value);
97 FileAttribs FILE_FileExists(const char * fileName);
98 bool FILE_FileGetSize(const char * fileName, FileSize * size);
99 bool FILE_FileGetStats(const char * fileName, FileStats stats);
100 void FILE_FileFixCase(char * file);
101 void FILE_FileOpen(const char * fileName, FileOpenMode mode, FILE ** input, FILE **output);
102
103 private:
104
105 FileSystem httpFileSystem;
106
107 public class FileSize : uint
108 {
109    // defaultAlignment = Right;
110    /*
111    void OnDisplay(Surface surface, int x, int y, int width, void * fieldData, int alignment, DataDisplayFlags displayFlags)
112    {
113       char string[16];
114       int len;
115       eUtils_PrintSize(string, *size, 2);
116       len = strlen(string);
117       surface.WriteTextDots(alignment, x, y, width, string, len);
118    }
119    */
120    int OnCompare(FileSize data2)
121    {
122       int result = 0;
123       if(&this && &data2)
124       {
125          if(this > data2)
126             result = 1;
127          else if(this < data2)
128             result = -1;
129       }
130       return result;
131    }
132
133    const char * OnGetString(char * string, void * fieldData, bool * needClass)
134    {
135       PrintSize(string, this, 2);
136       return string;
137    }
138
139    bool OnGetDataFromString(const char * string)
140    {
141       char * end;
142       double value = strtod(string, &end);
143       uint multiplier = 1;
144       if(strstr(end, "GB") || strstr(end, "gb")) multiplier = (uint)1024 * 1024 * 1024;
145       else if(strstr(end, "MB") || strstr(end, "mb")) multiplier = (uint)1024 * 1024;
146       else if(strstr(end, "KB") || strstr(end, "kb")) multiplier = 1024;
147
148       this = (uint)(multiplier * value);
149       return true;
150    }
151 };
152
153 public class FileSize64 : uint64
154 {
155    int OnCompare(FileSize64 data2)
156    {
157       int result = 0;
158       if(&this && &data2)
159       {
160          if(this > data2)
161             result = 1;
162          else if(this < data2)
163             result = -1;
164       }
165       return result;
166    }
167
168    const char * OnGetString(char * string, void * fieldData, bool * needClass)
169    {
170       PrintBigSize(string, this, 2);
171       return string;
172    }
173
174    bool OnGetDataFromString(const char * string)
175    {
176       char * end;
177       double value = strtod(string, &end);
178       uint64 multiplier = 1;
179            if(strstr(end, "PB") || strstr(end, "pb")) multiplier = (uint64)1024 * 1024 * 1024 * 1024;
180       else if(strstr(end, "TB") || strstr(end, "tb")) multiplier = (uint64)1024 * 1024 * 1024 * 1024;
181       else if(strstr(end, "GB") || strstr(end, "gb")) multiplier = (uint64)1024 * 1024 * 1024;
182       else if(strstr(end, "MB") || strstr(end, "mb")) multiplier = (uint64)1024 * 1024;
183       else if(strstr(end, "KB") || strstr(end, "kb")) multiplier = 1024;
184
185       this = (uint64)(multiplier * value);
186       return true;
187    }
188 };
189
190 class FileSystem
191 {
192    virtual File ::Open(const char * archive, const char * name, FileOpenMode mode);
193
194    // Query on names
195    virtual FileAttribs ::Exists(const char * archive, const char * fileName);
196    virtual bool ::GetSize(const char * archive, const char * fileName, FileSize * size);
197    virtual bool ::Stats(const char * archive, const char * fileName, FileStats stats);
198    virtual void ::FixCase(const char * archive, char * fileName);
199
200    // File Listing
201    virtual bool ::Find(FileDesc file, const char * archive, const char * name);
202    virtual bool ::FindNext(FileDesc file);
203    virtual void ::CloseDir(FileDesc file);
204
205    // Archive manipulation
206    virtual Archive ::OpenArchive(const char * fileName, ArchiveOpenFlags create);
207    virtual bool ::QuerySize(const char * fileName, FileSize * size);
208 };
209
210 public enum FileOpenMode { read = 1, write, append, readWrite, writeRead, appendRead };
211 public enum FileSeekMode { start, current, end };
212
213 #if !defined(ECERE_BOOTSTRAP)
214 static FileDialog fileDialog { text = $"Select File" };
215 #endif
216
217 public enum FileLock
218 {
219    unlocked = 0,     // LOCK_UN  _SH_DENYNO
220    shared = 1,       // LOCK_SH  _SH_DENYWR
221    exclusive = 2     // LOCK_EX  _SH_DENYRW
222 };
223
224 public class File : IOChannel
225 {
226    FILE * input, * output;
227
228    uint ReadData(byte * bytes, uint numBytes)
229    {
230       return Read(bytes, 1, numBytes);
231    }
232
233    uint WriteData(const byte * bytes, uint numBytes)
234    {
235       return Write(bytes, 1, numBytes);
236    }
237
238    ~File()
239    {
240       if(output && output != input)
241       {
242          openCount--;
243          fclose(output);
244       }
245       if(input)
246       {
247          openCount--;
248          fclose(input);
249       }
250       input = null;
251       output = null;
252    }
253
254    bool OnGetDataFromString(const char * string)
255    {
256       if(!string[0])
257       {
258          this = null;
259          return true;
260       }
261       else
262       {
263          File f = FileOpen(string, read);
264          if(f)
265          {
266             this = TempFile { };
267             while(!f.Eof())
268             {
269                byte buffer[4096];
270                uint read = f.Read(buffer, 1, sizeof(buffer));
271                Write(buffer, 1, read);
272             }
273             delete f;
274             return true;
275          }
276       }
277       return false;
278    }
279
280    const char * OnGetString(char * tempString, void * fieldData, bool * needClass)
281    {
282       if(this)
283       {
284          PrintSize(tempString, GetSize(), 2);
285          return tempString;
286       }
287       return null;
288    }
289
290 #ifndef ECERE_BOOTSTRAP
291    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
292    {
293       Window editData = class::OnEdit(dataBox, obsolete, x + 24, y, w - 48, h, userData);
294       Button load
295       {
296          dataBox, inactive = true, text = $"Import"."Imp", hotKey = f2,
297          position = { Max(x + 24, x + w - 24), y }, size = { 24, h };
298
299          bool DataBox::NotifyClicked(Button button, int x, int y, Modifiers mods)
300          {
301             fileDialog.master = rootWindow;
302             fileDialog.filePath = "";
303             fileDialog.type = open;
304
305             if(fileDialog.Modal() == ok)
306             {
307                const char * filePath = fileDialog.filePath;
308                File output = null;
309                if(output.OnGetDataFromString(filePath))
310                {
311                   SetData(output, false);
312                   Refresh();
313                }
314             }
315             return true;
316          }
317       };
318       Button save
319       {
320          dataBox, inactive = true, text = $"Export"."Exp", hotKey = f2,
321          position = { Max(x + 24, x + w - 48), y }, size = { 24, h };
322
323          bool DataBox::NotifyClicked(Button button, int x, int y, Modifiers mods)
324          {
325             fileDialog.master = rootWindow;
326             fileDialog.type = save;
327             fileDialog.filePath = "";
328             if(fileDialog.Modal() == ok)
329             {
330                const char * filePath = fileDialog.filePath;
331                File f = FileOpen(filePath, write);
332                if(f)
333                {
334                   File input = *(void **)data;
335                   input.Seek(0, start);
336                   while(!input.Eof())
337                   {
338                      byte buffer[4096];
339                      uint read = input.Read(buffer, 1, sizeof(buffer));
340                      f.Write(buffer, 1, read);
341                   }
342                   delete f;
343                }
344             }
345             return true;
346          }
347       };
348       load.Create();
349       save.Create();
350       return editData;
351    }
352 #endif //#ifndef ECERE_BOOTSTRAP
353
354 #if !defined(ECERE_VANILLA) && !defined(ECERE_NOARCHIVE) && !defined(ECERE_BOOTSTRAP)
355    void OnSerialize(IOChannel channel)
356    {
357       uint size = this ? GetSize() : MAXDWORD;
358       if(this)
359       {
360          byte * uncompressed = new byte[size];
361          Seek(0, start);
362          if(uncompressed || !size)
363          {
364             uint count = Read(uncompressed, 1,  size);
365             if(count == size)
366             {
367                uLongf cSize = size + size / 1000 + 12;
368                byte * compressed = new byte[cSize];
369                if(compressed)
370                {
371                   compress2(compressed, &cSize, uncompressed, size, 9);
372
373                   size.OnSerialize(channel);
374                   cSize.OnSerialize(channel);
375                   channel.WriteData(compressed, (uint)cSize);
376
377                   delete compressed;
378                }
379             }
380             delete uncompressed;
381          }
382       }
383       else
384          size.OnSerialize(channel);
385
386       /*
387       byte data[4096];
388       uint c;
389       size.OnSerialize(channel);
390
391       // Will add position...
392       if(this)
393       {
394          Seek(0, start);
395          for(c = 0; c<size; c += sizeof(data))
396          {
397             uint count = Read(data, 1, sizeof(data));
398             buffer.WriteData(data, count);
399          }
400       }
401       */
402    }
403
404    void OnUnserialize(IOChannel channel)
405    {
406       uLongf size;
407       uint cSize;
408
409       this = null;
410
411       size.OnUnserialize(channel);
412       if(size != MAXDWORD)
413       {
414          byte * compressed;
415          cSize.OnUnserialize(channel);
416
417          compressed = new byte[cSize];
418          if(compressed)
419          {
420             if(channel.ReadData(compressed, cSize) == cSize)
421             {
422                byte * uncompressed = new byte[size];
423                if(uncompressed || !size)
424                {
425                   this = TempFile { };
426                   uncompress(uncompressed, &size, compressed, cSize);
427                   Write(uncompressed, 1, (uint)size);
428                   Seek(0, start);
429
430                   delete uncompressed;
431                }
432             }
433             delete compressed;
434          }
435       }
436
437       /*
438       byte data[4096];
439       uint c;
440
441       size.OnUnserialize(channel);
442       if(size != MAXDWORD)
443       {
444          this = TempFile { };
445          for(c = 0; c<size; c += sizeof(data))
446          {
447             uint count = Min(size - c, sizeof(data));
448             channel.ReadData(data, count);
449             Write(data, 1, count);
450          }
451          Seek(0, start);
452       }
453       else
454          this = null;
455       */
456    }
457 #endif
458
459 public:
460
461    // Virtual Methods
462    virtual bool Seek(int pos, FileSeekMode mode)
463    {
464       uint fmode = SEEK_SET;
465       switch(mode)
466       {
467          case start: fmode = SEEK_SET; break;
468          case end: fmode = SEEK_END; break;
469          case current: fmode = SEEK_CUR; break;
470       }
471       return fseek(input ? input : output, pos, fmode) != EOF;
472    }
473
474    virtual uint Tell(void)
475    {
476       return (uint)(input ? ftell(input) : ftell(output));
477    }
478
479    virtual int Read(void * buffer, uint size, uint count)
480    {
481       return input ? (int)fread(buffer, size, count, input) : 0;
482    }
483
484    virtual int Write(const void * buffer, uint size, uint count)
485    {
486       return output ? (int)fwrite(buffer, size, count, output) : 0;
487    }
488
489    // UNICODE OR NOT?
490    virtual bool Getc(char * ch)
491    {
492       int ich = fgetc(input);
493       if(ich != EOF)
494       {
495          if(ch) *ch = (char)ich;
496          return true;
497       }
498       return false;
499    }
500
501    virtual bool Putc(char ch)
502    {
503       return (fputc((int)ch, output) == EOF) ? false : true;
504    }
505
506    virtual bool Puts(const char * string)
507    {
508       bool result = false;
509       if(output)
510       {
511          result = (fputs(string, output) == EOF) ? false : true;
512          // TODO: Check if any repercusions of commenting out fflush here
513          // This is what broke the debugger in 0.44d2 , it is required for outputting things to the DualPipe
514          // Added an explicit flush call in DualPipe::Puts
515          // fflush(output);
516       }
517       return result;
518    }
519
520    virtual bool Eof(void)
521    {
522       return input ? feof(input) != 0 : true;
523    }
524
525    virtual bool Truncate(FileSize size)
526    {
527    #ifdef ECERE_BOOTSTRAP
528       fprintf(stderr, "WARNING:  File::Truncate unimplemented in ecereBootstrap.\n");
529       return false;
530    #else
531    #if defined(__WIN32__)
532       return output ? (_chsize(fileno(output), size) == 0) : false;
533    #else
534       return output ? (ftruncate(fileno(output), size) == 0) : false;
535    #endif
536    #endif
537    }
538
539    virtual uint GetSize(void)
540    {
541       return FILE_GetSize(input);
542    }
543
544    virtual void CloseInput(void)
545    {
546       if(input)
547       {
548          fclose(input);
549          if(output == input)
550             output = null;
551          input = null;
552       }
553    }
554
555    virtual void CloseOutput(void)
556    {
557       if(output)
558       {
559          fclose(output);
560          if(input == output)
561             input = null;
562          output = null;
563       }
564    }
565
566    virtual bool Lock(FileLock type, uint64 start, uint64 length, bool wait)
567    {
568       return FILE_Lock(input, output, type, start, length, wait);
569    }
570
571    virtual bool Unlock(uint64 start, uint64 length, bool wait)
572    {
573       return Lock(unlocked, start, length, wait);
574    }
575
576    // Normal Methods
577    int Printf(const char * format, ...)
578    {
579       int result = 0;
580       if(format)
581       {
582          char text[MAX_F_STRING];
583          va_list args;
584          va_start(args, format);
585          vsnprintf(text, sizeof(text), format, args);
586          text[sizeof(text)-1] = 0;
587          if(Puts(text))
588             result = strlen(text);
589          va_end(args);
590       }
591       return result;
592    }
593
594    public void PrintLn(typed_object object, ...)
595    {
596       va_list args;
597       char buffer[4096];
598       va_start(args, object);
599       PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
600       Puts(buffer);
601       Putc('\n');
602       va_end(args);
603    }
604
605    public void Print(typed_object object, ...)
606    {
607       va_list args;
608       char buffer[4096];
609       va_start(args, object);
610       PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
611       Puts(buffer);
612       va_end(args);
613    }
614
615    bool Flush(void)
616    {
617       fflush(output);
618       return true;
619    }
620
621    bool GetLine(char *s, int max)
622    {
623       int c = 0;
624       bool result = true;
625       s[c]=0;
626
627       if(Eof())
628       {
629          result = false;
630       }
631       else
632       {
633          while(c<max-1)
634          {
635             char ch = 0;
636
637             if(/*!Peek() || */ !Getc(&ch))
638             {
639                result = false;
640                break;
641             }
642             if(ch =='\n')
643                break;
644             if(ch !='\r')
645                s[c++]=ch;
646          }
647       }
648       s[c]=0;
649       return result || c > 1;
650    }
651
652    // Strings and numbers separated by spaces, commas, tabs, or CR/LF, handling quotes
653    bool GetString(char * string, int max)
654    {
655       int c;
656       char ch;
657       bool quoted = false;
658       bool result = true;
659
660       *string = 0;
661       while(true)
662       {
663          if(!Getc(&ch))
664          {
665             result = false;
666             break;
667          }
668          if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
669             break;
670          if(Eof()) break;
671       }
672       if(result)
673       {
674          for(c=0; c<max-1; c++)
675          {
676             if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
677             {
678                result = true;
679                break;
680             }
681             if(ch == '\"')
682             {
683                quoted ^= 1;
684                c--;
685             }
686             else
687                string[c]=ch;
688
689             if(!Getc(&ch))
690             {
691                c++;
692                result = false;
693                break;
694             }
695          }
696          string[c]=0;
697       }
698       return result;
699    }
700
701    int GetValue(void)
702    {
703       char string[32];
704       GetString(string,sizeof(string));
705       return atoi(string);
706    }
707
708    unsigned int GetHexValue(void)
709    {
710       char string[32];
711       GetString(string, sizeof(string));
712       return (uint)strtoul(string, null, 16);
713    }
714
715    float GetFloat(void)
716    {
717       char string[32];
718       GetString(string, sizeof(string));
719       return (float)FloatFromString(string);
720    }
721
722    double GetDouble(void)
723    {
724       char string[32];
725       GetString(string, sizeof(string));
726       return FloatFromString(string);
727    }
728
729    property void * input { set { input = value; } get { return input; } }
730    property void * output { set { output = value; } get { return output; } }
731    property bool buffered
732    {
733       set
734       {
735          FILE_set_buffered(input, output, value);
736       }
737    }
738    property bool eof { get { return Eof(); } }
739
740    int GetLineEx(char *s, int max, bool *hasNewLineChar)
741    {
742       int c = 0;
743       s[c] = '\0';
744
745       if(!Eof())
746       {
747          char ch = '\0';
748          while(c < max - 1)
749          {
750             if(/*!Peek() || */ !Getc(&ch))
751                break;
752             if(ch == '\n')
753                break;
754             if(ch != '\r')
755                s[c++] = ch;
756          }
757          if(hasNewLineChar)
758             *hasNewLineChar = (ch == '\n');
759       }
760       s[c] = '\0';
761       return c;
762    }
763
764    bool CopyTo(const char * outputFileName)
765    {
766       bool result = false;
767       File f = FileOpen(outputFileName, write);
768       if(f)
769       {
770          byte buffer[65536];
771
772          result = true;
773          Seek(0, start);
774          while(!Eof())
775          {
776             uint count = Read(buffer, 1, sizeof(buffer));
777             if(count && !f.Write(buffer, 1, count))
778             {
779                result = false;
780                break;
781             }
782          }
783          delete f;
784       }
785       Seek(0, start);
786       return result;
787    }
788
789 #if 0
790    virtual bool Open(const char * fileName, FileOpenMode mode)
791    {
792       bool result = false;
793       if(this)
794       {
795          FILE_FileOpen(fileName, mode, &input, &output);
796
797          //file.mode = mode;
798          if(!input && !output);
799          else
800          {
801             openCount++;
802             result = true;
803             // TESTING ENABLING FILE BUFFERING BY DEFAULT... DOCUMENT ANY ISSUE
804             /*
805             if(file.input)
806                setvbuf(file.input, null, _IONBF, 0);
807             else
808                setvbuf(file.output, null, _IONBF, 0);
809             */
810          }
811          //if(!result)
812          {
813             /* TOFIX:
814             LogErrorCode((mode == Read || mode == ReadWrite) ?
815                ERR_FILE_NOT_FOUND : ERR_FILE_WRITE_FAILED, fileName);
816             */
817          }
818       }
819       return result;
820    }
821 #endif
822
823    virtual void Close()
824    {
825       CloseOutput();
826       CloseInput();
827    }
828 }
829
830 #if defined(__WIN32__)
831 default extern intptr_t stdinHandle;
832 default extern intptr_t stdoutHandle;
833 #endif
834
835 public class ConsoleFile : File
836 {
837    input = eC_stdin();
838    output = eC_stdout();
839
840 #if defined(__WIN32__)
841    void CloseInput()
842    {
843       CloseHandle((HANDLE)stdinHandle);
844    }
845    /*
846    void CloseOutput()
847    {
848       CloseHandle((HANDLE)stdoutHandle);
849    }*/
850 #endif
851
852    ~ConsoleFile()
853    {
854       input = null;
855       output = null;
856    }
857 };
858
859 public class FileAttribs : bool
860 {
861 public:
862    bool isFile:1, isArchive:1, isHidden:1, isReadOnly:1, isSystem:1, isTemporary:1, isDirectory:1;
863    bool isDrive:1, isCDROM:1, isRemote:1, isRemovable:1, isServer:1, isShare:1;
864    // property bool { };
865 };
866
867 #ifdef ECERE_BOOTSTRAP
868 public class SecSince1970 : int64;
869 #endif
870
871 public struct FileStats
872 {
873    FileAttribs attribs;
874    FileSize size;
875    SecSince1970 accessed;
876    SecSince1970 modified;
877    SecSince1970 created;
878 };
879
880 #if defined(__WIN32__)
881
882 // --- FileName functions ---
883
884 default TimeStamp Win32FileTimeToTimeStamp(FILETIME * fileTime)
885 {
886    // TIME_ZONE_INFORMATION tz = { 0 };
887    SYSTEMTIME st, lt;
888    DateTime t;
889
890    FileTimeToSystemTime(fileTime, &lt);
891
892    /*
893    GetTimeZoneInformation(&tz);
894    tz.Bias = 0;
895    _TzSpecificLocalTimeToSystemTime(&tz, &lt, &st);
896    */
897    st = lt;
898
899    t.year = st.wYear;
900    t.month = (Month)(st.wMonth - 1);
901    t.day = st.wDay;
902    t.hour = st.wHour;
903    t.minute = st.wMinute;
904    t.second = st.wSecond;
905    return t;
906 }
907
908 default void TimeStampToWin32FileTime(TimeStamp t, FILETIME * fileTime)
909 {
910    // TIME_ZONE_INFORMATION tz = { 0 };
911    SYSTEMTIME st, lt;
912    DateTime tm;
913
914    tm = t;
915
916    st.wYear = (short)tm.year;
917    st.wMonth = (short)(tm.month + 1);
918    st.wDay = (short)tm.day;
919    st.wHour = (short)tm.hour;
920    st.wMinute = (short)tm.minute;
921    st.wSecond = (short)tm.second;
922    st.wMilliseconds = 0;
923    st.wDayOfWeek = 0;
924
925    /*
926    GetTimeZoneInformation(&tz);
927    tz.Bias = 0;
928    SystemTimeToTzSpecificLocalTime(&tz, &st, &lt);
929    */
930
931    lt = st;
932    SystemTimeToFileTime(&lt, fileTime);
933 }
934 /*
935 default TimeStamp Win32FileTimeToTimeStamp(FILETIME * fileTime);
936 default void TimeStampToWin32FileTime(TimeStamp t, FILETIME * fileTime);
937 */
938 default bool WinReviveNetworkResource(uint16 * _wfileName);
939
940 #endif
941
942 public FileAttribs FileExists(const char * fileName)
943 {
944 #if !defined(ECERE_BOOTSTRAP)
945    char archiveName[MAX_LOCATION];
946    const char * archiveFile;
947    if(SplitArchivePath(fileName, archiveName, &archiveFile))
948    {
949       return EARFileSystem::Exists(archiveName, archiveFile);
950    }
951    else if(strstr(fileName, "http://") == fileName)
952    {
953       return FileAttribs { isFile = true };
954    }
955    else
956 #endif
957       return FILE_FileExists(fileName);
958 }
959
960 static int openCount;
961
962 public File FileOpen(const char * fileName, FileOpenMode mode)
963 {
964    File result = null;
965    if(fileName)
966    {
967 #if !defined(ECERE_BOOTSTRAP)
968       char archiveName[MAX_LOCATION];
969       const char * archiveFile;
970       if(SplitArchivePath(fileName, archiveName, &archiveFile))
971       {
972          result = EARFileSystem::Open(archiveName, archiveFile, mode);
973       }
974 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
975       else if(strstr(fileName, "http://") == fileName || strstr(fileName, "https://"))
976       {
977          result = FileOpenURL(fileName);
978       }
979 #endif
980       else
981 #endif
982       if(strstr(fileName, "File://") == fileName)
983       {
984          result = (File)(uintptr)strtoull(fileName+7, null, 16);
985          if(result)
986          {
987             if(result._class && eClass_IsDerived(result._class, class(File)))
988             {
989                if(!result._refCount) incref result;
990                incref result;
991                result.Seek(0, start);
992             }
993             else
994                result = null;
995          }
996       }
997       else
998       {
999          File file = File {};
1000          if(file)
1001          {
1002             FILE_FileOpen(fileName, mode, &file.input, &file.output);
1003
1004             //file.mode = mode;
1005             if(!file.input && !file.output);
1006             else
1007             {
1008                openCount++;
1009                result = file;
1010                // TESTING ENABLING FILE BUFFERING BY DEFAULT... DOCUMENT ANY ISSUE
1011                /*
1012                if(file.input)
1013                   setvbuf(file.input, null, _IONBF, 0);
1014                else
1015                   setvbuf(file.output, null, _IONBF, 0);
1016                */
1017             }
1018             if(!result)
1019             {
1020                delete file;
1021                /* TOFIX:
1022                LogErrorCode((mode == Read || mode == ReadWrite) ?
1023                   ERR_FILE_NOT_FOUND : ERR_FILE_WRITE_FAILED, fileName);
1024                */
1025             }
1026          }
1027       }
1028    }
1029    return result;
1030 }
1031
1032 public void FileFixCase(char * file)
1033 {
1034    FILE_FileFixCase(file);
1035 }
1036
1037 #if !defined(ECERE_BOOTSTRAP)
1038 public bool FileTruncate(const char * fileName, FileSize size)
1039 {
1040 #if defined(__WIN32__)
1041    uint16 * _wfileName = UTF8toUTF16(fileName, null);
1042    int f = _wopen(_wfileName, _O_RDWR|_O_CREAT, _S_IREAD|_S_IWRITE);
1043    bool result = false;
1044    if(f != -1)
1045    {
1046       if(!_chsize(f, size))
1047          result = true;
1048       _close(f);
1049    }
1050    delete _wfileName;
1051    return result;
1052 #else
1053    return truncate(fileName, size) == 0;
1054 #endif
1055 }
1056 #endif
1057
1058 public bool FileGetSize(const char * fileName, FileSize * size)
1059 {
1060    bool result = false;
1061    if(size)
1062    {
1063       *size = 0;
1064       if(fileName)
1065       {
1066 #if !defined(ECERE_BOOTSTRAP)
1067          char archiveName[MAX_LOCATION];
1068          const char * archiveFile;
1069          if(SplitArchivePath(fileName, archiveName, &archiveFile))
1070             return EARFileSystem::GetSize(archiveName, archiveFile, size);
1071          else
1072 #endif
1073             result = FILE_FileGetSize(fileName, size);
1074       }
1075    }
1076    return result;
1077 }
1078
1079 public bool FileGetStats(const char * fileName, FileStats stats)
1080 {
1081    bool result = false;
1082    if(stats && fileName)
1083    {
1084 #if !defined(ECERE_BOOTSTRAP)
1085       char archiveName[MAX_LOCATION];
1086       const char * archiveFile;
1087       if(SplitArchivePath(fileName, archiveName, &archiveFile))
1088          result = EARFileSystem::Stats(archiveName, archiveFile, stats);
1089       else
1090 #endif
1091          return FILE_FileGetStats(fileName, stats);
1092    }
1093    return result;
1094 }
1095
1096 #ifndef ECERE_BOOTSTRAP
1097
1098 public bool FileSetAttribs(const char * fileName, FileAttribs attribs)
1099 {
1100 #ifdef __WIN32__
1101    uint winAttribs = 0;
1102    uint16 * _wfileName = UTF8toUTF16(fileName, null);
1103
1104    if(attribs.isHidden)   winAttribs |= FILE_ATTRIBUTE_HIDDEN;
1105    if(attribs.isReadOnly) winAttribs |= FILE_ATTRIBUTE_READONLY;
1106
1107    SetFileAttributes(_wfileName, winAttribs);
1108    delete _wfileName;
1109 #endif
1110    return true;
1111 }
1112
1113 public bool FileSetTime(const char * fileName, TimeStamp created, TimeStamp accessed, TimeStamp modified)
1114 {
1115    bool result = false;
1116    TimeStamp currentTime = time(null);
1117    if(!created)  created = currentTime;
1118    if(!accessed) accessed = currentTime;
1119    if(!modified) modified = currentTime;
1120    if(fileName)
1121    {
1122 #ifdef __WIN32__
1123       uint16 * _wfileName = UTF8toUTF16(fileName, null);
1124       HANDLE hFile = CreateFile(_wfileName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, null,
1125          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, null);
1126       delete _wfileName;
1127       if(hFile != INVALID_HANDLE_VALUE)
1128       {
1129          FILETIME c, a, m;
1130
1131          TimeStampToWin32FileTime(created, &c);
1132          TimeStampToWin32FileTime(accessed, &a);
1133          TimeStampToWin32FileTime(modified, &m);
1134
1135          /*
1136          {
1137             uint cc,aa,mm;
1138
1139             cc = Win32FileTimeToTimeStamp(&c);
1140             aa = Win32FileTimeToTimeStamp(&a);
1141             mm = Win32FileTimeToTimeStamp(&m);
1142          }
1143          */
1144
1145          if(SetFileTime(hFile, &c, &a, &m))
1146             result = true;
1147
1148          CloseHandle(hFile);
1149       }
1150 #else
1151       struct utimbuf t = { (int)accessed, (int)modified };
1152       if(!utime(fileName, &t))
1153          result = true;
1154 #endif
1155    }
1156    return result;
1157 }
1158
1159 /****************************************************************************
1160  Directory Listing
1161 ****************************************************************************/
1162 // Directory Description for file listing
1163 private class Dir : struct
1164 {
1165 #if defined(__WIN32__)
1166    HANDLE fHandle;
1167
1168    int resource;
1169    NETRESOURCE * resources;
1170    int numResources;
1171
1172    int workGroup;
1173    NETRESOURCE * workGroups;
1174    int numWorkGroups;
1175 #else
1176    DIR * d;
1177 #endif
1178    char name[MAX_LOCATION];
1179 };
1180
1181 static FileDesc FileFind(const char * path, const char * extensions)
1182 {
1183    FileDesc result = null;
1184    FileDesc file;
1185
1186    if((file = FileDesc {}))
1187    {
1188       char archiveName[MAX_LOCATION];
1189       const char * archiveFile;
1190       if(SplitArchivePath(path, archiveName, &archiveFile))
1191       {
1192          if(EARFileSystem::Find(file, archiveName, archiveFile))
1193          {
1194             file.system = class(EARFileSystem);
1195             result = file;
1196          }
1197       }
1198       else
1199       {
1200          Dir d;
1201
1202          if((d = file.dir = Dir {}))
1203          {
1204 #if defined(__WIN32__)
1205             if(!strcmp(path, "/"))
1206             {
1207                int c;
1208                uint drives = 0xFFFFFFFF;
1209                d.fHandle = (HANDLE)(uintptr)drives; //GetLogicalDrives();
1210                for(c = 0; c<26; c++)
1211                   if(((uint)(uintptr)d.fHandle) & (1<<c))
1212                   {
1213                      char volume[MAX_FILENAME] = "";
1214                      uint16 _wvolume[MAX_FILENAME];
1215                      int driveType;
1216                      uint16 _wfilePath[4];
1217
1218                      strcpy(d.name, path);
1219                      file.stats.attribs = FileAttribs { isDirectory = true, isDrive = true };
1220                      _wfilePath[0] = file.path[0] = (char)('A' + c);
1221                      _wfilePath[1] = file.path[1] = ':';
1222                      _wfilePath[2] = file.path[2] = '\\';
1223                      _wfilePath[3] = file.path[3] = '\0';
1224                      file.stats.size = 0;
1225                      file.stats.accessed = file.stats.created = file.stats.modified = 0;
1226                      driveType = GetDriveType(_wfilePath);
1227                      switch(driveType)
1228                      {
1229                         case DRIVE_REMOVABLE: file.stats.attribs.isRemovable = true; break;
1230                         case DRIVE_REMOTE:    file.stats.attribs.isRemote = true; break;
1231                         case DRIVE_CDROM:     file.stats.attribs.isCDROM = true; break;
1232                      }
1233                      drives ^= (1<<c);
1234                      if(driveType == DRIVE_NO_ROOT_DIR) continue;
1235
1236                      if(driveType != DRIVE_REMOVABLE && driveType != DRIVE_REMOTE &&
1237                         GetVolumeInformation(_wfilePath, _wvolume, MAX_FILENAME - 1, null, null, null, null, 0))
1238                      {
1239                         file.path[2] = '\0';
1240                         UTF16toUTF8Buffer(_wvolume, volume, MAX_FILENAME);
1241                         sprintf(file.name, "%s [%s]", file.path, volume);
1242                      }
1243                      else
1244                      {
1245                         file.path[2] = '\0';
1246                         strcpy(file.name, file.path);
1247                      }
1248                      result = file;
1249                      break;
1250                   }
1251                d.fHandle = (HANDLE)(uintptr) drives;
1252                d.resource = 0;
1253             }
1254             else if(path[0] != '\\' || path[1] != '\\' || strstr(path+2, "\\"))
1255             {
1256                WIN32_FIND_DATA winFile;
1257                uint16 dir[MAX_PATH];
1258
1259                UTF8toUTF16Buffer(path, dir, MAX_LOCATION);
1260                if(path[0]) wcscat(dir, L"\\");
1261                wcscat(dir, L"*.*");
1262
1263                d.fHandle = FindFirstFile(dir, &winFile);
1264                if(d.fHandle == INVALID_HANDLE_VALUE && WinReviveNetworkResource(dir))
1265                   d.fHandle = FindFirstFile(dir, &winFile);
1266                if(d.fHandle != INVALID_HANDLE_VALUE)
1267                {
1268                   UTF16toUTF8Buffer(winFile.cFileName, file.name, MAX_FILENAME);
1269                   strcpy(file.path, path);
1270                   PathCat(file.path, file.name);
1271                   /*if(path[0])
1272                      strcat(file.path, DIR_SEPS);
1273                   strcat(file.path, file.name);*/
1274                   // file.sizeHigh = winFile.nFileSizeHigh;
1275                   file.stats.size = winFile.nFileSizeLow;
1276
1277                   file.stats.attribs = FileAttribs { };
1278                   file.stats.attribs.isArchive   = (winFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)   ? true : false;
1279                   file.stats.attribs.isHidden    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)    ? true : false;
1280                   file.stats.attribs.isReadOnly  = (winFile.dwFileAttributes & FILE_ATTRIBUTE_READONLY)  ? true : false;
1281                   file.stats.attribs.isSystem    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)    ? true : false;
1282                   file.stats.attribs.isTemporary = (winFile.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) ? true : false;
1283                   file.stats.attribs.isDirectory = (winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
1284                   file.stats.attribs.isFile = !(winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1285                   strcpy(d.name, path);
1286
1287                   file.stats.accessed = Win32FileTimeToTimeStamp(&winFile.ftLastAccessTime);
1288                   file.stats.modified = Win32FileTimeToTimeStamp(&winFile.ftLastWriteTime);
1289                   file.stats.created  = Win32FileTimeToTimeStamp(&winFile.ftCreationTime);
1290                   result = file;
1291                }
1292             }
1293             else
1294             {
1295                HANDLE handle = 0;
1296                DWORD count = 0xFFFFFFFF;
1297                DWORD size = 512 * sizeof(NETRESOURCE);
1298                NETRESOURCE * buffer = (NETRESOURCE *)new0 byte[size];
1299                NETRESOURCE nr = {0};
1300
1301                d.fHandle = null;
1302                nr.dwScope       = RESOURCE_GLOBALNET;
1303                nr.dwType        = RESOURCETYPE_DISK;
1304                nr.lpProvider = (uint16 *)L"Microsoft Windows Network";
1305
1306                strcpy(d.name, path);
1307                if(path[2])
1308                {
1309                   nr.lpRemoteName = UTF8toUTF16(path, null);
1310
1311                   // Server
1312                   WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr, &handle);
1313                   if(!handle)
1314                   {
1315                      WinReviveNetworkResource(nr.lpRemoteName);
1316                      WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr, &handle);
1317                   }
1318
1319                   if(handle)
1320                   {
1321                      while(true)
1322                      {
1323                         int returnCode = WNetEnumResource(handle, &count, buffer, &size);
1324                         if(returnCode != ERROR_MORE_DATA)
1325                            break;
1326                         count = 0xFFFFFFFF;
1327                         buffer = (NETRESOURCE *)renew0 buffer byte[size];
1328                      }
1329                      WNetCloseEnum(handle);
1330                   }
1331
1332                   delete nr.lpRemoteName;
1333                   if(count > 0)
1334                   {
1335                      file.stats.attribs = FileAttribs { isDirectory = true, isShare = true };
1336                      file.stats.size = 0;
1337                      file.stats.accessed = file.stats.created = file.stats.modified = 0;
1338
1339                      UTF16toUTF8Buffer(buffer->lpRemoteName, file.path, MAX_LOCATION);
1340                      GetLastDirectory(file.path, file.name);
1341
1342                      result = file;
1343                      d.resources = buffer;
1344                      d.numResources = count;
1345                      d.resource = 1;
1346                   }
1347                   else
1348                      delete buffer;
1349                }
1350                else
1351                {
1352                   int c;
1353                   nr.lpProvider = (uint16 *)L"Microsoft Windows Network";
1354
1355                   // Entire Network
1356                   WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr, &handle);
1357                   while(true)
1358                   {
1359                      int returnCode = WNetEnumResource(handle, &count, buffer, &size);
1360                      if(returnCode != ERROR_MORE_DATA)
1361                         break;
1362                      count = 0xFFFFFFFF;
1363                      buffer = (NETRESOURCE *)renew0 buffer byte[size];
1364                   }
1365                   WNetCloseEnum(handle);
1366
1367                   for(c = 0; c<count; c++)
1368                   {
1369                      NETRESOURCE * resources;
1370                      DWORD countInGroup = 0xFFFFFFFF;
1371
1372                      size = 512 * sizeof(NETRESOURCE);
1373                      resources = (NETRESOURCE *)new0 byte[size];
1374
1375                      // Entire Network
1376                      WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &buffer[c], &handle);
1377                      while(true)
1378                      {
1379                         int returnCode = WNetEnumResource(handle, &countInGroup, resources, &size);
1380                         if(returnCode != ERROR_MORE_DATA)
1381                            break;
1382                         countInGroup = 0xFFFFFFFF;
1383                         resources = (NETRESOURCE *)renew0 resources byte[size];
1384                      }
1385                      WNetCloseEnum(handle);
1386
1387                      if(countInGroup)
1388                      {
1389                         file.stats.attribs = FileAttribs { isDirectory = true, isServer = true };
1390                         file.stats.size = 0;
1391                         file.stats.accessed = file.stats.created = file.stats.modified = 0;
1392
1393                         UTF16toUTF8Buffer(resources->lpRemoteName, file.path, MAX_LOCATION);
1394                         strlwr(file.path);
1395                         file.path[2] = (char)toupper(file.path[2]);
1396                         GetLastDirectory(file.path, file.name);
1397
1398                         result = file;
1399
1400                         d.resources = resources;
1401                         d.numResources = countInGroup;
1402                         d.resource = 1;
1403
1404                         d.workGroups = buffer;
1405                         d.numWorkGroups = count;
1406                         d.workGroup = c;
1407                         break;
1408                      }
1409                      else
1410                         delete resources;
1411                   }
1412                   if(c >= count && buffer) delete buffer;
1413                }
1414             }
1415 #else
1416             struct dirent *de;
1417             struct stat s;
1418
1419             d.d = opendir((path && path[0]) ? path : ".");
1420             if(d.d && (de = readdir(d.d)))
1421             {
1422                if(path[0])
1423                {
1424                   strcpy(file.path, path);
1425                   if(path[1])
1426                      strcat(file.path, DIR_SEPS);
1427                }
1428                strcpy(file.name,de->d_name);
1429                strcat(file.path, file.name);
1430                if(!stat(file.path, &s))
1431                {
1432                   file.stats.attribs = (s.st_mode&S_IFDIR) ? FileAttribs { isDirectory = true } : FileAttribs { isFile = true };
1433                   file.stats.size = (FileSize)s.st_size;
1434                   file.stats.accessed = s.st_atime;
1435                   file.stats.modified = s.st_mtime;
1436                   file.stats.created = s.st_ctime;
1437                }
1438                strcpy(d.name, path);
1439
1440                result = file;
1441             }
1442 #endif
1443          }
1444
1445          if(!result)
1446             delete d;
1447       }
1448       if(!result)
1449          delete file;
1450    }
1451    if(result)
1452    {
1453       while(result && !result.Validate(extensions))
1454          result = result.FindNext(extensions);
1455    }
1456    return result;
1457 }
1458
1459 private class FileDesc : struct
1460 {
1461    FileStats stats;
1462    char name[MAX_FILENAME];
1463    char path[MAX_LOCATION];
1464
1465    subclass(FileSystem) system;
1466    Dir dir;
1467
1468    bool Validate(const char * extensions)
1469    {
1470       if(strcmp(name, "..") && strcmp(name, ".") && strcmp(name, ""))
1471       {
1472          if(extensions && !stats.attribs.isDirectory)
1473          {
1474             char extension[MAX_EXTENSION], compared[MAX_EXTENSION];
1475             int c;
1476
1477             GetExtension(name, extension);
1478             for(c = 0; extensions[c];)
1479             {
1480                int len = 0;
1481                char ch;
1482                for(;(ch = extensions[c]) && !IS_ALUNDER(ch); c++);
1483                for(;(ch = extensions[c]) &&  IS_ALUNDER(ch); c++)
1484                   compared[len++] = ch;
1485                compared[len] = '\0';
1486
1487                if(!strcmpi(extension, compared))
1488                   return true;
1489             }
1490          }
1491          else
1492             return true;
1493       }
1494       return false;
1495    }
1496
1497    FileDesc FindNext(const char * extensions)
1498    {
1499       FileDesc result = null;
1500
1501       Dir d = dir;
1502
1503       name[0] = '.';
1504       name[1] = '\0';
1505       while(!Validate(extensions))
1506       {
1507          result = null;
1508
1509          if(system)
1510          {
1511             if(system.FindNext(this))
1512                result = this;
1513             else
1514                break;
1515          }
1516          else
1517          {
1518 #if defined(__WIN32__)
1519             if(!strcmp(d.name, "/"))
1520             {
1521                int c;
1522                uint drives = (uint)(uintptr)d.fHandle;
1523                for(c = 0; c<26; c++)
1524                {
1525                   if(drives & (1<<c))
1526                   {
1527                      char volume[MAX_FILENAME] = "";
1528                      int driveType;
1529                      uint16 _wpath[4];
1530                      uint16 _wvolume[MAX_FILENAME];
1531
1532                      stats.attribs = FileAttribs { isDirectory = true, isDrive = true };
1533                      stats.size = 0;
1534                      stats.accessed = stats.created = stats.modified = 0;
1535                      _wpath[0] = path[0] = (char)('A' + c);
1536                      _wpath[1] = path[1] = ':';
1537                      _wpath[2] = path[2] = '\\';
1538                      _wpath[3] = path[3] = 0;
1539                      driveType = GetDriveType(_wpath);
1540                      drives ^= (1<<c);
1541
1542                      switch(driveType)
1543                      {
1544                         case DRIVE_REMOVABLE: stats.attribs.isRemovable = true; break;
1545                         case DRIVE_REMOTE:    stats.attribs.isRemote = true;    break;
1546                         case DRIVE_CDROM:     stats.attribs.isCDROM = true;     break;
1547                      }
1548                      if(driveType == DRIVE_NO_ROOT_DIR)
1549                      {
1550                         uint16 remoteName[1024];
1551                         int status;
1552                         DWORD size = 1024;
1553                         _wpath[2] = 0;
1554
1555                         status = WNetGetConnection(_wpath, remoteName, &size);
1556                         if(status != ERROR_CONNECTION_UNAVAIL)
1557                            continue;
1558
1559                         _wpath[2] = '\\';
1560                         _wpath[3] = 0;
1561                      }
1562
1563                      if(driveType != DRIVE_REMOVABLE && driveType != DRIVE_REMOTE &&
1564                         GetVolumeInformation(_wpath, _wvolume, MAX_FILENAME - 1, null, null, null, null, 0))
1565                      {
1566                         UTF16toUTF8Buffer(_wvolume, volume, MAX_FILENAME);
1567                         path[2] = '\0';
1568                         sprintf(name, "%s [%s]", path, volume);
1569                      }
1570                      else
1571                      {
1572                         path[2] = '\0';
1573                         strcpy(name, path);
1574                      }
1575                      result = this;
1576                      break;
1577                   }
1578                }
1579                d.fHandle = (HANDLE)(uintptr) drives;
1580                break;
1581             }
1582             else if(d.name[0] != '\\' || d.name[1] != '\\' || strstr(d.name+2, "\\"))
1583             {
1584                WIN32_FIND_DATA winFile;
1585                if(FindNextFile(d.fHandle, &winFile))
1586                {
1587                   UTF16toUTF8Buffer(winFile.cFileName, name, MAX_FILENAME);
1588                   stats.attribs = FileAttribs { };
1589                   stats.attribs.isArchive   = (winFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)   ? true : false;
1590                   stats.attribs.isHidden    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)    ? true : false;
1591                   stats.attribs.isReadOnly  = (winFile.dwFileAttributes & FILE_ATTRIBUTE_READONLY)  ? true : false;
1592                   stats.attribs.isSystem    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)    ? true : false;
1593                   stats.attribs.isTemporary = (winFile.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) ? true : false;
1594                   stats.attribs.isDirectory = (winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
1595                   stats.attribs.isFile      = !(winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1596                   stats.size = winFile.nFileSizeLow;
1597
1598                   stats.accessed = Win32FileTimeToTimeStamp(&winFile.ftLastAccessTime);
1599                   stats.modified = Win32FileTimeToTimeStamp(&winFile.ftLastWriteTime);
1600                   stats.created  = Win32FileTimeToTimeStamp(&winFile.ftCreationTime);
1601
1602                   strcpy(path, d.name);
1603                   PathCat(path, name);
1604                   /*if(d.name[0])
1605                      strcat(path, DIR_SEPS);
1606                   strcat(path, name);*/
1607                   result = this;
1608                }
1609                else
1610                   break;
1611             }
1612             else
1613             {
1614                if(d.name[2])
1615                {
1616                   if(d.resource < d.numResources)
1617                   {
1618                      stats.attribs = FileAttribs { isDirectory = true, isShare = true };
1619                      stats.size = 0;
1620                      stats.accessed = stats.created = stats.modified = 0;
1621
1622                      UTF16toUTF8Buffer(d.resources[d.resource].lpRemoteName, path, MAX_LOCATION);
1623                      GetLastDirectory(path, name);
1624
1625                      result = this;
1626
1627                      d.resource++;
1628                   }
1629                   else
1630                   {
1631                      delete d.resources;
1632                      break;
1633                   }
1634                }
1635                else
1636                {
1637                   int c;
1638                   for(c = d.workGroup; c<d.numWorkGroups; c++)
1639                   {
1640                      if(c != d.workGroup)
1641                      {
1642                         DWORD countInGroup = 0xFFFFFFFF;
1643                         HANDLE handle;
1644                         NETRESOURCE * resources;
1645                         DWORD size = 512 * sizeof(NETRESOURCE);
1646
1647                         resources = (NETRESOURCE *)new0 byte[size];
1648                         // Entire Network
1649                         WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &d.workGroups[c], &handle);
1650                         while(true)
1651                         {
1652                            int returnCode = WNetEnumResource(handle, &countInGroup, resources, &size);
1653                            if(returnCode != ERROR_MORE_DATA)
1654                               break;
1655                            countInGroup = 0xFFFFFFFF;
1656                            resources = (NETRESOURCE *)renew0 resources byte[size];
1657
1658                         }
1659                         WNetCloseEnum(handle);
1660                         d.numResources = countInGroup;
1661                         d.resources = resources;
1662                         d.resource = 0;
1663                      }
1664
1665                      if(d.resource < d.numResources)
1666                      {
1667                         stats.attribs = FileAttribs { isDirectory = true, isServer = true };
1668                         stats.size = 0;
1669                         stats.accessed = stats.created = stats.modified = 0;
1670
1671                         UTF16toUTF8Buffer(d.resources[d.resource].lpRemoteName, path, MAX_LOCATION);
1672                         strlwr(path);
1673                         path[2] = (char)toupper(path[2]);
1674                         GetLastDirectory(path, name);
1675
1676                         result = this;
1677
1678                         d.resource++;
1679                         break;
1680                      }
1681                      else
1682                      {
1683                         if(d.resources)
1684                            delete d.resources;
1685                      }
1686                   }
1687                   d.workGroup = c;
1688                   if(d.workGroup == d.numWorkGroups && d.resource == d.numResources)
1689                   {
1690                      delete d.workGroups;
1691                      break;
1692                   }
1693                }
1694             }
1695 #else
1696             struct dirent *de;
1697             struct stat s;
1698
1699             de = readdir(d.d);
1700             if(de)
1701             {
1702                strcpy(name,de->d_name);
1703                strcpy(path, d.name);
1704                if(d.name[0] && d.name[1])
1705                   strcat(path, DIR_SEPS);
1706                strcat(path, name);
1707                if(!stat(path, &s))
1708                {
1709                   stats.attribs = FileAttribs { };
1710                   stats.attribs = (s.st_mode&S_IFDIR) ? FileAttribs { isDirectory = true } : FileAttribs { isFile = true };
1711                   stats.size = (FileSize)s.st_size;
1712                   stats.accessed = s.st_atime;
1713                   stats.modified = s.st_mtime;
1714                   stats.created = s.st_ctime;
1715                }
1716                result = this;
1717             }
1718             else
1719                break;
1720 #endif
1721          }
1722       }
1723       if(!result)
1724          CloseDir();
1725       return result;
1726    }
1727
1728    void CloseDir(void)
1729    {
1730       if(system)
1731          system.CloseDir(this);
1732       else
1733       {
1734          Dir d = dir;
1735          if(d)
1736          {
1737 #if defined(__WIN32__)
1738             if(d.fHandle && strcmp(d.name, "/"))
1739                FindClose(d.fHandle);
1740 #else
1741             closedir(d.d);
1742 #endif
1743             delete d;
1744          }
1745       }
1746       delete this;
1747    }
1748 }
1749
1750 public struct FileListing
1751 {
1752 public:
1753    const char * directory;
1754    const char * extensions;
1755
1756    bool Find()
1757    {
1758       if(desc)
1759          desc = desc.FindNext(extensions);
1760       else
1761          desc = FileFind(directory, extensions);
1762       if(desc)
1763          return true;
1764       return false;
1765    }
1766
1767    void Stop()
1768    {
1769       if(desc)
1770          desc.CloseDir();
1771       desc = null;
1772    }
1773
1774    property const char * name { get { return desc ? desc.name : null; } };
1775    property const char * path { get { return desc ? desc.path : null; } };
1776    property FileStats stats { get { value = desc ? desc.stats : FileStats { }; } };
1777
1778 private:
1779    FileDesc desc;
1780 };
1781 #endif
1782
1783 public File CreateTemporaryFile(char * tempFileName, const char * template)
1784 {
1785 #ifndef ECERE_BOOTSTRAP // quick fix for now
1786    File f;
1787 #if defined(__unix__) || defined(__APPLE__)
1788    char buffer[MAX_FILENAME];
1789    int fd;
1790    strcpy(buffer, "/tmp/");
1791    strcat(buffer, template);
1792    //strcpy(buffer, template);
1793    strcat(buffer, "XXXXXX");
1794    // mktemp(buffer);
1795    fd = mkstemp(buffer);
1796    strcpy(tempFileName, buffer);
1797    f = { };
1798    f.output = f.input = fdopen(fd, "r+");
1799 #else
1800    char tempPath[MAX_LOCATION];
1801    GetTempPathA(MAX_LOCATION, tempPath);     // TODO: Patch this whole thing to support Unicode temp path
1802    GetTempFileNameA(tempPath, template, 0, tempFileName);
1803    f = FileOpen(tempFileName, readWrite);
1804 #endif
1805    return f;
1806 #else
1807    return null;
1808 #endif
1809 }
1810
1811 #undef DeleteFile
1812
1813 public void CreateTemporaryDir(char * tempFileName, const char * template)
1814 {
1815 #ifndef ECERE_BOOTSTRAP // quick fix for now
1816 #if defined(__unix__) || defined(__APPLE__)
1817    char buffer[MAX_FILENAME];
1818    strcpy(buffer, "/tmp/");
1819    strcat(buffer, template);
1820    //strcpy(buffer, template);
1821    strcat(buffer, "XXXXXX");
1822    // mkstemp(buffer);
1823    mkdtemp(buffer);
1824    strcpy(tempFileName, buffer);
1825 #else
1826    char tempPath[MAX_LOCATION];
1827    GetTempPathA(MAX_LOCATION, tempPath);     // TODO: Patch this whole thing to support Unicode temp path
1828    GetTempFileNameA(tempPath, template, 0, tempFileName);
1829    DeleteFile(tempFileName);
1830    MakeDir(tempFileName);
1831 #endif
1832 #endif
1833 }
1834
1835 public void MakeSlashPath(char * p)
1836 {
1837    FileFixCase(p);
1838    if(__runtimePlatform == win32)
1839       ChangeCh(p, '\\', '/');
1840 }
1841
1842 public void MakeSystemPath(char * p)
1843 {
1844    FileFixCase(p);
1845 }
1846
1847 public char * CopySystemPath(const char * p)
1848 {
1849    char * d = CopyString(p);
1850    if(d)
1851       MakeSystemPath(d);
1852    return d;
1853 }
1854
1855 public char * CopyUnixPath(const char * p)
1856 {
1857    char * d = CopyString(p);
1858    if(d)
1859       MakeSlashPath(d);
1860    return d;
1861 }
1862
1863 public char * GetSystemPathBuffer(char * d, const char * p)
1864 {
1865    if(d != p)
1866       strcpy(d, p ? p : "");
1867    MakeSystemPath(d);
1868    return d;
1869 }
1870
1871 public char * GetSlashPathBuffer(char * d, const char * p)
1872 {
1873    if(d != p)
1874       strcpy(d, p ? p : "");
1875    MakeSlashPath(d);
1876    return d;
1877 }