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