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