1 /********************************************************************************
2 *                                                                               *
3 *                        F i l e   S t a t i s t i c s                          *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 2005,2021 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (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                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxascii.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXStream.h"
29 #include "FXString.h"
30 #include "FXPath.h"
31 #include "FXStat.h"
32 #include "FXFile.h"
33 
34 
35 
36 /*
37   Notes:
38   - Find out stuff about files and directories.
39 */
40 
41 
42 using namespace FX;
43 
44 /*******************************************************************************/
45 
46 namespace FX {
47 
48 
49 // Return true if it is a hidden file (note: Windows-only attribute)
isHidden() const50 FXbool FXStat::isHidden() const {
51   return (modeFlags&FXIO::Hidden)!=0;
52   }
53 
54 // Return true if input path is a directory
isDirectory() const55 FXbool FXStat::isDirectory() const {
56   return (modeFlags&FXIO::Directory)!=0;
57   }
58 
59 // Return true if it is a regular file
isFile() const60 FXbool FXStat::isFile() const {
61   return (modeFlags&FXIO::File)!=0;
62   }
63 
64 // Return true if it is a link
isLink() const65 FXbool FXStat::isLink() const {
66   return (modeFlags&FXIO::SymLink)!=0;
67   }
68 
69 // Return true if the file sets the user id on execution
isSetUid() const70 FXbool FXStat::isSetUid() const {
71   return (modeFlags&FXIO::SetUser)!=0;
72   }
73 
74 // Return true if the file sets the group id on execution
isSetGid() const75 FXbool FXStat::isSetGid() const {
76   return (modeFlags&FXIO::SetGroup)!=0;
77   }
78 
79 // Return true if the file has the sticky bit set
isSetSticky() const80 FXbool FXStat::isSetSticky() const {
81   return (modeFlags&FXIO::Sticky)!=0;
82   }
83 
84 // Return true if special device (character or block device)
isDevice() const85 FXbool FXStat::isDevice() const {
86   return (modeFlags&(FXIO::Character|FXIO::Block))!=0;
87   }
88 
89 // Return true if character device
isCharacter() const90 FXbool FXStat::isCharacter() const {
91   return (modeFlags&FXIO::Character)!=0;
92   }
93 
94 // Return true if block device
isBlock() const95 FXbool FXStat::isBlock() const {
96   return (modeFlags&FXIO::Block)!=0;
97   }
98 
99 // Return true if socket device
isSocket() const100 FXbool FXStat::isSocket() const {
101   return (modeFlags&FXIO::Socket)!=0;
102   }
103 
104 // Return true if fifo device
isFifo() const105 FXbool FXStat::isFifo() const {
106   return (modeFlags&FXIO::Fifo)!=0;
107   }
108 
109 // Return true if file is readable
isReadable() const110 FXbool FXStat::isReadable() const {
111   return (modeFlags&(FXIO::OtherRead|FXIO::GroupRead|FXIO::OwnerRead))!=0;
112   }
113 
114 // Return true if file is writable
isWritable() const115 FXbool FXStat::isWritable() const {
116   return (modeFlags&(FXIO::OtherWrite|FXIO::GroupWrite|FXIO::OwnerWrite))!=0;
117   }
118 
119 // Return true if file is executable
isExecutable() const120 FXbool FXStat::isExecutable() const {
121   return (modeFlags&(FXIO::OtherExec|FXIO::GroupExec|FXIO::OwnerExec))!=0;
122   }
123 
124 // Return true if owner has read-write-execute permissions
isOwnerReadWriteExecute() const125 FXbool FXStat::isOwnerReadWriteExecute() const {
126   return (modeFlags&FXIO::OwnerExec) && (modeFlags&FXIO::OwnerWrite) && (modeFlags&FXIO::OwnerRead);
127   }
128 
129 // Return true if owner has read permissions
isOwnerReadable() const130 FXbool FXStat::isOwnerReadable() const {
131   return (modeFlags&FXIO::OwnerRead)!=0;
132   }
133 
134 // Return true if owner has write permissions
isOwnerWritable() const135 FXbool FXStat::isOwnerWritable() const {
136   return (modeFlags&FXIO::OwnerWrite)!=0;
137   }
138 
139 // Return true if owner has execute permissions
isOwnerExecutable() const140 FXbool FXStat::isOwnerExecutable() const {
141   return (modeFlags&FXIO::OwnerExec)!=0;
142   }
143 
144 // Return true if group has read-write-execute permissions
isGroupReadWriteExecute() const145 FXbool FXStat::isGroupReadWriteExecute() const {
146   return (modeFlags&FXIO::GroupExec) && (modeFlags&FXIO::GroupWrite) && (modeFlags&FXIO::GroupRead);
147   }
148 
149 // Return true if group has read permissions
isGroupReadable() const150 FXbool FXStat::isGroupReadable() const {
151   return (modeFlags&FXIO::GroupRead)!=0;
152   }
153 
154 // Return true if group has write permissions
isGroupWritable() const155 FXbool FXStat::isGroupWritable() const {
156   return (modeFlags&FXIO::GroupWrite)!=0;
157   }
158 
159 // Return true if group has execute permissions
isGroupExecutable() const160 FXbool FXStat::isGroupExecutable() const {
161   return (modeFlags&FXIO::GroupExec)!=0;
162   }
163 
164 // Return true if others have read-write-execute permissions
isOtherReadWriteExecute() const165 FXbool FXStat::isOtherReadWriteExecute() const {
166   return (modeFlags&FXIO::OtherExec) && (modeFlags&FXIO::OtherWrite) && (modeFlags&FXIO::OtherRead);
167   }
168 
169 // Return true if others have read permissions
isOtherReadable() const170 FXbool FXStat::isOtherReadable() const {
171   return (modeFlags&FXIO::OtherRead)!=0;
172   }
173 
174 // Return true if others have write permissions
isOtherWritable() const175 FXbool FXStat::isOtherWritable() const {
176   return (modeFlags&FXIO::OtherWrite)!=0;
177   }
178 
179 // Return true if others have execute permissions
isOtherExecutable() const180 FXbool FXStat::isOtherExecutable() const {
181   return (modeFlags&FXIO::OtherExec)!=0;
182   }
183 
184 
185 #if defined(WIN32)
186 
187 // Convert 100ns since 01/01/1601 to ns since 01/01/1970
fxunixtime(FXTime ft)188 static inline FXTime fxunixtime(FXTime ft){
189   return (ft-FXLONG(116444736000000000))*FXLONG(100);
190   }
191 
192 // Convert ns since 01/01/1970 to 100ns since 01/01/1601
fxwintime(FXTime ut)193 static inline FXTime fxwintime(FXTime ut){
194   return ut/FXLONG(100)+FXLONG(116444736000000000);
195   }
196 
197 #endif
198 
199 
200 // Get statistics of given file
statFile(const FXString & file,FXStat & info)201 FXbool FXStat::statFile(const FXString& file,FXStat& info){
202   FXbool result=false;
203   info.modeFlags=0;
204   info.userNumber=0;
205   info.groupNumber=0;
206   info.linkCount=0;
207   info.createTime=0;
208   info.accessTime=0;
209   info.modifyTime=0;
210   info.fileVolume=0;
211   info.fileIndex=0;
212   info.fileSize=0;
213   if(!file.empty()){
214 #ifdef WIN32
215 #ifdef UNICODE
216     FXnchar unifile[MAXPATHLEN];
217     HANDLE hfile;
218     utf2ncs(unifile,file.text(),MAXPATHLEN);
219     if((hfile=::CreateFile(unifile,FILE_READ_ATTRIBUTES,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,NULL))!=INVALID_HANDLE_VALUE){
220       BY_HANDLE_FILE_INFORMATION data;
221       if(::GetFileInformationByHandle(hfile,&data)){
222         info.modeFlags=FXIO::AllFull;
223         if(data.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) info.modeFlags|=FXIO::Hidden;
224         if(data.dwFileAttributes&FILE_ATTRIBUTE_READONLY) info.modeFlags&=~FXIO::AllWrite;
225         if(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) info.modeFlags|=FXIO::Directory|FXIO::AllWrite; else info.modeFlags|=FXIO::File;     // Directories (folders) always writable on Windows
226         if((info.modeFlags&FXIO::File) && !FXPath::hasExecExtension(file)) info.modeFlags&=~FXIO::AllExec;
227         info.userNumber=0;
228         info.groupNumber=0;
229         info.linkCount=data.nNumberOfLinks;
230         info.accessTime=fxunixtime(*((FXTime*)&data.ftLastAccessTime));
231         info.modifyTime=fxunixtime(*((FXTime*)&data.ftLastWriteTime));
232         info.createTime=fxunixtime(*((FXTime*)&data.ftCreationTime));
233         info.fileVolume=data.dwVolumeSerialNumber;
234         info.fileIndex=(((FXulong)data.nFileIndexHigh)<<32)|((FXulong)data.nFileIndexLow);
235         info.fileSize=(((FXlong)data.nFileSizeHigh)<<32)|((FXlong)data.nFileSizeLow);
236         result=true;
237         }
238       ::CloseHandle(hfile);
239       }
240 #else
241     HANDLE hfile;
242     if((hfile=::CreateFile(file.text(),FILE_READ_ATTRIBUTES,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,NULL))!=INVALID_HANDLE_VALUE){
243       BY_HANDLE_FILE_INFORMATION data;
244       if(::GetFileInformationByHandle(hfile,&data)){
245         info.modeFlags=FXIO::AllFull;
246         if(data.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) info.modeFlags|=FXIO::Hidden;
247         if(data.dwFileAttributes&FILE_ATTRIBUTE_READONLY) info.modeFlags&=~FXIO::AllWrite;
248         if(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) info.modeFlags|=FXIO::Directory|FXIO::AllWrite; else info.modeFlags|=FXIO::File;     // Directories (folders) always writable on Windows
249         if((info.modeFlags&FXIO::File) && !FXPath::hasExecExtension(file)) info.modeFlags&=~FXIO::AllExec;
250         info.userNumber=0;
251         info.groupNumber=0;
252         info.linkCount=data.nNumberOfLinks;
253         info.accessTime=fxunixtime(*((FXTime*)&data.ftLastAccessTime));
254         info.modifyTime=fxunixtime(*((FXTime*)&data.ftLastWriteTime));
255         info.createTime=fxunixtime(*((FXTime*)&data.ftCreationTime));
256         info.fileVolume=data.dwVolumeSerialNumber;
257         info.fileIndex=(((FXulong)data.nFileIndexHigh)<<32)|((FXulong)data.nFileIndexLow);
258         info.fileSize=(((FXlong)data.nFileSizeHigh)<<32)|((FXlong)data.nFileSizeLow);
259         result=true;
260         }
261       ::CloseHandle(hfile);
262       }
263 #endif
264 #else
265     const FXTime seconds=1000000000;
266     struct stat data;
267     if(::stat(file.text(),&data)==0){
268       info.modeFlags=(data.st_mode&FXIO::AllFull);
269       if(S_ISDIR(data.st_mode)) info.modeFlags|=FXIO::Directory;
270       if(S_ISREG(data.st_mode)) info.modeFlags|=FXIO::File;
271       if(S_ISLNK(data.st_mode)) info.modeFlags|=FXIO::SymLink;
272       if(S_ISCHR(data.st_mode)) info.modeFlags|=FXIO::Character;
273       if(S_ISBLK(data.st_mode)) info.modeFlags|=FXIO::Block;
274       if(S_ISFIFO(data.st_mode)) info.modeFlags|=FXIO::Fifo;
275       if(S_ISSOCK(data.st_mode)) info.modeFlags|=FXIO::Socket;
276       if(data.st_mode&S_ISUID) info.modeFlags|=FXIO::SetUser;
277       if(data.st_mode&S_ISGID) info.modeFlags|=FXIO::SetGroup;
278       if(data.st_mode&S_ISVTX) info.modeFlags|=FXIO::Sticky;
279       info.userNumber=data.st_uid;
280       info.groupNumber=data.st_gid;
281       info.linkCount=data.st_nlink;
282 #if (_POSIX_C_SOURCE >= 200809L) || (_XOPEN_SOURCE >= 700)
283       info.accessTime=data.st_atim.tv_sec*seconds+data.st_atim.tv_nsec;
284       info.modifyTime=data.st_mtim.tv_sec*seconds+data.st_mtim.tv_nsec;
285       info.createTime=data.st_ctim.tv_sec*seconds+data.st_ctim.tv_nsec;
286 #else
287       info.accessTime=data.st_atime*seconds;
288       info.modifyTime=data.st_mtime*seconds;
289       info.createTime=data.st_ctime*seconds;
290 #endif
291       info.fileVolume=(FXlong)data.st_dev;
292       info.fileIndex=(FXlong)data.st_ino;
293       info.fileSize=(FXlong)data.st_size;
294       result=true;
295       }
296 #endif
297     }
298   return result;
299   }
300 
301 
302 // Get statistice of the linked file
statLink(const FXString & file,FXStat & info)303 FXbool FXStat::statLink(const FXString& file,FXStat& info){
304   FXbool result=false;
305   info.modeFlags=0;
306   info.userNumber=0;
307   info.groupNumber=0;
308   info.linkCount=0;
309   info.createTime=0;
310   info.accessTime=0;
311   info.modifyTime=0;
312   info.fileVolume=0;
313   info.fileIndex=0;
314   info.fileSize=0;
315   if(!file.empty()){
316 #ifdef WIN32
317 #ifdef UNICODE
318     FXnchar unifile[MAXPATHLEN];
319     HANDLE hfile;
320     utf2ncs(unifile,file.text(),MAXPATHLEN);
321     if((hfile=::CreateFile(unifile,FILE_READ_ATTRIBUTES,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,NULL))!=INVALID_HANDLE_VALUE){
322       BY_HANDLE_FILE_INFORMATION data;
323       if(::GetFileInformationByHandle(hfile,&data)){
324         info.modeFlags=FXIO::AllFull;
325         if(data.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) info.modeFlags|=FXIO::Hidden;
326         if(data.dwFileAttributes&FILE_ATTRIBUTE_READONLY) info.modeFlags&=~FXIO::AllWrite;
327         if(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) info.modeFlags|=FXIO::Directory|FXIO::AllWrite; else info.modeFlags|=FXIO::File;     // Directories (folders) always writable on Windows
328         if((info.modeFlags&FXIO::File) && !FXPath::hasExecExtension(file)) info.modeFlags&=~FXIO::AllExec;
329         info.userNumber=0;
330         info.groupNumber=0;
331         info.linkCount=data.nNumberOfLinks;
332         info.accessTime=fxunixtime(*((FXTime*)&data.ftLastAccessTime));
333         info.modifyTime=fxunixtime(*((FXTime*)&data.ftLastWriteTime));
334         info.createTime=fxunixtime(*((FXTime*)&data.ftCreationTime));
335         info.fileVolume=data.dwVolumeSerialNumber;
336         info.fileIndex=(((FXulong)data.nFileIndexHigh)<<32)|((FXulong)data.nFileIndexLow);
337         info.fileSize=(((FXlong)data.nFileSizeHigh)<<32)|((FXlong)data.nFileSizeLow);
338         result=true;
339         }
340       ::CloseHandle(hfile);
341       }
342 #else
343     HANDLE hfile;
344     if((hfile=::CreateFile(file.text(),FILE_READ_ATTRIBUTES,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,NULL))!=INVALID_HANDLE_VALUE){
345       BY_HANDLE_FILE_INFORMATION data;
346       if(::GetFileInformationByHandle(hfile,&data)){
347         info.modeFlags=FXIO::AllFull;
348         if(data.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) info.modeFlags|=FXIO::Hidden;
349         if(data.dwFileAttributes&FILE_ATTRIBUTE_READONLY) info.modeFlags&=~FXIO::AllWrite;
350         if(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) info.modeFlags|=FXIO::Directory|FXIO::AllWrite; else info.modeFlags|=FXIO::File;     // Directories (folders) always writable on Windows
351         if((info.modeFlags&FXIO::File) && !FXPath::hasExecExtension(file)) info.modeFlags&=~FXIO::AllExec;
352         info.userNumber=0;
353         info.groupNumber=0;
354         info.linkCount=data.nNumberOfLinks;
355         info.accessTime=fxunixtime(*((FXTime*)&data.ftLastAccessTime));
356         info.modifyTime=fxunixtime(*((FXTime*)&data.ftLastWriteTime));
357         info.createTime=fxunixtime(*((FXTime*)&data.ftCreationTime));
358         info.fileVolume=data.dwVolumeSerialNumber;
359         info.fileIndex=(((FXulong)data.nFileIndexHigh)<<32)|((FXulong)data.nFileIndexLow);
360         info.fileSize=(((FXlong)data.nFileSizeHigh)<<32)|((FXlong)data.nFileSizeLow);
361         result=true;
362         }
363       ::CloseHandle(hfile);
364       }
365 #endif
366 #else
367     const FXTime seconds=1000000000;
368     struct stat data;
369     if(::lstat(file.text(),&data)==0){
370       info.modeFlags=(data.st_mode&FXIO::AllFull);
371       if(S_ISDIR(data.st_mode)) info.modeFlags|=FXIO::Directory;
372       if(S_ISREG(data.st_mode)) info.modeFlags|=FXIO::File;
373       if(S_ISLNK(data.st_mode)) info.modeFlags|=FXIO::SymLink;
374       if(S_ISCHR(data.st_mode)) info.modeFlags|=FXIO::Character;
375       if(S_ISBLK(data.st_mode)) info.modeFlags|=FXIO::Block;
376       if(S_ISFIFO(data.st_mode)) info.modeFlags|=FXIO::Fifo;
377       if(S_ISSOCK(data.st_mode)) info.modeFlags|=FXIO::Socket;
378       if(data.st_mode&S_ISUID) info.modeFlags|=FXIO::SetUser;
379       if(data.st_mode&S_ISGID) info.modeFlags|=FXIO::SetGroup;
380       if(data.st_mode&S_ISVTX) info.modeFlags|=FXIO::Sticky;
381       info.userNumber=data.st_uid;
382       info.groupNumber=data.st_gid;
383       info.linkCount=data.st_nlink;
384 #if (_POSIX_C_SOURCE >= 200809L) || (_XOPEN_SOURCE >= 700)
385       info.accessTime=data.st_atim.tv_sec*seconds+data.st_atim.tv_nsec;
386       info.modifyTime=data.st_mtim.tv_sec*seconds+data.st_mtim.tv_nsec;
387       info.createTime=data.st_ctim.tv_sec*seconds+data.st_ctim.tv_nsec;
388 #else
389       info.accessTime=data.st_atime*seconds;
390       info.modifyTime=data.st_mtime*seconds;
391       info.createTime=data.st_ctime*seconds;
392 #endif
393       info.fileVolume=(FXlong)data.st_dev;
394       info.fileIndex=(FXlong)data.st_ino;
395       info.fileSize=(FXlong)data.st_size;
396       result=true;
397       }
398 #endif
399     }
400   return result;
401   }
402 
403 
404 // Get statistice of the already open file
stat(const FXFile & file,FXStat & info)405 FXbool FXStat::stat(const FXFile& file,FXStat& info){
406   info.modeFlags=0;
407   info.userNumber=0;
408   info.groupNumber=0;
409   info.linkCount=0;
410   info.createTime=0;
411   info.accessTime=0;
412   info.modifyTime=0;
413   info.fileVolume=0;
414   info.fileIndex=0;
415   info.fileSize=0;
416 #ifdef WIN32
417   BY_HANDLE_FILE_INFORMATION data;
418   if(::GetFileInformationByHandle(file.handle(),&data)){
419     info.modeFlags=FXIO::AllFull;
420     if(data.dwFileAttributes&FILE_ATTRIBUTE_HIDDEN) info.modeFlags|=FXIO::Hidden;
421     if(data.dwFileAttributes&FILE_ATTRIBUTE_READONLY) info.modeFlags&=~FXIO::AllWrite;
422     if(data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) info.modeFlags|=FXIO::Directory|FXIO::AllWrite; else info.modeFlags|=FXIO::File; // Directories (folders) always writable on Windows
423     info.userNumber=0;
424     info.groupNumber=0;
425     info.linkCount=data.nNumberOfLinks;
426     info.accessTime=fxunixtime(*((FXTime*)&data.ftLastAccessTime));
427     info.modifyTime=fxunixtime(*((FXTime*)&data.ftLastWriteTime));
428     info.createTime=fxunixtime(*((FXTime*)&data.ftCreationTime));
429     info.fileVolume=data.dwVolumeSerialNumber;
430     info.fileIndex=(((FXulong)data.nFileIndexHigh)<<32)|((FXulong)data.nFileIndexLow);
431     info.fileSize=(((FXulong)data.nFileSizeHigh)<<32)|((FXulong)data.nFileSizeLow);
432     return true;
433     }
434 #else
435   const FXTime seconds=1000000000;
436   struct stat data;
437   if(::fstat(file.handle(),&data)==0){
438     info.modeFlags=(data.st_mode&FXIO::AllFull);
439     if(S_ISDIR(data.st_mode)) info.modeFlags|=FXIO::Directory;
440     if(S_ISREG(data.st_mode)) info.modeFlags|=FXIO::File;
441     if(S_ISLNK(data.st_mode)) info.modeFlags|=FXIO::SymLink;
442     if(S_ISCHR(data.st_mode)) info.modeFlags|=FXIO::Character;
443     if(S_ISBLK(data.st_mode)) info.modeFlags|=FXIO::Block;
444     if(S_ISFIFO(data.st_mode)) info.modeFlags|=FXIO::Fifo;
445     if(S_ISSOCK(data.st_mode)) info.modeFlags|=FXIO::Socket;
446     if(data.st_mode&S_ISUID) info.modeFlags|=FXIO::SetUser;
447     if(data.st_mode&S_ISGID) info.modeFlags|=FXIO::SetGroup;
448     if(data.st_mode&S_ISVTX) info.modeFlags|=FXIO::Sticky;
449     info.userNumber=data.st_uid;
450     info.groupNumber=data.st_gid;
451     info.linkCount=data.st_nlink;
452 #if (_POSIX_C_SOURCE >= 200809L) || (_XOPEN_SOURCE >= 700)
453     info.accessTime=data.st_atim.tv_sec*seconds+data.st_atim.tv_nsec;
454     info.modifyTime=data.st_mtim.tv_sec*seconds+data.st_mtim.tv_nsec;
455     info.createTime=data.st_ctim.tv_sec*seconds+data.st_ctim.tv_nsec;
456 #else
457     info.accessTime=data.st_atime*seconds;
458     info.modifyTime=data.st_mtime*seconds;
459     info.createTime=data.st_ctime*seconds;
460 #endif
461     info.fileVolume=(FXlong)data.st_dev;
462     info.fileIndex=(FXlong)data.st_ino;
463     info.fileSize=(FXlong)data.st_size;
464     return true;
465     }
466 #endif
467   return false;
468   }
469 
470 
471 // Return file mode flags
mode(const FXString & file)472 FXuint FXStat::mode(const FXString& file){
473   FXStat data;
474   statFile(file,data);
475   return data.mode();
476   }
477 
478 
479 
480 // Change the mode flags for this file
mode(const FXString & file,FXuint perm)481 FXbool FXStat::mode(const FXString& file,FXuint perm){
482   if(!file.empty()){
483 #ifdef WIN32
484 /*
485 #ifdef UNICODE
486     FXnchar unifile[MAXPATHLEN];
487     utf2ncs(unifile,file.text(),MAXPATHLEN);
488     FXuint flags=::GetFileAttributesW(unifile);
489     if(flags!=INVALID_FILE_ATTRIBUTES){
490       if(flags&FILE_ATTRIBUTE_DIRECTORY){
491         }
492       else{
493         }
494 
495       if((flags&FILE_ATTRIBUTE_DIRECTORY) || (perm&FXIO::AllWrite)){
496         flags&=~FILE_ATTRIBUTE_READONLY;
497         }
498       else{
499         flags|=FILE_ATTRIBUTE_READONLY;
500         }
501       if(perm&FXIO::Hidden) flags|=FILE_ATTRIBUTE_HIDDEN; else flags&=~FILE_ATTRIBUTE_HIDDEN;
502       flags&=FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_NOT_CONTENT_INDEXED|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY;
503       return ::SetFileAttributesW(unifile,flags)!=0;
504       }
505     return false;
506 #else
507     FXuint flags=::GetFileAttributesA(unifile);
508     if(flags!=INVALID_FILE_ATTRIBUTES){
509       if((flags&FILE_ATTRIBUTE_DIRECTORY) || (perm&FXIO::AllWrite)) flags&=~FILE_ATTRIBUTE_READONLY; else flags|=FILE_ATTRIBUTE_READONLY;
510       if(perm&FXIO::Hidden) flags|=FILE_ATTRIBUTE_HIDDEN; else flags&=~FILE_ATTRIBUTE_HIDDEN;
511       flags&=FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_NOT_CONTENT_INDEXED|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY;
512       return ::SetFileAttributesA(file.text(),flags)!=0;
513       }
514     return false;
515 #endif
516 */
517 #else
518     FXuint bits=perm&0777;
519     if(perm&FXIO::SetUser) bits|=S_ISUID;
520     if(perm&FXIO::SetGroup) bits|=S_ISGID;
521     if(perm&FXIO::Sticky) bits|=S_ISVTX;
522     return ::chmod(file.text(),bits)==0;
523 #endif
524     }
525   return false;
526   }
527 
528 
529 // Return true if file exists
exists(const FXString & file)530 FXbool FXStat::exists(const FXString& file){
531   FXTRACE((100,"FXStat::exists(\"%s\"\n",file.text()));
532   if(!file.empty()){
533 #ifdef WIN32
534 #ifdef UNICODE
535     FXnchar unifile[MAXPATHLEN];
536     utf2ncs(unifile,file.text(),MAXPATHLEN);
537     FXTRACE((100,"FXStat::exists: %d\n",(::GetFileAttributesW(unifile)!=INVALID_FILE_ATTRIBUTES)));
538     return ::GetFileAttributesW(unifile)!=INVALID_FILE_ATTRIBUTES;
539 #else
540     FXTRACE((100,"FXStat::exists: %d\n",(::GetFileAttributesA(file.text())!=INVALID_FILE_ATTRIBUTES)));
541     return ::GetFileAttributesA(file.text())!=INVALID_FILE_ATTRIBUTES;
542 #endif
543 #else
544     struct stat status;
545     FXTRACE((100,"FXStat::exists: %d\n",(::stat(file.text(),&status)==0)));
546     return ::stat(file.text(),&status)==0;
547 #endif
548     }
549   return false;
550   }
551 
552 
553 // Get file size
size(const FXString & file)554 FXlong FXStat::size(const FXString& file){
555   FXStat data;
556   statFile(file,data);
557   return data.size();
558   }
559 
560 
561 // Return file volume number
volume(const FXString & file)562 FXlong FXStat::volume(const FXString& file){
563   FXStat data;
564   statFile(file,data);
565   return data.volume();
566   }
567 
568 
569 // Return file index number
index(const FXString & file)570 FXlong FXStat::index(const FXString& file){
571   FXStat data;
572   statFile(file,data);
573   return data.index();
574   }
575 
576 
577 // Return number of links to file
links(const FXString & file)578 FXuint FXStat::links(const FXString& file){
579   FXStat data;
580   statFile(file,data);
581   return data.links();
582   }
583 
584 
585 // Return time file was last modified
modified(const FXString & file)586 FXTime FXStat::modified(const FXString& file){
587   FXStat data;
588   statFile(file,data);
589   return data.modified();
590   }
591 
592 
593 // Change tiome when file was last modified
modified(const FXString & file,FXTime ns)594 FXbool FXStat::modified(const FXString& file,FXTime ns){
595   if(!file.empty()){
596 #ifdef WIN32
597 #ifdef UNICODE
598     FXnchar unifile[MAXPATHLEN];
599     utf2ncs(unifile,file.text(),MAXPATHLEN);
600     FXInputHandle hnd=CreateFileW(unifile,GENERIC_READ|FILE_WRITE_ATTRIBUTES,0,NULL,OPEN_EXISTING,0,NULL);
601 #else
602     FXInputHandle hnd=CreateFileA(file.text(),GENERIC_READ|FILE_WRITE_ATTRIBUTES,0,NULL,OPEN_EXISTING,0,NULL);
603 #endif
604     if(hnd!=INVALID_HANDLE_VALUE){
605       FILETIME wintime;
606       *((FXTime*)&wintime)=fxwintime(ns);
607       if(SetFileTime(hnd,NULL,NULL,&wintime)!=0){
608         CloseHandle(hnd);
609         return true;
610         }
611       CloseHandle(hnd);
612       }
613 #else
614 #if (defined(_ATFILE_SOURCE) && defined(UTIME_OMIT))
615     const FXTime seconds=1000000000;
616     struct timespec values[2];
617     values[0].tv_sec=UTIME_OMIT;
618     values[0].tv_nsec=UTIME_OMIT;
619     values[1].tv_sec=ns/seconds;
620     values[1].tv_nsec=ns%seconds;
621     return utimensat(AT_FDCWD,file.text(),values,0)==0;
622 #else
623     const FXTime seconds=1000000;
624     struct stat data;
625     if(::stat(file.text(),&data)==0){
626       struct timeval values[2];
627       values[0].tv_sec=data.st_atime;
628       values[0].tv_usec=0;
629       values[1].tv_sec=ns/seconds;
630       values[1].tv_usec=ns%seconds;
631       return utimes(file.text(),values)==0;
632       }
633 #endif
634 #endif
635     }
636   return false;
637   }
638 
639 
640 // Return time file was last accessed
accessed(const FXString & file)641 FXTime FXStat::accessed(const FXString& file){
642   FXStat data;
643   statFile(file,data);
644   return data.accessed();
645   }
646 
647 
648 // Change tiome when file was last accessed
accessed(const FXString & file,FXTime ns)649 FXbool FXStat::accessed(const FXString& file,FXTime ns){
650   if(!file.empty()){
651 #ifdef WIN32
652 #ifdef UNICODE
653     FXnchar unifile[MAXPATHLEN];
654     utf2ncs(unifile,file.text(),MAXPATHLEN);
655     FXInputHandle hnd=CreateFileW(unifile,GENERIC_READ|FILE_WRITE_ATTRIBUTES,0,NULL,OPEN_EXISTING,0,NULL);
656 #else
657     FXInputHandle hnd=CreateFileA(file.text(),GENERIC_READ|FILE_WRITE_ATTRIBUTES,0,NULL,OPEN_EXISTING,0,NULL);
658 #endif
659     if(hnd!=INVALID_HANDLE_VALUE){
660       FILETIME wintime;
661       *((FXTime*)&wintime)=fxwintime(ns);
662       if(SetFileTime(hnd,NULL,&wintime,NULL)!=0){
663         CloseHandle(hnd);
664         return true;
665         }
666       CloseHandle(hnd);
667       }
668 #else
669 #if (defined(_ATFILE_SOURCE) && defined(UTIME_OMIT))
670     const FXTime seconds=1000000000;
671     struct timespec values[2];
672     values[0].tv_sec=ns/seconds;
673     values[0].tv_nsec=ns%seconds;
674     values[1].tv_sec=UTIME_OMIT;
675     values[1].tv_nsec=UTIME_OMIT;
676     return utimensat(AT_FDCWD,file.text(),values,0)==0;
677 #else
678     const FXTime seconds=1000000;
679     struct stat data;
680     if(::stat(file.text(),&data)==0){
681       struct timeval values[2];
682       values[0].tv_sec=ns/seconds;
683       values[0].tv_usec=ns%seconds;
684       values[1].tv_sec=data.st_mtime;
685       values[1].tv_usec=0;
686       return utimes(file.text(),values)==0;
687       }
688 #endif
689 #endif
690     }
691   return false;
692   }
693 
694 
695 // Return time when created
created(const FXString & file)696 FXTime FXStat::created(const FXString& file){
697   FXStat data;
698   statFile(file,data);
699   return data.created();
700   }
701 
702 
703 // Change time when file was last created
created(const FXString & file,FXTime ns)704 FXbool FXStat::created(const FXString& file,FXTime ns){
705   if(!file.empty()){
706 #ifdef WIN32
707 #ifdef UNICODE
708     FXnchar unifile[MAXPATHLEN];
709     utf2ncs(unifile,file.text(),MAXPATHLEN);
710     FXInputHandle hnd=CreateFileW(unifile,GENERIC_READ|FILE_WRITE_ATTRIBUTES,0,NULL,OPEN_EXISTING,0,NULL);
711 #else
712     FXInputHandle hnd=CreateFileA(file.text(),GENERIC_READ|FILE_WRITE_ATTRIBUTES,0,NULL,OPEN_EXISTING,0,NULL);
713 #endif
714     if(hnd!=INVALID_HANDLE_VALUE){
715       FILETIME wintime;
716       *((FXTime*)&wintime)=fxwintime(ns);
717       if(SetFileTime(hnd,&wintime,NULL,NULL)!=0){
718         CloseHandle(hnd);
719         return true;
720         }
721       CloseHandle(hnd);
722       }
723 #else
724     return false;               // Not available on *NIX
725 #endif
726     }
727   return false;
728   }
729 
730 
731 // Return true if file is hidden
isHidden(const FXString & file)732 FXbool FXStat::isHidden(const FXString& file){
733   FXStat data;
734   return statFile(file,data) && data.isHidden();
735   }
736 
737 
738 // Check if file represents a file
isFile(const FXString & file)739 FXbool FXStat::isFile(const FXString& file){
740   FXStat data;
741   return statFile(file,data) && data.isFile();
742   }
743 
744 
745 // Check if file represents a link
isLink(const FXString & file)746 FXbool FXStat::isLink(const FXString& file){
747   FXStat data;
748   return statLink(file,data) && data.isLink();
749   }
750 
751 
752 // Check if file represents a directory
isDirectory(const FXString & file)753 FXbool FXStat::isDirectory(const FXString& file){
754   FXStat data;
755   return statFile(file,data) && data.isDirectory();
756   }
757 
758 
759 // Return true if file is readable
isReadable(const FXString & file)760 FXbool FXStat::isReadable(const FXString& file){
761   FXStat data;
762   return statFile(file,data) && data.isReadable();
763   }
764 
765 
766 // Return true if file is writable
isWritable(const FXString & file)767 FXbool FXStat::isWritable(const FXString& file){
768   FXStat data;
769   return statFile(file,data) && data.isWritable();
770   }
771 
772 
773 // Return true if file is executable
isExecutable(const FXString & file)774 FXbool FXStat::isExecutable(const FXString& file){
775   FXStat data;
776   return statFile(file,data) && data.isExecutable();
777   }
778 
779 
780 // Check if owner has full permissions
isOwnerReadWriteExecute(const FXString & file)781 FXbool FXStat::isOwnerReadWriteExecute(const FXString& file){
782   FXStat data;
783   return statFile(file,data) && data.isOwnerReadWriteExecute();
784   }
785 
786 
787 // Check if owner can read
isOwnerReadable(const FXString & file)788 FXbool FXStat::isOwnerReadable(const FXString& file){
789   FXStat data;
790   return statFile(file,data) && data.isOwnerReadable();
791   }
792 
793 
794 // Check if owner can write
isOwnerWritable(const FXString & file)795 FXbool FXStat::isOwnerWritable(const FXString& file){
796   FXStat data;
797   return statFile(file,data) && data.isOwnerWritable();
798   }
799 
800 
801 // Check if owner can execute
isOwnerExecutable(const FXString & file)802 FXbool FXStat::isOwnerExecutable(const FXString& file){
803   FXStat data;
804   return statFile(file,data) && data.isOwnerExecutable();
805   }
806 
807 
808 // Check if group has full permissions
isGroupReadWriteExecute(const FXString & file)809 FXbool FXStat::isGroupReadWriteExecute(const FXString& file){
810   FXStat data;
811   return statFile(file,data) && data.isGroupReadWriteExecute();
812   }
813 
814 
815 // Check if group can read
isGroupReadable(const FXString & file)816 FXbool FXStat::isGroupReadable(const FXString& file){
817   FXStat data;
818   return statFile(file,data) && data.isGroupReadable();
819   }
820 
821 
822 // Check if group can write
isGroupWritable(const FXString & file)823 FXbool FXStat::isGroupWritable(const FXString& file){
824   FXStat data;
825   return statFile(file,data) && data.isGroupWritable();
826   }
827 
828 
829 // Check if group can execute
isGroupExecutable(const FXString & file)830 FXbool FXStat::isGroupExecutable(const FXString& file){
831   FXStat data;
832   return statFile(file,data) && data.isGroupExecutable();
833   }
834 
835 
836 // Check if everybody has full permissions
isOtherReadWriteExecute(const FXString & file)837 FXbool FXStat::isOtherReadWriteExecute(const FXString& file){
838   FXStat data;
839   return statFile(file,data) && data.isOtherReadWriteExecute();
840   }
841 
842 
843 // Check if everybody can read
isOtherReadable(const FXString & file)844 FXbool FXStat::isOtherReadable(const FXString& file){
845   FXStat data;
846   return statFile(file,data) && data.isOtherReadable();
847   }
848 
849 
850 // Check if everybody can write
isOtherWritable(const FXString & file)851 FXbool FXStat::isOtherWritable(const FXString& file){
852   FXStat data;
853   return statFile(file,data) && data.isOtherWritable();
854   }
855 
856 
857 // Check if everybody can execute
isOtherExecutable(const FXString & file)858 FXbool FXStat::isOtherExecutable(const FXString& file){
859   FXStat data;
860   return statFile(file,data) && data.isOtherExecutable();
861   }
862 
863 
864 // Test if suid bit set
isSetUid(const FXString & file)865 FXbool FXStat::isSetUid(const FXString& file){
866   FXStat data;
867   return statFile(file,data) && data.isSetUid();
868   }
869 
870 
871 // Test if sgid bit set
isSetGid(const FXString & file)872 FXbool FXStat::isSetGid(const FXString& file){
873   FXStat data;
874   return statFile(file,data) && data.isSetGid();
875   }
876 
877 
878 // Test if sticky bit set
isSetSticky(const FXString & file)879 FXbool FXStat::isSetSticky(const FXString& file){
880   FXStat data;
881   return statFile(file,data) && data.isSetSticky();
882   }
883 
884 
885 // Return true if file is accessible
isAccessible(const FXString & file,FXuint m)886 FXbool FXStat::isAccessible(const FXString& file,FXuint m){
887   if(!file.empty()){
888 #ifdef WIN32
889 #ifdef UNICODE
890     FXnchar unifile[MAXPATHLEN];
891     FXuint mode=0;
892     if(m&FXIO::ReadOnly) mode|=4;
893     if(m&FXIO::WriteOnly) mode|=2;
894     utf2ncs(unifile,file.text(),MAXPATHLEN);
895     return _waccess(unifile,mode)==0;
896 #else
897     FXuint mode=0;
898     if(m&FXIO::ReadOnly) mode|=4;
899     if(m&FXIO::WriteOnly) mode|=2;
900     return _access(file.text(),mode)==0;
901 #endif
902 #else
903     FXuint mode=F_OK;
904     if(m&FXIO::ReadOnly) mode|=R_OK;
905     if(m&FXIO::WriteOnly) mode|=W_OK;
906     if(m&FXIO::Executable) mode|=X_OK;
907     return access(file.text(),mode)==0;
908 #endif
909     }
910   return false;
911   }
912 
913 
914 // Obtain total amount of space on disk
getTotalDiskSpace(const FXString & path,FXulong & space)915 FXbool FXStat::getTotalDiskSpace(const FXString& path,FXulong& space){
916 #ifdef WIN32
917 #ifdef UNICODE
918   FXnchar unifile[MAXPATHLEN];
919   utf2ncs(unifile,path.text(),MAXPATHLEN);
920   if(GetDiskFreeSpaceExW(unifile,NULL,(PULARGE_INTEGER)&space,NULL)){
921     return true;
922     }
923 #else
924   if(GetDiskFreeSpaceExA(path.text(),NULL,(PULARGE_INTEGER)&space,NULL)){
925     return true;
926     }
927 #endif
928 #else
929 #if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H)
930   struct statvfs info;
931   if(statvfs(path.text(),&info)==0){
932     space=info.f_bsize*info.f_blocks;
933     return true;
934     }
935 #endif
936 #endif
937   return false;
938   }
939 
940 
941 // Obtain available amount of space on disk
getAvailableDiskSpace(const FXString & path,FXulong & space)942 FXbool FXStat::getAvailableDiskSpace(const FXString& path,FXulong& space){
943 #ifdef WIN32
944 #ifdef UNICODE
945   FXnchar unifile[MAXPATHLEN];
946   utf2ncs(unifile,path.text(),MAXPATHLEN);
947   if(GetDiskFreeSpaceExW(unifile,(PULARGE_INTEGER)&space,NULL,NULL)){
948     return true;
949     }
950 #else
951   if(GetDiskFreeSpaceExA(path.text(),(PULARGE_INTEGER)&space,NULL,NULL)){
952     return true;
953     }
954 #endif
955 #else
956 #if defined(HAVE_STATVFS) && defined(HAVE_SYS_STATVFS_H)
957   struct statvfs info;
958   if(statvfs(path.text(),&info)==0){
959     space=info.f_bsize*info.f_bfree;
960     return true;
961     }
962 #endif
963 #endif
964   return false;
965   }
966 
967 }
968