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