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