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