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