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