1 /*
2  * Part of WCM Commander
3  * https://github.com/corporateshark/WCMCommander
4  * wcm@linderdaum.com
5  */
6 
7 #include "vfs.h"
8 #include "string-util.h"
9 #include <sys/types.h>
10 #include "unicode_lc.h"
11 #include <algorithm>
12 
13 #if defined( __APPLE__ )
14 #  include <sys/param.h>
15 #  include <sys/mount.h>
16 #endif
17 
18 #include <unordered_map>
19 
20 ///////////////////////////////////////////////////  FS /////////////////////////////
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)21 int FS::OpenRead  ( FSPath& path, int flags, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)22 int FS::OpenCreate   ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
Close(int fd,int * err,FSCInfo * info)23 int FS::Close  ( int fd, int* err, FSCInfo* info )            { SetError( err, 0 ); return -1; }
Read(int fd,void * buf,int size,int * err,FSCInfo * info)24 int FS::Read   ( int fd, void* buf, int size, int* err, FSCInfo* info )      { SetError( err, 0 ); return -1; }
Write(int fd,void * buf,int size,int * err,FSCInfo * info)25 int FS::Write  ( int fd, void* buf, int size, int* err, FSCInfo* info )      { SetError( err, 0 ); return -1; }
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)26 int FS::Seek   ( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet,  int* err, FSCInfo* info )   { SetError( err, 0 ); return -1; }
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)27 int FS::Rename ( FSPath&  oldpath, FSPath& newpath, int* err, FSCInfo* info )   { SetError( err, 0 ); return -1; }
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)28 int FS::MkDir  ( FSPath& path, int mode, int* err,  FSCInfo* info )    { SetError( err, 0 ); return -1; }
Delete(FSPath & path,int * err,FSCInfo * info)29 int FS::Delete ( FSPath& path, int* err, FSCInfo* info )            { SetError( err, 0 ); return -1; }
RmDir(FSPath & path,int * err,FSCInfo * info)30 int FS::RmDir  ( FSPath& path, int* err, FSCInfo* info )            { SetError( err, 0 ); return -1; }
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)31 int FS::SetFileTime  ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )  { SetError( err, 0 ); return -1; }
ReadDir(FSList * list,FSPath & path,int * err,FSCInfo * info)32 int FS::ReadDir   ( FSList* list, FSPath& path,  int* err, FSCInfo* info )    { SetError( err, 0 ); return -1; }
Stat(FSPath & path,FSStat * st,int * err,FSCInfo * info)33 int FS::Stat( FSPath& path, FSStat* st, int* err, FSCInfo* info )         { SetError( err, 0 ); return -1; }
StatSetAttr(FSPath & path,const FSStat * st,int * err,FSCInfo * info)34 int FS::StatSetAttr( FSPath& path, const FSStat* st, int* err, FSCInfo* info ) { SetError( err, 0 ); return -1; }
FStat(int fd,FSStat * st,int * err,FSCInfo * info)35 int FS::FStat( int fd, FSStat* st, int* err, FSCInfo* info )     { SetError( err, 0 ); return -1; }
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)36 int FS::Symlink   ( FSPath& path, FSString& str, int* err, FSCInfo* info )      { SetError( err, 0 ); return -1; }
StatVfs(FSPath & path,FSStatVfs * st,int * err,FSCInfo * info)37 int FS::StatVfs( FSPath& path, FSStatVfs* st, int* err, FSCInfo* info )    { SetError( err, 0 ); return -1; }
GetFileSystemFreeSpace(FSPath & path,int * err)38 int64_t FS::GetFileSystemFreeSpace( FSPath& path, int* err ) { SetError( err, 0 ); return -1; }
39 
GetUserName(int user,unicode_t buf[64])40 unicode_t* FS::GetUserName( int user, unicode_t buf[64] ) { buf[0] = 0; return buf; };
GetGroupName(int group,unicode_t buf[64])41 unicode_t* FS::GetGroupName( int group, unicode_t buf[64] ) { buf[0] = 0; return buf; };
42 
~FS()43 FS::~FS() {}
44 
45 ////////////////////////////////////// FSCInfo
SmbLogon(FSSmbParam * a)46 bool FSCInfo::SmbLogon( FSSmbParam* a ) { return false; }
FtpLogon(FSFtpParam * a)47 bool FSCInfo::FtpLogon( FSFtpParam* a ) { return false; }
IsStopped() const48 bool FSCInfo::IsStopped() const { return false; }
Prompt(const unicode_t * header,const unicode_t * message,FSPromptData * p,int count)49 bool FSCInfo::Prompt( const unicode_t* header, const unicode_t* message, FSPromptData* p, int count ) { return false; }
~FSCInfo()50 FSCInfo::~FSCInfo() {}
51 
52 ////////////////////////////////////// FSCSimpleInfo
IsStopped() const53 bool FSCSimpleInfo::IsStopped() const { return m_Stopped; }
~FSCSimpleInfo()54 FSCSimpleInfo::~FSCSimpleInfo() {}
55 
56 //////////////////////////////////////////////////  FSSys ///////////////////////////
57 
58 
59 #ifdef _WIN32
60 //#error
61 #include <time.h>
62 
63 
TT_to_FILETIME(time_t t,FILETIME & ft)64 static void TT_to_FILETIME( time_t t, FILETIME& ft )
65 {
66 	LONGLONG ll;
67 	ll = Int32x32To64( t, 10000000 ) + 116444736000000000ll;
68 	ft.dwLowDateTime = ( DWORD )(ll & 0xFFFFFFFF );
69 	ft.dwHighDateTime = ( DWORD )(( ll >> 32 ) & 0xFFFFFFFF );
70 }
71 
72 //!!! Херня какая-то в преобразовании, надо разбираться
FILETIME_to_TT(FILETIME ft)73 static time_t FILETIME_to_TT( FILETIME ft )
74 {
75 	LONGLONG ll =  ft.dwHighDateTime;
76 	ll <<= 32;
77 	ll += ft.dwHighDateTime;
78 	ll -= 116444736000000000ll;
79 
80 	if ( ll < 0 ) { return 0; }
81 
82 	ll /= 10000000;
83 	return ll;
84 }
85 
GetTimeT()86 time_t FSTime::GetTimeT()
87 {
88 	if ( flags & TIME_T_OK ) { return tt; }
89 
90 	if ( ( flags & FILETIME_OK ) == 0 ) { return 0; }
91 
92 	tt = FILETIME_to_TT( ft );
93 	flags |= TIME_T_OK;
94 	return tt;
95 }
96 
GetFileTime()97 FILETIME FSTime::GetFileTime()
98 {
99 	if ( flags & FILETIME_OK ) { return ft; }
100 
101 	static FILETIME t0 = {0, 0};
102 
103 	if ( ( flags & TIME_T_OK ) == 0 ) { return t0; }
104 
105 	TT_to_FILETIME( tt, ft );
106 	flags |= FILETIME_OK;
107 	return ft;
108 }
109 
Utf16Chars(const wchar_t * s)110 inline int Utf16Chars( const wchar_t* s ) { int n = 0; for ( ; *s; s++ ) { n++; } return n; }
111 
UnicodeToUtf16_cat(const unicode_t * s,wchar_t * cat)112 inline std::vector<wchar_t> UnicodeToUtf16_cat( const unicode_t* s, wchar_t* cat )
113 {
114 	int lcat = Utf16Chars( cat );
115 	std::vector<wchar_t> p( unicode_strlen( s ) + lcat + 1 );
116 	wchar_t* d;
117 
118 	for ( d = p.data(); *s; s++, d++ ) { *d = *s; }
119 
120 	for ( ; *cat; cat++, d++ ) { *d = *cat; }
121 
122 	*d = 0;
123 	return p;
124 }
125 
126 
127 
Flags()128 unsigned FSSys::Flags() { return HAVE_READ | HAVE_WRITE | HAVE_SYMLINK | HAVE_SEEK; }
IsEEXIST(int err)129 bool  FSSys::IsEEXIST( int err ) { return err == ERROR_FILE_EXISTS; } //err == EEXIST; }
IsENOENT(int err)130 bool  FSSys::IsENOENT( int err ) { return err == ERROR_FILE_NOT_FOUND ; } // err == ENOENT; }
IsEXDEV(int err)131 bool  FSSys::IsEXDEV( int err ) { return false; } // err == EXDEV; }
132 
StrError(int err)133 FSString FSSys::StrError( int err )
134 {
135 	sys_char_t buf[1024];
136 	FSString ret;
137 	ret.SetSys( sys_error_str( err, buf, 0x100 ) );
138 	return ret;
139 }
140 
Equal(FS * fs)141 bool FSSys::Equal( FS* fs )
142 {
143 	if ( !fs || fs->Type() != FS::SYSTEM
144 #ifdef _WIN32
145 		|| _drive != ((FSSys*)fs)->_drive
146 #endif
147 		) { return false; }
148 
149 	return true;
150 }
151 
152 /* не поддерживает пути >265 длиной
153 static std::vector<wchar_t> SysPathStr(int drive, const unicode_t *s)
154 {
155    std::vector<wchar_t> p(2 + unicode_strlen(s)+1);
156    wchar_t *d =p.ptr();
157 
158    if (drive == -1) { //???
159       *(d++) = '\\';
160    } else {
161       d[0]=drive+'a';
162       d[1]=':';
163       d+=2;
164    }
165 
166    for (; *s; s++, d++) *d = *s;
167    *d=0;
168    return p;
169 }
170 */
171 
SysPathStr(int drive,const unicode_t * s)172 static std::vector<wchar_t> SysPathStr( int drive, const unicode_t* s )
173 {
174 	std::vector<wchar_t> p( 10 + unicode_strlen( s ) + 1 ); //+10 прозапас, вообще +8 (макс \\?\UNC\)
175 	wchar_t* d = p.data();
176 
177 	d[0] = '\\';
178 	d[1] = '\\';
179 	d[2] = '?';
180 	d[3] = '\\';
181 	d += 4;
182 
183 	if ( drive == -1 ) //???
184 	{
185 		d[0] = 'U';
186 		d[1] = 'N';
187 		d[2] = 'C';
188 		//d[3]='\\'; //еше \ добавится из пути
189 		d += 3;
190 	}
191 	else
192 	{
193 		d[0] = drive + 'a';
194 		d[1] = ':';
195 		d += 2;
196 	}
197 
198 	for ( ; *s; s++, d++ ) { *d = *s; }
199 
200 	*d = 0;
201 	return p;
202 }
203 
204 
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)205 int FSSys::OpenRead  ( FSPath& path, int flags, int* err, FSCInfo* info )
206 {
207 	int shareFlags = 0;
208 
209 	if ( flags & FS::SHARE_READ ) { shareFlags |= FILE_SHARE_READ; }
210 
211 	if ( flags & FS::SHARE_WRITE ) { shareFlags |= FILE_SHARE_WRITE; }
212 
213 	HANDLE h = CreateFileW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), GENERIC_READ, shareFlags, 0, OPEN_EXISTING, 0, 0 );
214 
215 	//file_open(SysPathStr(_drive, path.GetUnicode('\\')).ptr());
216 	if ( h == INVALID_HANDLE_VALUE )
217 	{
218 		SetError( err, GetLastError() );
219 		return -1;
220 	}
221 
222 	int fd = handles.New();
223 	HANDLE* p = this->handles.Handle( fd );
224 
225 	if ( !p )
226 	{
227 		SetError( err, ERROR_INVALID_PARAMETER );
228 		CloseHandle( h );
229 		return -1;
230 	}
231 
232 	*p = h;
233 	return fd;
234 }
235 
236 
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)237 int FSSys::OpenCreate   ( FSPath& path, bool overwrite, int mode, int flags,  int* err, FSCInfo* info )
238 {
239 	DWORD diseredAccess = GENERIC_READ | GENERIC_WRITE;
240 //	DWORD shareMode = 0;
241 	DWORD creationDisposition = ( overwrite ) ? CREATE_ALWAYS  : CREATE_NEW;
242 //???
243 
244 	HANDLE h = CreateFileW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), diseredAccess, FILE_SHARE_WRITE, 0, creationDisposition, 0, 0 );
245 
246 	if ( h == INVALID_HANDLE_VALUE )
247 	{
248 		SetError( err, GetLastError() );
249 		return -1;
250 	}
251 
252 	int fd = handles.New();
253 	HANDLE* p = this->handles.Handle( fd );
254 
255 	if ( !p )
256 	{
257 		SetError( err, ERROR_INVALID_PARAMETER );
258 		CloseHandle( h );
259 		return -1;
260 	}
261 
262 	*p = h;
263 	return fd;
264 }
265 
Close(int fd,int * err,FSCInfo * info)266 int FSSys::Close( int fd, int* err, FSCInfo* info )
267 {
268 	HANDLE* p = this->handles.Handle( fd );
269 
270 	if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
271 
272 	HANDLE h = *p;
273 	handles.Free( fd );
274 
275 	if ( !CloseHandle( h ) )
276 	{
277 		SetError( err, GetLastError() );
278 		return -1;
279 	}
280 
281 	return 0;
282 }
283 
Read(int fd,void * buf,int size,int * err,FSCInfo * info)284 int FSSys::Read( int fd, void* buf, int size, int* err, FSCInfo* info )
285 {
286 	HANDLE* p = this->handles.Handle( fd );
287 
288 	if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
289 
290 	DWORD bytes;
291 
292 	if ( !ReadFile( *p, buf, size, &bytes, 0 ) )
293 	{
294 		SetError( err, GetLastError() );
295 		return -1;
296 	}
297 
298 	return bytes;
299 }
300 
Write(int fd,void * buf,int size,int * err,FSCInfo * info)301 int FSSys::Write( int fd, void* buf, int size, int* err, FSCInfo* info )
302 {
303 	HANDLE* p = this->handles.Handle( fd );
304 
305 	if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
306 
307 	DWORD bytes;
308 
309 	if ( !WriteFile( *p, buf, size, &bytes, 0 ) )
310 	{
311 		SetError( err, GetLastError() );
312 		return -1;
313 	}
314 
315 	return bytes;
316 }
317 
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)318 int FSSys::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet,  int* err, FSCInfo* info )
319 {
320 	HANDLE* p = this->handles.Handle( fd );
321 
322 	if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
323 
324 	LARGE_INTEGER li;
325 	li.QuadPart = pos;
326 
327 	if ( !SetFilePointerEx( *p, li, &li, mode ) )
328 	{
329 		SetError( err, GetLastError() );
330 		return -1;
331 	}
332 
333 	if ( pRet )
334 	{
335 		*pRet = li.QuadPart;
336 	}
337 
338 	return 0;
339 }
340 
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)341 int FSSys::Rename ( FSPath&  oldpath, FSPath& newpath, int* err,  FSCInfo* info )
342 {
343 	if ( MoveFileW(
344 	        SysPathStr( _drive, oldpath.GetUnicode( '\\' ) ).data(),
345 	        SysPathStr( _drive, newpath.GetUnicode( '\\' ) ).data()
346 	     ) ) { return 0; }
347 
348 	SetError( err, GetLastError() );
349 	return -1;
350 }
351 
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)352 int FSSys::MkDir( FSPath& path, int mode, int* err,  FSCInfo* info )
353 {
354 	if ( CreateDirectoryW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), 0 ) ) { return 0; }
355 
356 	DWORD e = GetLastError();
357 
358 	if ( e == ERROR_ALREADY_EXISTS ) { return 0; }
359 
360 	SetError( err, e );
361 	return -1;
362 }
363 
Delete(FSPath & path,int * err,FSCInfo * info)364 int FSSys::Delete( FSPath& path, int* err, FSCInfo* info )
365 {
366 	std::vector<wchar_t> sp = SysPathStr( _drive, path.GetUnicode( '\\' ) );
367 
368 	if ( DeleteFileW( sp.data() ) ) { return 0; }
369 
370 	DWORD lastError  = GetLastError();
371 
372 	if ( lastError == ERROR_ACCESS_DENIED ) //возможно read only аттрибут, пытаемся сбросить
373 	{
374 		if ( SetFileAttributesW( sp.data(), 0 ) && DeleteFileW( sp.data() ) ) { return 0; }
375 
376 		lastError  = GetLastError();
377 	}
378 
379 	SetError( err, lastError );
380 	return -1;
381 }
382 
RmDir(FSPath & path,int * err,FSCInfo * info)383 int FSSys::RmDir( FSPath& path, int* err, FSCInfo* info )
384 {
385 	std::vector<wchar_t> sp = SysPathStr( _drive, path.GetUnicode( '\\' ) );
386 
387 	if ( RemoveDirectoryW( sp.data() ) ) { return 0; }
388 
389 	DWORD lastError  = GetLastError();
390 
391 	if ( lastError == ERROR_ACCESS_DENIED ) //возможно read only аттрибут, пытаемся сбросить
392 	{
393 		if ( SetFileAttributesW( sp.data(), 0 ) && RemoveDirectoryW( sp.data() ) ) { return 0; }
394 
395 		lastError  = GetLastError();
396 	}
397 
398 	SetError( err, lastError );
399 	return -1;
400 }
401 
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)402 int FSSys::SetFileTime  ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
403 {
404 	HANDLE h = CreateFileW( SysPathStr( _drive, path.GetUnicode( '\\' ) ).data(), FILE_WRITE_ATTRIBUTES , 0, 0, OPEN_EXISTING, 0, 0 );
405 
406 	if ( h == INVALID_HANDLE_VALUE )
407 	{
408 		SetError( err, GetLastError() );
409 		return -1;
410 	}
411 
412 	FILETIME ct = cTime;
413 	FILETIME at = aTime;
414 	FILETIME mt = mTime;
415 
416 	if ( !::SetFileTime( h, &ct, &at, &mt ) )
417 	{
418 		SetError( err, GetLastError() );
419 		CloseHandle( h );
420 		return -1;
421 	}
422 
423 	CloseHandle( h );
424 	return 0;
425 }
426 /* не поддерживает пути >265 длиной
427 static std::vector<wchar_t> FindPathStr(int drive, const unicode_t *s, wchar_t *cat)
428 {
429    int lcat = Utf16Chars(cat);
430 
431    std::vector<wchar_t> p(2 + unicode_strlen(s)+lcat+1);
432    wchar_t *d =p.ptr();
433 
434    if (drive == -1) {
435       *(d++) = '\\';
436    } else {
437       d[0]=drive+'a';
438       d[1]=':';
439       d+=2;
440    }
441 
442    for (; *s; s++, d++) *d = *s;
443    for (;*cat;cat++, d++) *d=*cat;
444    *d=0;
445    return p;
446 }
447 */
448 
449 // make UNC path by concati'ing input pars in an intelligent way
FindPathStr(int drive,const unicode_t * s,const wchar_t * cat)450 static std::vector<wchar_t> FindPathStr( int drive, const unicode_t* s, const wchar_t* cat )
451 {
452 	int lcat = Utf16Chars( cat );
453 
454 	std::vector<wchar_t> p( 10 + unicode_strlen( s ) + lcat + 1 );
455 	wchar_t* d = p.data();
456 
457 	d[0] = '\\';
458 	d[1] = '\\';
459 	d[2] = '?';
460 	d[3] = '\\';
461 	d += 4;
462 
463 	if ( drive == -1 ) //???
464 	{
465 		d[0] = 'U';
466 		d[1] = 'N';
467 		d[2] = 'C';
468 		//d[3]='\\'; //еше \ добавится из пути
469 		d += 3;
470 	}
471 	else
472 	{
473 		d[0] = drive + 'a';
474 		d[1] = ':';
475 		d += 2;
476 	}
477 
478 
479 	unicode_t lastChar = 0;
480 
481 	for ( ; *s; s++, d++ ) { lastChar = *d = *s; }
482 
483 	// ensure that we do not append double-backslash to the filepath.
484 	// FindFirstFileW does not like UNC like \\?\c:\\*, and prefers \\?\c:\*
485 	if ( lastChar == '\\' && *cat == '\\' ) { cat++; }
486 
487 	for ( ; *cat; cat++, d++ ) { *d = *cat; }
488 
489 	*d = 0;
490 
491 	return p;
492 }
493 
494 #ifdef _DEBUG
toStr(char * str,const wchar_t * wstr)495 static void toStr( char* str, const wchar_t* wstr )
496 {
497 	while ( *wstr )
498 	{
499 		*str++ = char( ( *wstr++ ) & 0xFF );
500 	}
501 
502 	*str = 0;
503 }
504 #endif
505 
ReadDir(FSList * list,FSPath & _path,int * err,FSCInfo * info)506 int FSSys::ReadDir( FSList* list, FSPath& _path, int* err, FSCInfo* info )
507 {
508 	list->Clear();
509 	FSPath path( _path );
510 	WIN32_FIND_DATAW ent;
511 
512 	HANDLE handle = FindFirstFileW( FindPathStr( _drive, path.GetUnicode(), L"\\*" ).data(), &ent );
513 
514 #if defined( _DEBUG ) & 0
515 	std::vector<wchar_t> wpath = FindPathStr( _drive, path.GetUnicode(), L"\\*" );
516 	char s[1024];
517 	toStr( s, wpath.data() );
518 	dbg_printf( "FSSys::ReadDir %s@UNC path=%s\n", handle == INVALID_HANDLE_VALUE ? "OK" : "failed", s );
519 #endif
520 
521 	if ( handle == INVALID_HANDLE_VALUE )
522 	{
523 		DWORD ret = GetLastError();
524 
525 		if ( ret == ERROR_FILE_NOT_FOUND ) { return 0; }
526 
527 		SetError( err, GetLastError() );
528 		return -1;
529 	}
530 
531 	try
532 	{
533 		while ( true )
534 		{
535 			if ( info && info->IsStopped() )
536 			{
537 				FindClose( handle );
538 				return -2;
539 			}
540 
541 			//skip . and ..
542 			if ( !( ent.cFileName[0] == '.' && ( !ent.cFileName[1] || ( ent.cFileName[1] == '.' && !ent.cFileName[2] ) ) ) )
543 			{
544 				clPtr<FSNode> pNode = new FSNode();
545 				pNode->name.Set( CS_UNICODE, Utf16ToUnicode( ent.cFileName ).data() );
546 
547 				pNode->st.dwFileAttributes = ent.dwFileAttributes;
548 				pNode->st.size = ( seek_t( ent.nFileSizeHigh ) << 32 ) + ent.nFileSizeLow;
549 
550 				pNode->st.m_CreationTime = ent.ftCreationTime;
551 				pNode->st.m_LastAccessTime = ent.ftLastAccessTime;
552 				pNode->st.m_LastWriteTime = ent.ftLastWriteTime;
553 				// TODO: use ZwQueryDirectoryFile() to get the change time
554 				pNode->st.m_ChangeTime = ent.ftLastWriteTime;
555 
556 				if ( ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
557 				{
558 					pNode->st.mode = S_IFDIR;
559 				}
560 				else
561 				{
562 					pNode->st.mode = S_IFREG;
563 				}
564 
565 				pNode->st.mode |= 0664;
566 				list->Append( pNode );
567 			}
568 
569 			if ( !FindNextFileW( handle, &ent ) )
570 			{
571 				if ( GetLastError() == ERROR_NO_MORE_FILES ) { break; }
572 
573 				SetError( err, GetLastError() );
574 				FindClose( handle );
575 				return -1;
576 			}
577 		};
578 
579 		FindClose( handle );
580 
581 		return 0;
582 	}
583 	catch ( ... )
584 	{
585 		FindClose( handle );
586 		throw;
587 	}
588 
589 	SetError( err, 100 );
590 	return -1;
591 }
592 
Stat(FSPath & path,FSStat * fsStat,int * err,FSCInfo * info)593 int FSSys::Stat( FSPath& path, FSStat* fsStat, int* err, FSCInfo* info )
594 {
595 	if ( ( _drive >= 0 && path.Count() == 1 ) || ( _drive == -1 && path.Count() == 3 ) )
596 	{
597 		//pseudo stat
598 		fsStat->size = 0;
599 		fsStat->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
600 		fsStat->mode = S_IFDIR;
601 		fsStat->m_CreationTime = 0;
602 		fsStat->m_LastAccessTime = 0;
603 		fsStat->m_LastWriteTime = 0;
604 		fsStat->m_ChangeTime = 0;
605 		fsStat->mode |= 0664;
606 		return 0;
607 	}
608 
609 	WIN32_FIND_DATAW ent;
610 	HANDLE handle = FindFirstFileW( SysPathStr( _drive, path.GetUnicode() ).data(), &ent );
611 
612 	if ( handle == INVALID_HANDLE_VALUE )
613 	{
614 		SetError( err, GetLastError() );
615 		return -1;
616 	}
617 
618 	try
619 	{
620 		fsStat->size = ( seek_t( ent.nFileSizeHigh ) << 32 ) + ent.nFileSizeLow;
621 		fsStat->dwFileAttributes = ent.dwFileAttributes;
622 		fsStat->m_CreationTime = ent.ftCreationTime;
623 		fsStat->m_LastAccessTime = ent.ftLastAccessTime;
624 		fsStat->m_LastWriteTime = ent.ftLastWriteTime;
625 		// TODO: use ZwQueryDirectoryFile() to get the change time
626 		fsStat->m_ChangeTime = ent.ftLastWriteTime;
627 
628 		if ( ent.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
629 		{
630 			fsStat->mode = S_IFDIR;
631 		}
632 		else
633 		{
634 			fsStat->mode = S_IFREG;
635 		}
636 
637 		fsStat->mode |= 0664;
638 		FindClose( handle );
639 		return 0;
640 	}
641 	catch ( ... )
642 	{
643 		FindClose( handle );
644 		throw;
645 	}
646 
647 	//...
648 	SetError( err, 50 );
649 	return -1;
650 }
651 
StatSetAttr(FSPath & path,const FSStat * st,int * err,FSCInfo * info)652 int FSSys::StatSetAttr( FSPath& path, const FSStat* st, int* err, FSCInfo* info )
653 {
654 	const unicode_t* lpFileName = path.GetUnicode();
655 
656 	DWORD Attr = st->dwFileAttributes;
657 
658 	return SetFileAttributesW( lpFileName, Attr ) ? 0 : -1;
659 }
660 
GetFileSystemFreeSpace(FSPath & path,int * err)661 int64_t FSSys::GetFileSystemFreeSpace( FSPath& path, int* err )
662 {
663 	DWORD SectorsPerCluster;
664 	DWORD BytesPerSector;
665 	DWORD NumberOfFreeClusters;
666 	DWORD TotalNumberOfClusters;
667 
668 	int d = Drive();
669 
670 	char RootPath[] = { char( d + 'A' ), ':', '\\', 0 };
671 
672 	if ( GetDiskFreeSpace( RootPath, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters ) != TRUE ) { return -1; }
673 
674 	return ( int64_t )SectorsPerCluster * ( int64_t )BytesPerSector * ( int64_t )NumberOfFreeClusters;
675 }
676 
FStat(int fd,FSStat * fsStat,int * err,FSCInfo * info)677 int FSSys::FStat( int fd, FSStat* fsStat, int* err, FSCInfo* info )
678 {
679 	BY_HANDLE_FILE_INFORMATION e;
680 	HANDLE* p = this->handles.Handle( fd );
681 
682 	if ( !p ) { SetError( err, ERROR_INVALID_PARAMETER ); return -1; }
683 
684 
685 	if ( !GetFileInformationByHandle( *p, &e ) )
686 	{
687 		SetError( err, GetLastError() );
688 		return -1;
689 	}
690 
691 	fsStat->size = ( seek_t( e.nFileSizeHigh ) << 32 ) + e.nFileSizeLow;
692 	fsStat->dwFileAttributes = e.dwFileAttributes;
693 	fsStat->m_CreationTime = e.ftCreationTime;
694 	fsStat->m_LastAccessTime = e.ftLastAccessTime;
695 	fsStat->m_LastWriteTime = e.ftLastWriteTime;
696 	// TODO: use ZwQueryDirectoryFile() to get the change time
697 	fsStat->m_ChangeTime = e.ftLastWriteTime;
698 
699 	if ( e.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
700 	{
701 		fsStat->mode = S_IFDIR;
702 	}
703 	else
704 	{
705 		fsStat->mode = S_IFREG;
706 	}
707 
708 	fsStat->mode |= 0664;
709 	return 0;
710 }
711 
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)712 int FSSys::Symlink( FSPath& path, FSString& str, int* err, FSCInfo* info )
713 {
714 	//...
715 	SetError( err, 50 );
716 	return -1;
717 }
718 
StatVfs(FSPath & path,FSStatVfs * vst,int * err,FSCInfo * info)719 int FSSys::StatVfs( FSPath& path, FSStatVfs* vst, int* err, FSCInfo* info )
720 {
721 	ccollect<wchar_t, 0x100> root;
722 
723 	root.append( '\\' );
724 	root.append( '\\' );
725 	root.append( '?' );
726 	root.append( '\\' );
727 
728 	if ( Drive() == -1 )
729 	{
730 		root.append( 'U' );
731 		root.append( 'N' );
732 		root.append( 'C' );
733 		root.append( '\\' );
734 		int n = path.Count() < 3 ? path.Count() : 3;
735 
736 		for ( int i = 1; i < n; i++ )
737 		{
738 			const unicode_t* s = path.GetItem( i )->GetUnicode();
739 
740 			for ( ; *s; s++ ) { root.append( *s ); }
741 
742 			root.append( '\\' );
743 		}
744 	}
745 	else
746 	{
747 		root.append( Drive() + 'a' );
748 		root.append( ':' );
749 		root.append( '\\' );
750 	}
751 
752 	root.append( 0 );
753 
754 	DWORD SectorsPerCluster;
755 	DWORD BytesPerSector;
756 	DWORD NumberOfFreeClusters;
757 	DWORD TotalNumberOfClusters;
758 
759 	if ( !GetDiskFreeSpaceW( root.ptr(), &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters ) )
760 	{
761 		SetError( err, GetLastError() );
762 		return -1;
763 	}
764 
765 	vst->size = int64_t( TotalNumberOfClusters ) * SectorsPerCluster * BytesPerSector;
766 	vst->avail = int64_t( NumberOfFreeClusters ) * SectorsPerCluster * BytesPerSector;
767 	return 0;
768 }
769 
Uri(FSPath & path)770 FSString FSSys::Uri( FSPath& path )
771 {
772 	unicode_t pref[0x100];
773 	unicode_t* p = pref;
774 
775 	if ( _drive > 0 && _drive < 'z' - 'a' + 1 ) //+1 ???
776 	{
777 		*( p++ ) = 'A' + _drive;
778 		*( p++ ) = ':';
779 	}
780 	else if ( _drive == -1 )
781 	{
782 		*( p++ ) = '\\';
783 	}
784 	else
785 	{
786 		*( p++ ) = '?';
787 		*( p++ ) = ':';
788 	}
789 
790 	*p = 0;
791 
792 	return FSString( carray_cat<unicode_t>( pref, path.GetUnicode() ).data() );
793 }
794 
795 static std::unordered_map<int, std::vector<unicode_t> > userList;
796 static std::unordered_map<int, std::vector<unicode_t> > groupList;
797 
798 static unicode_t c0 = 0;
799 
GetOSUserName(int id)800 static unicode_t* GetOSUserName( int id )
801 {
802 	return &c0;
803 }
804 
GetOSGroupName(int id)805 static unicode_t* GetOSGroupName( int id )
806 {
807 	return &c0;
808 }
809 
GetUserName(int user,unicode_t buf[64])810 unicode_t* FSSys::GetUserName( int user, unicode_t buf[64] )
811 {
812 	unicode_t* u = GetOSUserName( user );
813 	unicode_t* s = buf;
814 
815 	for ( int n = 63; n > 0 && *u; n--, u++, s++ )
816 	{
817 		*s = *u;
818 	}
819 
820 	*s = 0;
821 	return buf;
822 };
823 
824 
GetGroupName(int group,unicode_t buf[64])825 unicode_t* FSSys::GetGroupName( int group, unicode_t buf[64] )
826 {
827 	unicode_t* g = GetOSGroupName( group );
828 	unicode_t* s = buf;
829 
830 	for ( int n = 63; n > 0 && *g; n--, g++, s++ )
831 	{
832 		*s = *g;
833 	}
834 
835 	*s = 0;
836 
837 	return buf;
838 };
839 
840 //////////////////////////  W32NetRes  /////////////////////////
841 
Set(NETRESOURCEW * p)842 void W32NetRes::Set( NETRESOURCEW* p )
843 {
844 	Clear();
845 
846 	if ( !p ) { return; }
847 
848 	int nameLen = p->lpLocalName ? wcslen( p->lpLocalName ) + 1 : 0;
849 	int remoteNameLen = p->lpRemoteName ? wcslen( p->lpRemoteName ) + 1 : 0;
850 	int commentLen = p->lpComment ? wcslen( p->lpComment ) + 1 : 0;
851 	int providerLen = p->lpProvider ? wcslen( p->lpProvider ) + 1 : 0;
852 	int size = sizeof( Node ) + ( nameLen + remoteNameLen + commentLen + providerLen ) * sizeof( wchar_t );
853 	data = new unsigned char[size];
854 	Node& node = *( ( Node* )data );
855 	node.size = size;
856 	node.rs = *p;
857 	int offset = sizeof( node );
858 #if _MSC_VER > 1700
859 #  define QQQ(a, b, len) if (len>0) { a = (wchar_t*)(data + offset); Lwcsncpy(a, len, b, _TRUNCATE); } offset += len*sizeof(wchar_t);
860 #else
861 #  define QQQ(a, b, len) if (len>0) { a = (wchar_t*)(data + offset); Lwcsncpy(a, b, len); } offset += len*sizeof(wchar_t);
862 #endif
863 	QQQ( node.rs.lpLocalName, p->lpLocalName, nameLen );
864 	QQQ( node.rs.lpRemoteName, p->lpRemoteName, remoteNameLen );
865 	QQQ( node.rs.lpComment, p->lpComment, commentLen );
866 	QQQ( node.rs.lpProvider, p->lpProvider, providerLen );
867 #undef QQQ
868 }
869 
Copy(const unsigned char * a)870 unsigned char* W32NetRes::Copy( const unsigned char* a )
871 {
872 	if ( !a ) { return 0; }
873 
874 	Node& src = *( ( Node* )a );
875 
876 	unsigned char* b = new unsigned char [src.size];
877 	memcpy( b, a, src.size );
878 
879 	Node& dest = *( ( Node* )b );
880 #define QQQ(x) if (dest.rs.x) { dest.rs.x = (wchar_t*)(( ((char*)src.rs.x)-((char*)a)) + (char*)b); }
881 	QQQ( lpLocalName );
882 	QQQ( lpRemoteName );
883 	QQQ( lpComment );
884 	QQQ( lpProvider );
885 #undef QQQ
886 	return b;
887 };
888 
889 
890 /////////////////////////// FSWin32Net /////////////////////////
891 
Flags()892 unsigned FSWin32Net::Flags() { return 0; }
IsEEXIST(int err)893 bool FSWin32Net::IsEEXIST( int err ) { return false; };
IsENOENT(int err)894 bool FSWin32Net::IsENOENT( int err ) { return false; };
IsEXDEV(int err)895 bool FSWin32Net::IsEXDEV( int err ) { return false; };
896 
StrError(int err)897 FSString FSWin32Net::StrError( int err )
898 {
899 	if ( err == ERRNOSUPPORT )
900 	{
901 		return FSString( "Operation not supported by net fs" );
902 	}
903 
904 	FSString ret;
905 	sys_char_t buf[1024];
906 	ret.SetSys( sys_error_str( err, buf, 0x100 ) );
907 
908 	return ret;
909 }
910 
911 
Equal(FS * fs)912 bool FSWin32Net::Equal( FS* fs ) { return false; };
913 
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)914 int FSWin32Net::OpenRead   ( FSPath& path, int flags, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)915 int FSWin32Net::OpenCreate ( FSPath& path, bool overwrite, int mode, int flags, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Close(int fd,int * err,FSCInfo * info)916 int FSWin32Net::Close   ( int fd, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Read(int fd,void * buf,int size,int * err,FSCInfo * info)917 int FSWin32Net::Read ( int fd, void* buf, int size, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Write(int fd,void * buf,int size,int * err,FSCInfo * info)918 int FSWin32Net::Write   ( int fd, void* buf, int size, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)919 int FSWin32Net::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet,  int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)920 int FSWin32Net::Rename  ( FSPath&  oldpath, FSPath& newpath, int* err,  FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)921 int FSWin32Net::MkDir   ( FSPath& path, int mode, int* err,  FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
Delete(FSPath & path,int * err,FSCInfo * info)922 int FSWin32Net::Delete  ( FSPath& path, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
RmDir(FSPath & path,int * err,FSCInfo * info)923 int FSWin32Net::RmDir   ( FSPath& path, int* err, FSCInfo* info ) { SetError( err, ERRNOSUPPORT ); return -1; }
924 
925 class WNetEnumerator
926 {
927 	enum { BUFSIZE = 16 * 1024 };
928 	HANDLE handle;
929 	std::vector<char> buf;
930 	int pos, count;
931 	bool Fill( DWORD* pErr );
932 public:
WNetEnumerator()933 	WNetEnumerator(): handle( 0 ), buf( BUFSIZE ), pos( 0 ), count( 0 ) {}
934 
Close()935 	void Close() { if ( handle ) { WNetCloseEnum( handle ); handle = 0; pos = count = 0; } }
Open(NETRESOURCEW * p)936 	bool Open( NETRESOURCEW* p ) { return WNetOpenEnumW( RESOURCE_GLOBALNET, RESOURCETYPE_ANY, 0, p, &handle ) == NO_ERROR; }
937 
Next(DWORD * pErr)938 	NETRESOURCEW* Next( DWORD* pErr )
939 	{
940 
941 		if ( !Fill( pErr ) ) { return 0; }
942 
943 		if ( pErr ) { *pErr = 0; }
944 
945 		if ( count < 0 ) { return 0; }
946 
947 		NETRESOURCEW* p = ( ( NETRESOURCEW* )buf.data() ) + pos;
948 		pos++;
949 		return p;
950 	}
951 
~WNetEnumerator()952 	~WNetEnumerator() { Close(); }
953 };
954 
Fill(DWORD * pErr)955 bool WNetEnumerator::Fill( DWORD* pErr )
956 {
957 	if ( count < 0 || pos < count ) { return true; }
958 
959 	DWORD n = -1;
960 	DWORD bSize = BUFSIZE;
961 	DWORD res = WNetEnumResourceW( handle, &n, buf.data(), &bSize );
962 
963 	if ( res == ERROR_NO_MORE_ITEMS )
964 	{
965 		count = -1;
966 		return true;
967 	}
968 
969 	if ( res !=  NO_ERROR )
970 	{
971 		count = -1;
972 
973 		if ( pErr ) { *pErr = GetLastError(); }
974 
975 		return false;
976 	}
977 
978 	count = n;
979 	pos = 0;
980 	return true;
981 }
982 
983 
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)984 int FSWin32Net::SetFileTime( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
985 {
986 	if ( err ) { *err = ERRNOSUPPORT; } return -1;
987 }
988 
ReadDir(FSList * list,FSPath & path,int * err,FSCInfo * info)989 int FSWin32Net::ReadDir ( FSList* list, FSPath& path, int* err, FSCInfo* info )
990 {
991 	list->Clear();
992 
993 	if ( path.Count() > 1 )
994 	{
995 		if ( err ) { *err = ERRNOSUPPORT; }
996 
997 		return -1;
998 	}
999 
1000 
1001 	WNetEnumerator en;
1002 
1003 	if ( !en.Open( _res.Get() ) )
1004 	{
1005 		SetError( err, GetLastError() );
1006 		return -1;
1007 	}
1008 
1009 	try
1010 	{
1011 		while ( true )
1012 		{
1013 			if ( info && info->IsStopped() )
1014 			{
1015 				return -2;
1016 			}
1017 
1018 			DWORD dwErr = 0;
1019 			NETRESOURCEW* p = en.Next( &dwErr );
1020 
1021 			if ( !p )
1022 			{
1023 				if ( !dwErr ) { break; }
1024 
1025 				SetError( err, dwErr );
1026 				return -1;
1027 			}
1028 
1029 			if ( !p->lpRemoteName ) { continue; }
1030 
1031 			wchar_t* pName = p->lpRemoteName;
1032 
1033 			if ( p->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE || p->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER )
1034 			{
1035 				//выкинуть из названия шары имя сервера, а из названия сервера - косые символы
1036 				wchar_t* last = 0;
1037 
1038 				for ( wchar_t* s = pName; *s; s++ )
1039 					if ( *s == '\\' ) { last = s; }
1040 
1041 				if ( last && last[1] ) { pName = last + 1; }
1042 			}
1043 
1044 			clPtr<FSNode> pNode = new FSNode();
1045 
1046 			pNode->name.Set( CS_UNICODE, Utf16ToUnicode( pName ).data() );
1047 			pNode->st.mode = S_IFDIR;
1048 			pNode->st.mode |= 0664;
1049 
1050 			pNode->_w32NetRes = W32NetRes( p );
1051 
1052 			switch ( p->dwDisplayType )
1053 			{
1054 				//case RESOURCEDISPLAYTYPE_GENERIC: return "GENERIC";
1055 				case RESOURCEDISPLAYTYPE_DOMAIN:
1056 				case RESOURCEDISPLAYTYPE_GROUP:
1057 				case RESOURCEDISPLAYTYPE_NETWORK:
1058 					pNode->extType = FSNode::WORKGROUP;
1059 					break;
1060 
1061 				case RESOURCEDISPLAYTYPE_SERVER:
1062 					pNode->extType = FSNode::SERVER;
1063 					break;
1064 
1065 				case RESOURCEDISPLAYTYPE_DIRECTORY:
1066 				case RESOURCEDISPLAYTYPE_SHARE:
1067 					pNode->extType = FSNode::FILESHARE;
1068 					break;
1069 			};
1070 
1071 			list->Append( pNode );
1072 		};
1073 
1074 		return 0;
1075 	}
1076 	catch ( ... )
1077 	{
1078 		throw;
1079 	}
1080 
1081 	SetError( err, 100 );
1082 	return -1;
1083 }
1084 
Stat(FSPath & path,FSStat * st,int * err,FSCInfo * info)1085 int FSWin32Net::Stat ( FSPath& path, FSStat* st, int* err, FSCInfo* info ) { if ( err ) { *err = ERRNOSUPPORT; } return -1; }
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)1086 int FSWin32Net::Symlink ( FSPath& path, FSString& str, int* err, FSCInfo* info ) { if ( err ) { *err = ERRNOSUPPORT; } return -1; }
1087 
Uri(FSPath & path)1088 FSString FSWin32Net::Uri( FSPath& path )
1089 {
1090 	NETRESOURCEW* p = _res.Get();
1091 
1092 	if ( p && p->lpRemoteName )
1093 	{
1094 		return FSString( Utf16ToUnicode( p->lpRemoteName ).data() );
1095 	}
1096 
1097 	return FSString( "Network" );
1098 }
1099 
~FSWin32Net()1100 FSWin32Net::~FSWin32Net()
1101 {
1102 }
1103 
1104 #else
1105 
1106 #include <pwd.h>
1107 #include <grp.h>
1108 
1109 #include <sys/types.h>
1110 #include <dirent.h>
1111 #include <sys/time.h>
1112 
1113 // for statfs()
1114 #ifdef __linux__
1115 #  include <sys/statfs.h>
1116 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
1117 #  include <sys/param.h>
1118 #  include <sys/mount.h>
1119 #endif
1120 
1121 #ifdef __linux__
1122 #  define OPENFLAG_LARGEFILE (O_LARGEFILE)
1123 #else
1124 #  define OPENFLAG_LARGEFILE (0)
1125 #endif
1126 
Flags()1127 unsigned FSSys::Flags() { return HAVE_READ | HAVE_WRITE | HAVE_SYMLINK | HAVE_SEEK; }
IsEEXIST(int err)1128 bool  FSSys::IsEEXIST( int err ) { return err == EEXIST; }
IsENOENT(int err)1129 bool  FSSys::IsENOENT( int err ) { return err == ENOENT; }
IsEXDEV(int err)1130 bool  FSSys::IsEXDEV( int err ) { return err == EXDEV; }
1131 
StrError(int err)1132 FSString FSSys::StrError( int err )
1133 {
1134 	sys_char_t buf[1024] = "";
1135 	FSString ret( sys_charset_id, ( char* )sys_error_str( err, buf, sizeof( buf ) ) );
1136 	return ret;
1137 }
1138 
Equal(FS * fs)1139 bool FSSys::Equal( FS* fs )
1140 {
1141 	if ( !fs || fs->Type() != FS::SYSTEM ) { return false; }
1142 
1143 	return true;
1144 }
1145 
1146 
OpenRead(FSPath & path,int flags,int * err,FSCInfo * info)1147 int FSSys::OpenRead  ( FSPath& path, int flags, int* err, FSCInfo* info )
1148 {
1149 	int n =  open( ( char* ) path.GetString( sys_charset_id, '/' ),
1150 	               O_RDONLY | OPENFLAG_LARGEFILE, 0 );
1151 
1152 	if ( n < 0 ) { SetError( err, errno ); return -1; }
1153 
1154 	return n;
1155 }
1156 
1157 
OpenCreate(FSPath & path,bool overwrite,int mode,int flags,int * err,FSCInfo * info)1158 int FSSys::OpenCreate   ( FSPath& path, bool overwrite, int mode, int flags,   int* err, FSCInfo* info )
1159 {
1160 	int n =  open( ( char* ) path.GetString( sys_charset_id, '/' ),
1161 	               O_CREAT | O_WRONLY | O_TRUNC | OPENFLAG_LARGEFILE | ( overwrite ? 0 : O_EXCL ) , mode );
1162 
1163 	if ( n < 0 ) { SetError( err, errno ); return -1; }
1164 
1165 	return n;
1166 }
1167 
Close(int fd,int * err,FSCInfo * info)1168 int FSSys::Close( int fd, int* err, FSCInfo* info )
1169 {
1170 	if ( close( fd ) )
1171 	{
1172 		SetError( err, errno );
1173 		return -1;
1174 	}
1175 
1176 	return 0;
1177 }
1178 
Read(int fd,void * buf,int size,int * err,FSCInfo * info)1179 int FSSys::Read( int fd, void* buf, int size, int* err, FSCInfo* info )
1180 {
1181 	int n = read( fd, buf, size );
1182 
1183 	if ( n < 0 ) { SetError( err, errno ); return -1; }
1184 
1185 	return n;
1186 }
1187 
Seek(int fd,SEEK_FILE_MODE mode,seek_t pos,seek_t * pRet,int * err,FSCInfo * info)1188 int FSSys::Seek( int fd, SEEK_FILE_MODE mode, seek_t pos, seek_t* pRet,  int* err, FSCInfo* info )
1189 {
1190 
1191 	seek_t n =  lseek( fd, pos, mode );
1192 
1193 	if ( n < 0 ) { SetError( err, errno ); return -1; }
1194 
1195 	if ( pRet ) { *pRet = n; }
1196 
1197 	return 0;
1198 }
1199 
1200 
Write(int fd,void * buf,int size,int * err,FSCInfo * info)1201 int FSSys::Write( int fd, void* buf, int size, int* err, FSCInfo* info )
1202 {
1203 	int n = write( fd, buf, size );
1204 
1205 	if ( n < 0 ) { SetError( err, errno ); return -1; }
1206 
1207 	return n;
1208 }
1209 
Rename(FSPath & oldpath,FSPath & newpath,int * err,FSCInfo * info)1210 int FSSys::Rename ( FSPath&  oldpath, FSPath& newpath, int* err,  FSCInfo* info )
1211 {
1212 	if ( rename( ( char* ) oldpath.GetString( sys_charset_id, '/' ), ( char* ) newpath.GetString( sys_charset_id, '/' ) ) )
1213 	{
1214 		SetError( err, errno );
1215 		return -1;
1216 	}
1217 
1218 	return 0;
1219 }
1220 
MkDir(FSPath & path,int mode,int * err,FSCInfo * info)1221 int FSSys::MkDir( FSPath& path, int mode, int* err,  FSCInfo* info )
1222 {
1223 	if ( mkdir( ( char* ) path.GetString( sys_charset_id, '/' ), mode ) )
1224 	{
1225 		SetError( err, errno );
1226 		return -1;
1227 	}
1228 
1229 	return 0;
1230 
1231 }
1232 
Delete(FSPath & path,int * err,FSCInfo * info)1233 int FSSys::Delete( FSPath& path, int* err, FSCInfo* info )
1234 {
1235 	if ( unlink( ( char* ) path.GetString( sys_charset_id, '/' ) ) )
1236 	{
1237 		SetError( err, errno );
1238 		return -1;
1239 	}
1240 
1241 	return 0;
1242 }
1243 
RmDir(FSPath & path,int * err,FSCInfo * info)1244 int FSSys::RmDir( FSPath& path, int* err, FSCInfo* info )
1245 {
1246 	if ( rmdir( ( char* ) path.GetString( sys_charset_id, '/' ) ) )
1247 	{
1248 		SetError( err, errno );
1249 		return -1;
1250 	}
1251 
1252 	return 0;
1253 }
1254 
1255 
SetFileTime(FSPath & path,FSTime cTime,FSTime aTime,FSTime mTime,int * err,FSCInfo * info)1256 int FSSys::SetFileTime  ( FSPath& path, FSTime cTime, FSTime aTime, FSTime mTime, int* err, FSCInfo* info )
1257 {
1258 	struct timeval tv[2];
1259 	tv[0].tv_sec  = aTime;
1260 	tv[0].tv_usec = 0;
1261 	tv[1].tv_sec  = mTime;
1262 	tv[1].tv_usec = 0;
1263 
1264 	if ( utimes( ( char* ) path.GetString( sys_charset_id, '/' ), tv ) )
1265 	{
1266 		SetError( err, errno );
1267 		return -1;
1268 	}
1269 
1270 	return 0;
1271 
1272 }
1273 
ReadDir(FSList * list,FSPath & _path,int * err,FSCInfo * info)1274 int FSSys::ReadDir( FSList* list, FSPath& _path, int* err, FSCInfo* info )
1275 {
1276 	list->Clear();
1277 
1278 	FSPath path( _path );
1279 
1280 	DIR* d = opendir( ( char* )path.GetString( sys_charset_id ) );
1281 
1282 	if ( !d )
1283 	{
1284 		SetError( err, errno );
1285 		return -1;
1286 	}
1287 
1288 	try
1289 	{
1290 		struct dirent ent, *pEnt;
1291 
1292 		int n = path.Count();
1293 
1294 		while ( true )
1295 		{
1296 			if ( info && info->IsStopped() )
1297 			{
1298 				closedir( d );
1299 				return -2;
1300 			}
1301 
1302 			if ( readdir_r( d, &ent, &pEnt ) )
1303 			{
1304 				SetError( err, errno );
1305 				closedir( d );
1306 				return -1;
1307 			}
1308 
1309 			if ( !pEnt ) { break; }
1310 
1311 			//skip . and ..
1312 			if ( ent.d_name[0] == '.' && ( !ent.d_name[1] || ( ent.d_name[1] == '.' && !ent.d_name[2] ) ) )
1313 			{
1314 				continue;
1315 			}
1316 
1317 			clPtr<FSNode> pNode = new FSNode();
1318 			path.SetItem( n, sys_charset_id, ent.d_name );
1319 			Stat( path, &pNode->st, 0, info );
1320 #if defined(__APPLE__)
1321 			if ( sys_charset_id == CS_UTF8 )
1322 			{
1323 				std::string normname = normalize_utf8_NFC( ent.d_name );
1324 				pNode->name.Set( sys_charset_id, normname.data() );
1325 			}
1326 			else
1327 			{
1328 				pNode->name.Set( sys_charset_id, ent.d_name );
1329 			}
1330 #else
1331 			pNode->name.Set( sys_charset_id, ent.d_name );
1332 #endif
1333 			list->Append( pNode );
1334 		};
1335 
1336 		closedir( d );
1337 
1338 		return 0;
1339 	}
1340 	catch ( ... )
1341 	{
1342 		closedir( d );
1343 		throw;
1344 	}
1345 }
1346 
GetFileSystemFreeSpace(FSPath & path,int * err)1347 int64_t FSSys::GetFileSystemFreeSpace( FSPath& path, int* err )
1348 {
1349 #if defined( __linux__ ) && !defined( __APPLE__ )
1350 	struct statfs64 s;
1351 
1352 	if ( statfs64( path.GetUtf8(), &s ) == -1 )
1353 	{
1354 		SetError( err, errno );
1355 		return -1;
1356 	}
1357 
1358 #else
1359 	// FreeBSD and probably other systems have 64 bit support in regular statfs
1360 	struct statfs s;
1361 
1362 	if ( statfs( path.GetUtf8(), &s ) == -1 )
1363 	{
1364 		SetError( err, errno );
1365 		return -1;
1366 	}
1367 
1368 #endif
1369 
1370 	return ( int64_t )( s.f_bfree ) * ( int64_t )( s.f_bsize );
1371 }
1372 
Stat(FSPath & path,FSStat * fsStat,int * err,FSCInfo * info)1373 int FSSys::Stat( FSPath& path, FSStat* fsStat, int* err, FSCInfo* info )
1374 {
1375 	fsStat->link.Clear();
1376 
1377 #ifdef S_IFLNK
1378 	struct stat st_link;
1379 
1380 	if ( lstat( ( char* )path.GetString( sys_charset_id ), &st_link ) )
1381 	{
1382 		SetError( err, errno );
1383 		return -1;
1384 	};
1385 
1386 	if ( ( st_link.st_mode & S_IFMT ) == S_IFLNK )
1387 	{
1388 		char buf[1024];
1389 		ssize_t ret = readlink( ( char* )path.GetString( sys_charset_id ), buf, sizeof( buf ) );
1390 
1391 		if ( ret >= sizeof( buf ) ) { ret = sizeof( buf ) - 1; }
1392 
1393 		if ( ret >= 0 ) { buf[ret] = 0; }
1394 		else { buf[0] = 0; }
1395 
1396 		if ( ret >= 0 ) { fsStat->link.Set( sys_charset_id, buf ); }
1397 	}
1398 	else
1399 	{
1400 		fsStat->mode = st_link.st_mode;
1401 		fsStat->size   = st_link.st_size;
1402 		fsStat->m_CreationTime = st_link.st_ctime;
1403 		fsStat->m_LastAccessTime = st_link.st_atime;
1404 		fsStat->m_LastWriteTime = st_link.st_mtime;
1405 		fsStat->m_ChangeTime = st_link.st_mtime;
1406 		fsStat->gid = st_link.st_gid;
1407 		fsStat->uid = st_link.st_uid;
1408 
1409 		fsStat->dev = st_link.st_dev;
1410 		fsStat->ino = st_link.st_ino;
1411 
1412 		return 0;
1413 	}
1414 #endif
1415 
1416 	struct stat st;
1417 
1418 	if ( stat( ( char* )path.GetString( sys_charset_id ), &st ) )
1419 	{
1420 		SetError( err, errno );
1421 		return -1;
1422 	}
1423 
1424 	fsStat->mode = st.st_mode;
1425 	fsStat->size   = st.st_size;
1426 	fsStat->m_CreationTime = st.st_ctime;
1427 	fsStat->m_LastAccessTime = st.st_atime;
1428 	fsStat->m_LastWriteTime = st.st_mtime;
1429 	fsStat->m_ChangeTime = st.st_mtime;
1430 	fsStat->gid = st.st_gid;
1431 	fsStat->uid = st.st_uid;
1432 
1433 	fsStat->dev = st.st_dev;
1434 	fsStat->ino = st.st_ino;
1435 
1436 	return 0;
1437 }
1438 
StatSetAttr(FSPath & path,const FSStat * st,int * err,FSCInfo * info)1439 int FSSys::StatSetAttr( FSPath& path, const FSStat* st, int* err, FSCInfo* info )
1440 {
1441 	// TODO: Complete - at least, add a mc-style chown.
1442 	int status = chmod( path.GetUtf8(), (mode_t) st->mode );
1443 	if ( status != 0)
1444 	{
1445 		SetError( err, errno );
1446 	}
1447 	return status;
1448 }
1449 
FStat(int fd,FSStat * fsStat,int * err,FSCInfo * info)1450 int FSSys::FStat( int fd, FSStat* fsStat, int* err, FSCInfo* info )
1451 {
1452 	fsStat->link.Clear();
1453 
1454 	struct stat st;
1455 
1456 	if ( fstat( fd, &st ) )
1457 	{
1458 		SetError( err, errno );
1459 		return -1;
1460 	}
1461 
1462 	fsStat->mode = st.st_mode;
1463 	fsStat->size   = st.st_size;
1464 	fsStat->m_CreationTime = st.st_ctime;
1465 	fsStat->m_LastAccessTime = st.st_atime;
1466 	fsStat->m_LastWriteTime = st.st_mtime;
1467 	fsStat->m_ChangeTime = st.st_mtime;
1468 	fsStat->gid = st.st_gid;
1469 	fsStat->uid = st.st_uid;
1470 
1471 	fsStat->dev = st.st_dev;
1472 	fsStat->ino = st.st_ino;
1473 
1474 	return 0;
1475 }
1476 
1477 
Symlink(FSPath & path,FSString & str,int * err,FSCInfo * info)1478 int FSSys::Symlink( FSPath& path, FSString& str, int* err, FSCInfo* info )
1479 {
1480 	if ( symlink( ( char* )str.Get( sys_charset_id ), ( char* )path.GetString( sys_charset_id ) ) )
1481 	{
1482 		SetError( err, errno );
1483 		return -1;
1484 	}
1485 
1486 	return 0;
1487 }
1488 
1489 static std::unordered_map<int, std::vector<unicode_t> > userList;
1490 static std::unordered_map<int, std::vector<unicode_t> > groupList;
1491 
1492 #include <sys/statvfs.h>
1493 
StatVfs(FSPath & path,FSStatVfs * vst,int * err,FSCInfo * info)1494 int FSSys::StatVfs( FSPath& path, FSStatVfs* vst, int* err, FSCInfo* info )
1495 {
1496 	struct statvfs st;
1497 
1498 	if ( statvfs( ( char* )path.GetString( sys_charset_id ), &st ) )
1499 	{
1500 		SetError( err, errno );
1501 		return -1;
1502 	}
1503 
1504 	vst->size = int64_t( st.f_blocks ) * st.f_frsize;
1505 	vst->avail = int64_t( st.f_bavail ) * st.f_frsize;
1506 
1507 	return 0;
1508 }
1509 
Uri(FSPath & path)1510 FSString FSSys::Uri( FSPath& path )
1511 {
1512 	return FSString( path.GetUnicode() );
1513 }
1514 
GetOSUserName(int id)1515 static std::vector<unicode_t> GetOSUserName( int id )
1516 {
1517 	auto i = userList.find( id );
1518 
1519 	if ( i != userList.end() ) { return i->second; }
1520 
1521 	setpwent();
1522 	struct passwd* p = getpwuid( id );
1523 	char buf[64];
1524 	char* nameStr = p ? p->pw_name : buf;
1525 
1526 	if ( !p ) { sprintf( buf, "%i", id ); }
1527 
1528 	std::vector<unicode_t> str = sys_to_unicode_array( nameStr );
1529 	userList[id] = str;
1530 	endpwent();
1531 	return str;
1532 }
1533 
GetOSGroupName(int id)1534 static std::vector<unicode_t> GetOSGroupName( int id )
1535 {
1536 	auto i = groupList.find( id );
1537 
1538 	if ( i != groupList.end() ) { return i->second; }
1539 
1540 	setgrent();
1541 	struct group* p = getgrgid( id );
1542 	char buf[64];
1543 	char* nameStr = p ? p->gr_name : buf;
1544 
1545 	if ( !p ) { sprintf( buf, "%i", id ); }
1546 
1547 	std::vector<unicode_t> str = sys_to_unicode_array( nameStr );
1548 	groupList[id] = str;
1549 	endgrent();
1550 	return str;
1551 }
1552 
GetUserName(int user,unicode_t buf[64])1553 unicode_t* FSSys::GetUserName( int user, unicode_t buf[64] )
1554 {
1555 	std::vector<unicode_t> Name = GetOSUserName( user );
1556 	unicode_t* u = Name.data();
1557 	unicode_t* s = buf;
1558 
1559 	for ( int n = 63; n > 0 && *u; n--, u++, s++ )
1560 	{
1561 		*s = *u;
1562 	}
1563 
1564 	*s = 0;
1565 	return buf;
1566 };
1567 
1568 
GetGroupName(int group,unicode_t buf[64])1569 unicode_t* FSSys::GetGroupName( int group, unicode_t buf[64] )
1570 {
1571 	std::vector<unicode_t> Name = GetOSGroupName( group );
1572 
1573 	unicode_t* g = Name.data();
1574 	unicode_t* s = buf;
1575 
1576 	for ( int n = 63; n > 0 && *g; n--, g++, s++ )
1577 	{
1578 		*s = *g;
1579 	}
1580 
1581 	*s = 0;
1582 
1583 	return buf;
1584 };
1585 
1586 #endif
1587 
~FSSys()1588 FSSys::~FSSys() {}
1589 
1590 
1591 //////////////////////////////////////// FSList /////////////////////////////////////
1592 
Append(clPtr<FSNode> p)1593 void FSList::Append( clPtr<FSNode> p )
1594 {
1595 	p->next = 0;
1596 
1597 	if ( last )
1598 	{
1599 		last->next = p.ptr();
1600 	}
1601 	else
1602 	{
1603 		first = p.ptr();
1604 	}
1605 
1606 	last = p.ptr();
1607 	p.drop();
1608 	count++;
1609 }
1610 
Clear()1611 void FSList::Clear()
1612 {
1613 	for ( FSNode* p = first; p; )
1614 	{
1615 		FSNode* t = p;
1616 		p = p->next;
1617 		delete t;
1618 	}
1619 
1620 	first = last = 0;
1621 	count = 0;
1622 }
1623 
CopyFrom(const FSList & a,bool onlySelected)1624 void FSList::CopyFrom( const FSList& a, bool onlySelected )
1625 {
1626 	Clear();
1627 
1628 	for ( FSNode* p = a.first; p; p = p->next )
1629 	{
1630 		if ( onlySelected && !p->isSelected ) { continue; }
1631 
1632 		FSNode* node = new FSNode( *p );
1633 
1634 		if ( last )
1635 		{
1636 			last->next = node;
1637 		}
1638 		else
1639 		{
1640 			first = node;
1641 		}
1642 
1643 		last = node;
1644 		count++;
1645 	}
1646 }
1647 
CopyOne(FSNode * node)1648 void FSList::CopyOne( FSNode* node )
1649 {
1650 	Clear();
1651 	FSNode* p = new FSNode( *node );
1652 	first = last = p;
1653 	count = 1;
1654 }
1655 
GetArray()1656 std::vector<FSNode*> FSList::GetArray()
1657 {
1658 	std::vector<FSNode*> p( Count() );
1659 	FSNode* pNode = first;
1660 	int n = Count();
1661 
1662 	for ( int i = 0 ; i < n && pNode; i++, pNode = pNode->next )
1663 	{
1664 		p[i] = pNode;
1665 	}
1666 
1667 	return p;
1668 }
1669 
GetFilteredArray(bool showHidden,int * pCount)1670 std::vector<FSNode*> FSList::GetFilteredArray( bool showHidden, int* pCount )
1671 {
1672 	if ( pCount ) { *pCount = 0; }
1673 
1674 	std::vector<FSNode*> p( Count() );
1675 	FSNode* pNode = first;
1676 	int n = Count();
1677 	int i;
1678 
1679 	for ( i = 0 ; i < n && pNode; pNode = pNode->next )
1680 		if ( showHidden || !pNode->IsHidden() )
1681 		{
1682 			p[i++] = pNode;
1683 		}
1684 
1685 	if ( pCount ) { *pCount = i; }
1686 
1687 	return p;
1688 }
1689 
1690 
1691 // binary search.
1692 // Returns index of the found node
1693 // in EXACT_MATCH_ONLY mode returns -1 if node not found
1694 // in EXACT_OR_CLOSEST_PRECEDING_NODE modes returns -1 if n is less than the 1st nodeVector element
1695 // in EXACT_OR_CLOSEST_SUCCEEDING_NODE modes returns vsize if n is greater than the last nodeVector element
_BSearch(FSNode & n,const std::vector<FSNode * > & nodeVector,int (* CmpFunc)(FSNode * n1,FSNode * n2),BSearchMode bSearchMode)1696 static int _BSearch(FSNode& n, const std::vector<FSNode*>& nodeVector, int(*CmpFunc)(FSNode* n1, FSNode* n2), BSearchMode bSearchMode)
1697 {
1698 	int vsize = nodeVector.size();
1699 	if (vsize == 0)
1700 		return -1;
1701 
1702 	int iLeft = 0;
1703 	int iRight = vsize-1;
1704 	//int cmp = CmpFunc(&n, nodeVector[iLeft]);
1705 
1706 	for (int i = iRight / 2; iLeft <= iRight ; i = (iLeft + iRight) / 2)
1707 	{
1708 		int cmp = CmpFunc(&n, nodeVector[i]);
1709 		if (cmp == 0) // found exact match
1710 			return i;
1711 		if (cmp > 0)
1712 			iLeft = i + 1;
1713 		else
1714 			iRight = i - 1;
1715 	}
1716 
1717 	switch (bSearchMode)
1718 	{
1719 	case EXACT_OR_CLOSEST_PRECEDING_NODE:
1720 		return iRight;
1721 	case EXACT_OR_CLOSEST_SUCCEEDING_NODE:
1722 		return iLeft;
1723 	default:
1724 	case EXACT_MATCH_ONLY:
1725 		return -1;
1726 	}
1727 }
1728 
1729 
1730 // standard: returns n1 - n2, for bsearch
1731 template <bool isAscending, bool isCaseSensitive, SORT_MODE mode>
CmpFunc(FSNode * a,FSNode * b)1732 int CmpFunc(FSNode* a, FSNode* b)
1733 {
1734 	// directories always go first
1735 	int dirCmp = a->IsDir() - b->IsDir();
1736 	if (dirCmp)
1737 		return -dirCmp;
1738 	switch (mode)
1739 	{
1740 	case SORT_EXT:
1741 	{
1742 								int cmpExt = a->CmpByExt(*b, isCaseSensitive);
1743 								if (cmpExt)
1744 									return isAscending ? cmpExt : -cmpExt;
1745 	} // if extensions match, do name comparison using current ascending mode
1746 	  // i.e. fall into the next case
1747 	case SORT_NAME:
1748 	{
1749 								int cmpName = isCaseSensitive ? a->name.Cmp(b->name) : a->name.CmpNoCase(b->name);
1750 								return isAscending ? cmpName : -cmpName;
1751 	}
1752 	case SORT_SIZE:
1753 	{
1754 								 int64_t cmpSize = a->st.size - b->st.size;
1755 								 if (cmpSize)
1756 									 return isAscending ? (cmpSize > 0 ? 1 : -1) : (cmpSize > 0 ? -1 : 1);
1757 								 break;
1758 	}
1759 	case SORT_MTIME:
1760 	{
1761 								  time_t cmpTime = a->st.m_LastWriteTime - b->st.m_LastWriteTime;
1762 								  if (cmpTime)
1763 									  return isAscending ? (cmpTime > 0 ? 1 : -1) : (cmpTime > 0 ? -1 : 1);
1764 								  break;
1765 	}
1766 	default:
1767 		return 0;
1768 	}
1769 	// if size|mtime match, return name comparison in ascending=true mode
1770 	return  isCaseSensitive ? a->name.Cmp(b->name) : a->name.CmpNoCase(b->name);
1771 }
1772 
getCmpFunc(bool isAscending,bool isCaseSensitive,SORT_MODE sortMode)1773 FSNodeCmpFunc* FSNodeVectorSorter::getCmpFunc(bool isAscending, bool isCaseSensitive, SORT_MODE sortMode)
1774 {
1775 	switch (sortMode)
1776 	{
1777 	case SORT_NAME:
1778 		return isAscending ?
1779 			(isCaseSensitive ? CmpFunc<true, true, SORT_NAME> : CmpFunc<true, false, SORT_NAME>) :
1780 			(isCaseSensitive ? CmpFunc<false, true, SORT_NAME> : CmpFunc<false, false, SORT_NAME>);
1781 	case SORT_EXT:
1782 		return isAscending ?
1783 			(isCaseSensitive ? CmpFunc<true, true, SORT_EXT> : CmpFunc<true, false, SORT_EXT>) :
1784 			(isCaseSensitive ? CmpFunc<false, true, SORT_EXT> : CmpFunc<false, false, SORT_EXT>);
1785 		break;
1786 	case SORT_SIZE:
1787 		return isAscending ? CmpFunc<true, true, SORT_SIZE> : CmpFunc<false, true, SORT_SIZE>;
1788 		break;
1789 	case SORT_MTIME:
1790 		return isAscending ? CmpFunc<true, true, SORT_MTIME> : CmpFunc<false, true, SORT_MTIME>;
1791 	default: // SORT_NONE
1792 		break;
1793 	}
1794 	return nullptr;
1795 }
1796 
Sort(std::vector<FSNode * > & nodeVector,bool isAscending,bool isCaseSensitive,SORT_MODE sortMode)1797 void FSNodeVectorSorter::Sort(std::vector<FSNode*>& nodeVector,
1798 	bool isAscending, bool isCaseSensitive, SORT_MODE sortMode)
1799 {
1800 	struct GreaterCmp
1801 	{
1802 		FSNodeCmpFunc* pCmpFunc;
1803 		GreaterCmp(FSNodeCmpFunc* _pCmpFunc) : pCmpFunc(_pCmpFunc){}
1804 		bool operator() (FSNode* n1, FSNode* n2)
1805 		{
1806 			return pCmpFunc(n1, n2) < 0;
1807 		}
1808 	};
1809 
1810 	FSNodeCmpFunc* pFunc = getCmpFunc(isAscending, isCaseSensitive, sortMode);
1811 	if (pFunc)
1812 	{
1813 		std::sort(nodeVector.begin(), nodeVector.end(), GreaterCmp(pFunc));
1814 	}
1815 }
1816 
BSearch(FSNode & n,const std::vector<FSNode * > & nodeVector,BSearchMode searchMode,bool isAscending,bool isCaseSensitive,SORT_MODE sortMode)1817 int FSNodeVectorSorter::BSearch(FSNode& n, const std::vector<FSNode*>& nodeVector,
1818 	BSearchMode searchMode, bool isAscending, bool isCaseSensitive, SORT_MODE sortMode)
1819 {
1820 	FSNodeCmpFunc *pFunc = getCmpFunc(isAscending, isCaseSensitive, sortMode);
1821 	if (pFunc)
1822 		return _BSearch(n, nodeVector, pFunc, searchMode);
1823 	else
1824 		return -1;
1825 }
1826 
1827 /////////////////////////////////////  FSStat ////////////////////////////////////////
1828 
1829 
GetModeStr(unicode_t buf[64])1830 unicode_t* FSStat::GetModeStr( unicode_t buf[64] )
1831 {
1832 	unicode_t* p = buf;
1833 	/* print type */
1834 
1835 	if ( IsLnk() ) { *p++ = '>'; }
1836 
1837 	switch ( mode & S_IFMT )
1838 	{
1839 		case S_IFDIR:        /* directory */
1840 			*p++ = 'd';
1841 			break;
1842 
1843 		case S_IFCHR:        /* character special */
1844 			*p++ = 'c';
1845 			break;
1846 
1847 		case S_IFBLK:        /* block special */
1848 			*p++ = 'b';
1849 			break;
1850 
1851 		case S_IFREG:        /* regular */
1852 			*p++ = '-';
1853 			break;
1854 #ifdef S_IFLNK
1855 
1856 		case S_IFLNK:        /* symbolic link */
1857 			*p++ = 'l';
1858 			break;
1859 #endif
1860 #ifdef S_IFSOCK
1861 
1862 		case S_IFSOCK:       /* socket */
1863 			*p++ = 's';
1864 			break;
1865 #endif
1866 #ifdef S_IFIFO
1867 
1868 		case S_IFIFO:        /* fifo */
1869 			*p++ = 'p';
1870 			break;
1871 #endif
1872 
1873 		default:       /* unknown */
1874 			*p++ = '?';
1875 			break;
1876 	}
1877 
1878 	/* usr */
1879 	if ( mode & S_IRUSR )
1880 	{
1881 		*p++ = 'r';
1882 	}
1883 	else
1884 	{
1885 		*p++ = '-';
1886 	}
1887 
1888 	if ( mode & S_IWUSR )
1889 	{
1890 		*p++ = 'w';
1891 	}
1892 	else
1893 	{
1894 		*p++ = '-';
1895 	}
1896 
1897 	switch ( mode & ( S_IXUSR | S_ISUID ) )
1898 	{
1899 		case 0:
1900 			*p++ = '-';
1901 			break;
1902 
1903 		case S_IXUSR:
1904 			*p++ = 'x';
1905 			break;
1906 
1907 		case S_ISUID:
1908 			*p++ = 'S';
1909 			break;
1910 
1911 		case S_IXUSR | S_ISUID:
1912 			*p++ = 's';
1913 			break;
1914 	}
1915 
1916 	/* group */
1917 	if ( mode & S_IRGRP )
1918 	{
1919 		*p++ = 'r';
1920 	}
1921 	else
1922 	{
1923 		*p++ = '-';
1924 	}
1925 
1926 	if ( mode & S_IWGRP )
1927 	{
1928 		*p++ = 'w';
1929 	}
1930 	else
1931 	{
1932 		*p++ = '-';
1933 	}
1934 
1935 	switch ( mode & ( S_IXGRP | S_ISGID ) )
1936 	{
1937 		case 0:
1938 			*p++ = '-';
1939 			break;
1940 
1941 		case S_IXGRP:
1942 			*p++ = 'x';
1943 			break;
1944 
1945 		case S_ISGID:
1946 			*p++ = 'S';
1947 			break;
1948 
1949 		case S_IXGRP | S_ISGID:
1950 			*p++ = 's';
1951 			break;
1952 	}
1953 
1954 	/* other */
1955 	if ( mode & S_IROTH )
1956 	{
1957 		*p++ = 'r';
1958 	}
1959 	else
1960 	{
1961 		*p++ = '-';
1962 	}
1963 
1964 	if ( mode & S_IWOTH )
1965 	{
1966 		*p++ = 'w';
1967 	}
1968 	else
1969 	{
1970 		*p++ = '-';
1971 	}
1972 
1973 	switch ( mode & ( S_IXOTH | S_ISVTX ) )
1974 	{
1975 		case 0:
1976 			*p++ = '-';
1977 			break;
1978 
1979 		case S_IXOTH:
1980 			*p++ = 'x';
1981 			break;
1982 
1983 		case S_ISVTX:
1984 			*p++ = 'T';
1985 			break;
1986 
1987 		case S_IXOTH | S_ISVTX:
1988 			*p++ = 't';
1989 			break;
1990 	}
1991 
1992 	*p++ = ' ';    /* will be a '+' if ACL's implemented */
1993 	*p = '\0';
1994 	return buf;
1995 }
1996 
GetFSTimeStrTime(FSTime TimeValue)1997 std::string GetFSTimeStrTime( FSTime TimeValue )
1998 {
1999 	char str[64];
2000 #ifdef _WIN32
2001 	FILETIME mt = TimeValue;
2002 	FILETIME lt;
2003 	SYSTEMTIME st;
2004 
2005 	if (!FileTimeToLocalFileTime(&mt, &lt) || !FileTimeToSystemTime(&lt, &st))
2006 	{
2007 		return std::string("?");
2008 	}
2009 
2010 	Lsnprintf( str, sizeof(str), "%02i:%02i:%02i,%03i", int(st.wHour), int(st.wMinute), int(st.wSecond), int(st.wMilliseconds) );
2011 #else
2012 	time_t mt = TimeValue;
2013 	struct tm* p = localtime(&mt);
2014 
2015 	if (p)
2016 	{
2017 		sprintf( str, "%02i:%02i:%02i", p->tm_hour, p->tm_min, p->tm_sec );
2018 	}
2019 	else
2020 	{
2021 		sprintf( str, "%02i:%02i:%02i", int(0), int(0), int(0) );
2022 	}
2023 #endif
2024 	return std::string( str );
2025 }
2026 
GetFSTimeStrDate(FSTime TimeValue)2027 std::string GetFSTimeStrDate( FSTime TimeValue )
2028 {
2029 	char str[64];
2030 #ifdef _WIN32
2031 	FILETIME mt = TimeValue;
2032 	FILETIME lt;
2033 	SYSTEMTIME st;
2034 
2035 	if ( !FileTimeToLocalFileTime(&mt, &lt) || !FileTimeToSystemTime(&lt, &st) )
2036 	{
2037 		return std::string("?");
2038 	}
2039 
2040 	Lsnprintf(str, sizeof(str), "%02i.%02i.%04i", int( st.wDay ), int( st.wMonth ), int( st.wYear ) );
2041 
2042 #else
2043 	time_t mt = TimeValue;
2044 	struct tm* p = localtime(&mt);
2045 
2046 	if ( p )
2047 	{
2048 		sprintf( str, "%02i.%02i.%04i", p->tm_mday, p->tm_mon + 1, p->tm_year + 1900 );
2049 	}
2050 	else
2051 	{
2052 		sprintf(str, "%02i.%02i.%04i", int(0), int(0), int(0) + 1900 );
2053 
2054 	}
2055 #endif
2056 	return std::string(str);
2057 }
2058 
GetFSTimeFromStr(const std::string & Date,const std::string & Time)2059 FSTime GetFSTimeFromStr( const std::string& Date, const std::string& Time )
2060 {
2061 	if ( Date.empty() || Time.empty() ) return FSTime();
2062 
2063 	int Day = 0;
2064 	int Month = 0;
2065 	int Year = 0;
2066 	int Hour = 0;
2067 	int Minute = 0;
2068 	int Second = 0;
2069 	int Milliseconds = 0;
2070 
2071 	if ( Lsscanf( Date.c_str(), "%i.%i.%i", &Day, &Month, &Year ) != 3 ) return FSTime();
2072 	int NumValues = Lsscanf( Time.c_str(), "%i:%i:%i,%i", &Hour, &Minute, &Second, &Milliseconds );
2073 	if ( NumValues != 3 && NumValues != 4 ) return FSTime();
2074 
2075 	FSTime Result;
2076 
2077 #if defined(_WIN32)
2078 	SYSTEMTIME SystemTime;
2079 	memset( &SystemTime, 0, sizeof(SystemTime) );
2080 	SystemTime.wDay = Day;
2081 	SystemTime.wMonth = Month;
2082 	SystemTime.wYear = Year;
2083 	SystemTime.wHour = Hour;
2084 	SystemTime.wMinute = Minute;
2085 	SystemTime.wSecond = Second;
2086 	SystemTime.wMilliseconds = Milliseconds;
2087 
2088 	FILETIME LocalTime;
2089 	FILETIME FileTime;
2090 
2091 	if ( !SystemTimeToFileTime( &SystemTime, &LocalTime ) ) return FSTime();
2092 	if ( !LocalFileTimeToFileTime( &LocalTime, &FileTime ) ) return FSTime();
2093 
2094 	Result = FileTime;
2095 #else
2096 	tm p;
2097 	p.tm_mday = Day;
2098 	p.tm_mon = Month - 1;
2099 	p.tm_year = Year - 1900;
2100 	p.tm_hour = Hour;
2101 	p.tm_min = Minute;
2102 	p.tm_sec = Second;
2103 
2104 	time_t mt = mktime( &p );
2105 
2106 	Result = mt;
2107 #endif
2108 
2109 	return Result;
2110 }
2111 
FSTimeToStr(unicode_t ret[64],FSTime TimeValue)2112 unicode_t* FSTimeToStr( unicode_t ret[64], FSTime TimeValue )
2113 {
2114 	char str[64];
2115 	unicode_t* t = ret;
2116 #ifdef _WIN32
2117 	FILETIME mt = TimeValue;
2118 	FILETIME lt;
2119 	SYSTEMTIME st;
2120 
2121 	if ( !FileTimeToLocalFileTime( &mt, &lt ) || !FileTimeToSystemTime( &lt, &st ) ) { ret[0] = '?'; ret[1] = 0; return ret; }
2122 
2123 	Lsnprintf( str, sizeof( str ), "%02i.%02i.%04i  %02i:%02i:%02i",
2124 	           int( st.wDay ), int( st.wMonth ), int( st.wYear ),
2125 	           int( st.wHour ), int( st.wMinute ), int( st.wSecond ) );
2126 #else
2127 	time_t mt = TimeValue;
2128 	struct tm* p = localtime( &mt );
2129 
2130 	if ( p )
2131 	{
2132 		sprintf( str, "%02i.%02i.%04i  %02i:%02i:%02i",
2133 		         p->tm_mday, p->tm_mon + 1, p->tm_year + 1900, // % 100,
2134 		         p->tm_hour, p->tm_min, p->tm_sec );
2135 	}
2136 	else
2137 	{
2138 		sprintf( str, "%02i.%02i.%04i  %02i:%02i:%02i",
2139 		         int( 0 ), int( 0 ), int( 0 ) + 1900, // % 100,
2140 		         int( 0 ), int( 0 ), int( 0 ) );
2141 
2142 	}
2143 
2144 #endif
2145 
2146 	for ( char* s = str; *s; s++, t++ )
2147 	{
2148 		*t = *s;
2149 	}
2150 
2151 	*t = 0;
2152 	return ret;
2153 }
2154 
GetMTimeStr(unicode_t ret[64])2155 unicode_t* FSStat::GetMTimeStr( unicode_t ret[64] )
2156 {
2157 	return FSTimeToStr( ret, m_LastWriteTime );
2158 }
2159 
GetPrintableSizeStr(unicode_t buf[64])2160 unicode_t* FSStat::GetPrintableSizeStr( unicode_t buf[64] )
2161 {
2162 	unicode_t str[10];
2163 	str[0] = 0;
2164 
2165 	seek_t num = size;
2166 
2167 	if ( num >= seek_t( 10l ) * 1024 * 1024 * 1024 )
2168 	{
2169 		num /= seek_t( 1024l ) * 1024 * 1024;
2170 		str[0] = ' ';
2171 		str[1] = 'G';
2172 		str[2] = 0;
2173 	}
2174 	else if ( num >= 10l * 1024 * 1024 )
2175 	{
2176 		num /= 1024 * 1024;
2177 		str[0] = ' ';
2178 		str[1] = 'M';
2179 		str[2] = 0;
2180 	}
2181 	else if ( num >= 1024 * 1024 )
2182 	{
2183 		num /= 1024;
2184 		str[0] = ' ';
2185 		str[1] = 'K';
2186 		str[2] = 0;
2187 	}
2188 
2189 
2190 	std::string Str = ToString( num );
2191 
2192 	unicode_t* us = buf;
2193 
2194 	for ( const char* s = Str.c_str(); *s; s++ )
2195 	{
2196 		*( us++ ) = *s;
2197 	}
2198 
2199 	for ( unicode_t* t = str; *t; t++ )
2200 	{
2201 		*( us++ ) = *t;
2202 	}
2203 
2204 	*us = 0;
2205 
2206 	return buf;
2207 }
2208 
2209 
2210 /////////////////////////////////////  FSNode ////////////////////////////////////////
2211 
unicode_rchr(const unicode_t * s,int c)2212 inline const unicode_t* unicode_rchr( const unicode_t* s, int c )
2213 {
2214 	const unicode_t* p = 0;
2215 
2216 	if ( s )
2217 		for ( ; *s; s++ ) if ( *s == c ) { p = s; }
2218 
2219 	return p;
2220 }
2221 
CmpNoCase(const unicode_t * a,const unicode_t * b)2222 inline int CmpNoCase( const unicode_t* a, const unicode_t* b )
2223 {
2224 	unicode_t au = 0;
2225 	unicode_t bu = 0;
2226 
2227 	for ( ; *a; a++, b++ )
2228 	{
2229 		au = UnicodeLC( *a );
2230 		bu = UnicodeLC( *b );
2231 
2232 		if ( au != bu ) { break; }
2233 	};
2234 
2235 	return ( *a ? ( *b ? ( au < bu ? -1 : ( au == bu ? 0 : 1 ) ) : 1 ) : ( *b ? -1 : 0 ) );
2236 }
2237 
2238 
2239 #ifdef _WIN32
2240 
IsExe()2241 bool FSNode::IsExe()
2242 {
2243 	const unicode_t* s = unicode_rchr( GetUnicodeName(), '.' );
2244 
2245 	if ( !s || !*s ) { return false; }
2246 
2247 	s++;
2248 
2249 	if ( UnicodeLC( s[0] ) == 'e' && UnicodeLC( s[1] ) == 'x' && UnicodeLC( s[2] ) == 'e' && !s[3] ) { return true; }
2250 
2251 	if ( UnicodeLC( s[0] ) == 'b' && UnicodeLC( s[1] ) == 'a' && UnicodeLC( s[2] ) == 't' && !s[3] ) { return true; }
2252 
2253 	if ( UnicodeLC( s[0] ) == 'c' && UnicodeLC( s[1] ) == 'm' && UnicodeLC( s[2] ) == 'd' && !s[3] ) { return true; }
2254 
2255 	return false;
2256 }
2257 
2258 #endif
2259 
2260 
2261 
CmpByExt(FSNode & a,bool case_sensitive)2262 int FSNode::CmpByExt( FSNode& a, bool case_sensitive )
2263 {
2264 	const unicode_t* s1 = unicode_rchr( GetUnicodeName(), '.' );
2265 	const unicode_t* s2 = unicode_rchr( a.GetUnicodeName(), '.' );
2266 
2267 	if ( s1 )
2268 		return ( s2 ) ?
2269 		       ( case_sensitive ? CmpStr<const unicode_t>( s1, s2 ) : CmpNoCase( s1, s2 ) )
2270 		       : 1;
2271 	else
2272 	{
2273 		return s2 ? -1 : 0;
2274 	}
2275 }
2276 
2277 
2278 
~FSNode()2279 FSNode::~FSNode() {}
2280 
2281 ////////////////////////////////////  Utils ///////////////////////////////////////////
2282 
ParzeLink(FSPath & path,FSString & link)2283 bool ParzeLink( FSPath& path, FSString& link )
2284 {
2285 	FSPath t( link );
2286 
2287 	if ( !path.IsAbsolute() && !t.IsAbsolute() ) { return false; } //не абсолютный путь
2288 
2289 	int first = 0;
2290 
2291 	if ( t.IsAbsolute() )
2292 	{
2293 		path.Clear();
2294 		path.PushStr( FSString( "" ) );
2295 		first = 1;
2296 	}
2297 
2298 	for ( int i = first; i < t.Count(); i++ )
2299 	{
2300 		FSString p = *( t.GetItem( i ) );
2301 
2302 		if ( p.IsDot() ) { continue; }
2303 
2304 		if ( p.Is2Dot() )
2305 		{
2306 			if ( path.Count() > 1 ) { path.Pop(); }
2307 		}
2308 		else
2309 		{
2310 			path.PushStr( p );
2311 		}
2312 	}
2313 
2314 	return true;
2315 }
2316