1 /********************************************************************************
2 *                                                                               *
3 *                             F i l e   C l a s s                               *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2000,2006 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (at your option) any later version.            *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXFile.cpp,v 1.249.2.2 2006/11/07 15:58:52 fox Exp $                         *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxascii.h"
28 #include "FXHash.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXPath.h"
32 #include "FXIO.h"
33 #include "FXStat.h"
34 #include "FXFile.h"
35 #include "FXPipe.h"
36 #include "FXDir.h"
37 
38 
39 
40 /*
41   Notes:
42 
43   - Implemented many functions in terms of FXFile and FXDir
44     so we won't have to worry about unicode stuff.
45   - Perhaps we should assume FXString contains string encoded in the locale
46     of the system [which in case of Windows would mean it contains UTF-16]?
47     Because it isn't between 8-bit or 16-bit, but also about utf-8 v.s. other
48     encodings..
49   - This should be in FXSystem; FXSystem needs to determine the locale, then
50     determine the codec needed for that locale, and then use this codec for
51     encoding our strings to that locale.
52 */
53 
54 #ifdef WIN32
55 #define BadHandle INVALID_HANDLE_VALUE
56 #else
57 #define BadHandle -1
58 #endif
59 
60 #ifdef WIN32
61 #ifndef INVALID_SET_FILE_POINTER
62 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
63 #endif
64 #endif
65 
66 using namespace FX;
67 
68 /*******************************************************************************/
69 
70 namespace FX {
71 
72 
73 
74 // Construct file and attach existing handle h
FXFile(FXInputHandle handle,FXuint mode)75 FXFile::FXFile(FXInputHandle handle,FXuint mode){
76   FXIO::open(handle,mode);
77   }
78 
79 
80 // Construct and open a file
FXFile(const FXString & file,FXuint mode,FXuint perm)81 FXFile::FXFile(const FXString& file,FXuint mode,FXuint perm){
82   open(file,mode,perm);
83   }
84 
85 
86 // Open file
open(const FXString & file,FXuint mode,FXuint perm)87 bool FXFile::open(const FXString& file,FXuint mode,FXuint perm){
88   if(!file.empty() && !isOpen()){
89 #ifdef WIN32
90     DWORD flags=GENERIC_READ;
91     DWORD creation=OPEN_EXISTING;
92 
93     // Basic access mode
94     switch(mode&(ReadOnly|WriteOnly)){
95       case ReadOnly: flags=GENERIC_READ; break;
96       case WriteOnly: flags=GENERIC_WRITE; break;
97       case ReadWrite: flags=GENERIC_READ|GENERIC_WRITE; break;
98       }
99 
100     // Creation and truncation mode
101     switch(mode&(Create|Truncate|Exclusive)){
102       case Create: creation=OPEN_ALWAYS; break;
103       case Truncate: creation=TRUNCATE_EXISTING; break;
104       case Create|Truncate: creation=CREATE_ALWAYS; break;
105       case Create|Truncate|Exclusive: creation=CREATE_NEW; break;
106       }
107 
108     // Non-blocking mode
109     if(mode&NonBlocking){
110       // FIXME
111       }
112 
113 #ifdef UNICODE
114     FXnchar unifile[1024];
115     utf2ncs(unifile,file.text(),file.length()+1);
116     device=::CreateFileW(unifile,flags,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,creation,FILE_ATTRIBUTE_NORMAL,NULL);
117 #else
118     device=::CreateFileA(file.text(),flags,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,creation,FILE_ATTRIBUTE_NORMAL,NULL);
119 #endif
120     access=mode;
121 
122     // Appending
123     if(mode&Append) ::SetFilePointer(device,0,NULL,FILE_END);
124     return (device!=BadHandle);
125 #else
126     FXuint bits=perm&0777;
127     FXuint flags=0;
128 
129     // Basic access mode
130     switch(mode&(ReadOnly|WriteOnly)){
131       case ReadOnly: flags=O_RDONLY; break;
132       case WriteOnly: flags=O_WRONLY; break;
133       case ReadWrite: flags=O_RDWR; break;
134       }
135 // O_LARGEFILE
136 
137     // Appending and truncation
138     if(mode&Append) flags|=O_APPEND;
139     if(mode&Truncate) flags|=O_TRUNC;
140 
141     // Non-blocking mode
142     if(mode&NonBlocking) flags|=O_NONBLOCK;
143 
144     // Creation mode
145     if(mode&Create){
146       flags|=O_CREAT;
147       if(mode&Exclusive) flags|=O_EXCL;
148       }
149 
150     // Permission bits
151     if(perm&FXIO::SetUser) bits|=S_ISUID;
152     if(perm&FXIO::SetGroup) bits|=S_ISGID;
153     if(perm&FXIO::Sticky) bits|=S_ISVTX;
154 
155     // Do it
156     device=::open(file.text(),flags,bits);
157     access=mode;
158     return (device!=BadHandle);
159 #endif
160     }
161   return false;
162   }
163 
164 
165 // Open device with access mode and handle
open(FXInputHandle handle,FXuint mode)166 bool FXFile::open(FXInputHandle handle,FXuint mode){
167   return FXIO::open(handle,mode);
168   }
169 
170 
171 // Get position
position() const172 FXlong FXFile::position() const {
173   if(isOpen()){
174 #ifdef WIN32
175     LARGE_INTEGER pos;
176     pos.QuadPart=0;
177     pos.LowPart=::SetFilePointer(device,0,&pos.HighPart,FILE_CURRENT);
178     if(pos.LowPart==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) pos.QuadPart=-1;
179     return pos.QuadPart;
180 #else
181     return ::lseek(device,0,SEEK_CUR);
182 #endif
183     }
184   return -1;
185   }
186 
187 
188 // Move to position
position(FXlong offset,FXuint from)189 FXlong FXFile::position(FXlong offset,FXuint from){
190   if(isOpen()){
191 #ifdef WIN32
192     LARGE_INTEGER pos;
193     pos.QuadPart=offset;
194     pos.LowPart=::SetFilePointer(device,pos.LowPart,&pos.HighPart,from);
195     if(pos.LowPart==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) pos.QuadPart=-1;
196     return pos.QuadPart;
197 #else
198     return ::lseek(device,offset,from);
199 #endif
200     }
201   return -1;
202   }
203 
204 
205 // Read block
readBlock(void * data,FXival count)206 FXival FXFile::readBlock(void* data,FXival count){
207   FXival nread=-1;
208   if(isOpen()){
209 #ifdef WIN32
210     DWORD nr;
211     if(::ReadFile(device,data,(DWORD)count,&nr,NULL)!=0){
212       nread=(FXival)nr;
213       }
214 #else
215     do{
216       nread=::read(device,data,count);
217       }
218     while(nread<0 && errno==EINTR);
219 #endif
220     }
221   return nread;
222   }
223 
224 
225 // Write block
writeBlock(const void * data,FXival count)226 FXival FXFile::writeBlock(const void* data,FXival count){
227   FXival nwritten=-1;
228   if(isOpen()){
229 #ifdef WIN32
230     DWORD nw;
231     if(::WriteFile(device,data,(DWORD)count,&nw,NULL)!=0){
232       nwritten=(FXival)nw;
233       }
234 #else
235     do{
236       nwritten=::write(device,data,count);
237       }
238     while(nwritten<0 && errno==EINTR);
239 #endif
240     }
241   return nwritten;
242   }
243 
244 
245 // Truncate file
truncate(FXlong size)246 FXlong FXFile::truncate(FXlong size){
247   if(isOpen()){
248 #ifdef WIN32
249     LARGE_INTEGER oldpos,newpos;
250     oldpos.QuadPart=0;
251     newpos.QuadPart=size;
252     oldpos.LowPart=::SetFilePointer(device,0,&oldpos.HighPart,FILE_CURRENT);
253     newpos.LowPart=::SetFilePointer(device,newpos.LowPart,&newpos.HighPart,FILE_BEGIN);
254     if((newpos.LowPart==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) || ::SetEndOfFile(device)==0) newpos.QuadPart=-1;
255     ::SetFilePointer(device,oldpos.LowPart,&oldpos.HighPart,FILE_BEGIN);
256     return newpos.QuadPart;
257 #else
258     if(::ftruncate(device,size)==0) return size;
259 #endif
260     }
261   return -1;
262   }
263 
264 
265 // Flush to disk
flush()266 bool FXFile::flush(){
267   if(isOpen()){
268 #ifdef WIN32
269     return ::FlushFileBuffers(device)!=0;
270 #else
271     return ::fsync(device)==0;
272 #endif
273     }
274   return false;
275   }
276 
277 
278 // Test if we're at the end
eof()279 bool FXFile::eof(){
280   if(isOpen()){
281     register FXlong pos=position();
282     return 0<=pos && size()<=pos;
283     }
284   return true;
285   }
286 
287 
288 // Return file size
size()289 FXlong FXFile::size(){
290   if(isOpen()){
291 #ifdef WIN32
292     ULARGE_INTEGER result;
293     result.LowPart=::GetFileSize(device,&result.HighPart);
294     return result.QuadPart;
295 #else
296     struct stat data;
297     if(::fstat(device,&data)==0) return data.st_size;
298 #endif
299     }
300   return -1;
301   }
302 
303 
304 // Close file
close()305 bool FXFile::close(){
306   if(isOpen()){
307     FXInputHandle dev=device;
308     device=BadHandle;
309 #ifdef WIN32
310     return ::CloseHandle(dev)!=0;
311 #else
312     return ::close(dev)==0;
313 #endif
314     }
315   return false;
316   }
317 
318 
319 // Create new (empty) file
create(const FXString & file,FXuint perm)320 bool FXFile::create(const FXString& file,FXuint perm){
321   if(!file.empty()){
322 #ifdef WIN32
323 #ifdef UNICODE
324     FXnchar buffer[1024];
325     utf2ncs(buffer,file.text(),file.length()+1);
326     FXInputHandle h=::CreateFileW(buffer,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
327 #else
328     FXInputHandle h=::CreateFileA(file.text(),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
329 #endif
330     if(h!=BadHandle){ ::CloseHandle(h); return true; }
331 #else
332     FXInputHandle h=::open(file.text(),O_CREAT|O_WRONLY|O_TRUNC|O_EXCL,perm);
333     if(h!=BadHandle){ ::close(h); return true; }
334 #endif
335     }
336   return false;
337   }
338 
339 
340 // Remove a file
remove(const FXString & file)341 bool FXFile::remove(const FXString& file){
342   if(!file.empty()){
343 #ifdef WIN32
344 #ifdef UNICODE
345     FXnchar buffer[1024];
346     utf2ncs(buffer,file.text(),file.length()+1);
347     return ::DeleteFileW(buffer)!=0;
348 #else
349     return ::DeleteFileA(file.text())!=0;
350 #endif
351 #else
352     return ::unlink(file.text())==0;
353 #endif
354     }
355   return false;
356   }
357 
358 
359 // Rename file
rename(const FXString & srcfile,const FXString & dstfile)360 bool FXFile::rename(const FXString& srcfile,const FXString& dstfile){
361   if(srcfile!=dstfile){
362 #ifdef WIN32
363 #ifdef UNICODE
364     FXnchar oldname[1024],newname[1024];
365     utf2ncs(oldname,srcfile.text(),srcfile.length()+1);
366     utf2ncs(newname,dstfile.text(),dstfile.length()+1);
367     return ::MoveFileExW(oldname,newname,MOVEFILE_REPLACE_EXISTING)!=0;
368 #else
369     return ::MoveFileExA(srcfile.text(),dstfile.text(),MOVEFILE_REPLACE_EXISTING)!=0;
370 #endif
371 #else
372     return ::rename(srcfile.text(),dstfile.text())==0;
373 #endif
374     }
375   return false;
376   }
377 
378 
379 #ifdef WIN32
380 
381 typedef BOOL (WINAPI *FunctionCreateHardLink)(const TCHAR*,const TCHAR*,LPSECURITY_ATTRIBUTES);
382 
383 static BOOL WINAPI HelpCreateHardLink(const TCHAR*,const TCHAR*,LPSECURITY_ATTRIBUTES);
384 
385 static FunctionCreateHardLink MyCreateHardLink=HelpCreateHardLink;
386 
387 
388 // The first time its called, we're setting the function pointer, so
389 // subsequent calls will experience no additional overhead whatsoever!
HelpCreateHardLink(const TCHAR * newname,const TCHAR * oldname,LPSECURITY_ATTRIBUTES sa)390 static BOOL WINAPI HelpCreateHardLink(const TCHAR* newname,const TCHAR* oldname,LPSECURITY_ATTRIBUTES sa){
391 #ifdef UNICODE
392   HMODULE hkernel=LoadLibraryW(L"Kernel32");
393   if(hkernel){
394     MyCreateHardLink=(FunctionCreateHardLink)::GetProcAddress(hkernel,"CreateHardLinkW");
395     ::FreeLibrary(hkernel);
396     return MyCreateHardLink(newname,oldname,sa);
397     }
398 #else
399   HMODULE hkernel=LoadLibraryA("Kernel32");
400   if(hkernel){
401     MyCreateHardLink=(FunctionCreateHardLink)::GetProcAddress(hkernel,"CreateHardLinkA");
402     ::FreeLibrary(hkernel);
403     return MyCreateHardLink(newname,oldname,sa);
404     }
405 #endif
406   return 0;
407   }
408 
409 #endif
410 
411 
412 // Link file
link(const FXString & oldfile,const FXString & newfile)413 bool FXFile::link(const FXString& oldfile,const FXString& newfile){
414   if(newfile!=oldfile){
415 #ifdef WIN32
416 #ifdef UNICODE
417     FXnchar oldname[1024],newname[1024];
418     utf2ncs(oldname,oldfile.text(),oldfile.length()+1);
419     utf2ncs(newname,newfile.text(),newfile.length()+1);
420     return MyCreateHardLink(newname,oldname,NULL)!=0;
421 #else
422     return MyCreateHardLink(newfile.text(),oldfile.text(),NULL)!=0;
423 #endif
424 #else
425     return ::link(oldfile.text(),newfile.text())==0;
426 #endif
427     }
428   return false;
429   }
430 
431 
432 // Read symbolic link
symlink(const FXString & file)433 FXString FXFile::symlink(const FXString& file){
434   if(!file.empty()){
435 #ifndef WIN32
436     FXchar lnk[MAXPATHLEN+1];
437     FXint len=::readlink(file.text(),lnk,MAXPATHLEN);
438     if(0<=len){
439       return FXString(lnk,len);
440       }
441 #endif
442     }
443   return FXString::null;
444   }
445 
446 
447 // Symbolic Link file
symlink(const FXString & oldfile,const FXString & newfile)448 bool FXFile::symlink(const FXString& oldfile,const FXString& newfile){
449   if(newfile!=oldfile){
450 #ifndef WIN32
451     return ::symlink(oldfile.text(),newfile.text())==0;
452 #endif
453     }
454   return false;
455   }
456 
457 
458 // Return true if files are identical
identical(const FXString & file1,const FXString & file2)459 bool FXFile::identical(const FXString& file1,const FXString& file2){
460   if(file1!=file2){
461 #ifdef WIN32
462     BY_HANDLE_FILE_INFORMATION info1,info2;
463     HANDLE hFile1,hFile2;
464     bool same=false;
465 #ifdef UNICODE
466     FXnchar name1[1024],name2[1024];
467     utf2ncs(name1,file1.text(),file1.length()+1);
468     utf2ncs(name2,file2.text(),file2.length()+1);
469     hFile1=::CreateFile(name1,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
470     if(hFile1!=INVALID_HANDLE_VALUE){
471       hFile2=::CreateFile(name2,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
472       if(hFile2!=INVALID_HANDLE_VALUE){
473         if(::GetFileInformationByHandle(hFile1,&info1) && ::GetFileInformationByHandle(hFile2,&info2)){
474           same=(info1.nFileIndexLow==info2.nFileIndexLow && info1.nFileIndexHigh==info2.nFileIndexHigh && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber);
475           }
476         ::CloseHandle(hFile2);
477         }
478       ::CloseHandle(hFile1);
479       }
480     return same;
481 #else
482     hFile1=::CreateFile(file1.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
483     if(hFile1!=INVALID_HANDLE_VALUE){
484       hFile2=::CreateFile(file2.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
485       if(hFile2!=INVALID_HANDLE_VALUE){
486         if(::GetFileInformationByHandle(hFile1,&info1) && ::GetFileInformationByHandle(hFile2,&info2)){
487           same=(info1.nFileIndexLow==info2.nFileIndexLow && info1.nFileIndexHigh==info2.nFileIndexHigh && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber);
488           }
489         ::CloseHandle(hFile2);
490         }
491       ::CloseHandle(hFile1);
492       }
493     return same;
494 #endif
495 #else
496     struct stat stat1,stat2;
497     return !::lstat(file1.text(),&stat1) && !::lstat(file2.text(),&stat2) && stat1.st_ino==stat2.st_ino && stat1.st_dev==stat2.st_dev;
498 #endif
499     }
500   return true;
501   }
502 
503 
504 // Copy srcfile to dstfile, overwriting dstfile if allowed
copy(const FXString & srcfile,const FXString & dstfile,bool overwrite)505 bool FXFile::copy(const FXString& srcfile,const FXString& dstfile,bool overwrite){
506   if(srcfile!=dstfile){
507     FXuchar buffer[4096]; FXival nwritten,nread; FXStat stat;
508     FXFile src(srcfile,FXIO::Reading);
509     if(src.isOpen()){
510       if(FXStat::stat(src,stat)){
511         FXFile dst(dstfile,overwrite?FXIO::Writing:FXIO::Writing|FXIO::Exclusive,stat.mode());
512         if(dst.isOpen()){
513           while(1){
514             nread=src.readBlock(buffer,sizeof(buffer));
515             if(nread<0) return false;
516             if(nread==0) break;
517             nwritten=dst.writeBlock(buffer,nread);
518             if(nwritten<0) return false;
519             }
520           return true;
521           }
522         }
523       }
524     }
525   return false;
526   }
527 
528 
529 // Concatenate srcfile1 and srcfile2 to dstfile, overwriting dstfile if allowed
concat(const FXString & srcfile1,const FXString & srcfile2,const FXString & dstfile,bool overwrite)530 bool FXFile::concat(const FXString& srcfile1,const FXString& srcfile2,const FXString& dstfile,bool overwrite){
531   FXuchar buffer[4096]; FXival nwritten,nread;
532   if(srcfile1!=dstfile && srcfile2!=dstfile){
533     FXFile src1(srcfile1,FXIO::Reading);
534     if(src1.isOpen()){
535       FXFile src2(srcfile2,FXIO::Reading);
536       if(src2.isOpen()){
537         FXFile dst(dstfile,overwrite?FXIO::Writing:FXIO::Writing|FXIO::Exclusive);
538         if(dst.isOpen()){
539           while(1){
540             nread=src1.readBlock(buffer,sizeof(buffer));
541             if(nread<0) return false;
542             if(nread==0) break;
543             nwritten=dst.writeBlock(buffer,nread);
544             if(nwritten<0) return false;
545             }
546           while(1){
547             nread=src2.readBlock(buffer,sizeof(buffer));
548             if(nread<0) return false;
549             if(nread==0) break;
550             nwritten=dst.writeBlock(buffer,nread);
551             if(nwritten<0) return false;
552             }
553           return true;
554           }
555         }
556       }
557     }
558   return false;
559   }
560 
561 
562 // Recursively copy files or directories from srcfile to dstfile, overwriting dstfile if allowed
copyFiles(const FXString & srcfile,const FXString & dstfile,bool overwrite)563 bool FXFile::copyFiles(const FXString& srcfile,const FXString& dstfile,bool overwrite){
564   if(srcfile!=dstfile){
565     FXString name,linkname;
566     FXStat srcstat;
567     FXStat dststat;
568     FXTRACE((100,"FXFile::copyFiles(%s,%s)\n",srcfile.text(),dstfile.text()));
569     if(FXStat::statLink(srcfile,srcstat)){
570 
571       // Destination is a directory?
572       if(FXStat::statLink(dstfile,dststat)){
573         if(!dststat.isDirectory()){
574           if(!overwrite) return false;
575           //FXTRACE((100,"FXFile::remove(%s)\n",dstfile.text()));
576           if(!FXFile::remove(dstfile)) return false;
577           }
578         }
579 
580       // Source is a directory
581       if(srcstat.isDirectory()){
582 
583         // Make destination directory if needed
584         if(!dststat.isDirectory()){
585           //FXTRACE((100,"FXDir::create(%s)\n",dstfile.text()));
586 
587           // Make directory
588           if(!FXDir::create(dstfile,srcstat.mode()|FXIO::OwnerWrite)) return false;
589           }
590 
591         // Open source directory
592         FXDir dir(srcfile);
593 
594         // Copy source directory
595         while(dir.next()){
596 
597           // Next name
598           name=dir.name();
599 
600           // Skip '.' and '..'
601           if(name[0]=='.' && (name[1]=='\0' || (name[1]=='.' && name[2]=='\0'))) continue;
602 
603           // Recurse
604           if(!FXFile::copyFiles(srcfile+PATHSEP+name,dstfile+PATHSEP+name,overwrite)) return false;
605           }
606 
607         // OK
608         return true;
609         }
610 
611       // Source is a file
612       if(srcstat.isFile()){
613         //FXTRACE((100,"FXFile::copyFile(%s,%s)\n",srcfile.text(),dstfile.text()));
614 
615         // Simply copy
616         if(!FXFile::copy(srcfile,dstfile,overwrite)) return false;
617 
618         // OK
619         return true;
620         }
621 
622       // Source is symbolic link: make a new one
623       if(srcstat.isLink()){
624         linkname=FXFile::symlink(srcfile);
625         //FXTRACE((100,"symlink(%s,%s)\n",srcfile.text(),dstfile.text()));
626 
627         // New symlink to whatever old one referred to
628         if(!FXFile::symlink(srcfile,dstfile)) return false;
629 
630         // OK
631         return true;
632         }
633 
634       // Source is fifo: make a new one
635       if(srcstat.isFifo()){
636         //FXTRACE((100,"FXPipe::create(%s)\n",dstfile.text()));
637 
638         // Make named pipe
639         if(!FXPipe::create(dstfile,srcstat.mode())) return false;
640 
641         // OK
642         return true;
643         }
644 
645 /*
646   // Source is device: make a new one
647   if(S_ISBLK(status1.st_mode) || S_ISCHR(status1.st_mode) || S_ISSOCK(status1.st_mode)){
648     FXTRACE((100,"mknod(%s)\n",newfile.text()));
649     return ::mknod(newfile.text(),status1.st_mode,status1.st_rdev)==0;
650     }
651 */
652 
653       }
654     }
655   return false;
656   }
657 
658 
659 
660 // Recursively copy or move files or directories from srcfile to dstfile, overwriting dstfile if allowed
moveFiles(const FXString & srcfile,const FXString & dstfile,bool overwrite)661 bool FXFile::moveFiles(const FXString& srcfile,const FXString& dstfile,bool overwrite){
662   if(srcfile!=dstfile){
663     if(FXStat::exists(srcfile)){
664       if(FXStat::exists(dstfile)){
665         if(!overwrite) return false;
666         if(!FXFile::removeFiles(dstfile,true)) return false;
667         }
668       if(FXDir::rename(srcfile,dstfile)) return true;
669       if(FXFile::copyFiles(srcfile,dstfile,overwrite)){
670         return FXFile::removeFiles(srcfile,true);
671         }
672       }
673     }
674   return false;
675   }
676 
677 
678 // Remove file or directory, recursively if allowed
removeFiles(const FXString & path,bool recursive)679 bool FXFile::removeFiles(const FXString& path,bool recursive){
680   FXStat stat;
681   FXTRACE((100,"removeFiles(%s)\n",path.text()));
682   if(FXStat::statLink(path,stat)){
683     if(stat.isDirectory()){
684       if(recursive){
685         FXDir dir(path);
686         FXString name;
687         while(dir.next()){
688           name=dir.name();
689           if(name[0]=='.' && (name[1]=='\0' || (name[1]=='.' && name[2]=='\0'))) continue;
690           if(!FXFile::removeFiles(path+PATHSEP+name,true)) return false;
691           }
692         }
693       return FXDir::remove(path);
694       }
695     return FXFile::remove(path);
696     }
697   return false;
698   }
699 
700 
701 // Destroy
~FXFile()702 FXFile::~FXFile(){
703   close();
704   }
705 
706 
707 }
708 
709