ecere/com/containers; sys/File: Fixed refCount of deserialized containers, Files
[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                   incref this;
427                   uncompress(uncompressed, &size, compressed, cSize);
428                   Write(uncompressed, 1, (uint)size);
429                   Seek(0, start);
430
431                   delete uncompressed;
432                }
433             }
434             delete compressed;
435          }
436       }
437
438       /*
439       byte data[4096];
440       uint c;
441
442       size.OnUnserialize(channel);
443       if(size != MAXDWORD)
444       {
445          this = TempFile { };
446          for(c = 0; c<size; c += sizeof(data))
447          {
448             uint count = Min(size - c, sizeof(data));
449             channel.ReadData(data, count);
450             Write(data, 1, count);
451          }
452          Seek(0, start);
453       }
454       else
455          this = null;
456       */
457    }
458 #endif
459
460 public:
461
462    // Virtual Methods
463    virtual bool Seek(int pos, FileSeekMode mode)
464    {
465       uint fmode = SEEK_SET;
466       switch(mode)
467       {
468          case start: fmode = SEEK_SET; break;
469          case end: fmode = SEEK_END; break;
470          case current: fmode = SEEK_CUR; break;
471       }
472       return fseek(input ? input : output, pos, fmode) != EOF;
473    }
474
475    virtual uint Tell(void)
476    {
477       return (uint)(input ? ftell(input) : ftell(output));
478    }
479
480    virtual int Read(void * buffer, uint size, uint count)
481    {
482       return input ? (int)fread(buffer, size, count, input) : 0;
483    }
484
485    virtual int Write(const void * buffer, uint size, uint count)
486    {
487       return output ? (int)fwrite(buffer, size, count, output) : 0;
488    }
489
490    // UNICODE OR NOT?
491    virtual bool Getc(char * ch)
492    {
493       int ich = fgetc(input);
494       if(ich != EOF)
495       {
496          if(ch) *ch = (char)ich;
497          return true;
498       }
499       return false;
500    }
501
502    virtual bool Putc(char ch)
503    {
504       return (fputc((int)ch, output) == EOF) ? false : true;
505    }
506
507    virtual bool Puts(const char * string)
508    {
509       bool result = false;
510       if(output)
511       {
512          result = (fputs(string, output) == EOF) ? false : true;
513          // TODO: Check if any repercusions of commenting out fflush here
514          // This is what broke the debugger in 0.44d2 , it is required for outputting things to the DualPipe
515          // Added an explicit flush call in DualPipe::Puts
516          // fflush(output);
517       }
518       return result;
519    }
520
521    virtual bool Eof(void)
522    {
523       return input ? feof(input) != 0 : true;
524    }
525
526    virtual bool Truncate(FileSize size)
527    {
528    #ifdef ECERE_BOOTSTRAP
529       fprintf(stderr, "WARNING:  File::Truncate unimplemented in ecereBootstrap.\n");
530       return false;
531    #else
532    #if defined(__WIN32__)
533       return output ? (_chsize(fileno(output), size) == 0) : false;
534    #else
535       return output ? (ftruncate(fileno(output), size) == 0) : false;
536    #endif
537    #endif
538    }
539
540    virtual uint GetSize(void)
541    {
542       return FILE_GetSize(input);
543    }
544
545    virtual void CloseInput(void)
546    {
547       if(input)
548       {
549          fclose(input);
550          if(output == input)
551             output = null;
552          input = null;
553       }
554    }
555
556    virtual void CloseOutput(void)
557    {
558       if(output)
559       {
560          fclose(output);
561          if(input == output)
562             input = null;
563          output = null;
564       }
565    }
566
567    virtual bool Lock(FileLock type, uint64 start, uint64 length, bool wait)
568    {
569       return FILE_Lock(input, output, type, start, length, wait);
570    }
571
572    virtual bool Unlock(uint64 start, uint64 length, bool wait)
573    {
574       return Lock(unlocked, start, length, wait);
575    }
576
577    // Normal Methods
578    int Printf(const char * format, ...)
579    {
580       int result = 0;
581       if(format)
582       {
583          char text[MAX_F_STRING];
584          va_list args;
585          va_start(args, format);
586          vsnprintf(text, sizeof(text), format, args);
587          text[sizeof(text)-1] = 0;
588          if(Puts(text))
589             result = strlen(text);
590          va_end(args);
591       }
592       return result;
593    }
594
595    public void PrintLn(typed_object object, ...)
596    {
597       va_list args;
598       char buffer[4096];
599       va_start(args, object);
600       PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
601       Puts(buffer);
602       Putc('\n');
603       va_end(args);
604    }
605
606    public void Print(typed_object object, ...)
607    {
608       va_list args;
609       char buffer[4096];
610       va_start(args, object);
611       PrintStdArgsToBuffer(buffer, sizeof(buffer), object, args);
612       Puts(buffer);
613       va_end(args);
614    }
615
616    bool Flush(void)
617    {
618       fflush(output);
619       return true;
620    }
621
622    bool GetLine(char *s, int max)
623    {
624       int c = 0;
625       bool result = true;
626       s[c]=0;
627
628       if(Eof())
629       {
630          result = false;
631       }
632       else
633       {
634          while(c<max-1)
635          {
636             char ch = 0;
637
638             if(/*!Peek() || */ !Getc(&ch))
639             {
640                result = false;
641                break;
642             }
643             if(ch =='\n')
644                break;
645             if(ch !='\r')
646                s[c++]=ch;
647          }
648       }
649       s[c]=0;
650       return result || c > 1;
651    }
652
653    // Strings and numbers separated by spaces, commas, tabs, or CR/LF, handling quotes
654    bool GetString(char * string, int max)
655    {
656       int c;
657       char ch;
658       bool quoted = false;
659       bool result = true;
660
661       *string = 0;
662       while(true)
663       {
664          if(!Getc(&ch))
665          {
666             result = false;
667             break;
668          }
669          if( (ch!='\n') && (ch!='\r') && (ch!=' ') && (ch!=',') && (ch!='\t'))
670             break;
671          if(Eof()) break;
672       }
673       if(result)
674       {
675          for(c=0; c<max-1; c++)
676          {
677             if(!quoted && ((ch=='\n')||(ch=='\r')||(ch==' ')||(ch==',')||(ch=='\t')))
678             {
679                result = true;
680                break;
681             }
682             if(ch == '\"')
683             {
684                quoted ^= 1;
685                c--;
686             }
687             else
688                string[c]=ch;
689
690             if(!Getc(&ch))
691             {
692                c++;
693                result = false;
694                break;
695             }
696          }
697          string[c]=0;
698       }
699       return result;
700    }
701
702    int GetValue(void)
703    {
704       char string[32];
705       GetString(string,sizeof(string));
706       return atoi(string);
707    }
708
709    unsigned int GetHexValue(void)
710    {
711       char string[32];
712       GetString(string, sizeof(string));
713       return (uint)strtoul(string, null, 16);
714    }
715
716    float GetFloat(void)
717    {
718       char string[32];
719       GetString(string, sizeof(string));
720       return (float)FloatFromString(string);
721    }
722
723    double GetDouble(void)
724    {
725       char string[32];
726       GetString(string, sizeof(string));
727       return FloatFromString(string);
728    }
729
730    property void * input { set { input = value; } get { return input; } }
731    property void * output { set { output = value; } get { return output; } }
732    property bool buffered
733    {
734       set
735       {
736          FILE_set_buffered(input, output, value);
737       }
738    }
739    property bool eof { get { return Eof(); } }
740
741    int GetLineEx(char *s, int max, bool *hasNewLineChar)
742    {
743       int c = 0;
744       s[c] = '\0';
745
746       if(!Eof())
747       {
748          char ch = '\0';
749          while(c < max - 1)
750          {
751             if(/*!Peek() || */ !Getc(&ch))
752                break;
753             if(ch == '\n')
754                break;
755             if(ch != '\r')
756                s[c++] = ch;
757          }
758          if(hasNewLineChar)
759             *hasNewLineChar = (ch == '\n');
760       }
761       s[c] = '\0';
762       return c;
763    }
764
765    bool CopyTo(const char * outputFileName)
766    {
767       bool result = false;
768       File f = FileOpen(outputFileName, write);
769       if(f)
770       {
771          byte buffer[65536];
772
773          result = true;
774          Seek(0, start);
775          while(!Eof())
776          {
777             uint count = Read(buffer, 1, sizeof(buffer));
778             if(count && !f.Write(buffer, 1, count))
779             {
780                result = false;
781                break;
782             }
783          }
784          delete f;
785       }
786       Seek(0, start);
787       return result;
788    }
789
790 #if 0
791    virtual bool Open(const char * fileName, FileOpenMode mode)
792    {
793       bool result = false;
794       if(this)
795       {
796          FILE_FileOpen(fileName, mode, &input, &output);
797
798          //file.mode = mode;
799          if(!input && !output);
800          else
801          {
802             openCount++;
803             result = true;
804             // TESTING ENABLING FILE BUFFERING BY DEFAULT... DOCUMENT ANY ISSUE
805             /*
806             if(file.input)
807                setvbuf(file.input, null, _IONBF, 0);
808             else
809                setvbuf(file.output, null, _IONBF, 0);
810             */
811          }
812          //if(!result)
813          {
814             /* TOFIX:
815             LogErrorCode((mode == Read || mode == ReadWrite) ?
816                ERR_FILE_NOT_FOUND : ERR_FILE_WRITE_FAILED, fileName);
817             */
818          }
819       }
820       return result;
821    }
822 #endif
823
824    virtual void Close()
825    {
826       CloseOutput();
827       CloseInput();
828    }
829 }
830
831 #if defined(__WIN32__)
832 default extern intptr_t stdinHandle;
833 default extern intptr_t stdoutHandle;
834 #endif
835
836 public class ConsoleFile : File
837 {
838    input = eC_stdin();
839    output = eC_stdout();
840
841 #if defined(__WIN32__)
842    void CloseInput()
843    {
844       CloseHandle((HANDLE)stdinHandle);
845    }
846    /*
847    void CloseOutput()
848    {
849       CloseHandle((HANDLE)stdoutHandle);
850    }*/
851 #endif
852
853    ~ConsoleFile()
854    {
855       input = null;
856       output = null;
857    }
858 };
859
860 public class FileAttribs : bool
861 {
862 public:
863    bool isFile:1, isArchive:1, isHidden:1, isReadOnly:1, isSystem:1, isTemporary:1, isDirectory:1;
864    bool isDrive:1, isCDROM:1, isRemote:1, isRemovable:1, isServer:1, isShare:1;
865    // property bool { };
866 };
867
868 #ifdef ECERE_BOOTSTRAP
869 public class SecSince1970 : int64;
870 #endif
871
872 public struct FileStats
873 {
874    FileAttribs attribs;
875    FileSize size;
876    SecSince1970 accessed;
877    SecSince1970 modified;
878    SecSince1970 created;
879 };
880
881 #if defined(__WIN32__)
882
883 // --- FileName functions ---
884
885 default TimeStamp Win32FileTimeToTimeStamp(FILETIME * fileTime)
886 {
887    // TIME_ZONE_INFORMATION tz = { 0 };
888    SYSTEMTIME st, lt;
889    DateTime t;
890
891    FileTimeToSystemTime(fileTime, &lt);
892
893    /*
894    GetTimeZoneInformation(&tz);
895    tz.Bias = 0;
896    _TzSpecificLocalTimeToSystemTime(&tz, &lt, &st);
897    */
898    st = lt;
899
900    t.year = st.wYear;
901    t.month = (Month)(st.wMonth - 1);
902    t.day = st.wDay;
903    t.hour = st.wHour;
904    t.minute = st.wMinute;
905    t.second = st.wSecond;
906    return t;
907 }
908
909 default void TimeStampToWin32FileTime(TimeStamp t, FILETIME * fileTime)
910 {
911    // TIME_ZONE_INFORMATION tz = { 0 };
912    SYSTEMTIME st, lt;
913    DateTime tm;
914
915    tm = t;
916
917    st.wYear = (short)tm.year;
918    st.wMonth = (short)(tm.month + 1);
919    st.wDay = (short)tm.day;
920    st.wHour = (short)tm.hour;
921    st.wMinute = (short)tm.minute;
922    st.wSecond = (short)tm.second;
923    st.wMilliseconds = 0;
924    st.wDayOfWeek = 0;
925
926    /*
927    GetTimeZoneInformation(&tz);
928    tz.Bias = 0;
929    SystemTimeToTzSpecificLocalTime(&tz, &st, &lt);
930    */
931
932    lt = st;
933    SystemTimeToFileTime(&lt, fileTime);
934 }
935 /*
936 default TimeStamp Win32FileTimeToTimeStamp(FILETIME * fileTime);
937 default void TimeStampToWin32FileTime(TimeStamp t, FILETIME * fileTime);
938 */
939 default bool WinReviveNetworkResource(uint16 * _wfileName);
940
941 #endif
942
943 public FileAttribs FileExists(const char * fileName)
944 {
945 #if !defined(ECERE_BOOTSTRAP)
946    char archiveName[MAX_LOCATION];
947    const char * archiveFile;
948    if(SplitArchivePath(fileName, archiveName, &archiveFile))
949    {
950       return EARFileSystem::Exists(archiveName, archiveFile);
951    }
952    else if(strstr(fileName, "http://") == fileName)
953    {
954       return FileAttribs { isFile = true };
955    }
956    else
957 #endif
958       return FILE_FileExists(fileName);
959 }
960
961 static int openCount;
962
963 public File FileOpen(const char * fileName, FileOpenMode mode)
964 {
965    File result = null;
966    if(fileName)
967    {
968 #if !defined(ECERE_BOOTSTRAP)
969       char archiveName[MAX_LOCATION];
970       const char * archiveFile;
971       if(SplitArchivePath(fileName, archiveName, &archiveFile))
972       {
973          result = EARFileSystem::Open(archiveName, archiveFile, mode);
974       }
975 #if !defined(ECERE_VANILLA) && !defined(ECERE_NONET)
976       else if(strstr(fileName, "http://") == fileName || strstr(fileName, "https://"))
977       {
978          result = FileOpenURL(fileName);
979       }
980 #endif
981       else
982 #endif
983       if(strstr(fileName, "File://") == fileName)
984       {
985          result = (File)(uintptr)strtoull(fileName+7, null, 16);
986          if(result)
987          {
988             if(result._class && eClass_IsDerived(result._class, class(File)))
989             {
990                if(!result._refCount) incref result;
991                incref result;
992                result.Seek(0, start);
993             }
994             else
995                result = null;
996          }
997       }
998       else
999       {
1000          File file = File {};
1001          if(file)
1002          {
1003             FILE_FileOpen(fileName, mode, &file.input, &file.output);
1004
1005             //file.mode = mode;
1006             if(!file.input && !file.output);
1007             else
1008             {
1009                openCount++;
1010                result = file;
1011                // TESTING ENABLING FILE BUFFERING BY DEFAULT... DOCUMENT ANY ISSUE
1012                /*
1013                if(file.input)
1014                   setvbuf(file.input, null, _IONBF, 0);
1015                else
1016                   setvbuf(file.output, null, _IONBF, 0);
1017                */
1018             }
1019             if(!result)
1020             {
1021                delete file;
1022                /* TOFIX:
1023                LogErrorCode((mode == Read || mode == ReadWrite) ?
1024                   ERR_FILE_NOT_FOUND : ERR_FILE_WRITE_FAILED, fileName);
1025                */
1026             }
1027          }
1028       }
1029    }
1030    return result;
1031 }
1032
1033 public void FileFixCase(char * file)
1034 {
1035    FILE_FileFixCase(file);
1036 }
1037
1038 #if !defined(ECERE_BOOTSTRAP)
1039 public bool FileTruncate(const char * fileName, FileSize size)
1040 {
1041 #if defined(__WIN32__)
1042    uint16 * _wfileName = UTF8toUTF16(fileName, null);
1043    int f = _wopen(_wfileName, _O_RDWR|_O_CREAT, _S_IREAD|_S_IWRITE);
1044    bool result = false;
1045    if(f != -1)
1046    {
1047       if(!_chsize(f, size))
1048          result = true;
1049       _close(f);
1050    }
1051    delete _wfileName;
1052    return result;
1053 #else
1054    return truncate(fileName, size) == 0;
1055 #endif
1056 }
1057 #endif
1058
1059 public bool FileGetSize(const char * fileName, FileSize * size)
1060 {
1061    bool result = false;
1062    if(size)
1063    {
1064       *size = 0;
1065       if(fileName)
1066       {
1067 #if !defined(ECERE_BOOTSTRAP)
1068          char archiveName[MAX_LOCATION];
1069          const char * archiveFile;
1070          if(SplitArchivePath(fileName, archiveName, &archiveFile))
1071             return EARFileSystem::GetSize(archiveName, archiveFile, size);
1072          else
1073 #endif
1074             result = FILE_FileGetSize(fileName, size);
1075       }
1076    }
1077    return result;
1078 }
1079
1080 public bool FileGetStats(const char * fileName, FileStats stats)
1081 {
1082    bool result = false;
1083    if(stats && fileName)
1084    {
1085 #if !defined(ECERE_BOOTSTRAP)
1086       char archiveName[MAX_LOCATION];
1087       const char * archiveFile;
1088       if(SplitArchivePath(fileName, archiveName, &archiveFile))
1089          result = EARFileSystem::Stats(archiveName, archiveFile, stats);
1090       else
1091 #endif
1092          return FILE_FileGetStats(fileName, stats);
1093    }
1094    return result;
1095 }
1096
1097 #ifndef ECERE_BOOTSTRAP
1098
1099 public bool FileSetAttribs(const char * fileName, FileAttribs attribs)
1100 {
1101 #ifdef __WIN32__
1102    uint winAttribs = 0;
1103    uint16 * _wfileName = UTF8toUTF16(fileName, null);
1104
1105    if(attribs.isHidden)   winAttribs |= FILE_ATTRIBUTE_HIDDEN;
1106    if(attribs.isReadOnly) winAttribs |= FILE_ATTRIBUTE_READONLY;
1107
1108    SetFileAttributes(_wfileName, winAttribs);
1109    delete _wfileName;
1110 #endif
1111    return true;
1112 }
1113
1114 public bool FileSetTime(const char * fileName, TimeStamp created, TimeStamp accessed, TimeStamp modified)
1115 {
1116    bool result = false;
1117    TimeStamp currentTime = time(null);
1118    if(!created)  created = currentTime;
1119    if(!accessed) accessed = currentTime;
1120    if(!modified) modified = currentTime;
1121    if(fileName)
1122    {
1123 #ifdef __WIN32__
1124       uint16 * _wfileName = UTF8toUTF16(fileName, null);
1125       HANDLE hFile = CreateFile(_wfileName, GENERIC_WRITE|GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, null,
1126          OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, null);
1127       delete _wfileName;
1128       if(hFile != INVALID_HANDLE_VALUE)
1129       {
1130          FILETIME c, a, m;
1131
1132          TimeStampToWin32FileTime(created, &c);
1133          TimeStampToWin32FileTime(accessed, &a);
1134          TimeStampToWin32FileTime(modified, &m);
1135
1136          /*
1137          {
1138             uint cc,aa,mm;
1139
1140             cc = Win32FileTimeToTimeStamp(&c);
1141             aa = Win32FileTimeToTimeStamp(&a);
1142             mm = Win32FileTimeToTimeStamp(&m);
1143          }
1144          */
1145
1146          if(SetFileTime(hFile, &c, &a, &m))
1147             result = true;
1148
1149          CloseHandle(hFile);
1150       }
1151 #else
1152       struct utimbuf t = { (int)accessed, (int)modified };
1153       if(!utime(fileName, &t))
1154          result = true;
1155 #endif
1156    }
1157    return result;
1158 }
1159
1160 /****************************************************************************
1161  Directory Listing
1162 ****************************************************************************/
1163 // Directory Description for file listing
1164 private class Dir : struct
1165 {
1166 #if defined(__WIN32__)
1167    HANDLE fHandle;
1168
1169    int resource;
1170    NETRESOURCE * resources;
1171    int numResources;
1172
1173    int workGroup;
1174    NETRESOURCE * workGroups;
1175    int numWorkGroups;
1176 #else
1177    DIR * d;
1178 #endif
1179    char name[MAX_LOCATION];
1180 };
1181
1182 static FileDesc FileFind(const char * path, const char * extensions)
1183 {
1184    FileDesc result = null;
1185    FileDesc file;
1186
1187    if((file = FileDesc {}))
1188    {
1189       char archiveName[MAX_LOCATION];
1190       const char * archiveFile;
1191       if(SplitArchivePath(path, archiveName, &archiveFile))
1192       {
1193          if(EARFileSystem::Find(file, archiveName, archiveFile))
1194          {
1195             file.system = class(EARFileSystem);
1196             result = file;
1197          }
1198       }
1199       else
1200       {
1201          Dir d;
1202
1203          if((d = file.dir = Dir {}))
1204          {
1205 #if defined(__WIN32__)
1206             if(!strcmp(path, "/"))
1207             {
1208                int c;
1209                uint drives = 0xFFFFFFFF;
1210                d.fHandle = (HANDLE)(uintptr)drives; //GetLogicalDrives();
1211                for(c = 0; c<26; c++)
1212                   if(((uint)(uintptr)d.fHandle) & (1<<c))
1213                   {
1214                      char volume[MAX_FILENAME] = "";
1215                      uint16 _wvolume[MAX_FILENAME];
1216                      int driveType;
1217                      uint16 _wfilePath[4];
1218
1219                      strcpy(d.name, path);
1220                      file.stats.attribs = FileAttribs { isDirectory = true, isDrive = true };
1221                      _wfilePath[0] = file.path[0] = (char)('A' + c);
1222                      _wfilePath[1] = file.path[1] = ':';
1223                      _wfilePath[2] = file.path[2] = '\\';
1224                      _wfilePath[3] = file.path[3] = '\0';
1225                      file.stats.size = 0;
1226                      file.stats.accessed = file.stats.created = file.stats.modified = 0;
1227                      driveType = GetDriveType(_wfilePath);
1228                      switch(driveType)
1229                      {
1230                         case DRIVE_REMOVABLE: file.stats.attribs.isRemovable = true; break;
1231                         case DRIVE_REMOTE:    file.stats.attribs.isRemote = true; break;
1232                         case DRIVE_CDROM:     file.stats.attribs.isCDROM = true; break;
1233                      }
1234                      drives ^= (1<<c);
1235                      if(driveType == DRIVE_NO_ROOT_DIR) continue;
1236
1237                      if(driveType != DRIVE_REMOVABLE && driveType != DRIVE_REMOTE &&
1238                         GetVolumeInformation(_wfilePath, _wvolume, MAX_FILENAME - 1, null, null, null, null, 0))
1239                      {
1240                         file.path[2] = '\0';
1241                         UTF16toUTF8Buffer(_wvolume, volume, MAX_FILENAME);
1242                         sprintf(file.name, "%s [%s]", file.path, volume);
1243                      }
1244                      else
1245                      {
1246                         file.path[2] = '\0';
1247                         strcpy(file.name, file.path);
1248                      }
1249                      result = file;
1250                      break;
1251                   }
1252                d.fHandle = (HANDLE)(uintptr) drives;
1253                d.resource = 0;
1254             }
1255             else if(path[0] != '\\' || path[1] != '\\' || strstr(path+2, "\\"))
1256             {
1257                WIN32_FIND_DATA winFile;
1258                uint16 dir[MAX_PATH];
1259
1260                UTF8toUTF16Buffer(path, dir, MAX_LOCATION);
1261                if(path[0]) wcscat(dir, L"\\");
1262                wcscat(dir, L"*.*");
1263
1264                d.fHandle = FindFirstFile(dir, &winFile);
1265                if(d.fHandle == INVALID_HANDLE_VALUE && WinReviveNetworkResource(dir))
1266                   d.fHandle = FindFirstFile(dir, &winFile);
1267                if(d.fHandle != INVALID_HANDLE_VALUE)
1268                {
1269                   UTF16toUTF8Buffer(winFile.cFileName, file.name, MAX_FILENAME);
1270                   strcpy(file.path, path);
1271                   PathCat(file.path, file.name);
1272                   /*if(path[0])
1273                      strcat(file.path, DIR_SEPS);
1274                   strcat(file.path, file.name);*/
1275                   // file.sizeHigh = winFile.nFileSizeHigh;
1276                   file.stats.size = winFile.nFileSizeLow;
1277
1278                   file.stats.attribs = FileAttribs { };
1279                   file.stats.attribs.isArchive   = (winFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)   ? true : false;
1280                   file.stats.attribs.isHidden    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)    ? true : false;
1281                   file.stats.attribs.isReadOnly  = (winFile.dwFileAttributes & FILE_ATTRIBUTE_READONLY)  ? true : false;
1282                   file.stats.attribs.isSystem    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)    ? true : false;
1283                   file.stats.attribs.isTemporary = (winFile.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) ? true : false;
1284                   file.stats.attribs.isDirectory = (winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
1285                   file.stats.attribs.isFile = !(winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1286                   strcpy(d.name, path);
1287
1288                   file.stats.accessed = Win32FileTimeToTimeStamp(&winFile.ftLastAccessTime);
1289                   file.stats.modified = Win32FileTimeToTimeStamp(&winFile.ftLastWriteTime);
1290                   file.stats.created  = Win32FileTimeToTimeStamp(&winFile.ftCreationTime);
1291                   result = file;
1292                }
1293             }
1294             else
1295             {
1296                HANDLE handle = 0;
1297                DWORD count = 0xFFFFFFFF;
1298                DWORD size = 512 * sizeof(NETRESOURCE);
1299                NETRESOURCE * buffer = (NETRESOURCE *)new0 byte[size];
1300                NETRESOURCE nr = {0};
1301
1302                d.fHandle = null;
1303                nr.dwScope       = RESOURCE_GLOBALNET;
1304                nr.dwType        = RESOURCETYPE_DISK;
1305                nr.lpProvider = (uint16 *)L"Microsoft Windows Network";
1306
1307                strcpy(d.name, path);
1308                if(path[2])
1309                {
1310                   nr.lpRemoteName = UTF8toUTF16(path, null);
1311
1312                   // Server
1313                   WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr, &handle);
1314                   if(!handle)
1315                   {
1316                      WinReviveNetworkResource(nr.lpRemoteName);
1317                      WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr, &handle);
1318                   }
1319
1320                   if(handle)
1321                   {
1322                      while(true)
1323                      {
1324                         int returnCode = WNetEnumResource(handle, &count, buffer, &size);
1325                         if(returnCode != ERROR_MORE_DATA)
1326                            break;
1327                         count = 0xFFFFFFFF;
1328                         buffer = (NETRESOURCE *)renew0 buffer byte[size];
1329                      }
1330                      WNetCloseEnum(handle);
1331                   }
1332
1333                   delete nr.lpRemoteName;
1334                   if(count > 0)
1335                   {
1336                      file.stats.attribs = FileAttribs { isDirectory = true, isShare = true };
1337                      file.stats.size = 0;
1338                      file.stats.accessed = file.stats.created = file.stats.modified = 0;
1339
1340                      UTF16toUTF8Buffer(buffer->lpRemoteName, file.path, MAX_LOCATION);
1341                      GetLastDirectory(file.path, file.name);
1342
1343                      result = file;
1344                      d.resources = buffer;
1345                      d.numResources = count;
1346                      d.resource = 1;
1347                   }
1348                   else
1349                      delete buffer;
1350                }
1351                else
1352                {
1353                   int c;
1354                   nr.lpProvider = (uint16 *)L"Microsoft Windows Network";
1355
1356                   // Entire Network
1357                   WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &nr, &handle);
1358                   while(true)
1359                   {
1360                      int returnCode = WNetEnumResource(handle, &count, buffer, &size);
1361                      if(returnCode != ERROR_MORE_DATA)
1362                         break;
1363                      count = 0xFFFFFFFF;
1364                      buffer = (NETRESOURCE *)renew0 buffer byte[size];
1365                   }
1366                   WNetCloseEnum(handle);
1367
1368                   for(c = 0; c<count; c++)
1369                   {
1370                      NETRESOURCE * resources;
1371                      DWORD countInGroup = 0xFFFFFFFF;
1372
1373                      size = 512 * sizeof(NETRESOURCE);
1374                      resources = (NETRESOURCE *)new0 byte[size];
1375
1376                      // Entire Network
1377                      WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &buffer[c], &handle);
1378                      while(true)
1379                      {
1380                         int returnCode = WNetEnumResource(handle, &countInGroup, resources, &size);
1381                         if(returnCode != ERROR_MORE_DATA)
1382                            break;
1383                         countInGroup = 0xFFFFFFFF;
1384                         resources = (NETRESOURCE *)renew0 resources byte[size];
1385                      }
1386                      WNetCloseEnum(handle);
1387
1388                      if(countInGroup)
1389                      {
1390                         file.stats.attribs = FileAttribs { isDirectory = true, isServer = true };
1391                         file.stats.size = 0;
1392                         file.stats.accessed = file.stats.created = file.stats.modified = 0;
1393
1394                         UTF16toUTF8Buffer(resources->lpRemoteName, file.path, MAX_LOCATION);
1395                         strlwr(file.path);
1396                         file.path[2] = (char)toupper(file.path[2]);
1397                         GetLastDirectory(file.path, file.name);
1398
1399                         result = file;
1400
1401                         d.resources = resources;
1402                         d.numResources = countInGroup;
1403                         d.resource = 1;
1404
1405                         d.workGroups = buffer;
1406                         d.numWorkGroups = count;
1407                         d.workGroup = c;
1408                         break;
1409                      }
1410                      else
1411                         delete resources;
1412                   }
1413                   if(c >= count && buffer) delete buffer;
1414                }
1415             }
1416 #else
1417             struct dirent *de;
1418             struct stat s;
1419
1420             d.d = opendir((path && path[0]) ? path : ".");
1421             if(d.d && (de = readdir(d.d)))
1422             {
1423                if(path[0])
1424                {
1425                   strcpy(file.path, path);
1426                   if(path[1])
1427                      strcat(file.path, DIR_SEPS);
1428                }
1429                strcpy(file.name,de->d_name);
1430                strcat(file.path, file.name);
1431                if(!stat(file.path, &s))
1432                {
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                }
1439                strcpy(d.name, path);
1440
1441                result = file;
1442             }
1443 #endif
1444          }
1445
1446          if(!result)
1447             delete d;
1448       }
1449       if(!result)
1450          delete file;
1451    }
1452    if(result)
1453    {
1454       while(result && !result.Validate(extensions))
1455          result = result.FindNext(extensions);
1456    }
1457    return result;
1458 }
1459
1460 private class FileDesc : struct
1461 {
1462    FileStats stats;
1463    char name[MAX_FILENAME];
1464    char path[MAX_LOCATION];
1465
1466    subclass(FileSystem) system;
1467    Dir dir;
1468
1469    bool Validate(const char * extensions)
1470    {
1471       if(strcmp(name, "..") && strcmp(name, ".") && strcmp(name, ""))
1472       {
1473          if(extensions && !stats.attribs.isDirectory)
1474          {
1475             char extension[MAX_EXTENSION], compared[MAX_EXTENSION];
1476             int c;
1477
1478             GetExtension(name, extension);
1479             for(c = 0; extensions[c];)
1480             {
1481                int len = 0;
1482                char ch;
1483                for(;(ch = extensions[c]) && !IS_ALUNDER(ch); c++);
1484                for(;(ch = extensions[c]) &&  IS_ALUNDER(ch); c++)
1485                   compared[len++] = ch;
1486                compared[len] = '\0';
1487
1488                if(!strcmpi(extension, compared))
1489                   return true;
1490             }
1491          }
1492          else
1493             return true;
1494       }
1495       return false;
1496    }
1497
1498    FileDesc FindNext(const char * extensions)
1499    {
1500       FileDesc result = null;
1501
1502       Dir d = dir;
1503
1504       name[0] = '.';
1505       name[1] = '\0';
1506       while(!Validate(extensions))
1507       {
1508          result = null;
1509
1510          if(system)
1511          {
1512             if(system.FindNext(this))
1513                result = this;
1514             else
1515                break;
1516          }
1517          else
1518          {
1519 #if defined(__WIN32__)
1520             if(!strcmp(d.name, "/"))
1521             {
1522                int c;
1523                uint drives = (uint)(uintptr)d.fHandle;
1524                for(c = 0; c<26; c++)
1525                {
1526                   if(drives & (1<<c))
1527                   {
1528                      char volume[MAX_FILENAME] = "";
1529                      int driveType;
1530                      uint16 _wpath[4];
1531                      uint16 _wvolume[MAX_FILENAME];
1532
1533                      stats.attribs = FileAttribs { isDirectory = true, isDrive = true };
1534                      stats.size = 0;
1535                      stats.accessed = stats.created = stats.modified = 0;
1536                      _wpath[0] = path[0] = (char)('A' + c);
1537                      _wpath[1] = path[1] = ':';
1538                      _wpath[2] = path[2] = '\\';
1539                      _wpath[3] = path[3] = 0;
1540                      driveType = GetDriveType(_wpath);
1541                      drives ^= (1<<c);
1542
1543                      switch(driveType)
1544                      {
1545                         case DRIVE_REMOVABLE: stats.attribs.isRemovable = true; break;
1546                         case DRIVE_REMOTE:    stats.attribs.isRemote = true;    break;
1547                         case DRIVE_CDROM:     stats.attribs.isCDROM = true;     break;
1548                      }
1549                      if(driveType == DRIVE_NO_ROOT_DIR)
1550                      {
1551                         uint16 remoteName[1024];
1552                         int status;
1553                         DWORD size = 1024;
1554                         _wpath[2] = 0;
1555
1556                         status = WNetGetConnection(_wpath, remoteName, &size);
1557                         if(status != ERROR_CONNECTION_UNAVAIL)
1558                            continue;
1559
1560                         _wpath[2] = '\\';
1561                         _wpath[3] = 0;
1562                      }
1563
1564                      if(driveType != DRIVE_REMOVABLE && driveType != DRIVE_REMOTE &&
1565                         GetVolumeInformation(_wpath, _wvolume, MAX_FILENAME - 1, null, null, null, null, 0))
1566                      {
1567                         UTF16toUTF8Buffer(_wvolume, volume, MAX_FILENAME);
1568                         path[2] = '\0';
1569                         sprintf(name, "%s [%s]", path, volume);
1570                      }
1571                      else
1572                      {
1573                         path[2] = '\0';
1574                         strcpy(name, path);
1575                      }
1576                      result = this;
1577                      break;
1578                   }
1579                }
1580                d.fHandle = (HANDLE)(uintptr) drives;
1581                break;
1582             }
1583             else if(d.name[0] != '\\' || d.name[1] != '\\' || strstr(d.name+2, "\\"))
1584             {
1585                WIN32_FIND_DATA winFile;
1586                if(FindNextFile(d.fHandle, &winFile))
1587                {
1588                   UTF16toUTF8Buffer(winFile.cFileName, name, MAX_FILENAME);
1589                   stats.attribs = FileAttribs { };
1590                   stats.attribs.isArchive   = (winFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)   ? true : false;
1591                   stats.attribs.isHidden    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)    ? true : false;
1592                   stats.attribs.isReadOnly  = (winFile.dwFileAttributes & FILE_ATTRIBUTE_READONLY)  ? true : false;
1593                   stats.attribs.isSystem    = (winFile.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)    ? true : false;
1594                   stats.attribs.isTemporary = (winFile.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) ? true : false;
1595                   stats.attribs.isDirectory = (winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? true : false;
1596                   stats.attribs.isFile      = !(winFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1597                   stats.size = winFile.nFileSizeLow;
1598
1599                   stats.accessed = Win32FileTimeToTimeStamp(&winFile.ftLastAccessTime);
1600                   stats.modified = Win32FileTimeToTimeStamp(&winFile.ftLastWriteTime);
1601                   stats.created  = Win32FileTimeToTimeStamp(&winFile.ftCreationTime);
1602
1603                   strcpy(path, d.name);
1604                   PathCat(path, name);
1605                   /*if(d.name[0])
1606                      strcat(path, DIR_SEPS);
1607                   strcat(path, name);*/
1608                   result = this;
1609                }
1610                else
1611                   break;
1612             }
1613             else
1614             {
1615                if(d.name[2])
1616                {
1617                   if(d.resource < d.numResources)
1618                   {
1619                      stats.attribs = FileAttribs { isDirectory = true, isShare = true };
1620                      stats.size = 0;
1621                      stats.accessed = stats.created = stats.modified = 0;
1622
1623                      UTF16toUTF8Buffer(d.resources[d.resource].lpRemoteName, path, MAX_LOCATION);
1624                      GetLastDirectory(path, name);
1625
1626                      result = this;
1627
1628                      d.resource++;
1629                   }
1630                   else
1631                   {
1632                      delete d.resources;
1633                      break;
1634                   }
1635                }
1636                else
1637                {
1638                   int c;
1639                   for(c = d.workGroup; c<d.numWorkGroups; c++)
1640                   {
1641                      if(c != d.workGroup)
1642                      {
1643                         DWORD countInGroup = 0xFFFFFFFF;
1644                         HANDLE handle;
1645                         NETRESOURCE * resources;
1646                         DWORD size = 512 * sizeof(NETRESOURCE);
1647
1648                         resources = (NETRESOURCE *)new0 byte[size];
1649                         // Entire Network
1650                         WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &d.workGroups[c], &handle);
1651                         while(true)
1652                         {
1653                            int returnCode = WNetEnumResource(handle, &countInGroup, resources, &size);
1654                            if(returnCode != ERROR_MORE_DATA)
1655                               break;
1656                            countInGroup = 0xFFFFFFFF;
1657                            resources = (NETRESOURCE *)renew0 resources byte[size];
1658
1659                         }
1660                         WNetCloseEnum(handle);
1661                         d.numResources = countInGroup;
1662                         d.resources = resources;
1663                         d.resource = 0;
1664                      }
1665
1666                      if(d.resource < d.numResources)
1667                      {
1668                         stats.attribs = FileAttribs { isDirectory = true, isServer = true };
1669                         stats.size = 0;
1670                         stats.accessed = stats.created = stats.modified = 0;
1671
1672                         UTF16toUTF8Buffer(d.resources[d.resource].lpRemoteName, path, MAX_LOCATION);
1673                         strlwr(path);
1674                         path[2] = (char)toupper(path[2]);
1675                         GetLastDirectory(path, name);
1676
1677                         result = this;
1678
1679                         d.resource++;
1680                         break;
1681                      }
1682                      else
1683                      {
1684                         if(d.resources)
1685                            delete d.resources;
1686                      }
1687                   }
1688                   d.workGroup = c;
1689                   if(d.workGroup == d.numWorkGroups && d.resource == d.numResources)
1690                   {
1691                      delete d.workGroups;
1692                      break;
1693                   }
1694                }
1695             }
1696 #else
1697             struct dirent *de;
1698             struct stat s;
1699
1700             de = readdir(d.d);
1701             if(de)
1702             {
1703                strcpy(name,de->d_name);
1704                strcpy(path, d.name);
1705                if(d.name[0] && d.name[1])
1706                   strcat(path, DIR_SEPS);
1707                strcat(path, name);
1708                if(!stat(path, &s))
1709                {
1710                   stats.attribs = FileAttribs { };
1711                   stats.attribs = (s.st_mode&S_IFDIR) ? FileAttribs { isDirectory = true } : FileAttribs { isFile = true };
1712                   stats.size = (FileSize)s.st_size;
1713                   stats.accessed = s.st_atime;
1714                   stats.modified = s.st_mtime;
1715                   stats.created = s.st_ctime;
1716                }
1717                result = this;
1718             }
1719             else
1720                break;
1721 #endif
1722          }
1723       }
1724       if(!result)
1725          CloseDir();
1726       return result;
1727    }
1728
1729    void CloseDir(void)
1730    {
1731       if(system)
1732          system.CloseDir(this);
1733       else
1734       {
1735          Dir d = dir;
1736          if(d)
1737          {
1738 #if defined(__WIN32__)
1739             if(d.fHandle && strcmp(d.name, "/"))
1740                FindClose(d.fHandle);
1741 #else
1742             closedir(d.d);
1743 #endif
1744             delete d;
1745          }
1746       }
1747       delete this;
1748    }
1749 }
1750
1751 public struct FileListing
1752 {
1753 public:
1754    const char * directory;
1755    const char * extensions;
1756
1757    bool Find()
1758    {
1759       if(desc)
1760          desc = desc.FindNext(extensions);
1761       else
1762          desc = FileFind(directory, extensions);
1763       if(desc)
1764          return true;
1765       return false;
1766    }
1767
1768    void Stop()
1769    {
1770       if(desc)
1771          desc.CloseDir();
1772       desc = null;
1773    }
1774
1775    property const char * name { get { return desc ? desc.name : null; } };
1776    property const char * path { get { return desc ? desc.path : null; } };
1777    property FileStats stats { get { value = desc ? desc.stats : FileStats { }; } };
1778
1779 private:
1780    FileDesc desc;
1781 };
1782 #endif
1783
1784 public File CreateTemporaryFile(char * tempFileName, const char * template)
1785 {
1786 #ifndef ECERE_BOOTSTRAP // quick fix for now
1787    File f;
1788 #if defined(__unix__) || defined(__APPLE__)
1789    char buffer[MAX_FILENAME];
1790    int fd;
1791    strcpy(buffer, "/tmp/");
1792    strcat(buffer, template);
1793    //strcpy(buffer, template);
1794    strcat(buffer, "XXXXXX");
1795    // mktemp(buffer);
1796    fd = mkstemp(buffer);
1797    strcpy(tempFileName, buffer);
1798    f = { };
1799    f.output = f.input = fdopen(fd, "r+");
1800 #else
1801    char tempPath[MAX_LOCATION];
1802    GetTempPathA(MAX_LOCATION, tempPath);     // TODO: Patch this whole thing to support Unicode temp path
1803    GetTempFileNameA(tempPath, template, 0, tempFileName);
1804    f = FileOpen(tempFileName, readWrite);
1805 #endif
1806    return f;
1807 #else
1808    return null;
1809 #endif
1810 }
1811
1812 #undef DeleteFile
1813
1814 public void CreateTemporaryDir(char * tempFileName, const char * template)
1815 {
1816 #ifndef ECERE_BOOTSTRAP // quick fix for now
1817 #if defined(__unix__) || defined(__APPLE__)
1818    char buffer[MAX_FILENAME];
1819    strcpy(buffer, "/tmp/");
1820    strcat(buffer, template);
1821    //strcpy(buffer, template);
1822    strcat(buffer, "XXXXXX");
1823    // mkstemp(buffer);
1824    mkdtemp(buffer);
1825    strcpy(tempFileName, buffer);
1826 #else
1827    char tempPath[MAX_LOCATION];
1828    GetTempPathA(MAX_LOCATION, tempPath);     // TODO: Patch this whole thing to support Unicode temp path
1829    GetTempFileNameA(tempPath, template, 0, tempFileName);
1830    DeleteFile(tempFileName);
1831    MakeDir(tempFileName);
1832 #endif
1833 #endif
1834 }
1835
1836 public void MakeSlashPath(char * p)
1837 {
1838    FileFixCase(p);
1839    if(__runtimePlatform == win32)
1840       ChangeCh(p, '\\', '/');
1841 }
1842
1843 public void MakeSystemPath(char * p)
1844 {
1845    FileFixCase(p);
1846 }
1847
1848 public char * CopySystemPath(const char * p)
1849 {
1850    char * d = CopyString(p);
1851    if(d)
1852       MakeSystemPath(d);
1853    return d;
1854 }
1855
1856 public char * CopyUnixPath(const char * p)
1857 {
1858    char * d = CopyString(p);
1859    if(d)
1860       MakeSlashPath(d);
1861    return d;
1862 }
1863
1864 public char * GetSystemPathBuffer(char * d, const char * p)
1865 {
1866    if(d != p)
1867       strcpy(d, p ? p : "");
1868    MakeSystemPath(d);
1869    return d;
1870 }
1871
1872 public char * GetSlashPathBuffer(char * d, const char * p)
1873 {
1874    if(d != p)
1875       strcpy(d, p ? p : "");
1876    MakeSlashPath(d);
1877    return d;
1878 }