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