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