1 /********************************************************************************
2 * *
3 * F i l e I n f o r m a t i o n a n d M a n i p u l a t i o n *
4 * *
5 *********************************************************************************
6 * Copyright (C) 2000,2005 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.190.2.1 2005/04/10 03:24:47 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXString.h"
30 #include "FXFile.h"
31 #ifdef WIN32
32 #include <shellapi.h>
33 #endif
34
35
36
37 /*
38 Notes:
39 - Thanks to Sean Hubbell for the original impetus for these functions.
40 - Windows flavors of some of these functions are not perfect yet.
41 - Windows 95 and NT:
42 - 1 to 255 character name.
43 - Complete path for a file or project name cannot exceed 259
44 characters, including the separators.
45 - May not begin or end with a space.
46 - May not begin with a $
47 - May contain 1 or more file extensions (eg. MyFile.Ext1.Ext2.Ext3.Txt).
48 - Legal characters in the range of 32 - 255 but not ?"/\<>*|:
49 - Filenames may be mixed case.
50 - Filename comparisons are case insensitive (eg. ThIs.TXT = this.txt).
51 - MS-DOS and Windows 3.1:
52 - 1 to 11 characters in the 8.3 naming convention.
53 - Legal characters are A-Z, 0-9, Double Byte Character Set (DBCS)
54 characters (128 - 255), and _^$~!#%&-{}@'()
55 - May not contain spaces, 0 - 31, and "/\[]:;|=,
56 - Must not begin with $
57 - Uppercase only filename.
58 - Perhaps use GetEnvironmentVariable instead of getenv?
59 - FXFile::search() what if some paths are quoted, like
60
61 \this\dir;"\that\dir with a ;";\some\other\dir
62
63 - Need function to contract filenames, e.g. change:
64
65 /home/jeroen/junk
66 /home/someoneelse/junk
67
68 to:
69
70 ~/junk
71 ~someoneelse/junk
72
73 - Perhaps also taking into account certain environment variables in the
74 contraction function?
75 - FXFile::copy( "C:\tmp", "c:\tmp\tmp" ) results infinite-loop.
76
77 */
78
79
80 #ifndef TIMEFORMAT
81 #define TIMEFORMAT "%m/%d/%Y %H:%M:%S"
82 #endif
83
84
85 using namespace FX;
86
87 /*******************************************************************************/
88
89 namespace FX {
90
91 #ifdef __SC__
92 using namespace FXFile;
93 #endif
94
95
96 // Return value of environment variable name
getEnvironment(const FXString & name)97 FXString FXFile::getEnvironment(const FXString& name){
98 return FXString(getenv(name.text()));
99 }
100
101
102 // Get current user name
getCurrentUserName()103 FXString FXFile::getCurrentUserName(){
104 #ifndef WIN32
105 #if defined(FOX_THREAD_SAFE) && !defined(__FreeBSD__)
106 struct passwd pwdresult,*pwd;
107 char buffer[1024];
108 if(getpwuid_r(geteuid(),&pwdresult,buffer,sizeof(buffer),&pwd)==0 && pwd) return pwd->pw_name;
109 #else
110 struct passwd *pwd=getpwuid(geteuid());
111 if(pwd) return pwd->pw_name;
112 #endif
113 #else
114 char buffer[1024];
115 DWORD size=sizeof(buffer);
116 if(GetUserName(buffer,&size)) return buffer;
117 #endif
118 return FXString::null;
119 }
120
121
122 // Get current working directory
getCurrentDirectory()123 FXString FXFile::getCurrentDirectory(){
124 FXchar buffer[MAXPATHLEN];
125 #ifndef WIN32
126 if(getcwd(buffer,MAXPATHLEN)) return FXString(buffer);
127 #else
128 if(GetCurrentDirectory(MAXPATHLEN,buffer)) return FXString(buffer);
129 #endif
130 return FXString::null;
131 }
132
133
134 // Change current directory
setCurrentDirectory(const FXString & path)135 FXbool FXFile::setCurrentDirectory(const FXString& path){
136 #ifdef WIN32
137 return !path.empty() && SetCurrentDirectory(path.text());
138 #else
139 return !path.empty() && chdir(path.text())==0;
140 #endif
141 }
142
143
144 // Get current drive prefix "a:", if any
145 // This is the same method as used in VC++ CRT.
getCurrentDrive()146 FXString FXFile::getCurrentDrive(){
147 #ifdef WIN32
148 FXchar buffer[MAXPATHLEN];
149 if(GetCurrentDirectory(MAXPATHLEN,buffer) && isalpha((FXuchar)buffer[0]) && buffer[1]==':') return FXString(buffer,2);
150 #endif
151 return FXString::null;
152 }
153
154
155 #ifdef WIN32
156
157 // Change current drive prefix "a:"
158 // This is the same method as used in VC++ CRT.
setCurrentDrive(const FXString & prefix)159 FXbool FXFile::setCurrentDrive(const FXString& prefix){
160 FXchar buffer[3];
161 if(!prefix.empty() && isalpha((FXuchar)prefix[0]) && prefix[1]==':'){
162 buffer[0]=prefix[0];
163 buffer[1]=':';
164 buffer[2]='\0';
165 return SetCurrentDirectory(buffer);
166 }
167 return FALSE;
168 }
169
170 #else
171
172 // Change current drive prefix "a:"
setCurrentDrive(const FXString &)173 FXbool FXFile::setCurrentDrive(const FXString&){
174 return TRUE;
175 }
176
177 #endif
178
179
180 // Get home directory for a given user
getUserDirectory(const FXString & user)181 FXString FXFile::getUserDirectory(const FXString& user){
182 #ifndef WIN32
183 #if defined(FOX_THREAD_SAFE) && !defined(__FreeBSD__)
184 struct passwd pwdresult,*pwd;
185 char buffer[1024];
186 if(user.empty()){
187 register const FXchar* str;
188 if((str=getenv("HOME"))!=NULL) return str;
189 if((str=getenv("USER"))!=NULL || (str=getenv("LOGNAME"))!=NULL){
190 if(getpwnam_r(str,&pwdresult,buffer,sizeof(buffer),&pwd)==0 && pwd) return pwd->pw_dir;
191 }
192 if(getpwuid_r(getuid(),&pwdresult,buffer,sizeof(buffer),&pwd)==0 && pwd) return pwd->pw_dir;
193 return PATHSEPSTRING;
194 }
195 if(getpwnam_r(user.text(),&pwdresult,buffer,sizeof(buffer),&pwd)==0 && pwd) return pwd->pw_dir;
196 return PATHSEPSTRING;
197 #else
198 register struct passwd *pwd;
199 if(user.empty()){
200 register const FXchar* str;
201 if((str=getenv("HOME"))!=NULL) return str;
202 if((str=getenv("USER"))!=NULL || (str=getenv("LOGNAME"))!=NULL){
203 if((pwd=getpwnam(str))!=NULL) return pwd->pw_dir;
204 }
205 if((pwd=getpwuid(getuid()))!=NULL) return pwd->pw_dir;
206 return PATHSEPSTRING;
207 }
208 if((pwd=getpwnam(user.text()))!=NULL) return pwd->pw_dir;
209 return PATHSEPSTRING;
210 #endif
211 #else
212 if(user.empty()){
213 register const FXchar *str1,*str2;
214 if((str1=getenv("USERPROFILE"))!=NULL) return str1; // Dani�l H�rchner <dbjh@gmx.net>
215 if((str1=getenv("HOME"))!=NULL) return str1;
216 if((str2=getenv("HOMEPATH"))!=NULL){ // This should be good for WinNT, Win2K according to MSDN
217 if((str1=getenv("HOMEDRIVE"))==NULL) str1="c:";
218 return FXString(str1,str2);
219 }
220 // FXchar buffer[MAX_PATH]
221 // if(SHGetFolderPath(NULL,CSIDL_PERSONAL|CSIDL_FLAG_CREATE,NULL,O,buffer)==S_OK){
222 // return buffer;
223 // }
224 HKEY hKey;
225 if(RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",0,KEY_READ,&hKey)==ERROR_SUCCESS){
226 FXchar home[MAXPATHLEN];
227 DWORD size=MAXPATHLEN;
228 LONG result=RegQueryValueEx(hKey,"Personal",NULL,NULL,(LPBYTE)home,&size); // Change "Personal" to "Desktop" if you want...
229 RegCloseKey(hKey);
230 if(result==ERROR_SUCCESS) return home;
231 }
232 return "c:" PATHSEPSTRING;
233 }
234 return "c:" PATHSEPSTRING;
235 #endif
236 }
237
238
239 // Return the home directory for the current user.
getHomeDirectory()240 FXString FXFile::getHomeDirectory(){
241 return getUserDirectory(FXString::null);
242 }
243
244
245 // Get executable path
getExecPath()246 FXString FXFile::getExecPath(){
247 return FXString(getenv("PATH"));
248 }
249
250
251 // Return temporary directory.
getTempDirectory()252 FXString FXFile::getTempDirectory(){
253 #ifndef WIN32
254 // Conform Linux File Hierarchy standard; this should be
255 // good for SUN, SGI, HP-UX, AIX, and OSF1 also.
256 return FXString("/tmp",4);
257 #else
258 FXchar buffer[MAXPATHLEN];
259 FXuint len=GetTempPath(MAXPATHLEN,buffer);
260 if(1<len && ISPATHSEP(buffer[len-1]) && !ISPATHSEP(buffer[len-2])) len--;
261 return FXString(buffer,len);
262 #endif
263 }
264
265
266 // Return directory part of pathname, assuming full pathname.
267 // Note that directory("/bla/bla/") is "/bla/bla" and NOT "/bla".
268 // However, directory("/bla/bla") is "/bla" as we expect!
directory(const FXString & file)269 FXString FXFile::directory(const FXString& file){
270 register FXint n,i;
271 if(!file.empty()){
272 i=0;
273 #ifdef WIN32
274 if(isalpha((FXuchar)file[0]) && file[1]==':') i=2;
275 #endif
276 if(ISPATHSEP(file[i])) i++;
277 n=i;
278 while(file[i]){
279 if(ISPATHSEP(file[i])) n=i;
280 i++;
281 }
282 return FXString(file.text(),n);
283 }
284 return FXString::null;
285 }
286
287
288 // Return name and extension part of pathname.
289 // Note that name("/bla/bla/") is "" and NOT "bla".
290 // However, name("/bla/bla") is "bla" as we expect!
name(const FXString & file)291 FXString FXFile::name(const FXString& file){
292 register FXint f,n;
293 if(!file.empty()){
294 n=0;
295 #ifdef WIN32
296 if(isalpha((FXuchar)file[0]) && file[1]==':') n=2;
297 #endif
298 f=n;
299 while(file[n]){
300 if(ISPATHSEP(file[n])) f=n+1;
301 n++;
302 }
303 return FXString(file.text()+f,n-f);
304 }
305 return FXString::null;
306 }
307
308
309 // Return file title, i.e. document name only:
310 //
311 // /path/aa -> aa
312 // /path/aa.bb -> aa
313 // /path/aa.bb.cc -> aa.bb
314 // /path/.aa -> .aa
title(const FXString & file)315 FXString FXFile::title(const FXString& file){
316 register FXint f,e,b,i;
317 if(!file.empty()){
318 i=0;
319 #ifdef WIN32
320 if(isalpha((FXuchar)file[0]) && file[1]==':') i=2;
321 #endif
322 f=i;
323 while(file[i]){
324 if(ISPATHSEP(file[i])) f=i+1;
325 i++;
326 }
327 b=f;
328 if(file[b]=='.') b++; // Leading '.'
329 e=i;
330 while(b<i){
331 if(file[--i]=='.'){ e=i; break; }
332 }
333 return FXString(file.text()+f,e-f);
334 }
335 return FXString::null;
336 }
337
338
339 // Return extension, if there is one:
340 //
341 // /path/aa -> ""
342 // /path/aa.bb -> bb
343 // /path/aa.bb.cc -> cc
344 // /path/.aa -> ""
extension(const FXString & file)345 FXString FXFile::extension(const FXString& file){
346 register FXint f,e,i,n;
347 if(!file.empty()){
348 n=0;
349 #ifdef WIN32
350 if(isalpha((FXuchar)file[0]) && file[1]==':') n=2;
351 #endif
352 f=n;
353 while(file[n]){
354 if(ISPATHSEP(file[n])) f=n+1;
355 n++;
356 }
357 if(file[f]=='.') f++; // Leading '.'
358 e=i=n;
359 while(f<i){
360 if(file[--i]=='.'){ e=i+1; break; }
361 }
362 return FXString(file.text()+e,n-e);
363 }
364 return FXString::null;
365 }
366
367
368 // Return file name less the extension
369 //
370 // /path/aa -> /path/aa
371 // /path/aa.bb -> /path/aa
372 // /path/aa.bb.cc -> /path/aa.bb
373 // /path/.aa -> /path/.aa
stripExtension(const FXString & file)374 FXString FXFile::stripExtension(const FXString& file){
375 register FXint f,e,n;
376 if(!file.empty()){
377 n=0;
378 #ifdef WIN32
379 if(isalpha((FXuchar)file[0]) && file[1]==':') n=2;
380 #endif
381 f=n;
382 while(file[n]){
383 if(ISPATHSEP(file[n])) f=n+1;
384 n++;
385 }
386 if(file[f]=='.') f++; // Leading '.'
387 e=n;
388 while(f<n){
389 if(file[--n]=='.'){ e=n; break; }
390 }
391 return FXString(file.text(),e);
392 }
393 return FXString::null;
394 }
395
396
397 #ifdef WIN32
398
399 // Return drive letter prefix "c:"
drive(const FXString & file)400 FXString FXFile::drive(const FXString& file){
401 FXchar buffer[3];
402 if(isalpha((FXuchar)file[0]) && file[1]==':'){
403 buffer[0]=tolower((FXuchar)file[0]);
404 buffer[1]=':';
405 buffer[2]='\0';
406 return FXString(buffer,2);
407 }
408 return FXString::null;
409 }
410
411 #else
412
413 // Return drive letter prefix "c:"
drive(const FXString &)414 FXString FXFile::drive(const FXString&){
415 return FXString::null;
416 }
417
418 #endif
419
420 // Perform tilde or environment variable expansion
expand(const FXString & file)421 FXString FXFile::expand(const FXString& file){
422 #ifndef WIN32
423 if(!file.empty()){
424 register FXint b,e,n;
425 FXString result;
426
427 // Expand leading tilde of the form ~/filename or ~user/filename
428 n=0;
429 if(file[n]=='~'){
430 n++;
431 b=n;
432 while(file[n] && !ISPATHSEP(file[n])) n++;
433 e=n;
434 result.append(getUserDirectory(file.mid(b,e-b)));
435 }
436
437 // Expand environment variables of the form $HOME, ${HOME}, or $(HOME)
438 while(file[n]){
439 if(file[n]=='$'){
440 n++;
441 if(file[n]=='{' || file[n]=='(') n++;
442 b=n;
443 while(isalnum((FXuchar)file[n]) || file[n]=='_') n++;
444 e=n;
445 if(file[n]=='}' || file[n]==')') n++;
446 result.append(getEnvironment(file.mid(b,e-b)));
447 continue;
448 }
449 result.append(file[n]);
450 n++;
451 }
452 return result;
453 }
454 return FXString::null;
455 #else
456 if(!file.empty()){
457 FXchar buffer[2048];
458
459 // Expand environment variables of the form %HOMEPATH%
460 if(ExpandEnvironmentStrings(file.text(),buffer,sizeof(buffer))){
461 return buffer;
462 }
463 return file;
464 }
465 return FXString::null;
466 #endif
467 }
468
469
470
471 // Simplify a file path; the path will remain relative if it was relative,
472 // or absolute if it was absolute. Also, a trailing "/" will be preserved
473 // as this is important in other functions.
474 //
475 // Examples:
476 //
477 // /aa/bb/../cc -> /aa/cc
478 // /aa/bb/../cc/ -> /aa/cc/
479 // /aa/bb/../.. -> /
480 // ../../bb -> ../../bb
481 // ../../bb/ -> ../../bb/
482 // /../ -> /
483 // ./aa/bb/../../ -> ./
484 // a/.. -> .
485 // a/../ -> ./
486 // ./a -> ./a
487 // /////./././ -> /
488 // c:/../ -> c:/
489 // c:a/.. -> c:
490 // /. -> /
simplify(const FXString & file)491 FXString FXFile::simplify(const FXString& file){
492 if(!file.empty()){
493 FXString result=file;
494 register FXint p,q,s;
495 p=q=0;
496 #ifndef WIN32
497 if(ISPATHSEP(result[q])){
498 result[p++]=PATHSEP;
499 while(ISPATHSEP(result[q])) q++;
500 }
501 #else
502 if(ISPATHSEP(result[q])){ // UNC
503 result[p++]=PATHSEP;
504 q++;
505 if(ISPATHSEP(result[q])){
506 result[p++]=PATHSEP;
507 while(ISPATHSEP(result[q])) q++;
508 }
509 }
510 else if(isalpha((FXuchar)result[q]) && result[q+1]==':'){
511 result[p++]=result[q++];
512 result[p++]=':';
513 q++;
514 if(ISPATHSEP(result[q])){
515 result[p++]=PATHSEP;
516 while(ISPATHSEP(result[q])) q++;
517 }
518 }
519 #endif
520 s=p;
521 while(result[q]){
522 while(result[q] && !ISPATHSEP(result[q])){
523 result[p++]=result[q++];
524 }
525 if(2<=p && result[p-1]=='.' && ISPATHSEP(result[p-2]) && result[q]==0){
526 p-=1;
527 }
528 else if(2<=p && result[p-1]=='.' && ISPATHSEP(result[p-2]) && ISPATHSEP(result[q])){
529 p-=2;
530 }
531 else if(3<=p && result[p-1]=='.' && result[p-2]=='.' && ISPATHSEP(result[p-3]) && !(5<=p && result[p-4]=='.' && result[p-5]=='.')){
532 p-=2;
533 if(s+2<=p){
534 p-=2;
535 while(s<p && !ISPATHSEP(result[p])) p--;
536 if(p==0) result[p++]='.';
537 }
538 }
539 if(ISPATHSEP(result[q])){
540 while(ISPATHSEP(result[q])) q++;
541 if(!ISPATHSEP(result[p-1])) result[p++]=PATHSEP;
542 }
543 }
544 return result.trunc(p);
545 }
546 return FXString::null;
547 }
548
549
550 // Build absolute pathname
absolute(const FXString & file)551 FXString FXFile::absolute(const FXString& file){
552 if(file.empty()) return FXFile::getCurrentDirectory();
553 #ifndef WIN32
554 if(ISPATHSEP(file[0])) return FXFile::simplify(file);
555 #else
556 if(ISPATHSEP(file[0])){
557 if(ISPATHSEP(file[1])) return FXFile::simplify(file); // UNC
558 return FXFile::simplify(FXFile::getCurrentDrive()+file);
559 }
560 if(isalpha((FXuchar)file[0]) && file[1]==':'){
561 if(ISPATHSEP(file[2])) return FXFile::simplify(file);
562 return FXFile::simplify(file.mid(0,2)+PATHSEPSTRING+file.mid(2,2147483647));
563 }
564 #endif
565 return FXFile::simplify(FXFile::getCurrentDirectory()+PATHSEPSTRING+file);
566 }
567
568
569 // Build absolute pathname from parts
absolute(const FXString & base,const FXString & file)570 FXString FXFile::absolute(const FXString& base,const FXString& file){
571 if(file.empty()) return FXFile::absolute(base);
572 #ifndef WIN32
573 if(ISPATHSEP(file[0])) return FXFile::simplify(file);
574 #else
575 if(ISPATHSEP(file[0])){
576 if(ISPATHSEP(file[1])) return FXFile::simplify(file); // UNC
577 return FXFile::simplify(FXFile::getCurrentDrive()+file);
578 }
579 if(isalpha((FXuchar)file[0]) && file[1]==':'){
580 if(ISPATHSEP(file[2])) return FXFile::simplify(file);
581 return FXFile::simplify(file.mid(0,2)+PATHSEPSTRING+file.mid(2,2147483647));
582 }
583 #endif
584 return FXFile::simplify(FXFile::absolute(base)+PATHSEPSTRING+file);
585 }
586
587
588 #ifndef WIN32
589
590 // Return root of given path; this is just "/" or "" if not absolute
root(const FXString & file)591 FXString FXFile::root(const FXString& file){
592 if(ISPATHSEP(file[0])){
593 return PATHSEPSTRING;
594 }
595 return FXString::null;
596 }
597
598 #else
599
600 // Return root of given path; this may be "\\" or "C:\" or "" if not absolute
root(const FXString & file)601 FXString FXFile::root(const FXString& file){
602 if(ISPATHSEP(file[0])){
603 if(ISPATHSEP(file[1])) return PATHSEPSTRING PATHSEPSTRING; // UNC
604 return FXFile::getCurrentDrive()+PATHSEPSTRING;
605 }
606 if(isalpha((FXuchar)file[0]) && file[1]==':'){
607 if(ISPATHSEP(file[2])) return file.left(3);
608 return file.left(2)+PATHSEPSTRING;
609 }
610 return FXString::null;
611 }
612
613 #endif
614
615
616 // Return relative path of file to given base directory
617 //
618 // Examples:
619 //
620 // Base File Result
621 // /a/b/c /a/b/c/d d
622 // /a/b/c/ /a/b/c/d d
623 // /a/b/c/d /a/b/c ../
624 // ../a/b/c ../a/b/c/d d
625 // /a/b/c/d /a/b/q ../../q
626 // /a/b/c /a/b/c .
627 // /a/b/c/ /a/b/c/ .
628 // ./a ./b ../b
629 // a b ../b
relative(const FXString & base,const FXString & file)630 FXString FXFile::relative(const FXString& base,const FXString& file){
631 register FXint p,q,b;
632 FXString result;
633
634 // Find branch point
635 #ifndef WIN32
636 for(p=b=0; base[p] && base[p]==file[p]; p++){
637 if(ISPATHSEP(file[p])) b=p;
638 }
639 #else
640 for(p=b=0; base[p] && tolower((FXuchar)base[p])==tolower((FXuchar)file[p]); p++){
641 if(ISPATHSEP(file[p])) b=p;
642 }
643 #endif
644
645 // Paths are equal
646 if((base[p]=='\0' || (ISPATHSEP(base[p]) && base[p+1]=='\0')) && (file[p]=='\0' || (ISPATHSEP(file[p]) && file[p+1]=='\0'))){
647 return ".";
648 }
649
650 // Directory base is prefix of file
651 if((base[p]=='\0' && ISPATHSEP(file[p])) || (file[p]=='\0' && ISPATHSEP(base[p]))){
652 b=p;
653 }
654
655 // Up to branch point
656 for(p=q=b; base[p]; p=q){
657 while(base[q] && !ISPATHSEP(base[q])) q++;
658 if(q>p) result.append(".." PATHSEPSTRING);
659 while(base[q] && ISPATHSEP(base[q])) q++;
660 }
661
662 // Strip leading path character off, if any
663 while(ISPATHSEP(file[b])) b++;
664
665 // Append tail end
666 result.append(&file[b]);
667
668 return result;
669 }
670
671
672 // Return relative path of file to the current directory
relative(const FXString & file)673 FXString FXFile::relative(const FXString& file){
674 return FXFile::relative(getCurrentDirectory(),file);
675 }
676
677
678 // Generate unique filename of the form pathnameXXX.ext, where
679 // pathname.ext is the original input file, and XXX is a number,
680 // possibly empty, that makes the file unique.
681 // (From: Mathew Robertson <mathew.robertson@mi-services.com>)
unique(const FXString & file)682 FXString FXFile::unique(const FXString& file){
683 if(!exists(file)) return file;
684 FXString ext=extension(file);
685 FXString path=stripExtension(file); // Use the new API (Jeroen)
686 FXString filename;
687 register FXint count=0;
688 if(!ext.empty()) ext.prepend('.'); // Only add period when non-empty extension
689 while(count<1000){
690 filename.format("%s%i%s",path.text(),count,ext.text());
691 if(!exists(filename)) return filename; // Return result here (Jeroen)
692 count++;
693 }
694 return FXString::null;
695 }
696
697
698 // Search pathlist for file
search(const FXString & pathlist,const FXString & file)699 FXString FXFile::search(const FXString& pathlist,const FXString& file){
700 if(!file.empty()){
701 FXString path;
702 FXint beg,end;
703 #ifndef WIN32
704 if(ISPATHSEP(file[0])){
705 if(exists(file)) return file;
706 return FXString::null;
707 }
708 #else
709 if(ISPATHSEP(file[0])){
710 if(ISPATHSEP(file[1])){
711 if(exists(file)) return file; // UNC
712 return FXString::null;
713 }
714 path=FXFile::getCurrentDrive()+file;
715 if(exists(path)) return path;
716 return FXString::null;
717 }
718 if(isalpha((FXuchar)file[0]) && file[1]==':'){
719 if(exists(file)) return file;
720 return FXString::null;
721 }
722 #endif
723 for(beg=0; pathlist[beg]; beg=end){
724 while(pathlist[beg]==PATHLISTSEP) beg++;
725 for(end=beg; pathlist[end] && pathlist[end]!=PATHLISTSEP; end++);
726 if(beg==end) break;
727 path=absolute(expand(pathlist.mid(beg,end-beg)),file);
728 if(exists(path)) return path;
729 }
730 }
731 return FXString::null;
732 }
733
734
735 // Up one level, given absolute path
upLevel(const FXString & file)736 FXString FXFile::upLevel(const FXString& file){
737 if(!file.empty()){
738 FXint beg=0;
739 FXint end=file.length();
740 #ifndef WIN32
741 if(ISPATHSEP(file[0])) beg++;
742 #else
743 if(ISPATHSEP(file[0])){
744 beg++;
745 if(ISPATHSEP(file[1])) beg++; // UNC
746 }
747 else if(isalpha((FXuchar)file[0]) && file[1]==':'){
748 beg+=2;
749 if(ISPATHSEP(file[2])) beg++;
750 }
751 #endif
752 if(beg<end && ISPATHSEP(file[end-1])) end--;
753 while(beg<end){ --end; if(ISPATHSEP(file[end])) break; }
754 return file.left(end);
755 }
756 return PATHSEPSTRING;
757 }
758
759
760 // Check if file represents absolute pathname
isAbsolute(const FXString & file)761 FXbool FXFile::isAbsolute(const FXString& file){
762 #ifndef WIN32
763 return !file.empty() && ISPATHSEP(file[0]);
764 #else
765 return !file.empty() && (ISPATHSEP(file[0]) || (isalpha((FXuchar)file[0]) && file[1]==':'));
766 #endif
767 }
768
769
770 // Does file represent topmost directory
isTopDirectory(const FXString & file)771 FXbool FXFile::isTopDirectory(const FXString& file){
772 #ifndef WIN32
773 return !file.empty() && ISPATHSEP(file[0]) && file[1]=='\0';
774 #else
775 return !file.empty() && ((ISPATHSEP(file[0]) && (file[1]=='\0' || (ISPATHSEP(file[1]) && file[2]=='\0'))) || (isalpha((FXuchar)file[0]) && file[1]==':' && (file[2]=='\0' || (ISPATHSEP(file[2]) && file[3]=='\0'))));
776 #endif
777 }
778
779
780 // Check if file represents a file
isFile(const FXString & file)781 FXbool FXFile::isFile(const FXString& file){
782 #ifndef WIN32
783 struct stat status;
784 return !file.empty() && (::stat(file.text(),&status)==0) && S_ISREG(status.st_mode);
785 #else
786 DWORD atts;
787 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF) && !(atts&FILE_ATTRIBUTE_DIRECTORY);
788 #endif
789 }
790
791
792 // Check if file represents a link
isLink(const FXString & file)793 FXbool FXFile::isLink(const FXString& file){
794 #ifndef WIN32
795 struct stat status;
796 return !file.empty() && (::lstat(file.text(),&status)==0) && S_ISLNK(status.st_mode);
797 #else
798 return FALSE;
799 #endif
800 }
801
802
803 // Check if file represents a file share
isShare(const FXString & file)804 FXbool FXFile::isShare(const FXString& file){
805 #ifndef WIN32
806 return FALSE;
807 #else
808 return ISPATHSEP(file[0]) && ISPATHSEP(file[1]) && file.find(PATHSEP,2)<0;
809 #endif
810 }
811
812
813 /*
814
815
816 // Return true if input path represents a file share of the form "\\" or "\\server"
817 FXbool FXFile::isShare(const FXString& file){
818 #ifndef WIN32
819 return FALSE;
820 #else
821 if(ISPATHSEP(file[0]) && ISPATHSEP(file[1]) && file.find(PATHSEP,2)<0){
822 HANDLE hEnum;
823 NETRESOURCE host;
824 host.dwScope=RESOURCE_GLOBALNET;
825 host.dwType=RESOURCETYPE_DISK;
826 host.dwDisplayType=RESOURCEDISPLAYTYPE_GENERIC;
827 host.dwUsage=RESOURCEUSAGE_CONTAINER;
828 host.lpLocalName=NULL;
829 host.lpRemoteName=(char*)file.text();
830 host.lpComment=NULL;
831 host.lpProvider=NULL;
832
833 // This shit thows "First-chance exception in blabla.exe (KERNEL32.DLL): 0x000006BA: (no name)"
834 // when non-existing server name is passed in. Don't know if this is dangerous...
835 if(WNetOpenEnum((file[2]?RESOURCE_GLOBALNET:RESOURCE_CONTEXT),RESOURCETYPE_DISK,0,(file[2]?&host:NULL),&hEnum)==NO_ERROR){
836 WNetCloseEnum(hEnum);
837 return TRUE;
838 }
839 }
840 return FALSE;
841 #endif
842 }
843 */
844
845
846 // Check if file represents a directory
isDirectory(const FXString & file)847 FXbool FXFile::isDirectory(const FXString& file){
848 #ifndef WIN32
849 struct stat status;
850 return !file.empty() && (::stat(file.text(),&status)==0) && S_ISDIR(status.st_mode);
851 #else
852 DWORD atts;
853 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF) && (atts&FILE_ATTRIBUTE_DIRECTORY);
854 #endif
855 }
856
857
858 // Return true if file is readable (thanks to gehriger@linkcad.com)
isReadable(const FXString & file)859 FXbool FXFile::isReadable(const FXString& file){
860 return !file.empty() && access(file.text(),R_OK)==0;
861 }
862
863
864 // Return true if file is writable (thanks to gehriger@linkcad.com)
isWritable(const FXString & file)865 FXbool FXFile::isWritable(const FXString& file){
866 return !file.empty() && access(file.text(),W_OK)==0;
867 }
868
869
870 // Return true if file is executable (thanks to gehriger@linkcad.com)
isExecutable(const FXString & file)871 FXbool FXFile::isExecutable(const FXString& file){
872 #ifndef WIN32
873 return !file.empty() && access(file.text(),X_OK)==0;
874 #else
875 SHFILEINFO sfi;
876 return !file.empty() && SHGetFileInfo(file.text(),0,&sfi,sizeof(SHFILEINFO),SHGFI_EXETYPE)!=0;
877 #endif
878 }
879
880
881 // Check if owner has full permissions
isOwnerReadWriteExecute(const FXString & file)882 FXbool FXFile::isOwnerReadWriteExecute(const FXString& file){
883 #ifndef WIN32
884 struct stat status;
885 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IRUSR) && (status.st_mode&S_IWUSR) && (status.st_mode&S_IXUSR);
886 #else
887 return TRUE;
888 #endif
889 }
890
891
892 // Check if owner can read
isOwnerReadable(const FXString & file)893 FXbool FXFile::isOwnerReadable(const FXString& file){
894 #ifndef WIN32
895 struct stat status;
896 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IRUSR);
897 #else
898 DWORD atts;
899 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF);
900 #endif
901 }
902
903
904 // Check if owner can write
isOwnerWritable(const FXString & file)905 FXbool FXFile::isOwnerWritable(const FXString& file){
906 #ifndef WIN32
907 struct stat status;
908 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IWUSR);
909 #else
910 DWORD atts;
911 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF) && !(atts&FILE_ATTRIBUTE_READONLY);
912 #endif
913 }
914
915
916 // Check if owner can execute
isOwnerExecutable(const FXString & file)917 FXbool FXFile::isOwnerExecutable(const FXString& file){
918 #ifndef WIN32
919 struct stat status;
920 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IXUSR);
921 #else
922 SHFILEINFO sfi;
923 return !file.empty() && SHGetFileInfo(file.text(),0,&sfi,sizeof(SHFILEINFO),SHGFI_EXETYPE)!=0;
924 #endif
925 }
926
927
928 // Check if group has full permissions
isGroupReadWriteExecute(const FXString & file)929 FXbool FXFile::isGroupReadWriteExecute(const FXString& file){
930 #ifndef WIN32
931 struct stat status;
932 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IRGRP) && (status.st_mode&S_IWGRP) && (status.st_mode&S_IXGRP);
933 #else
934 return TRUE;
935 #endif
936 }
937
938
939 // Check if group can read
isGroupReadable(const FXString & file)940 FXbool FXFile::isGroupReadable(const FXString& file){
941 #ifndef WIN32
942 struct stat status;
943 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IRGRP);
944 #else
945 DWORD atts;
946 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF);
947 #endif
948 }
949
950
951 // Check if group can write
isGroupWritable(const FXString & file)952 FXbool FXFile::isGroupWritable(const FXString& file){
953 #ifndef WIN32
954 struct stat status;
955 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IWGRP);
956 #else
957 DWORD atts;
958 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF) && !(atts&FILE_ATTRIBUTE_READONLY);
959 #endif
960 }
961
962
963 // Check if group can execute
isGroupExecutable(const FXString & file)964 FXbool FXFile::isGroupExecutable(const FXString& file){
965 #ifndef WIN32
966 struct stat status;
967 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IXGRP);
968 #else
969 SHFILEINFO sfi;
970 return !file.empty() && SHGetFileInfo(file.text(),0,&sfi,sizeof(SHFILEINFO),SHGFI_EXETYPE)!=0;
971 #endif
972 }
973
974
975 // Check if everybody has full permissions
isOtherReadWriteExecute(const FXString & file)976 FXbool FXFile::isOtherReadWriteExecute(const FXString& file){
977 #ifndef WIN32
978 struct stat status;
979 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IROTH) && (status.st_mode&S_IWOTH) && (status.st_mode&S_IXOTH);
980 #else
981 return TRUE;
982 #endif
983 }
984
985
986 // Check if everybody can read
isOtherReadable(const FXString & file)987 FXbool FXFile::isOtherReadable(const FXString& file){
988 #ifndef WIN32
989 struct stat status;
990 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IROTH);
991 #else
992 DWORD atts;
993 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF);
994 #endif
995 }
996
997
998 // Check if everybody can write
isOtherWritable(const FXString & file)999 FXbool FXFile::isOtherWritable(const FXString& file){
1000 #ifndef WIN32
1001 struct stat status;
1002 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IWOTH);
1003 #else
1004 DWORD atts;
1005 return !file.empty() && ((atts=GetFileAttributes(file.text()))!=0xFFFFFFFF) && !(atts&FILE_ATTRIBUTE_READONLY);
1006 #endif
1007 }
1008
1009
1010 // Check if everybody can execute
isOtherExecutable(const FXString & file)1011 FXbool FXFile::isOtherExecutable(const FXString& file){
1012 #ifndef WIN32
1013 struct stat status;
1014 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_IXOTH);
1015 #else
1016 SHFILEINFO sfi;
1017 return !file.empty() && SHGetFileInfo(file.text(),0,&sfi,sizeof(SHFILEINFO),SHGFI_EXETYPE)!=0;
1018 #endif
1019 }
1020
1021
1022 // These 5 functions below contributed by calvin@users.sourceforge.net
1023
1024
1025 // Test if suid bit set
isSetUid(const FXString & file)1026 FXbool FXFile::isSetUid(const FXString& file){
1027 #ifndef WIN32
1028 struct stat status;
1029 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_ISUID);
1030 #else
1031 return FALSE;
1032 #endif
1033 }
1034
1035
1036 // Test if sgid bit set
isSetGid(const FXString & file)1037 FXbool FXFile::isSetGid(const FXString& file){
1038 #ifndef WIN32
1039 struct stat status;
1040 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_ISGID);
1041 #else
1042 return FALSE;
1043 #endif
1044 }
1045
1046
1047 // Test if sticky bit set
isSetSticky(const FXString & file)1048 FXbool FXFile::isSetSticky(const FXString& file){
1049 #ifndef WIN32
1050 struct stat status;
1051 return !file.empty() && (::stat(file.text(),&status)==0) && (status.st_mode&S_ISVTX);
1052 #else
1053 return FALSE;
1054 #endif
1055 }
1056
1057
1058 // Return owner name from uid
owner(FXuint uid)1059 FXString FXFile::owner(FXuint uid){
1060 FXchar result[64];
1061 #ifndef WIN32
1062 #if defined(FOX_THREAD_SAFE) && !defined(__FreeBSD__)
1063 struct passwd pwdresult,*pwd;
1064 char buffer[1024];
1065 if(getpwuid_r(uid,&pwdresult,buffer,sizeof(buffer),&pwd)==0 && pwd) return pwd->pw_name;
1066 #else
1067 struct passwd *pwd=getpwuid(uid);
1068 if(pwd) return pwd->pw_name;
1069 #endif
1070 #endif
1071 sprintf(result,"%u",uid);
1072 return result;
1073 }
1074
1075
1076 // Return group name from gid
group(FXuint gid)1077 FXString FXFile::group(FXuint gid){
1078 FXchar result[64];
1079 #ifndef WIN32
1080 #if defined(FOX_THREAD_SAFE) && !defined(__FreeBSD__)
1081 ::group grpresult;
1082 ::group *grp;
1083 char buffer[1024];
1084 if(getgrgid_r(gid,&grpresult,buffer,sizeof(buffer),&grp)==0 && grp) return grp->gr_name;
1085 #else
1086 ::group *grp=getgrgid(gid);
1087 if(grp) return grp->gr_name;
1088 #endif
1089 #endif
1090 sprintf(result,"%u",gid);
1091 return result;
1092 }
1093
1094
1095 // Return owner name of file
owner(const FXString & file)1096 FXString FXFile::owner(const FXString& file){
1097 struct stat status;
1098 if(!file.empty() && ::stat(file.text(),&status)==0){
1099 return FXFile::owner(status.st_uid);
1100 }
1101 return FXString::null;
1102 }
1103
1104
1105 // Return group name of file
group(const FXString & file)1106 FXString FXFile::group(const FXString& file){
1107 struct stat status;
1108 if(!file.empty() && ::stat(file.text(),&status)==0){
1109 return FXFile::group(status.st_gid);
1110 }
1111 return FXString::null;
1112 }
1113
1114
1115 /// Return permissions string
permissions(FXuint mode)1116 FXString FXFile::permissions(FXuint mode){
1117 FXchar result[11];
1118 #ifndef WIN32
1119 result[0]=S_ISLNK(mode) ? 'l' : S_ISREG(mode) ? '-' : S_ISDIR(mode) ? 'd' : S_ISCHR(mode) ? 'c' : S_ISBLK(mode) ? 'b' : S_ISFIFO(mode) ? 'p' : S_ISSOCK(mode) ? 's' : '?';
1120 result[1]=(mode&S_IRUSR) ? 'r' : '-';
1121 result[2]=(mode&S_IWUSR) ? 'w' : '-';
1122 result[3]=(mode&S_ISUID) ? 's' : (mode&S_IXUSR) ? 'x' : '-';
1123 result[4]=(mode&S_IRGRP) ? 'r' : '-';
1124 result[5]=(mode&S_IWGRP) ? 'w' : '-';
1125 result[6]=(mode&S_ISGID) ? 's' : (mode&S_IXGRP) ? 'x' : '-';
1126 result[7]=(mode&S_IROTH) ? 'r' : '-';
1127 result[8]=(mode&S_IWOTH) ? 'w' : '-';
1128 result[9]=(mode&S_ISVTX) ? 't' : (mode&S_IXOTH) ? 'x' : '-';
1129 result[10]=0;
1130 #else
1131 result[0]='-';
1132 #ifdef _S_IFDIR
1133 if(mode&_S_IFDIR) result[0]='d';
1134 #endif
1135 #ifdef _S_IFCHR
1136 if(mode&_S_IFCHR) result[0]='c';
1137 #endif
1138 #ifdef _S_IFIFO
1139 if(mode&_S_IFIFO) result[0]='p';
1140 #endif
1141 result[1]='r';
1142 result[2]='w';
1143 result[3]='x';
1144 result[4]='r';
1145 result[5]='w';
1146 result[6]='x';
1147 result[7]='r';
1148 result[8]='w';
1149 result[9]='x';
1150 result[10]=0;
1151 #endif
1152 return result;
1153 }
1154
1155 /*
1156 // Convert FILETIME (# 100ns since 01/01/1601) to time_t (# s since 01/01/1970)
1157 static time_t fxfiletime(const FILETIME& ft){
1158 FXlong ll=(((FXlong)ft.dwHighDateTime)<<32) | (FXlong)ft.dwLowDateTime;
1159 #if defined(__CYGWIN__) || defined(__MINGW32__)
1160 ll=ll-116444736000000000LL;
1161 #else
1162 ll=ll-116444736000000000L; // 0x19DB1DED53E8000
1163 #endif
1164 ll=ll/10000000;
1165 if(ll<0) ll=0;
1166 return (time_t)ll;
1167 }
1168 */
1169
1170 // hFile=CreateFile(pathname,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
1171 // if(hFile!=INVALID_HANDLE_VALUE){
1172 // GetFileTime(hFile,NULL,NULL,&ftLastWriteTime);
1173 // CloseHandle(hFile);
1174
1175 // filetime=fxfiletime(ftLastWriteTime);
1176
1177 // Return time file was last modified
modified(const FXString & file)1178 FXTime FXFile::modified(const FXString& file){
1179 #ifndef WIN32
1180 struct stat status;
1181 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)status.st_mtime : 0L;
1182 #else
1183 struct stat status;
1184 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)status.st_mtime : 0L;
1185 #endif
1186 }
1187
1188
1189 // Return time file was last accessed
accessed(const FXString & file)1190 FXTime FXFile::accessed(const FXString& file){
1191 #ifndef WIN32
1192 struct stat status;
1193 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)status.st_atime : 0L;
1194 #else
1195 struct stat status;
1196 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)status.st_atime : 0L;
1197 #endif
1198 }
1199
1200
1201 // Return time when created
created(const FXString & file)1202 FXTime FXFile::created(const FXString& file){
1203 #ifndef WIN32
1204 struct stat status;
1205 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)status.st_ctime : 0L;
1206 #else
1207 struct stat status;
1208 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)status.st_ctime : 0L;
1209 #endif
1210 }
1211
1212
1213 // Return time when "touched"
touched(const FXString & file)1214 FXTime FXFile::touched(const FXString& file){
1215 #ifndef WIN32
1216 struct stat status;
1217 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)FXMAX(status.st_ctime,status.st_mtime) : 0L;
1218 #else
1219 struct stat status;
1220 return !file.empty() && (::stat(file.text(),&status)==0) ? (FXTime)FXMAX(status.st_ctime,status.st_mtime) : 0L;
1221 #endif
1222 }
1223
1224
1225
1226 #ifndef WIN32 // UNIX
1227
1228
1229 // List all the files in directory
listFiles(FXString * & filelist,const FXString & path,const FXString & pattern,FXuint flags)1230 FXint FXFile::listFiles(FXString*& filelist,const FXString& path,const FXString& pattern,FXuint flags){
1231 FXuint matchmode=FILEMATCH_FILE_NAME|FILEMATCH_NOESCAPE;
1232 FXString pathname;
1233 FXString name;
1234 struct dirent *dp;
1235 FXString *newlist;
1236 FXint count=0;
1237 FXint size=0;
1238 DIR *dirp;
1239 struct stat inf;
1240
1241 // Initialize to empty
1242 filelist=NULL;
1243 /*
1244 // One single root under Unix
1245 if(path.empty()){
1246 filelist=new FXString[2];
1247 list[count++]=PATHSEPSTRING;
1248 return count;
1249 }
1250 */
1251 // Folding case
1252 if(flags&LIST_CASEFOLD) matchmode|=FILEMATCH_CASEFOLD;
1253
1254 // Get directory stream pointer
1255 dirp=opendir(path.text());
1256 if(dirp){
1257
1258 // Loop over directory entries
1259 #ifdef FOX_THREAD_SAFE
1260 struct fxdirent dirresult;
1261 while(!readdir_r(dirp,&dirresult,&dp) && dp){
1262 #else
1263 while((dp=readdir(dirp))!=NULL){
1264 #endif
1265
1266 // Get name
1267 name=dp->d_name;
1268
1269 // Build full pathname
1270 pathname=path;
1271 if(!ISPATHSEP(pathname[pathname.length()-1])) pathname+=PATHSEPSTRING;
1272 pathname+=name;
1273
1274 // Get info on file
1275 if(!info(pathname,inf)) continue;
1276
1277 // Filter out files; a bit tricky...
1278 if(!S_ISDIR(inf.st_mode) && ((flags&LIST_NO_FILES) || (name[0]=='.' && !(flags&LIST_HIDDEN_FILES)) || (!(flags&LIST_ALL_FILES) && !match(pattern,name,matchmode)))) continue;
1279
1280 // Filter out directories; even more tricky!
1281 if(S_ISDIR(inf.st_mode) && ((flags&LIST_NO_DIRS) || (name[0]=='.' && (name[1]==0 || (name[1]=='.' && name[2]==0 && (flags&LIST_NO_PARENT)) || (name[1]!='.' && !(flags&LIST_HIDDEN_DIRS)))) || (!(flags&LIST_ALL_DIRS) && !match(pattern,name,matchmode)))) continue;
1282
1283 // Grow list
1284 if(count+1>=size){
1285 size=size?(size<<1):256;
1286 newlist=new FXString [size];
1287 for(int i=0; i<count; i++) newlist[i]=filelist[i];
1288 delete [] filelist;
1289 filelist=newlist;
1290 }
1291
1292 // Add to list
1293 filelist[count++]=name;
1294 }
1295 closedir(dirp);
1296 }
1297 return count;
1298 }
1299
1300
1301 #else // WINDOWS
1302
1303
1304 // List all the files in directory
1305 FXint FXFile::listFiles(FXString*& filelist,const FXString& path,const FXString& pattern,FXuint flags){
1306 FXuint matchmode=FILEMATCH_FILE_NAME|FILEMATCH_NOESCAPE;
1307 FXString pathname;
1308 FXString name;
1309 FXString *newlist;
1310 FXint count=0;
1311 FXint size=0;
1312 WIN32_FIND_DATA ffData;
1313 DWORD nCount,nSize,i,j;
1314 HANDLE hFindFile,hEnum;
1315 FXchar server[200];
1316
1317 // Initialize to empty
1318 filelist=NULL;
1319
1320 /*
1321 // Each drive is a root on windows
1322 if(path.empty()){
1323 FXchar letter[4];
1324 letter[0]='a';
1325 letter[1]=':';
1326 letter[2]=PATHSEP;
1327 letter[3]='\0';
1328 filelist=new FXString[28];
1329 for(DWORD mask=GetLogicalDrives(); mask; mask>>=1,letter[0]++){
1330 if(mask&1) list[count++]=letter;
1331 }
1332 filelist[count++]=PATHSEPSTRING PATHSEPSTRING; // UNC for file shares
1333 return count;
1334 }
1335 */
1336 /*
1337 // A UNC name was given of the form "\\" or "\\server"
1338 if(ISPATHSEP(path[0]) && ISPATHSEP(path[1]) && path.find(PATHSEP,2)<0){
1339 NETRESOURCE host;
1340
1341 // Fill in
1342 host.dwScope=RESOURCE_GLOBALNET;
1343 host.dwType=RESOURCETYPE_DISK;
1344 host.dwDisplayType=RESOURCEDISPLAYTYPE_GENERIC;
1345 host.dwUsage=RESOURCEUSAGE_CONTAINER;
1346 host.lpLocalName=NULL;
1347 host.lpRemoteName=(char*)path.text();
1348 host.lpComment=NULL;
1349 host.lpProvider=NULL;
1350
1351 // Open network enumeration
1352 if(WNetOpenEnum((path[2]?RESOURCE_GLOBALNET:RESOURCE_CONTEXT),RESOURCETYPE_DISK,0,(path[2]?&host:NULL),&hEnum)==NO_ERROR){
1353 NETRESOURCE resource[16384/sizeof(NETRESOURCE)];
1354 FXTRACE((1,"Enumerating=%s\n",path.text()));
1355 while(1){
1356 nCount=-1; // Read as many as will fit
1357 nSize=sizeof(resource);
1358 if(WNetEnumResource(hEnum,&nCount,resource,&nSize)!=NO_ERROR) break;
1359 for(i=0; i<nCount; i++){
1360
1361 // Dump what we found
1362 FXTRACE((1,"dwScope=%s\n",resource[i].dwScope==RESOURCE_CONNECTED?"RESOURCE_CONNECTED":resource[i].dwScope==RESOURCE_GLOBALNET?"RESOURCE_GLOBALNET":resource[i].dwScope==RESOURCE_REMEMBERED?"RESOURCE_REMEMBERED":"?"));
1363 FXTRACE((1,"dwType=%s\n",resource[i].dwType==RESOURCETYPE_ANY?"RESOURCETYPE_ANY":resource[i].dwType==RESOURCETYPE_DISK?"RESOURCETYPE_DISK":resource[i].dwType==RESOURCETYPE_PRINT?"RESOURCETYPE_PRINT":"?"));
1364 FXTRACE((1,"dwDisplayType=%s\n",resource[i].dwDisplayType==RESOURCEDISPLAYTYPE_DOMAIN?"RESOURCEDISPLAYTYPE_DOMAIN":resource[i].dwDisplayType==RESOURCEDISPLAYTYPE_SERVER?"RESOURCEDISPLAYTYPE_SERVER":resource[i].dwDisplayType==RESOURCEDISPLAYTYPE_SHARE?"RESOURCEDISPLAYTYPE_SHARE":resource[i].dwDisplayType==RESOURCEDISPLAYTYPE_GENERIC?"RESOURCEDISPLAYTYPE_GENERIC":resource[i].dwDisplayType==6?"RESOURCEDISPLAYTYPE_NETWORK":resource[i].dwDisplayType==7?"RESOURCEDISPLAYTYPE_ROOT":resource[i].dwDisplayType==8?"RESOURCEDISPLAYTYPE_SHAREADMIN":resource[i].dwDisplayType==9?"RESOURCEDISPLAYTYPE_DIRECTORY":resource[i].dwDisplayType==10?"RESOURCEDISPLAYTYPE_TREE":resource[i].dwDisplayType==11?"RESOURCEDISPLAYTYPE_NDSCONTAINER":"?"));
1365 FXTRACE((1,"dwUsage=%s\n",resource[i].dwUsage==RESOURCEUSAGE_CONNECTABLE?"RESOURCEUSAGE_CONNECTABLE":resource[i].dwUsage==RESOURCEUSAGE_CONTAINER?"RESOURCEUSAGE_CONTAINER":"?"));
1366 FXTRACE((1,"lpLocalName=%s\n",resource[i].lpLocalName));
1367 FXTRACE((1,"lpRemoteName=%s\n",resource[i].lpRemoteName));
1368 FXTRACE((1,"lpComment=%s\n",resource[i].lpComment));
1369 FXTRACE((1,"lpProvider=%s\n\n",resource[i].lpProvider));
1370
1371 // Grow list
1372 if(count+1>=size){
1373 size=size?(size<<1):256;
1374 newlist=new FXString[size];
1375 for(j=0; j<count; j++) newlist[j]=list[j];
1376 delete [] filelist;
1377 filelist=newlist;
1378 }
1379
1380 // Add remote name to list
1381 filelist[count]=resource[i].lpRemoteName;
1382 count++;
1383 }
1384 }
1385 WNetCloseEnum(hEnum);
1386 }
1387 return count;
1388 }
1389 */
1390 // Folding case
1391 if(flags&LIST_CASEFOLD) matchmode|=FILEMATCH_CASEFOLD;
1392
1393 // Copy directory name
1394 pathname=path;
1395 if(!ISPATHSEP(pathname[pathname.length()-1])) pathname+=PATHSEPSTRING;
1396 pathname+="*";
1397
1398 // Open directory
1399 hFindFile=FindFirstFile(pathname.text(),&ffData);
1400 if(hFindFile!=INVALID_HANDLE_VALUE){
1401
1402 // Loop over directory entries
1403 do{
1404
1405 // Get name
1406 name=ffData.cFileName;
1407
1408 // Filter out files; a bit tricky...
1409 if(!(ffData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && ((flags&LIST_NO_FILES) || ((ffData.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) && !(flags&LIST_HIDDEN_FILES)) || (!(flags&LIST_ALL_FILES) && !match(pattern,name,matchmode)))) continue;
1410
1411 // Filter out directories; even more tricky!
1412 if((ffData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && ((flags&LIST_NO_DIRS) || ((ffData.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) && !(flags&LIST_HIDDEN_DIRS)) || (name[0]=='.' && (name[1]==0 || (name[1]=='.' && name[2]==0 && (flags&LIST_NO_PARENT)))) || (!(flags&LIST_ALL_DIRS) && !match(pattern,name,matchmode)))) continue;
1413
1414 // Grow list
1415 if(count+1>=size){
1416 size=size?(size<<1):256;
1417 newlist=new FXString[size];
1418 for(int f=0; f<count; f++) newlist[f]=filelist[f];
1419 delete [] filelist;
1420 filelist=newlist;
1421 }
1422
1423 // Add to list
1424 filelist[count++]=name;
1425 }
1426 while(FindNextFile(hFindFile,&ffData));
1427 FindClose(hFindFile);
1428 }
1429 return count;
1430 }
1431
1432 #endif
1433
1434
1435 // Convert file time to string as per strftime format
1436 FXString FXFile::time(const FXchar *format,FXTime filetime){
1437 #ifndef WIN32
1438 #if defined(FOX_THREAD_SAFE) && !defined(__FreeBSD__)
1439 time_t tmp=(time_t)FXMAX(filetime,0);
1440 struct tm tmresult;
1441 FXchar buffer[512];
1442 FXint len=strftime(buffer,sizeof(buffer),format,localtime_r(&tmp,&tmresult));
1443 return FXString(buffer,len);
1444 #else
1445 time_t tmp=(time_t)FXMAX(filetime,0);
1446 FXchar buffer[512];
1447 FXint len=strftime(buffer,sizeof(buffer),format,localtime(&tmp));
1448 return FXString(buffer,len);
1449 #endif
1450 #else
1451 time_t tmp=(time_t)FXMAX(filetime,0);
1452 FXchar buffer[512];
1453 FXint len=strftime(buffer,sizeof(buffer),format,localtime(&tmp));
1454 return FXString(buffer,len);
1455 #endif
1456 }
1457
1458
1459
1460 // Convert file time to string
1461 FXString FXFile::time(FXTime filetime){
1462 return FXFile::time(TIMEFORMAT,filetime);
1463 }
1464
1465
1466 // Return current time
1467 FXTime FXFile::now(){
1468 return (FXTime)::time(NULL);
1469 }
1470
1471
1472 // Get file info
1473 FXbool FXFile::info(const FXString& file,struct stat& inf){
1474 #ifndef WIN32
1475 return !file.empty() && (::stat(file.text(),&inf)==0);
1476 #else
1477 return !file.empty() && (::stat(file.text(),&inf)==0);
1478 #endif
1479 }
1480
1481
1482 // Get file info
1483 FXbool FXFile::linkinfo(const FXString& file,struct stat& inf){
1484 #ifndef WIN32
1485 return !file.empty() && (::lstat(file.text(),&inf)==0);
1486 #else
1487 return !file.empty() && (::stat(file.text(),&inf)==0);
1488 #endif
1489 }
1490
1491
1492 // Get file size
1493 FXlong FXFile::size(const FXString& file){
1494 if(!file.empty()){
1495 #ifndef WIN32
1496 struct stat status;
1497 if(::stat(file.text(),&status)==0) return (FXlong)status.st_size;
1498 #else
1499 HANDLE fh=CreateFile(file.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,0);
1500 if(fh!=INVALID_HANDLE_VALUE){
1501 DWORD lo,hi;
1502 lo=GetFileSize(fh,&hi);
1503 CloseHandle(fh);
1504 return (((FXlong)hi)<<32)+((FXlong)lo);
1505 }
1506 #endif
1507 }
1508 return 0L;
1509 }
1510
1511
1512 FXbool FXFile::exists(const FXString& file){
1513 #ifndef WIN32
1514 struct stat status;
1515 return !file.empty() && (::stat(file.text(),&status)==0);
1516 #else
1517 return !file.empty() && (GetFileAttributes(file.text())!=0xFFFFFFFF);
1518 #endif
1519 }
1520
1521
1522
1523 #ifndef WIN32 // UNIX
1524
1525 // Enquote filename to make safe for shell
1526 FXString FXFile::enquote(const FXString& file,FXbool forcequotes){
1527 FXString result;
1528 register FXint i,c;
1529 for(i=0; (c=file[i])!='\0'; i++){
1530 switch(c){
1531 case '\'': // Quote needs to be escaped
1532 result+="\\\'";
1533 break;
1534 case '\\': // Backspace needs to be escaped, of course
1535 result+="\\\\";
1536 break;
1537 case '#':
1538 case '~':
1539 if(i) goto noquote; // Only quote if at begin of filename
1540 case '!': // Special in csh
1541 case '"':
1542 case '$': // Variable substitution
1543 case '&':
1544 case '(':
1545 case ')':
1546 case ';':
1547 case '<': // Redirections, pipe
1548 case '>':
1549 case '|':
1550 case '`': // Command substitution
1551 case '^': // Special in sh
1552 case '*': // Wildcard characters
1553 case '?':
1554 case '[':
1555 case ']':
1556 case '\t': // White space
1557 case '\n':
1558 case ' ':
1559 forcequotes=TRUE;
1560 default: // Normal characters just added
1561 noquote:result+=c;
1562 break;
1563 }
1564 }
1565 if(forcequotes) return "'"+result+"'";
1566 return result;
1567 }
1568
1569
1570 // Decode filename to get original again
1571 FXString FXFile::dequote(const FXString& file){
1572 FXString result;
1573 register FXint i,c;
1574 i=0;
1575 while((c=file[i])!='\0' && isspace((FXuchar)c)) i++;
1576 if(file[i]=='\''){
1577 i++;
1578 while((c=file[i])!='\0' && c!='\''){
1579 if(c=='\\' && file[i+1]!='\0') c=file[++i];
1580 result+=c;
1581 i++;
1582 }
1583 }
1584 else{
1585 while((c=file[i])!='\0' && !isspace((FXuchar)c)){
1586 if(c=='\\' && file[i+1]!='\0') c=file[++i];
1587 result+=c;
1588 i++;
1589 }
1590 }
1591 return result;
1592 }
1593
1594
1595
1596 #else // WINDOWS
1597
1598 // Enquote filename to make safe for shell
1599 FXString FXFile::enquote(const FXString& file,FXbool forcequotes){
1600 FXString result;
1601 register FXint i,c;
1602 for(i=0; (c=file[i])!='\0'; i++){
1603 switch(c){
1604 case '<': // Redirections
1605 case '>':
1606 case '|':
1607 case '$':
1608 case ':':
1609 case '*': // Wildcards
1610 case '?':
1611 case ' ': // White space
1612 forcequotes=TRUE;
1613 default: // Normal characters just added
1614 result+=c;
1615 break;
1616 }
1617 }
1618 if(forcequotes) return "\""+result+"\"";
1619 return result;
1620 }
1621
1622
1623 // Decode filename to get original again
1624 FXString FXFile::dequote(const FXString& file){
1625 register FXint i,c;
1626 FXString result;
1627 i=0;
1628 while((c=file[i])!='\0' && isspace((FXuchar)c)) i++;
1629 if(file[i]=='"'){
1630 i++;
1631 while((c=file[i])!='\0' && c!='"'){
1632 result+=c;
1633 i++;
1634 }
1635 }
1636 else{
1637 while((c=file[i])!='\0' && !isspace((FXuchar)c)){
1638 result+=c;
1639 i++;
1640 }
1641 }
1642 return result;
1643 }
1644
1645 #endif
1646
1647
1648 // Match filenames using *, ?, [^a-z], and so on
1649 FXbool FXFile::match(const FXString& pattern,const FXString& file,FXuint flags){
1650 return fxfilematch(pattern.text(),file.text(),flags);
1651 }
1652
1653
1654 // Return true if files are identical
1655 FXbool FXFile::identical(const FXString& file1,const FXString& file2){
1656 if(file1!=file2){
1657 #ifndef WIN32
1658 struct stat stat1,stat2;
1659 return !::lstat(file1.text(),&stat1) && !::lstat(file2.text(),&stat2) && stat1.st_ino==stat2.st_ino && stat1.st_dev==stat2.st_dev;
1660 #else
1661 FXbool same=FALSE;
1662 HANDLE hFile1;
1663 HANDLE hFile2;
1664 hFile1=CreateFile(file1.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
1665 if(hFile1!=INVALID_HANDLE_VALUE){
1666 hFile2=CreateFile(file2.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
1667 if(hFile2!=INVALID_HANDLE_VALUE){
1668 BY_HANDLE_FILE_INFORMATION info1;
1669 BY_HANDLE_FILE_INFORMATION info2;
1670 if(GetFileInformationByHandle(hFile1,&info1) && GetFileInformationByHandle(hFile2,&info2)){
1671 same=(info1.nFileIndexLow==info2.nFileIndexLow && info1.nFileIndexHigh==info2.nFileIndexHigh && info1.dwVolumeSerialNumber==info2.dwVolumeSerialNumber);
1672 }
1673 CloseHandle(hFile2);
1674 }
1675 CloseHandle(hFile1);
1676 }
1677 return same;
1678 #endif
1679 }
1680 return TRUE;
1681 }
1682
1683
1684 // Return file mode flags
1685 FXuint FXFile::mode(const FXString& file){
1686 #ifndef WIN32
1687 struct stat status;
1688 return !file.empty() && (::stat(file.text(),&status)==0) ? status.st_mode : 0;
1689 #else
1690 struct stat status;
1691 return !file.empty() && (::stat(file.text(),&status)==0) ? status.st_mode : 0;
1692 #endif
1693 }
1694
1695
1696 // Change the mode flags for this file
1697 FXbool FXFile::mode(const FXString& file,FXuint mode){
1698 #ifndef WIN32
1699 return !file.empty() && chmod(file.text(),mode)==0;
1700 #else
1701 return FALSE; // Unimplemented yet
1702 #endif
1703 }
1704
1705
1706 // Create new directory
1707 FXbool FXFile::createDirectory(const FXString& path,FXuint mode){
1708 #ifndef WIN32
1709 return mkdir(path.text(),mode)==0;
1710 #else
1711 return CreateDirectory(path.text(),NULL)!=0;
1712 #endif
1713 }
1714
1715
1716 // Create new (empty) file
1717 FXbool FXFile::createFile(const FXString& file,FXuint mode){
1718 #ifndef WIN32
1719 FXint fd=open(file.text(),O_CREAT|O_WRONLY|O_TRUNC|O_EXCL,mode);
1720 if(fd>=0){ close(fd); return TRUE; }
1721 return FALSE;
1722 #else
1723 HANDLE hFile=CreateFile(file.text(),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
1724 if(hFile!=INVALID_HANDLE_VALUE){ CloseHandle(hFile); return TRUE; }
1725 return FALSE;
1726 #endif
1727 }
1728
1729
1730 // Hack code below for testing if volume is mounted
1731
1732 // #if defined (HKS_NT)
1733 //
1734 // static int check_nfs (const char* name)
1735 // {
1736 // char drive[8];
1737 //
1738 // char* cp = strchr (name, ':');
1739 // if (cp)
1740 // {
1741 // strncpy (drive, name, cp - name);
1742 // drive[cp - name] = '\0';
1743 // }
1744 // else
1745 // {
1746 // drive[0] = 'A' + _getdrive() - 1;
1747 // drive[1] = '\0';
1748 // }
1749 //
1750 // strcat (drive, ":\\");
1751 //
1752 // return GetDriveType(drive) == DRIVE_REMOTE;
1753 // }
1754 //
1755 // #elif defined(LINUX)
1756 //
1757 // static int check_nfs (int fd)
1758 // {
1759 // struct statfs statbuf;
1760 // if (fstatfs(fd,&statbuf) < 0)
1761 // {
1762 // RFM_RAISE_SYSTEM_ERROR("statfs");
1763 // return 0;
1764 // }
1765 // if (statbuf.f_type == NFS_SUPER_MAGIC)
1766 // return 1;
1767 // else
1768 // return 0;
1769 // }
1770 //
1771 // #else
1772 //
1773 // static int check_nfs (int fd)
1774 // {
1775 //
1776 // struct statvfs statbuf;
1777 //
1778 // if (fstatvfs (fd, &statbuf) < 0)
1779 // {
1780 // RFM_RAISE_SYSTEM_ERROR ("fstatvfs");
1781 // }
1782 // return strncmp (statbuf.f_basetype, "nfs", 3) == 0 || strncmp
1783 // (statbuf.f_basetype, "NFS", 3) == 0;
1784 // }
1785 // #endif
1786
1787
1788
1789
1790
1791
1792 #ifndef WIN32
1793
1794
1795 // Read bytes
1796 static long fullread(int fd,unsigned char *ptr,long len){
1797 long nread;
1798 #ifdef EINTR
1799 do{nread=read(fd,ptr,len);}while(nread<0 && errno==EINTR);
1800 #else
1801 nread=read(fd,ptr,len);
1802 #endif
1803 return nread;
1804 }
1805
1806
1807 // Write bytes
1808 static long fullwrite(int fd,const unsigned char *ptr,long len){
1809 long nwritten,ntotalwritten=0;
1810 while(len>0){
1811 nwritten=write(fd,ptr,len);
1812 if(nwritten<0){
1813 #ifdef EINTR
1814 if(errno==EINTR) continue;
1815 #endif
1816 return -1;
1817 }
1818 ntotalwritten+=nwritten;
1819 ptr+=nwritten;
1820 len-=nwritten;
1821 }
1822 return ntotalwritten;
1823 }
1824
1825
1826 // Concatenate srcfile1 and srcfile2 to a dstfile
1827 FXbool FXFile::concatenate(const FXString& srcfile1,const FXString& srcfile2,const FXString& dstfile,FXbool overwrite){
1828 unsigned char buffer[4096];
1829 struct stat status;
1830 int src1,src2,dst;
1831 long nread,nwritten;
1832 FXbool ok=FALSE;
1833 if(srcfile1==dstfile || srcfile2==dstfile) return FALSE;
1834 if(::lstat(dstfile.text(),&status)==0){
1835 if(!overwrite) return FALSE;
1836 }
1837 dst=open(dstfile.text(),O_CREAT|O_WRONLY|O_TRUNC,0777);
1838 if(0<=dst){
1839 src1=open(srcfile1.text(),O_RDONLY);
1840 if(0<=src1){
1841 src2=open(srcfile2.text(),O_RDONLY);
1842 if(0<=src2){
1843 while(1){
1844 nread=fullread(src1,buffer,sizeof(buffer));
1845 if(nread<0) goto err;
1846 if(nread==0) break;
1847 nwritten=fullwrite(dst,buffer,nread);
1848 if(nwritten<0) goto err;
1849 }
1850 while(1){
1851 nread=fullread(src2,buffer,sizeof(buffer));
1852 if(nread<0) goto err;
1853 if(nread==0) break;
1854 nwritten=fullwrite(dst,buffer,nread);
1855 if(nwritten<0) goto err;
1856 }
1857 ok=TRUE;
1858 err: close(src2);
1859 }
1860 close(src1);
1861 }
1862 close(dst);
1863 }
1864 return ok;
1865 }
1866
1867
1868 // Copy ordinary file
1869 static FXbool copyfile(const FXString& oldfile,const FXString& newfile){
1870 unsigned char buffer[4096];
1871 struct stat status;
1872 long nread,nwritten;
1873 int src,dst;
1874 FXbool ok=FALSE;
1875 if((src=open(oldfile.text(),O_RDONLY))>=0){
1876 if(::stat(oldfile.text(),&status)==0){
1877 if((dst=open(newfile.text(),O_WRONLY|O_CREAT|O_TRUNC,status.st_mode))>=0){
1878 while(1){
1879 nread=fullread(src,buffer,sizeof(buffer));
1880 if(nread<0) goto err;
1881 if(nread==0) break;
1882 nwritten=fullwrite(dst,buffer,nread);
1883 if(nwritten<0) goto err;
1884 }
1885 ok=TRUE;
1886 err: close(dst);
1887 }
1888 }
1889 close(src);
1890 }
1891 return ok;
1892 }
1893
1894
1895 // To search visited inodes
1896 struct inodelist {
1897 ino_t st_ino;
1898 inodelist *next;
1899 };
1900
1901
1902 // Forward declararion
1903 static FXbool copyrec(const FXString& oldfile,const FXString& newfile,FXbool overwrite,inodelist* inodes);
1904
1905
1906 // Copy directory
1907 static FXbool copydir(const FXString& oldfile,const FXString& newfile,FXbool overwrite,struct stat& parentstatus,inodelist* inodes){
1908 FXString oldchild,newchild;
1909 struct stat status;
1910 inodelist *in,inode;
1911 struct dirent *dp;
1912 DIR *dirp;
1913
1914 // See if visited this inode already
1915 for(in=inodes; in; in=in->next){
1916 if(in->st_ino==parentstatus.st_ino) return TRUE;
1917 }
1918
1919 // Try make directory, if none exists yet
1920 if(mkdir(newfile.text(),parentstatus.st_mode|S_IWUSR)!=0 && errno!=EEXIST) return FALSE;
1921
1922 // Can we stat it
1923 if(::lstat(newfile.text(),&status)!=0 || !S_ISDIR(status.st_mode)) return FALSE;
1924
1925 // Try open directory to copy
1926 dirp=opendir(oldfile.text());
1927 if(!dirp) return FALSE;
1928
1929 // Add this to the list
1930 inode.st_ino=status.st_ino;
1931 inode.next=inodes;
1932
1933 // Copy stuff
1934 #ifdef FOX_THREAD_SAFE
1935 struct fxdirent dirresult;
1936 while(!readdir_r(dirp,&dirresult,&dp) && dp){
1937 #else
1938 while((dp=readdir(dirp))!=NULL){
1939 #endif
1940 if(dp->d_name[0]!='.' || (dp->d_name[1]!='\0' && (dp->d_name[1]!='.' || dp->d_name[2]!='\0'))){
1941 oldchild=oldfile;
1942 if(!ISPATHSEP(oldchild[oldchild.length()-1])) oldchild.append(PATHSEP);
1943 oldchild.append(dp->d_name);
1944 newchild=newfile;
1945 if(!ISPATHSEP(newchild[newchild.length()-1])) newchild.append(PATHSEP);
1946 newchild.append(dp->d_name);
1947 if(!copyrec(oldchild,newchild,overwrite,&inode)){
1948 closedir(dirp);
1949 return FALSE;
1950 }
1951 }
1952 }
1953
1954 // Close directory
1955 closedir(dirp);
1956
1957 // Success
1958 return TRUE;
1959 }
1960
1961
1962
1963
1964 // Recursive copy
1965 static FXbool copyrec(const FXString& oldfile,const FXString& newfile,FXbool overwrite,inodelist* inodes){
1966 struct stat status1,status2;
1967
1968 // Old file or directory does not exist
1969 if(::lstat(oldfile.text(),&status1)!=0) return FALSE;
1970
1971 // If target is not a directory, remove it if allowed
1972 if(::lstat(newfile.text(),&status2)==0){
1973 if(!S_ISDIR(status2.st_mode)){
1974 if(!overwrite) return FALSE;
1975 FXTRACE((100,"unlink(%s)\n",newfile.text()));
1976 if(::unlink(newfile.text())!=0) return FALSE;
1977 }
1978 }
1979
1980 // Source is direcotory: copy recursively
1981 if(S_ISDIR(status1.st_mode)){
1982 return copydir(oldfile,newfile,overwrite,status1,inodes);
1983 }
1984
1985 // Source is regular file: copy block by block
1986 if(S_ISREG(status1.st_mode)){
1987 FXTRACE((100,"copyfile(%s,%s)\n",oldfile.text(),newfile.text()));
1988 return copyfile(oldfile,newfile);
1989 }
1990
1991 // Source is fifo: make a new one
1992 if(S_ISFIFO(status1.st_mode)){
1993 FXTRACE((100,"mkfifo(%s)\n",newfile.text()));
1994 return ::mkfifo(newfile.text(),status1.st_mode);
1995 }
1996
1997 // Source is device: make a new one
1998 if(S_ISBLK(status1.st_mode) || S_ISCHR(status1.st_mode) || S_ISSOCK(status1.st_mode)){
1999 FXTRACE((100,"mknod(%s)\n",newfile.text()));
2000 return ::mknod(newfile.text(),status1.st_mode,status1.st_rdev)==0;
2001 }
2002
2003 // Source is symbolic link: make a new one
2004 if(S_ISLNK(status1.st_mode)){
2005 FXString lnkfile=FXFile::symlink(oldfile);
2006 FXTRACE((100,"symlink(%s,%s)\n",lnkfile.text(),newfile.text()));
2007 return ::symlink(lnkfile.text(),newfile.text())==0;
2008 }
2009
2010 // This shouldn't happen
2011 return FALSE;
2012 }
2013
2014
2015 #else
2016
2017
2018 // Concatenate srcfile1 and srcfile2 to a dstfile
2019 FXbool FXFile::concatenate(const FXString& srcfile1,const FXString& srcfile2,const FXString& dstfile,FXbool overwrite){
2020 unsigned char buffer[4096];
2021 HANDLE src1,src2,dst;
2022 DWORD nread,nwritten;
2023 FXbool ok=FALSE;
2024 if(srcfile1==dstfile || srcfile2==dstfile) return FALSE;
2025 if(GetFileAttributes(dstfile.text())!=0xFFFFFFFF){
2026 if(!overwrite) return FALSE;
2027 }
2028 dst=CreateFile(dstfile.text(),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
2029 if(dst!=INVALID_HANDLE_VALUE){
2030 src1=CreateFile(srcfile1.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
2031 if(src1!=INVALID_HANDLE_VALUE){
2032 src2=CreateFile(srcfile2.text(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
2033 if(src2!=INVALID_HANDLE_VALUE){
2034 while(1){
2035 if(!ReadFile(src1,buffer,sizeof(buffer),&nread,NULL)) goto err;
2036 if(nread==0) break;
2037 if(!WriteFile(dst,buffer,nread,&nwritten,NULL)) goto err;
2038 }
2039 while(1){
2040 if(!ReadFile(src2,buffer,sizeof(buffer),&nread,NULL)) goto err;
2041 if(nread==0) break;
2042 if(!WriteFile(dst,buffer,nread,&nwritten,NULL)) goto err;
2043 }
2044 ok=TRUE;
2045 err: CloseHandle(src2);
2046 }
2047 CloseHandle(src1);
2048 }
2049 CloseHandle(dst);
2050 }
2051 return ok;
2052 }
2053
2054
2055 // Forward declararion
2056 static FXbool copyrec(const FXString& oldfile,const FXString& newfile,FXbool overwrite);
2057
2058
2059 // Copy directory
2060 static FXbool copydir(const FXString& oldfile,const FXString& newfile,FXbool overwrite){
2061 FXString oldchild,newchild;
2062 DWORD atts;
2063 WIN32_FIND_DATA ffData;
2064 HANDLE hFindFile;
2065
2066 // Try make directory, if none exists yet
2067 // if(CreateDirectory(newfile.text(),NULL)==0 && GetLastError()!=ERROR_FILE_EXISTS) return FALSE;
2068 if(CreateDirectory(newfile.text(),NULL)==0){ // patch from "Malcolm Dane" <danem@talk21.com>
2069 switch(GetLastError()){
2070 case ERROR_FILE_EXISTS:
2071 case ERROR_ALREADY_EXISTS: break;
2072 default: return FALSE;
2073 }
2074 }
2075
2076 // Can we stat it
2077 if((atts=GetFileAttributes(newfile.text()))==0xffffffff || !(atts&FILE_ATTRIBUTE_DIRECTORY)) return FALSE;
2078
2079 // Try open directory to copy
2080 hFindFile=FindFirstFile((oldfile+PATHSEPSTRING+"*").text(),&ffData);
2081 if(hFindFile==INVALID_HANDLE_VALUE) return FALSE;
2082
2083 // Copy stuff
2084 do{
2085 if(ffData.cFileName[0]!='.' && (ffData.cFileName[1]!='\0' && (ffData.cFileName[1]!='.' || ffData.cFileName[2]!='\0'))){
2086 oldchild=oldfile;
2087 if(!ISPATHSEP(oldchild[oldchild.length()-1])) oldchild.append(PATHSEP);
2088 oldchild.append(ffData.cFileName);
2089 newchild=newfile;
2090 if(!ISPATHSEP(newchild[newchild.length()-1])) newchild.append(PATHSEP);
2091 newchild.append(ffData.cFileName);
2092 if(!copyrec(oldchild,newchild,overwrite)){
2093 FindClose(hFindFile);
2094 return FALSE;
2095 }
2096 }
2097 }
2098 while(FindNextFile(hFindFile,&ffData));
2099
2100 // Close directory
2101 FindClose(hFindFile);
2102
2103 // Success
2104 return TRUE;
2105 }
2106
2107
2108 // Recursive copy
2109 static FXbool copyrec(const FXString& oldfile,const FXString& newfile,FXbool overwrite){
2110 DWORD atts1,atts2;
2111
2112 // Old file or directory does not exist
2113 if((atts1=GetFileAttributes(oldfile.text()))==0xffffffff) return FALSE;
2114
2115 // If target is not a directory, remove it if allowed
2116 if((atts2=GetFileAttributes(newfile.text()))!=0xffffffff){
2117 if(!(atts2&FILE_ATTRIBUTE_DIRECTORY)){
2118 if(!overwrite) return FALSE;
2119 FXTRACE((100,"DeleteFile(%s)\n",newfile.text()));
2120 if(DeleteFile(newfile.text())==0) return FALSE;
2121 }
2122 }
2123
2124 // Source is direcotory: copy recursively
2125 if(atts1&FILE_ATTRIBUTE_DIRECTORY){
2126 return copydir(oldfile,newfile,overwrite);
2127 }
2128
2129 // Source is regular file: copy block by block
2130 if(!(atts1&FILE_ATTRIBUTE_DIRECTORY)){
2131 FXTRACE((100,"CopyFile(%s,%s)\n",oldfile.text(),newfile.text()));
2132 return CopyFile(oldfile.text(),newfile.text(),!overwrite);
2133 }
2134
2135 // This shouldn't happen
2136 return FALSE;
2137 }
2138
2139
2140 #endif
2141
2142
2143 // Copy file
2144 FXbool FXFile::copy(const FXString& oldfile,const FXString& newfile,FXbool overwrite){
2145 if(newfile!=oldfile){
2146 #ifndef WIN32
2147 return copyrec(oldfile,newfile,overwrite,NULL);
2148 #else
2149 return copyrec(oldfile,newfile,overwrite); // No symlinks, so no need to check if directories are visited already
2150 #endif
2151 }
2152 return FALSE;
2153 }
2154
2155
2156 // Remove file or directory
2157 FXbool FXFile::remove(const FXString& file){
2158 #ifndef WIN32
2159 struct stat status;
2160 if(::lstat(file.text(),&status)==0){
2161 if(S_ISDIR(status.st_mode)){
2162 DIR *dirp=::opendir(file.text());
2163 if(dirp){
2164 FXString child;
2165 struct dirent *dp;
2166 #ifdef FOX_THREAD_SAFE
2167 struct fxdirent dirresult;
2168 while(!readdir_r(dirp,&dirresult,&dp) && dp){
2169 #else
2170 while((dp=readdir(dirp))!=NULL){
2171 #endif
2172 if(dp->d_name[0]!='.' || (dp->d_name[1]!='\0' && (dp->d_name[1]!='.' || dp->d_name[2]!='\0'))){
2173 child=file;
2174 if(!ISPATHSEP(child[child.length()-1])) child.append(PATHSEP);
2175 child.append(dp->d_name);
2176 if(!FXFile::remove(child)){
2177 ::closedir(dirp);
2178 return FALSE;
2179 }
2180 }
2181 }
2182 ::closedir(dirp);
2183 }
2184 FXTRACE((100,"rmdir(%s)\n",file.text()));
2185 return ::rmdir(file.text())==0;
2186 }
2187 else{
2188 FXTRACE((100,"unlink(%s)\n",file.text()));
2189 return ::unlink(file.text())==0;
2190 }
2191 }
2192 return FALSE;
2193 #else
2194 DWORD atts;
2195 if((atts=GetFileAttributes(file.text()))!=0xffffffff){
2196 if(atts&FILE_ATTRIBUTE_DIRECTORY){
2197 WIN32_FIND_DATA ffData;
2198 HANDLE hFindFile;
2199 hFindFile=FindFirstFile((file+PATHSEPSTRING+"*").text(),&ffData); // FIXME we may want to formalize the "walk over directory" in a few API's here also...
2200 if(hFindFile!=INVALID_HANDLE_VALUE){
2201 FXString child;
2202 do{
2203 if(ffData.cFileName[0]!='.' && (ffData.cFileName[1]!='\0' && (ffData.cFileName[1]!='.' || ffData.cFileName[2]!='\0'))){
2204 child=file;
2205 if(!ISPATHSEP(child[child.length()-1])) child.append(PATHSEP);
2206 child.append(ffData.cFileName);
2207 if(!FXFile::remove(child)){
2208 FindClose(hFindFile);
2209 return FALSE;
2210 }
2211 }
2212 }
2213 while(FindNextFile(hFindFile,&ffData));
2214 FindClose(hFindFile);
2215 }
2216 FXTRACE((100,"RemoveDirectory(%s)\n",file.text()));
2217 return RemoveDirectory(file.text())!=0;
2218 }
2219 else{
2220 FXTRACE((100,"DeleteFile(%s)\n",file.text()));
2221 return DeleteFile(file.text())!=0;
2222 }
2223 }
2224 return FALSE;
2225 #endif
2226 }
2227
2228
2229 // Rename or move file, or copy and delete old if different file systems
2230 FXbool FXFile::move(const FXString& oldfile,const FXString& newfile,FXbool overwrite){
2231 if(newfile!=oldfile){
2232 #ifndef WIN32
2233 if(!FXFile::exists(oldfile)) return FALSE;
2234 if(FXFile::exists(newfile)){
2235 if(!overwrite) return FALSE;
2236 if(!FXFile::remove(newfile)) return FALSE;
2237 }
2238 FXTRACE((100,"rename(%s,%s)\n",oldfile.text(),newfile.text()));
2239 if(::rename(oldfile.text(),newfile.text())==0) return TRUE;
2240 if(errno!=EXDEV) return FALSE;
2241 if(FXFile::copy(oldfile,newfile)){
2242 return FXFile::remove(oldfile);
2243 }
2244 #else
2245 if(!FXFile::exists(oldfile)) return FALSE;
2246 if(FXFile::exists(newfile)){
2247 if(!overwrite) return FALSE;
2248 if(!FXFile::remove(newfile)) return FALSE;
2249 }
2250 FXTRACE((100,"MoveFile(%s,%s)\n",oldfile.text(),newfile.text()));
2251 if(::MoveFile(oldfile.text(),newfile.text())!=0) return TRUE;
2252 if(GetLastError()!=ERROR_NOT_SAME_DEVICE) return FALSE;
2253 if(FXFile::copy(oldfile,newfile)){
2254 return FXFile::remove(oldfile);
2255 }
2256 #endif
2257 }
2258 return FALSE;
2259 }
2260
2261
2262 // Link file
2263 FXbool FXFile::link(const FXString& oldfile,const FXString& newfile,FXbool overwrite){
2264 if(newfile!=oldfile){
2265 #ifndef WIN32
2266 if(!FXFile::exists(oldfile)) return FALSE;
2267 if(FXFile::exists(newfile)){
2268 if(!overwrite) return FALSE;
2269 if(!FXFile::remove(newfile)) return FALSE;
2270 }
2271 FXTRACE((100,"link(%s,%s)\n",oldfile.text(),newfile.text()));
2272 return ::link(oldfile.text(),newfile.text())==0;
2273 #else
2274 typedef BOOL (WINAPI *PFN_CHL)(LPCTSTR,LPCTSTR,LPSECURITY_ATTRIBUTES);
2275 static PFN_CHL chl=NULL;
2276 if(!chl){
2277 HMODULE hkernel=LoadLibraryA("Kernel32");
2278 if(!hkernel) return FALSE;
2279 chl=(PFN_CHL)::GetProcAddress(hkernel,"CreateHardLinkA");
2280 FreeLibrary(hkernel);
2281 }
2282 if(!FXFile::exists(oldfile)) return FALSE;
2283 if(FXFile::exists(newfile)){
2284 if(!overwrite) return FALSE;
2285 if(!FXFile::remove(newfile)) return FALSE;
2286 }
2287 FXTRACE((100,"CreateHardLink(%s,%s)\n",oldfile.text(),newfile.text()));
2288 return chl && (*chl)(newfile.text(),oldfile.text(),NULL)!=0;
2289 #endif
2290 }
2291 return FALSE;
2292 }
2293
2294
2295 // Symbolic Link file
2296 FXbool FXFile::symlink(const FXString& oldfile,const FXString& newfile,FXbool overwrite){
2297 #ifndef WIN32
2298 if(newfile!=oldfile){
2299 if(!FXFile::exists(oldfile)) return FALSE;
2300 if(FXFile::exists(newfile)){
2301 if(!overwrite) return FALSE;
2302 if(!FXFile::remove(newfile)) return FALSE;
2303 }
2304 FXTRACE((100,"symlink(%s,%s)\n",oldfile.text(),newfile.text()));
2305 return ::symlink(oldfile.text(),newfile.text())==0;
2306 }
2307 #endif
2308 return FALSE;
2309 }
2310
2311
2312 // Read symbolic link
2313 FXString FXFile::symlink(const FXString& file){
2314 #ifndef WIN32
2315 FXchar lnk[MAXPATHLEN+1];
2316 FXint len=::readlink(file.text(),lnk,MAXPATHLEN);
2317 if(0<=len) return FXString(lnk,len);
2318 #endif
2319 return FXString::null;
2320 }
2321
2322 }
2323
2324