1 #include <Core/Core.h>
2 #include "Functions4U.h"
3 
4 
5 #ifdef PLATFORM_WIN32 // || defined (PLATFORM_WIN64)
6 	#define Ptr Ptr_
7 	#define byte byte_
8 	#ifndef win32_CY_
9 		#define win32_CY_ long
10 	#endif
11 	#define CY win32_CY_
12 
13 	#include <shellapi.h>
14 	#include <wincon.h>
15 	#include <shlobj.h>
16 
17 	#undef Ptr
18 	#undef byte
19 	#undef CY
20 #endif
21 
22 #define TFILE <Functions4U/Functions4U.t>
23 #include <Core/t.h>
24 
25 
26 /*
27 Hi Koldo,
28 
29 I checked the functions in Functions4U. Here are some notes about trashing:
30 
31     * On older systems, the trash folder was $HOME/.Trash
32     * Your implementation of disregards the folder $HOME/.local/share/trash/info. You should create
33     there a .trashinfo file when moving something in trash and delete it when deleting corresponding file permanently.
34     * If you delete something on different partition than $HOME, you should also check if .Trash-XXXX
35     exists in root of that partition (XXXX is id of user who deleted the files in it).
36 
37 .local/share/Trash/files
38 .local/share/Trash/info
39 
40 A file every time a file is removed with
41 
42 KK.trashinfo
43 [Trash Info]
44 Path=/home/pubuntu/KK
45 DeletionDate=2010-05-19T18:00:52
46 
47 
48 You might also be interested in following:
49 
50     * Official trash specification from freedesktop.org
51     * Project implementing command line access to trash (unfortunately in python) according to the specification mentioned above
52 
53 
54 Hope this might help Smile It definitely learned me a lot of new things Very Happy
55 
56 Best regards,
57 Honza
58 */
59 
60 namespace Upp {
61 
62 /////////////////////////////////////////////////////////////////////
63 // LaunchFile
64 
65 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
LaunchFileCreateProcess(const char * file,const char *,const char * directory)66 bool LaunchFileCreateProcess(const char *file, const char *, const char *directory) {
67 	STARTUPINFOW startInfo;
68     PROCESS_INFORMATION procInfo;
69 
70     ZeroMemory(&startInfo, sizeof(startInfo));
71     startInfo.cb = sizeof(startInfo);
72     ZeroMemory(&procInfo, sizeof(procInfo));
73 
74 	WString wexec;
75 	wexec = Format("\"%s\" \"%s\"", GetExtExecutable(GetFileExt(file)), file).ToWString();
76 	WStringBuffer wsbexec(wexec);
77 
78 	if (!CreateProcessW(NULL, wsbexec, NULL, NULL, FALSE, 0, NULL, ToSystemCharsetW(directory), &startInfo, &procInfo))
79 		return false;
80 
81    	WaitForSingleObject(procInfo.hProcess, 0);
82 
83     CloseHandle(procInfo.hProcess);
84     CloseHandle(procInfo.hThread);
85 	return true;
86 }
87 
LaunchFileShellExecute(const char * file,const char * params,const char * directory)88 bool LaunchFileShellExecute(const char *file, const char *params, const char *directory) {
89 	uint64 ret = uint64(ShellExecuteW(NULL, L"open", ToSystemCharsetW(file), ToSystemCharsetW(params), ToSystemCharsetW(directory), SW_SHOWNORMAL));
90 	return 32 < ret;
91 }
92 
LaunchFile(const char * file,const char * params,const char * directory)93 bool LaunchFile(const char *file, const char *params, const char *directory) {
94 	String _file = Trim(WinPath(file));
95 	String _params, _directory;
96 	if (params)
97 		_params = WinPath(params);
98 	if (directory)
99 		_directory = WinPath(directory);
100 	if (!LaunchFileShellExecute(_file, _params, _directory))			// First try
101 	   	return LaunchFileCreateProcess(_file, _params, _directory);		// Second try
102 	return true;
103 }
104 #endif
105 
106 #ifdef PLATFORM_POSIX
107 
GetDesktopManagerNew()108 String GetDesktopManagerNew() {
109 	if(GetEnv("GNOME_DESKTOP_SESSION_ID").GetCount() || GetEnv("GNOME_KEYRING_SOCKET").GetCount())
110 		return "gnome";
111 	else if(GetEnv("KDE_FULL_SESSION").GetCount() || GetEnv("KDEDIR").GetCount() || GetEnv("KDE_MULTIHEAD").GetCount())
112         return "kde";
113 	else {
114 		StringParse desktopStr;
115 		if (Sys("xprop -root _DT_SAVE_MODE").Find("xfce") >= 0)
116 			return "xfce";
117 		else if ((desktopStr = Sys("xprop -root")).Find("ENLIGHTENMENT") >= 0)
118 			return "enlightenment";
119 		else
120 			return GetEnv("DESKTOP_SESSION");
121 	}
122 }
123 
LaunchFile(const char * _file,const char * _params,const char *)124 bool LaunchFile(const char *_file, const char *_params, const char *) {
125 	String file = UnixPath(_file);
126 	String params = UnixPath(_params);
127 	int ret;
128 	if (GetDesktopManagerNew() == "gnome")
129 		ret = system("gnome-open \"" + file + "\" " + params);
130 	else if (GetDesktopManagerNew() == "kde")
131 		ret = system("kfmclient exec \"" + file + "\" " + params + " &");
132 	else if (GetDesktopManagerNew() == "enlightenment") {
133 		String mime = GetExtExecutable(GetFileExt(file));
134 		String program = mime.Left(mime.Find("."));		// Left side of mime executable is the program to run
135 		ret = system(program + " \"" + file + "\" " + params + " &");
136 	} else
137 		ret = system("xdg-open \"" + String(file) + "\"");
138 	return (ret >= 0);
139 }
140 #endif
141 
142 /////////////////////////////////////////////////////////////////////
143 // General utilities
144 
FileCat(const char * file,const char * appendFile)145 bool FileCat(const char *file, const char *appendFile) {
146 	if (!FileExists(file))
147 		return FileCopy(appendFile, file);
148 	FileAppend f(file);
149 	if(!f.IsOpen())
150 		return false;
151 	FileIn fi(appendFile);
152 	if(!fi.IsOpen())
153 		return false;
154 	CopyStream(f, fi, fi.GetLeft());
155 	fi.Close();
156 	f.Close();
157 	if(f.IsError())
158 		return false;
159 	return true;
160 }
161 
FileStrAppend(const char * file,const char * str)162 bool FileStrAppend(const char *file, const char *str) {
163 	FileAppend f(file);
164 	if(!f.IsOpen())
165 		return false;
166 	f << str;
167 	f.Close();
168 	if(f.IsError())
169 		return false;
170 	return true;
171 }
172 
AppendFile(const char * file,const char * str)173 bool AppendFile(const char *file, const char *str) {return FileStrAppend(file, str);}
174 
FormatLong(long a)175 String FormatLong(long a) {
176 	return Sprintf("%ld", a);
177 }
178 
Replace(String str,String find,String replace)179 String Replace(String str, String find, String replace) {
180 	String ret;
181 
182 	int lenStr = str.GetCount();
183 	int lenFind = find.GetCount();
184 	int i = 0, j;
185 	while ((j = str.Find(find, i)) >= i) {
186 		ret += str.Mid(i, j-i) + replace;
187 		i = j + lenFind;
188 		if (i >= lenStr)
189 			break;
190 	}
191 	ret += str.Mid(i);
192 	return ret;
193 }
194 
Replace(String str,char find,char replace)195 String Replace(String str, char find, char replace) {
196 	StringBuffer ret(str);
197 	for (int i = 0; i < ret.GetCount(); ++i) {
198 		if (ret[i] == find)
199 			ret[i] = replace;
200 	}
201 	return ret;
202 }
203 
204 // Rename file or folder
FileMoveX(const char * oldpath,const char * newpath,EXT_FILE_FLAGS flags)205 bool FileMoveX(const char *oldpath, const char *newpath, EXT_FILE_FLAGS flags) {
206 	bool usr, grp, oth;
207 	if (flags & DELETE_READ_ONLY) {
208 		if (IsReadOnly(oldpath, usr, grp, oth))
209 			SetReadOnly(oldpath, false, false, false);
210 	}
211 	bool ret = FileMove(oldpath, newpath);
212 	if (flags & DELETE_READ_ONLY)
213 		SetReadOnly(newpath, usr, grp, oth);
214 	return ret;
215 }
216 
FileDeleteX(const char * path,EXT_FILE_FLAGS flags)217 bool FileDeleteX(const char *path, EXT_FILE_FLAGS flags) {
218 	if (flags & USE_TRASH_BIN)
219 		return FileToTrashBin(path);
220 	else {
221 		if (flags & DELETE_READ_ONLY)
222 			SetReadOnly(path, false, false, false);
223 		return FileDelete(path);
224 	}
225 }
226 
FolderDeleteX(const char * path,EXT_FILE_FLAGS flags)227 bool FolderDeleteX(const char *path, EXT_FILE_FLAGS flags) {
228 	if (flags & USE_TRASH_BIN)
229 		return FileToTrashBin(path);
230 	else {
231 		if (flags & DELETE_READ_ONLY)
232 			SetReadOnly(path, false, false, false);
233 		return DirectoryDelete(path);
234 	}
235 }
236 
237 
DirectoryExistsX_Each(const char * name)238 bool DirectoryExistsX_Each(const char *name) {
239 #if defined(PLATFORM_WIN32)
240 	if(name[0] && name[1] == ':' && name[2] == '\\' && name[3] == 0 &&
241 	   GetDriveType(name) != DRIVE_NO_ROOT_DIR)
242 	    return true;
243 	DWORD res = GetFileAttributes(ToSystemCharset(name));
244 	if (!(res & FILE_ATTRIBUTE_DIRECTORY))
245 		return false;
246 	if (res != INVALID_FILE_ATTRIBUTES)
247 		return true;
248 	if (!(name[0] && name[1] == ':'))
249 		return false;
250 	if (!(ERROR_PATH_NOT_FOUND == GetLastError()))
251 		return false;
252 
253 	String localName = String(name, 2);
254 	char remoteName[256];
255 	DWORD lenRemoteName = sizeof(remoteName);
256 	res = WNetGetConnection(localName, remoteName, &lenRemoteName);
257 	if (res != ERROR_CONNECTION_UNAVAIL)
258 		return false;
259 
260 	NETRESOURCE nr;
261 	memset(&nr, 0, sizeof(NETRESOURCE));
262 	nr.dwType = RESOURCETYPE_DISK;
263 	nr.lpLocalName = const_cast<char *>(localName.Begin());
264 	nr.lpRemoteName = remoteName;
265 	nr.lpProvider = NULL;
266     DWORD dwFlags = CONNECT_UPDATE_PROFILE;
267     res = WNetAddConnection2(&nr, NULL, NULL, dwFlags);
268    	if (res != NO_ERROR)
269    		return false;
270 
271 	res = GetFileAttributes(ToSystemCharset(name));
272 	return (res != INVALID_FILE_ATTRIBUTES &&
273            (res & FILE_ATTRIBUTE_DIRECTORY));
274 
275 #else
276 	FindFile ff(name);
277 	return ff && ff.IsDirectory();
278 #endif
279 }
280 
DirectoryExistsX(const char * path,EXT_FILE_FLAGS flags)281 bool DirectoryExistsX(const char *path, EXT_FILE_FLAGS flags) {
282 	String spath = path;
283 	if (spath.EndsWith(DIR_SEPS))
284 		spath = spath.Left(spath.GetCount() - 1);
285 	if (!(flags & BROWSE_LINKS))
286 		return DirectoryExistsX_Each(spath);
287 	if (DirectoryExistsX_Each(spath))
288 		return true;
289 	if (!IsSymLink(spath))
290 		return false;
291 	String filePath;
292 	filePath = GetSymLinkPath(spath);
293 	if (filePath.IsEmpty())
294 		return false;
295 	return DirectoryExistsX_Each(filePath);
296 }
297 
IsFile(const char * fileName)298 bool IsFile(const char *fileName) {
299 	FindFile ff;
300 	if(ff.Search(fileName) && ff.IsFile())
301 		return true;
302 	return false;
303 }
304 
IsFolder(const char * fileName)305 bool IsFolder(const char *fileName) {
306 	FindFile ff;
307 	if(ff.Search(fileName) && ff.IsFolder())
308 		return true;
309 	return false;
310 }
311 
GetRelativePath(String from,String path,String & ret,bool normalize)312 bool GetRelativePath(String from, String path, String& ret, bool normalize) {
313 	if (normalize) {
314 		String creplace = DIR_SEP == '\\' ? "/" : "\\";
315 		from.Replace(creplace, DIR_SEPS);
316 		path.Replace(creplace, DIR_SEPS);
317 		if (!PLATFORM_PATH_HAS_CASE) {
318 			from = ToLower(from);
319 			path = ToLower(path);
320 		}
321 	}
322 	ret.Clear();
323 	int pos_from = 0, pos_path = 0;
324 	bool first = true;
325 	while (!IsNull(pos_from)) {
326 		String f_from = Tokenize2(from, DIR_SEPS, pos_from);
327 		String f_path = Tokenize2(path, DIR_SEPS, pos_path);
328 		if (f_from != f_path) {
329 			if (first)
330 				return false;
331 			ret << f_path;
332 			String fileName = path.Mid(pos_path);
333 			if (!fileName.IsEmpty())
334 				ret << DIR_SEPS << fileName;
335 			while (!IsNull(pos_from)) {
336 				ret.Insert(0, String("..") + DIR_SEPS);
337 				Tokenize2(from, DIR_SEPS, pos_from);
338 			}
339 			ret.Insert(0, String("..") + DIR_SEPS);
340 			return true;
341 		}
342 		first = false;
343 	}
344 	ret = path.Mid(pos_path);
345 	return true;
346 }
347 
SetReadOnly(const char * path,bool readOnly)348 bool SetReadOnly(const char *path, bool readOnly) {
349 	return SetReadOnly(path, readOnly, readOnly, readOnly);
350 }
351 
SetReadOnly(const char * path,bool usr,bool,bool)352 bool SetReadOnly(const char *path, bool usr, bool, bool) {
353 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
354 	DWORD attr = GetFileAttributesW(ToSystemCharsetW(path));
355 
356 	if (attr == INVALID_FILE_ATTRIBUTES)
357 		return false;
358 
359 	DWORD newattr;
360 	if (usr)
361 		newattr = attr | FILE_ATTRIBUTE_READONLY;
362 	else
363 		newattr = attr & ~FILE_ATTRIBUTE_READONLY;
364 
365 	if (attr != newattr)
366 		return SetFileAttributesW(ToSystemCharsetW(path), newattr);
367 	else
368 		return true;
369 #else
370 	struct stat buffer;
371 	//int status;
372 
373 	if(0 != stat(ToSystemCharset(path), &buffer))
374 		return false;
375 
376 	mode_t m = buffer.st_mode;
377 	mode_t newmode = (m & S_IRUSR) | (m & S_IRGRP) | (m & S_IROTH);
378 
379 	if (newmode != buffer.st_mode)
380 		return 0 == chmod(ToSystemCharset(path), newmode);
381 	else
382 		return true;
383 #endif
384 }
385 
IsReadOnly(const char * path,bool & usr,bool & grp,bool & oth)386 bool IsReadOnly(const char *path, bool &usr, bool &grp, bool &oth) {
387 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
388 	DWORD attr = GetFileAttributesW(ToSystemCharsetW(path));
389 
390 	if (attr == INVALID_FILE_ATTRIBUTES)
391 		return false;
392 
393 	usr = grp = oth = attr & FILE_ATTRIBUTE_READONLY;
394 	return true;
395 #else
396 	struct stat buffer;
397 
398 	if(0 != stat(ToSystemCharset(path), &buffer))
399 		return false;
400 
401 	usr = buffer.st_mode & S_IRUSR;
402 	grp = buffer.st_mode & S_IRGRP;
403 	oth = buffer.st_mode & S_IROTH;
404 
405 	return true;
406 #endif
407 }
408 
409 #ifdef PLATFORM_POSIX
410 
GetUid()411 int GetUid() {
412 	String proc = LoadFile_Safe("/etc/passwd");
413 	int pos = proc.Find(GetUserName());
414 	if (pos < 0)
415 		return -1;
416 	pos = proc.Find(':', pos);
417 	if (pos < 0)
418 		return -1;
419 	pos = proc.Find(':', pos+1);
420 	if (pos < 0)
421 		return -1;
422 	int posend = proc.Find(':', pos+1);
423 	if (posend < 0)
424 		return -1;
425 	return ScanInt(proc.Mid(pos+1, posend-pos-1));
426 }
427 
GetMountDirectory(const String & path)428 String GetMountDirectory(const String &path) {
429 	Vector<String> drives = GetDriveList();
430 	for (int i = 0; i < drives.GetCount(); ++i) {
431 		if (path.Find(drives[i]) == 0)
432 			return drives[i];
433 	}
434 	String localPath = AppendFileName(GetCurrentDirectory(), path);
435 	if (!FileExists(localPath) && !DirectoryExists(localPath))
436 		return "";
437 	for (int i = 0; i < drives.GetCount(); ++i) {
438 		if (localPath.Find(drives[i]) == 0)
439 			return drives[i];
440 	}
441 	return "";
442 }
443 
GetTrashBinDirectory()444 String GetTrashBinDirectory()
445 {
446 	String ret = GetEnv("XDG_DATA_HOME");
447 	if (ret.IsEmpty())
448 		ret = AppendFileName(GetHomeDirectory(), ".local/share/Trash");
449 	else
450 		ret = AppendFileName(ret, "Trash");
451 	return ret;
452 }
453 
FileToTrashBin(const char * path)454 bool FileToTrashBin(const char *path)
455 {
456 	String newPath = AppendFileName(GetTrashBinDirectory(), GetFileName(path));
457 	return FileMove(path, newPath);
458 }
459 
TrashBinGetCount()460 int64 TrashBinGetCount()
461 {
462 	int64 ret = 0;
463 	FindFile ff;
464 	if(ff.Search(AppendFileName(GetTrashBinDirectory(), "*"))) {
465 		do {
466 			String name = ff.GetName();
467 			if (name != "." && name != "..")
468 				ret++;
469 		} while(ff.Next());
470 	}
471 	return ret;
472 }
473 
TrashBinClear()474 bool TrashBinClear()
475 {
476 	FindFile ff;
477 	String trashBinDirectory = GetTrashBinDirectory();
478 	if(ff.Search(AppendFileName(trashBinDirectory, "*"))) {
479 		do {
480 			String name = ff.GetName();
481 			if (name != "." && name != "..") {
482 				String path = AppendFileName(trashBinDirectory, name);
483 				if (ff.IsFile())
484 					FileDelete(path);
485 				else if (ff.IsDirectory())
486 					DeleteFolderDeep(path);
487 			}
488 		} while(ff.Next());
489 	}
490 	return true;
491 }
492 
493 #endif
494 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
495 
DirectoryMove(const char * dir,const char * newPlace)496 bool DirectoryMove(const char *dir, const char *newPlace) {
497 	if (strcmp(dir, newPlace) == 0)
498 		return true;
499 
500 	WString wDir(dir), wNewPlace(newPlace);
501     wDir.Cat() << L'\0';
502     wNewPlace.Cat() << L'\0';
503 
504     SHFILEOPSTRUCTW fileOp = {};
505   	fileOp.hwnd = NULL;
506     fileOp.wFunc = FO_MOVE;
507     fileOp.pFrom = ~wDir;
508     fileOp.pTo = ~wNewPlace;
509     fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT;
510 
511     int ret = SHFileOperationW(&fileOp);
512  	return ret == 0;
513 }
514 
FileToTrashBin(const char * path)515 bool FileToTrashBin(const char *path) {
516     if (!FileExists(path) && !DirectoryExists(path))
517         return false;
518 
519     WString wpath(path);
520     wpath.Cat() << L'\0';
521 
522     SHFILEOPSTRUCTW fileOp = {};
523     fileOp.hwnd = NULL;
524     fileOp.wFunc = FO_DELETE;
525     fileOp.pFrom = ~wpath;
526     fileOp.pTo = NULL;
527     fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT;
528 
529     int ret = SHFileOperationW(&fileOp);
530  	return ret == 0;
531 }
532 
TrashBinGetCount()533 int64 TrashBinGetCount() {
534 	SHQUERYRBINFO shqbi;
535 
536  	shqbi.cbSize = sizeof(SHQUERYRBINFO);
537 	if (S_OK != SHQueryRecycleBin(0, &shqbi))
538 		return -1;
539 	return shqbi.i64NumItems;
540 }
541 
TrashBinClear()542 bool TrashBinClear() {
543 	if (S_OK != SHEmptyRecycleBin(0, 0, SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND))
544 		return false;
545 	return true;
546 }
547 
548 #endif
549 
550 #include <sys/types.h>
551 #include <sys/stat.h>
552 #include <fcntl.h>
553 
LoadFile_Safe(const String fileName)554 String LoadFile_Safe(const String fileName)
555 {
556 #ifdef PLATFORM_POSIX
557 	int fid = open(fileName, O_RDONLY);
558 #else
559 	int fid = _wopen(fileName.ToWString(), O_RDONLY|O_BINARY);
560 #endif
561 	if (fid < 0)
562 		return String();
563 	const int size = 1024;
564 	int nsize;
565 	StringBuffer s;
566 	char buf[size];
567 	while((nsize = read(fid, buf, size)) == size)
568 		s.Cat(buf, size);
569 	if (nsize > 1)
570 		s.Cat(buf, nsize-1);
571 	close(fid);
572 	return s;
573 }
574 
LoadFile(const char * fileName,off_t from,size_t len)575 String LoadFile(const char *fileName, off_t from, size_t len)
576 {
577 #ifdef PLATFORM_POSIX
578 	int fid = open(fileName, O_RDONLY);
579 #else
580 	int fid = _wopen(String(fileName).ToWString(), O_RDONLY|O_BINARY);
581 #endif
582 	if (fid < 0)
583 		return String();
584 	if (0 > lseek(fid, from, SEEK_SET))
585 		return String();
586 	size_t size = 1024;
587 	if (len != 0 && size > len)
588 		size = len;
589 	size_t nsize;
590 	StringBuffer s;
591 	Buffer<char> buf(size);
592 	size_t loaded;
593 	for (loaded = 0; (nsize = read(fid, buf, unsigned(size))) == size && (len == 0 || loaded < len); loaded += nsize) {
594 		if (len != 0 && loaded + size > len)
595 			size = len - loaded;
596 		s.Cat(buf, int(size));
597 	}
598 	if (nsize > 1 && (len == 0 || loaded < len))
599 		s.Cat(buf, int(nsize-1));
600 	close(fid);
601 	return s;
602 }
603 
GetExtExecutable(const String _ext)604 String GetExtExecutable(const String _ext)
605 {
606 	String ext = _ext;
607 	String exeFile = "";
608 	if (ext[0] != '.')
609 		ext = String(".") + ext;
610 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
611 	String file = AppendFileName(GetHomeDirectory(), String("dummy") + ext); // Required by FindExecutableW
612 	SaveFile(file, "   ");
613 	if (!FileExists(file))
614 		return "";
615 	HINSTANCE ret;
616 	WString fileW(file);
617 	WCHAR exe[1024];
618 	ret = FindExecutableW(fileW, NULL, exe);
619 	if (reinterpret_cast<uint64>(ret) > 32)
620 		exeFile = WString(exe).ToString();
621 	DeleteFile(file);
622 #endif
623 #ifdef PLATFORM_POSIX
624 	StringParse mime;
625 	//if (LaunchCommand(String("xdg-mime query filetype ") + file, mime) >= 0) 	// xdg-mime query filetype does not work properly in Enlightenment
626 	mime = LoadFile_Safe("/etc/mime.types");	// Search in /etc/mime.types the mime type for the extension
627 	if ((mime.GoAfter_Init(String(" ") + ext.Right(ext.GetCount()-1))) || (mime.GoAfter_Init(String("\t") + ext.Right(ext.GetCount()-1)))) {
628 		mime.GoBeginLine();
629 		mime = mime.GetText();
630 		exeFile = TrimRight(Sys(String("xdg-mime query default ") + mime));
631 	}
632 #endif
633 	return exeFile;
634 }
635 
636 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
GetDriveList()637 Vector<String> GetDriveList() {
638 	char drvStr[26*4+1];		// A, B, C, ...
639 	Vector<String> ret;
640 
641 	int lenDrvStrs = ::GetLogicalDriveStrings(sizeof(drvStr), drvStr);
642 	// To get the error call GetLastError()
643 	if (lenDrvStrs == 0)
644 		return ret;
645 
646 	ret.Add(drvStr);
647 	for (int i = 0; i < lenDrvStrs-1; ++i) {
648 		if (drvStr[i] == '\0')
649 			ret.Add(drvStr + i + 1);
650 	}
651 	return ret;
652 }
653 #endif
654 #if defined(PLATFORM_POSIX)
GetDriveList()655 Vector<String> GetDriveList() {
656 	Vector<String> ret;
657 	// Search for mountable file systems
658 	String mountableFS = "cofs.";
659 	StringParse sfileSystems(LoadFile_Safe("/proc/filesystems"));
660 	String str;
661 	while (true) {
662 		str = sfileSystems.GetText();
663 		if (str == "")
664 			break;
665 		else if (str != "nodev")
666 			mountableFS << str << ".";
667 		else
668 			str = sfileSystems.GetText();
669 	}
670 	// Get mounted drives
671 	StringParse smounts(LoadFile_Safe("/proc/mounts"));
672 	StringParse smountLine(Trim(smounts.GetText("\r\n")));
673 	do {
674 		String devPath 	 = smountLine.GetText();
675 		String mountPath = smountLine.GetText();
676 		String fs        = smountLine.GetText();
677 		if ((mountableFS.Find(fs) >= 0) && (mountPath.Find("/dev") < 0)
678 		 && (mountPath.Find("/rofs") < 0) && (mountPath != "/"))	// Is mountable
679 			ret.Add(mountPath);
680 		smountLine = Trim(smounts.GetText("\r\n"));
681 	} while (smountLine != "");
682 	ret.Add("/");	// Last but not least
683 	return ret;
684 }
685 #endif
686 
687 
688 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
GetShellFolder2(int clsid)689 String GetShellFolder2(int clsid)
690 {
691 	wchar path[MAX_PATH];
692 	if(SHGetFolderPathW(NULL, clsid, NULL, //SHGFP_TYPE_CURRENT
693 											0, path) == S_OK)
694 		return FromUnicodeBuffer(path);
695 	return Null;
696 }
697 
GetShellFolder2(const char * local,const char * users)698 String GetShellFolder2(const char *local, const char *users)
699 {
700 	String ret = FromSystemCharset(GetWinRegString(local, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
701 									   HKEY_CURRENT_USER));
702 	if (ret == "" && *users != '\0')
703 		return FromSystemCharset(GetWinRegString(users, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
704 									   HKEY_LOCAL_MACHINE));
705 	return ret;
706 }
707 
GetPersonalFolder()708 String GetPersonalFolder()	{return GetShellFolder2("Personal", 0);}
GetStartupFolder()709 String GetStartupFolder()	{return GetShellFolder2(CSIDL_STARTUP);}
710 
GetTempFolder()711 String GetTempFolder()
712 {
713 	String ret;
714 	if ((ret = GetEnv("TEMP")) == "")	// One or the other one
715 		ret = GetEnv("TMP");
716 	return ret;
717 }
718 
GetOsFolder()719 String GetOsFolder()
720 {
721 	char ret[MAX_PATH];
722 	::GetWindowsDirectory(ret, MAX_PATH);
723 	return String(ret);
724 }
GetSystemFolder()725 String GetSystemFolder()
726 {
727 	char ret[MAX_PATH];
728 	::GetSystemDirectory(ret, MAX_PATH);
729 	return String(ret);
730 }
731 
732 #ifdef PLATFORM_WIN32
GetCommonAppDataFolder()733 String GetCommonAppDataFolder() {
734 	wchar path[MAX_PATH];
735 	if(SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, 0, path) == S_OK)
736 		return FromUnicodeBuffer(path);
737 	return Null;
738 }
739 #endif
740 
SetEnv(const char * id,const char * val)741 bool SetEnv(const char *id, const char *val)
742 {
743 //	EnvMap().Put(WString(id), WString(val));
744 #ifdef PLATFORM_POSIX
745 	return setenv(id, val, 1) == 0;
746 #else
747 	return _wputenv(WString(id) + "=" + WString(val)) == 0;
748 #endif
749 }
750 
751 
752 #endif
753 #ifdef PLATFORM_POSIX
754 
GetPathXdg2(String xdgConfigHome,String xdgConfigDirs)755 String GetPathXdg2(String xdgConfigHome, String xdgConfigDirs)
756 {
757 	String ret;
758 	if (FileExists(ret = AppendFileName(xdgConfigHome, "user-dirs.dirs")))
759 		;
760   	else if (FileExists(ret = AppendFileName(xdgConfigDirs, "user-dirs.defaults")))
761   		;
762   	else if (FileExists(ret = AppendFileName(xdgConfigDirs, "user-dirs.dirs")))
763   		;
764   	return ret;
765 }
GetPathDataXdg2(String fileConfig,const char * folder)766 String GetPathDataXdg2(String fileConfig, const char *folder)
767 {
768 	StringParse fileData = LoadFile(fileConfig);
769 
770 	if (!fileData.GoAfter(folder)) return "";
771 	if (!fileData.GoAfter("=")) return "";
772 
773 	String ret = "";
774 	StringParse path = fileData.GetText();
775 	if(path.GoAfter("$HOME/"))
776 		ret = AppendFileName(GetHomeDirectory(), path.Right());
777 	else if (!FileExists(path))
778 		ret = AppendFileName(GetHomeDirectory(), path);
779 
780 	return ret;
781 }
GetShellFolder2(const char * local,const char * users)782 String GetShellFolder2(const char *local, const char *users)
783 {
784  	String xdgConfigHome = GetEnv("XDG_CONFIG_HOME");
785   	if (xdgConfigHome == "")		// By default
786   		xdgConfigHome = AppendFileName(GetHomeDirectory(), ".config");
787   	String xdgConfigDirs = GetEnv("XDG_CONFIG_DIRS");
788   	if (xdgConfigDirs == "")			// By default
789   		xdgConfigDirs = "/etc/xdg";
790   	String xdgFileConfigData = GetPathXdg2(xdgConfigHome, xdgConfigDirs);
791   	String ret = GetPathDataXdg2(xdgFileConfigData, local);
792   	if (ret == "" && *users != '\0')
793   		return GetPathDataXdg2(xdgFileConfigData, users);
794   	else
795   		return ret;
796 }
797 
GetTempFolder()798 String GetTempFolder()
799 {
800 	return GetHomeDirectory();
801 }
GetOsFolder()802 String GetOsFolder()
803 {
804 	return String("/bin");
805 }
GetSystemFolder()806 String GetSystemFolder()
807 {
808 	return String("");
809 }
810 
GetPersonalFolder()811 String GetPersonalFolder()	{return GetShellFolder2("XDG_DOCUMENTS_DIR", "DOCUMENTS");}
812 
813 #endif
814 
815 struct StringNormalCompare__ {
operator ()Upp::StringNormalCompare__816 	int operator()(char a, char b) const { return a - b; }
817 };
818 
Compare(const String & a,int i0,const String & b,int len)819 int Compare(const String& a, int i0, const String& b, int len) {
820 	return IterCompare(a.Begin() + i0, a.Begin() + i0 + len, b.Begin(), b.Begin() + len, StringNormalCompare__());
821 }
822 
ReverseFind(const String & s,const String & toFind,int from)823 int ReverseFind(const String& s, const String& toFind, int from) {
824 	ASSERT(from >= 0 && from <= s.GetLength());
825 	int lc = toFind.GetLength();
826 	for (int i = from; i >= 0; --i) {
827 		if (Compare(s, i, toFind, lc) == 0)
828 			return i;
829 	}
830 	return -1;
831 }
832 
StrToTime(const char * s)833 Time StrToTime(const char *s) {
834 	Time ret;
835 	if (StrToTime(ret, s))
836 		return ret;
837 	else
838 		return Null;
839 }
840 
StrToDate(const char * s)841 Date StrToDate(const char *s) {
842 	Time ret;
843 	if (StrToDate(ret, s))
844 		return ret;
845 	else
846 		return Null;
847 }
848 
StringToHMS(String durat,int & hour,int & min,double & seconds)849 void StringToHMS(String durat, int &hour, int &min, double &seconds) {
850 	StringParse duration(durat);
851 	String s1, s2, s3;
852 	s1 = duration.GetText(":");
853 	s2 = duration.GetText(":");
854 	s3 = duration.GetText();
855 
856 	if (s3 != "") {
857 		hour = ScanInt(s1);
858 		min = ScanInt(s2);
859 		seconds = ScanDouble(s3);
860 	} else if (s2 != "") {
861 		hour = 0;
862 		min = ScanInt(s1);
863 		seconds = ScanDouble(s2);
864 	} else {
865 		hour = 0;
866 		min = 0;
867 		seconds = ScanDouble(s1);
868 	}
869 	if (IsNull(hour) || IsNull(min) || IsNull(seconds)) {
870 		hour = min = Null;
871 		seconds = Null;
872 	} else if (hour < 0 || min < 0 || seconds < 0) {
873 		hour = Neg(hour);
874 		min = Neg(min);
875 		seconds = Neg(seconds);
876 	}
877 }
StringToSeconds(String duration)878 double StringToSeconds(String duration) {
879 	int hour, min;
880 	double secs;
881 	StringToHMS(duration, hour, min, secs);
882 	return 3600.*hour + 60.*min + secs;
883 }
884 
formatSeconds(double seconds,int dec,bool fill)885 String formatSeconds(double seconds, int dec, bool fill) {
886 	int iseconds = int(seconds);
887 	String ret;
888 	if (fill)
889 		ret = FormatIntDec(iseconds, 2, '0');
890 	else
891 		ret = FormatInt(iseconds);
892 	double decs = seconds - iseconds;
893 	if (decs > 0 && dec > 0)
894 		ret << "." << FormatIntDec(static_cast<int>(decs*pow(10, dec)), dec, '0');
895 	return ret;
896 }
897 /*
898 String HMSToString0(int hour, int min, double seconds, bool units, int dec) {
899 	String sunits;
900 	if (units) {
901 		if (hour >= 1)
902 			sunits = t_("hours");
903 		else if (min >= 2)
904 			sunits = t_("mins");
905 		else if (min == 1)
906 			sunits = t_("min");
907 		else if (seconds > 1)
908 			sunits = t_("secs");
909 		else
910 			sunits = t_("sec");
911 	}
912 	String ret;
913 	if (hour > 0)
914 		ret << hour << ":";
915 	if (min > 0 || hour > 0)
916 	    ret << (ret.IsEmpty() ? FormatInt(min) : FormatIntDec(min, 2, '0')) << ":";
917 
918 	ret << formatSeconds(seconds, dec, min > 0 || hour > 0);
919 	if (units)
920 		ret << " " << sunits;
921 	return ret;
922 }*/
923 
HMSToString(int hour,int min,double seconds,int dec,bool units,bool space,bool tp,bool longUnits,bool forcesec)924 String HMSToString(int hour, int min, double seconds, int dec, bool units, bool space, bool tp,
925 					bool longUnits, bool forcesec) {
926 	String ret;
927 	bool isneg = hour < 0 || min < 0 || seconds < 0;
928 
929 	if (hour > 0) {
930 		ret << hour;
931 		if (space)
932 			ret << " ";
933 		if (tp)
934 			ret << ":";
935 		if (units)
936 			ret << (longUnits ? ((hour > 1) ? t_("hours") : t_("hour")) : t_("h"));
937 	}
938 
939 	if (min > 0) {
940 		if (tp) {
941 			String fmt = hour > 0 ? "%02d" : "%d";
942 			ret << Format(fmt, min);
943 		} else
944 			ret << (ret.IsEmpty() ? "" : " ") << min;
945 		if (space)
946 			ret << " ";
947 		if (tp && forcesec)
948 			ret << ":";
949 		if (units)
950 			ret << (longUnits ? ((min > 1) ? t_("mins") : t_("min")) : t_("m"));
951 	} else if (tp) {
952 		if (hour > 0) {
953 			ret << "00";
954 			if (forcesec)
955 				ret << ":";
956 		}
957 	}
958 
959 	if (forcesec || (hour == 0 && (seconds > 1 || (seconds > 0 && dec > 0)))) {
960 		ret << (ret.IsEmpty() || tp ? "" : " ") << formatSeconds(seconds, dec, tp);
961 		if (space)
962 			ret << " ";
963 		if (units)
964  			ret << (longUnits ? ((seconds > 1) ? t_("secs") : t_("sec")) : t_("s"));
965 	}
966 
967 	if (isneg)
968 		ret = "-" + ret;
969 	return ret;
970 }
971 
SecondsToString(double seconds,int dec,bool units,bool space,bool tp,bool longUnits,bool forcesec)972 String SecondsToString(double seconds, int dec, bool units, bool space, bool tp,
973 					bool longUnits, bool forcesec) {
974 	int hour, min;
975 	hour = static_cast<int>(seconds/3600.);
976 	seconds -= hour*3600;
977 	min = static_cast<int>(seconds/60.);
978 	seconds -= min*60;
979 	return HMSToString(hour, min, seconds, dec, units, space, tp, longUnits, forcesec);
980 }
981 
SeasonName(int iseason)982 String SeasonName(int iseason) {
983 	static const char *season[] = {t_("winter"), t_("spring"), t_("summer"), t_("autumn")};
984 	return iseason >= 0 && iseason < 4 ? season[iseason] : "";
985 }
986 
GetSeason(Date & date)987 int GetSeason(Date &date) {
988 	return int((date.month - 1)/3.);
989 }
990 
BytesToString(uint64 _bytes,bool units)991 String BytesToString(uint64 _bytes, bool units)
992 {
993 	String ret;
994 	uint64 bytes = _bytes;
995 
996 	if (bytes >= 1024) {
997 		bytes /= 1024;
998 		if (bytes >= 1024) {
999 			bytes /= 1024;
1000 			if (bytes >= 1024) {
1001 				bytes /= 1024;
1002 				if (bytes >= 1024) {
1003 					bytes /= 1024;
1004 					ret = Format("%.1f %s", _bytes/(1024*1024*1024*1024.), units ? "Tb" : "");
1005 				} else
1006 					ret = Format("%.1f %s", _bytes/(1024*1024*1024.), units ? "Gb" : "");
1007 			} else
1008 				ret = Format("%.1f %s", _bytes/(1024*1024.), units ? "Mb" : "");
1009 		} else
1010 			ret = Format("%.1f %s", _bytes/1024., units ? "Kb" : "");
1011 	} else
1012 		ret << _bytes << (units ? "b" : "");
1013 	return ret;
1014 }
1015 
FormatDoubleAdjust(double d,double range)1016 String FormatDoubleAdjust(double d, double range) {
1017 	if (fabs(d) <= 1e-15)
1018 		d = 0;
1019 	if 		(0.001 <= range && range < 0.01) 	return FormatDouble(d,5);
1020 	else if (0.01  <= range && range < 0.1) 	return FormatDouble(d,4);
1021 	else if (0.1   <= range && range < 1) 		return FormatDouble(d,3);
1022 	else if (1     <= range && range < 10) 		return FormatDouble(d,2);
1023 	else if (10	   <= range && range < 100) 	return FormatDouble(d,1);
1024 	else if (100   <= range && range < 100000) 	return FormatDouble(d,0);
1025 	else return FormatDoubleExp(d,2);
1026 }
1027 
RemoveAccent(wchar c)1028 String RemoveAccent(wchar c) {
1029 	WString wsret;
1030 
1031 	if (IsAlNum(c) || IsSpace(c)) {
1032 		wsret.Cat(c);
1033 		return wsret.ToString();
1034 	}
1035 	//const WString accented = "ÂÃÀÁÇÈÉÊËẼÌÍÎÏÑÒÓÔÕÙÚÛÝàáâãçèéêëẽìíîïñòóôõøùúûýÿ";
1036 	const WString accented = "\303\202\303\203\303\200\303\201\303\207\303\210\303\211\303\212\303\213\341\272\274\303\214\303\215\303\216\303\217\303\221\303\222\303\223\303\224\303\225\303\231\303\232\303\233\303\235\303\240\303\241\303\242\303\243\303\247\303\250\303\251\303\252\303\253\341\272\275\303\254\303\255\303\256\303\257\303\261\303\262\303\263\303\264\303\265\303\270\303\271\303\272\303\273\303\275\303\277";
1037 	const char *unaccented = "AAAACEEEEEIIIINOOOOUUUYaaaaceeeeeiiiinooooouuuyy";
1038 
1039 	for (int i = 0; accented[i]; ++i) {
1040 		if (accented[i] == c) {
1041 			wsret.Cat(unaccented[i]);
1042 			return wsret.ToString();
1043 		}
1044 	}
1045 	//const WString maccented = "ÅåÆæØøþÞßÐðÄäÖöÜü";
1046 	const WString maccented = "\303\205\303\245\303\206\303\246\303\230\303\270\303\276\303\236\303\237\303\220\303\260\303\204\303\244\303\226\303\266\303\234\303\274";
1047 	const char *unmaccented[] = {"AA", "aa", "AE", "ae", "OE", "oe", "TH", "th", "SS", "ETH",
1048 								 "eth", "AE", "ae", "OE", "oe", "UE", "ue"};
1049 	for (int i = 0; maccented[i]; ++i) {
1050 		if (maccented[i] == c)
1051 			return unmaccented[i];
1052 	}
1053 	wsret.Cat(c);
1054 	return wsret.ToString();
1055 }
1056 
IsPunctuation(wchar c)1057 bool IsPunctuation(wchar c) {
1058 	//const WString punct = "\"’'()[]{}<>:;,‒–—―….,¡!¿?«»-‐‘’“”/\\&@*\\•^©¤฿¢$€ƒ£₦¥₩₪†‡〃#№ºª\%‰‱ ¶′®§℠℗~™|¦=";
1059 	const WString punct = "\"\342\200\231'()[]{}<>:;,\342\200\222\342\200\223\342\200\224\342\200\225\342\200\246.,\302\241!\302\277?\302\253\302\273-\342\200\220\342\200\230\342\200\231\342\200\234\342\200\235/\\&@*\\\342\200\242^\302\251\302\244\340\270\277\302\242$\342\202\254\306\222\302\243\342\202\246\302\245\342\202\251\342\202\252\342\200\240\342\200\241\343\200\203#\342\204\226\302\272\302\252%\342\200\260\342\200\261 "
1060      					  "\302\266\342\200\262\302\256\302\247\342\204\240\342\204\227~\342\204\242|\302\246=";
1061 	for (int i = 0; punct[i]; ++i) {
1062 		if (punct[i] == c)
1063 			return true;
1064 	}
1065 	return false;
1066 }
1067 
RemoveAccents(String str)1068 String RemoveAccents(String str) {
1069 	String ret;
1070 	WString wstr = str.ToWString();
1071 	for (int i = 0; i < wstr.GetCount(); ++i) {
1072 		String schar = RemoveAccent(wstr[i]);
1073 		if (i == 0 || ((i > 0) && ((IsSpace(wstr[i-1]) || IsPunctuation(wstr[i-1]))))) {
1074 			if (schar.GetCount() > 1) {
1075 				if (IsUpper(schar[0]) && IsLower(wstr[1]))
1076 				 	schar = String(schar[0], 1) + ToLower(schar.Mid(1));
1077 			}
1078 		}
1079 		ret += schar;
1080 	}
1081 	return ret;
1082 }
1083 
RemovePunctuation(String str)1084 String RemovePunctuation(String str) {
1085 	String ret;
1086 	WString wstr = str.ToWString();
1087 	for (int i = 0; i < wstr.GetCount(); ++i) {
1088 		if (!IsPunctuation(wstr[i]))
1089 		    ret += wstr[i];
1090 	}
1091 	return ret;
1092 }
1093 
FitFileName(const String fileName,int len)1094 String FitFileName(const String fileName, int len) {
1095 	if (fileName.GetCount() <= len)
1096 		return fileName;
1097 
1098 	Vector<String> path;
1099 
1100 	const char *s = fileName;
1101 	char c;
1102 	int pos0 = 0;
1103 	for (int pos1 = 0; (c = s[pos1]) != '\0'; ++pos1) {
1104 	#if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
1105 		if(c == '\\' || c == '/') {
1106 	#else
1107 		if(c == '/') {
1108 	#endif
1109 			path.Add(fileName.Mid(pos0, pos1-pos0));
1110 			pos0 = ++pos1;
1111 		}
1112 	}
1113 	path.Add(fileName.Mid(pos0));
1114 
1115 	String begin, end;
1116 	int iBegin = 0;
1117 	int iEnd = path.GetCount() - 1;
1118 
1119 	if (path[iEnd].GetCount() >= len)
1120 		return path[iEnd].Left(len);
1121 	if (path[iEnd].GetCount() >= len-4)
1122 		return path[iEnd];
1123 
1124 	len -= 3;	// ...
1125 
1126 	for (; iBegin <= iEnd; iBegin++, iEnd--) {
1127 		if (path[iEnd].GetCount() + 1 > len)
1128 			break;
1129 		end = DIR_SEPS + path[iEnd] + end;
1130 		len -= path[iEnd].GetCount() + 1;
1131 		if (path[iBegin].GetCount() + 1 > len)
1132 			break;
1133 		begin += path[iBegin] + DIR_SEPS;
1134 		len -= path[iBegin].GetCount() + 1;
1135 	}
1136 	return begin + "..." + end;
1137 }
1138 
1139 String Tokenize2(const String &str, const String &token, int &pos) {
1140 	if (IsNull(pos) || pos >= str.GetCount()) {
1141 		pos = Null;
1142 		return Null;
1143 	}
1144 	int npos = str.Find(token, pos);
1145 /*	for (int i = 0; i < token.GetCount(); ++i) {
1146 		if ((npos = str.Find(token[i], pos)) >= 0)
1147 			break;
1148 	}*/
1149 	int oldpos = pos;
1150 	if (npos < 0) {
1151 		pos = Null;
1152 		return str.Mid(oldpos);
1153 	} else {
1154 		pos = npos + token.GetCount();
1155 		return str.Mid(oldpos, npos - oldpos);
1156 	}
1157 }
1158 
1159 String Tokenize2(const String &str, const String &token) {
1160 	int dummy = 0;
1161 	return Tokenize2(str, token, dummy);
1162 }
1163 
1164 Vector<String> Tokenize(const String &str, const String &token, int pos) {
1165 	Vector<String> ret;
1166 
1167 	Tokenize(str, token, ret, pos);
1168 
1169 	return ret;
1170 }
1171 
1172 void Tokenize(const String &str, const String &token, Vector<String> &ret, int pos) {
1173 	int _pos = pos;
1174 	while (true) {
1175 		String strRet = Tokenize2(str, token, _pos);
1176 		if (IsNull(_pos)) {
1177 			if (!IsNull(strRet))
1178 				ret << strRet;
1179 			break;
1180 		}
1181 		ret << strRet;
1182 	}
1183 }
1184 
1185 String GetLine(const String &str, int &pos) {
1186 	String ret;
1187 	int npos = str.Find("\n", pos);
1188 	if (npos == -1) {
1189 		ret = str.Mid(pos);
1190 		pos = -1;
1191 	} else {
1192 		ret = str.Mid(pos, npos - pos);
1193 		pos = npos + 1;
1194 	}
1195 	return TrimBoth(ret);
1196 }
1197 
1198 Value GetField(const String &str, int &pos, char separator, char decimalSign, bool onlyStrings) {
1199 	ASSERT(separator != '\"');
1200 	String sret;
1201 	int npos = str.Find(separator, pos);
1202 	int spos = str.Find('\"', pos);
1203 	if (spos < 0 || spos > npos) {
1204 		if (npos < 0) {
1205 			int lspos = str.Find('\"', spos + 1);
1206 			if (lspos < 0)
1207 				sret = str.Mid(max(pos, spos));
1208 			else
1209 				sret = str.Mid(spos + 1, lspos - spos - 1);
1210 			pos = -1;
1211 		} else {
1212 			sret = Trim(str.Mid(pos, npos - pos));
1213 			pos = npos + 1;
1214 		}
1215 	} else {
1216 		int lspos = str.Find('\"', spos + 1);
1217 		if (lspos < 0) {
1218 			sret = str.Mid(spos);
1219 			pos = -1;
1220 		} else {
1221 			sret = str.Mid(spos + 1, lspos - spos - 1);
1222 			npos = str.Find(separator, lspos);
1223 			pos = npos + 1;
1224 		}
1225 	}
1226 	if (onlyStrings)
1227 		return sret;
1228 
1229 	if (sret.IsEmpty())
1230 		return Null;
1231 
1232 	bool hasDecimal = false, hasLetter = false;
1233 	for (int i = 0; i < sret.GetCount(); ++i) {
1234 		if (sret[i] == decimalSign)
1235 			hasDecimal = true;
1236 		else if (!IsNumber(sret[i]))
1237 			hasLetter = true;
1238 	}
1239 	if (!hasLetter) {
1240 		if (hasDecimal) {
1241 			double dbl = ScanDouble(sret, NULL, decimalSign == ',');
1242 			if (IsNull(dbl))
1243 				return sret;
1244 			else
1245 				return dbl;
1246 		} else {
1247 			int64 it64 = ScanInt64(sret);
1248 			if (IsNull(it64))
1249 				return sret;
1250 			int it = int(it64);
1251 			if (it64 != it)
1252 				return it64;
1253 			else
1254 				return it;
1255 		}
1256 	} else {
1257 		Time t = ScanTime(sret);
1258 		if (IsNull(t))
1259 			return sret;
1260 		else if (t.hour == 0 && t.minute == 0 && t.second == 0)
1261 			return Date(t);
1262 		else
1263 			return t;
1264 	}
1265 }
1266 
1267 Vector<Vector <Value> > ReadCSV(const String strFile, char separator, bool bycols, bool removeRepeated, char decimalSign, bool onlyStrings, int fromRow) {
1268 	Vector<Vector<Value> > result;
1269 
1270 	if (strFile.IsEmpty())
1271 		return result;
1272 
1273 	int posLine = 0;
1274 	for (int i = 0; i < fromRow; ++i)
1275 		GetLine(strFile, posLine);
1276 
1277 	String line;
1278 	int pos = 0;
1279 	if (bycols) {
1280 		line = GetLine(strFile, posLine);
1281 		while (pos >= 0) {
1282 			Value name = GetField(line, pos, separator, decimalSign, onlyStrings);
1283 			if (/*pos >= 0 && */!IsNull(name)) {
1284 				Vector<Value> &data = result.Add();
1285 				data.Add(name);
1286 			}
1287 		}
1288 		while (posLine >= 0) {
1289 			pos = 0;
1290 			line = GetLine(strFile, posLine);
1291 			if (!line.IsEmpty()) {
1292 				bool repeated = removeRepeated;
1293 				int row = result[0].GetCount() - 1;
1294 				for (int col = 0; col < result.GetCount(); col++) {
1295 					if (pos >= 0) {
1296 						Value data = GetField(line, pos, separator, decimalSign, onlyStrings);
1297 						result[col].Add(data);
1298 						if (row > 0 && result[col][row] != data)
1299 							repeated = false;
1300 					} else
1301 						result[col].Add();
1302 				}
1303 				if (row > 0 && repeated) {
1304 					for (int col = 0; col < result.GetCount(); col++)
1305 						result[col].Remove(row+1);
1306 				}
1307 			} else
1308 				break;
1309 		}
1310 	} else {
1311 		int row = 0;
1312 		while (posLine >= 0) {
1313 			pos = 0;
1314 			line = GetLine(strFile, posLine);
1315 			bool repeated = removeRepeated;
1316 			if (!line.IsEmpty()) {
1317 				Vector <Value> &linedata = result.Add();
1318 				int col = 0;
1319 				while (pos >= 0) {
1320 					Value val = GetField(line, pos, separator, decimalSign, onlyStrings);
1321 					if (val.IsNull())
1322 						linedata << "";
1323 					else
1324 						linedata << val;
1325 					if (row > 0 && (result[row - 1].GetCount() <= col || result[row - 1][col] != val))
1326 						repeated = false;
1327 					col++;
1328 				}
1329 			} else
1330 				break;
1331 			if (row > 0 && repeated)
1332 				result.Remove(row);
1333 			else
1334 				row++;
1335 		}
1336 	}
1337 	return result;
1338 }
1339 
1340 Vector<Vector <Value> > ReadCSVFile(const String fileName, char separator, bool bycols, bool removeRepeated, char decimalSign, bool onlyStrings, int fromRow) {
1341 	return ReadCSV(LoadFile(fileName), separator, bycols, removeRepeated,  decimalSign, onlyStrings, fromRow);
1342 }
1343 
1344 bool ReadCSVFileByLine(const String fileName, Gate<int, Vector<Value>&, String &> WhenRow, char separator, char decimalSign, bool onlyStrings, int fromRow) {
1345 	Vector<Value> result;
1346 
1347 	FindFile ff(fileName);
1348 	if(!ff || !ff.IsFile())
1349 		return false;
1350 
1351 	FileIn in(fileName);
1352 	in.ClearError();
1353 
1354 	for (int i = 0; i < fromRow; ++i)
1355 		in.GetLine();
1356 
1357 	for (int row = 0; true; row++) {
1358 		String line = in.GetLine();
1359 		if (line.IsVoid()) {
1360 			WhenRow(row, result, line);
1361 			return true;
1362 		}
1363 		int pos = 0;
1364 		while (pos >= 0) {
1365 			Value val = GetField(line, pos, separator, decimalSign, onlyStrings);
1366 			if (val.IsNull())
1367 				result << "";
1368 			else
1369 				result << val;
1370 		}
1371 		if (!WhenRow(row, result, line))
1372 			return false;
1373 		result.Clear();
1374 	}
1375 	return false;
1376 }
1377 
1378 String ToStringDecimalSign(Value &val, const String &decimalSign) {
1379 	String ret = val.ToString();
1380 	if (val.Is<double>() && decimalSign != ".")
1381 		ret.Replace(".", decimalSign);
1382 	return ret;
1383 }
1384 
1385 String WriteCSV(Vector<Vector <Value> > &data, char separator, bool bycols, char decimalSign) {
1386 	String ret;
1387 
1388 	String _decimalSign(decimalSign, 1);
1389 
1390 	if (bycols) {
1391 		int maxr = 0;
1392 		for (int c = 0; c < data.GetCount(); ++c)
1393 			maxr = max(maxr, data[c].GetCount());
1394 
1395 		for (int r = 0; r < maxr; ++r) {
1396 			if (r > 0)
1397 				ret << "\n";
1398 			for (int c = 0; c < data.GetCount(); ++c) {
1399 				if (c > 0)
1400 					ret << separator;
1401 				if (r >= data[c].GetCount())
1402 					continue;
1403 				String str = ToStringDecimalSign(data[c][r], _decimalSign);
1404 				if (str.Find(separator) >= 0)
1405 					ret << '\"' << str << '\"';
1406 				else
1407 					ret << str;
1408 			}
1409 		}
1410 	} else {
1411 		for (int r = 0; r < data.GetCount(); ++r) {
1412 			if (r > 0)
1413 				ret << "\n";
1414 			for (int c = 0; c < data[r].GetCount(); ++c) {
1415 				if (c > 0)
1416 					ret << separator;
1417 				String str = ToStringDecimalSign(data[r][c], _decimalSign);
1418 				if (str.Find(separator) >= 0)
1419 					ret << '\"' << str << '\"';
1420 				else
1421 					ret << str;
1422 			}
1423 		}
1424 	}
1425 	return ret;
1426 }
1427 
1428 bool WriteCSVFile(const String fileName, Vector<Vector <Value> > &data, char separator, bool bycols, char decimalSign) {
1429 	String str = WriteCSV(data, separator, bycols, decimalSign);
1430 	return SaveFile(fileName, str);
1431 }
1432 
1433 
1434 #ifdef PLATFORM_POSIX
1435 String FileRealName(const char *_fileName) {
1436 	String fileName = GetFullPath(_fileName);
1437 	FindFile ff(fileName);
1438 	if (!ff)
1439 		return String("");
1440 	else
1441 		return fileName;
1442 }
1443 #endif
1444 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
1445 bool GetRealName_Next(String &real, String file) {
1446 	bool ret;
1447 	String old;
1448 	int from = real.GetCount()+1;
1449 	int to = file.Find(DIR_SEP, from);
1450 	if (to >= 0) {
1451 		old = file.Mid(from, to-from);
1452 		ret = true;
1453 	} else {
1454 		old = file.Mid(from);
1455 		ret = false;
1456 	}
1457 	real += DIR_SEP;
1458 	FindFile ff(real + old);
1459 	real += ff.GetName();
1460 	return ret;
1461 }
1462 
1463 String FileRealName(const char *_fileName) {
1464 	String fileName = GetFullPath(_fileName);
1465 	int len = fileName.GetCount();
1466 
1467 	if (len == 3) {
1468 		FindFile ff(fileName + "*");
1469 		if (!ff)
1470 			return String("");
1471 		else
1472 			return fileName;
1473 	}
1474 	FindFile ff(fileName);
1475 	if (!ff)
1476 		return String("");
1477 	String ret;
1478 
1479 	ret.Reserve(len);
1480 
1481 	if (fileName.Left(2) == "\\\\")
1482 		return String("");	// Not valid for UNC paths
1483 
1484 	ret = ToUpper(fileName.Left(1)) + ":";
1485 
1486 	while (GetRealName_Next(ret, fileName)) ;
1487 
1488 	return ret;
1489 }
1490 #endif
1491 
1492 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
1493 
1494 #define Ptr Ptr_
1495 #define byte byte_
1496 #define CY win32_CY_
1497 
1498 #include <winnls.h>
1499 #include <winnetwk.h>
1500 
1501 #include <wincon.h>
1502 #include <shlobj.h>
1503 
1504 #undef Ptr
1505 #undef byte
1506 #undef CY
1507 
1508 #endif
1509 
1510 bool IsSymLink(const char *path) {
1511 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
1512 	return GetFileExt(path) == ".lnk";
1513 #else
1514 	struct stat stf;
1515 	lstat(path, &stf);
1516 	return S_ISLNK(stf.st_mode);
1517 #endif
1518 }
1519 
1520 String GetNextFolder(const String &folder, const String &lastFolder) {
1521 	int pos = lastFolder.Find(DIR_SEP, folder.GetCount()+1);
1522 	if (pos >= 0)
1523 		return lastFolder.Left(pos);
1524 	else
1525 		return lastFolder;
1526 }
1527 
1528 bool IsRootFolder(const char *folderName) {
1529 	if (!folderName)
1530 		return false;
1531 	if (folderName[0] == '\0')
1532 		return false;
1533 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
1534 	if (strlen(folderName) == 3 && folderName[1] == ':' && folderName[2] == DIR_SEP)
1535 #else
1536 	if (strlen(folderName) == 1 && folderName[0] == DIR_SEP)
1537 #endif
1538 		return true;
1539 	return false;
1540 }
1541 
1542 String GetUpperFolder(const String &folderName) {
1543 	if (IsRootFolder(folderName))
1544 		return folderName;
1545 	int len = folderName.GetCount();
1546 	if (len == 0)
1547 		return String();
1548 	if (folderName[len-1] == DIR_SEP)
1549 		len--;
1550 	int pos = folderName.ReverseFind(DIR_SEP, len-1);
1551 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
1552 	if (pos == 2)
1553 #else
1554 	if (pos == 0)
1555 #endif
1556 		pos++;
1557 	return folderName.Left(pos);
1558 }
1559 /*
1560 bool CreateFolderDeep(const char *dir)
1561 {
1562 	if (RealizePath(dir))
1563 		return DirectoryCreate(dir);
1564 	else
1565 		return false;
1566 }*/
1567 
1568 bool DeleteDeepWildcardsX(const char *pathwc, bool filefolder, EXT_FILE_FLAGS flags, bool deep)
1569 {
1570 	return DeleteDeepWildcardsX(GetFileFolder(pathwc), GetFileName(pathwc), filefolder, flags, deep);
1571 }
1572 
1573 bool DeleteDeepWildcardsX(const char *path, const char *namewc, bool filefolder, EXT_FILE_FLAGS flags, bool deep)
1574 {
1575 	FindFile ff(AppendFileName(path, "*.*"));
1576 	while(ff) {
1577 		String name = ff.GetName();
1578 		String full = AppendFileName(path, name);
1579 		if (PatternMatch(namewc, name)) {
1580 			if (ff.IsFolder() && !filefolder) {
1581 				if (!DeleteFolderDeepX(full, flags))
1582 					return false;
1583 			} else if (ff.IsFile() && filefolder) {
1584 				if (!FileDeleteX(full, flags))
1585 					return false;
1586 			}
1587 		} else if(deep && ff.IsFolder()) {
1588 			if (!DeleteDeepWildcardsX(full, namewc, filefolder, flags))
1589 				return false;
1590 		}
1591 		ff.Next();
1592 	}
1593 	return true;
1594 }
1595 
1596 bool DeleteFolderDeepWildcardsX(const char *path, EXT_FILE_FLAGS flags)
1597 {
1598 	return DeleteDeepWildcardsX(path, false, flags, true);
1599 }
1600 
1601 bool DeleteFolderDeepWildcardsX(const char *path, const char *name, EXT_FILE_FLAGS flags)
1602 {
1603 	return DeleteDeepWildcardsX(path, name, false, flags, true);
1604 }
1605 
1606 bool DeleteFileDeepWildcardsX(const char *path, EXT_FILE_FLAGS flags)
1607 {
1608 	return DeleteDeepWildcardsX(path, true, flags, true);
1609 }
1610 
1611 bool DeleteFileWildcardsX(const char *path, EXT_FILE_FLAGS flags)
1612 {
1613 	return DeleteDeepWildcardsX(path, true, flags, false);
1614 }
1615 
1616 bool DeleteFolderDeepX_Folder(const char *dir, EXT_FILE_FLAGS flags)
1617 {
1618 	FindFile ff(AppendFileName(dir, "*.*"));
1619 	while(ff) {
1620 		String name = ff.GetName();
1621 		String p = AppendFileName(dir, name);
1622 		if(ff.IsFile())
1623 			FileDeleteX(p, flags);
1624 		else
1625 		if(ff.IsFolder())
1626 			DeleteFolderDeepX_Folder(p, flags);
1627 		ff.Next();
1628 	}
1629 	return FolderDeleteX(dir, flags);
1630 }
1631 
1632 bool DeleteFolderDeepX(const char *path, EXT_FILE_FLAGS flags)
1633 {
1634 	if (flags & USE_TRASH_BIN)
1635 		return FileToTrashBin(path);
1636 	return DeleteFolderDeepX_Folder(path, flags);
1637 }
1638 
1639 bool RenameDeepWildcardsX(const char *path, const char *namewc, const char *newname, bool forfile, bool forfolder, EXT_FILE_FLAGS flags)
1640 {
1641 	FindFile ff(AppendFileName(path, "*.*"));
1642 	while(ff) {
1643 		String name = ff.GetName();
1644 		String full = AppendFileName(path, name);
1645 		if(ff.IsFolder()) {
1646 			if (!RenameDeepWildcardsX(full, namewc, newname, forfile, forfolder, flags))
1647 				return false;
1648 		}
1649 		if (PatternMatch(namewc, name)) {
1650 			if ((ff.IsFolder() && forfolder) || (ff.IsFile() && forfile)) {
1651 				if (!FileMoveX(full, AppendFileName(path, newname), flags))
1652 					return false;
1653 			}
1654 		}
1655 		ff.Next();
1656 	}
1657 	return true;
1658 }
1659 
1660 void DirectoryCopy_Each(const char *dir, const char *newPlace, String relPath, bool replaceOnlyNew, String filesToExclude, String &errorList)
1661 {
1662 	String dirPath = AppendFileName(dir, relPath);
1663 	String newPath = AppendFileName(newPlace, relPath);
1664 	LOG(dirPath);
1665 	LOG(newPath);
1666 	LOG (AppendFileName(dirPath, "*.*"));
1667 	FindFile ff(AppendFileName(dirPath, "*.*"));
1668 	while(ff) {
1669 		String name = ff.GetName();
1670 		String newFullPath = AppendFileName(newPath, name);
1671 		if(ff.IsFile()) {
1672 			bool copy = !replaceOnlyNew;
1673 			if (replaceOnlyNew) {
1674 				Time newPathTime = FileGetTime(newFullPath);
1675 				if (IsNull(newPathTime) || (newPathTime < Time(ff.GetLastWriteTime())))
1676 					copy = true;
1677 			}
1678 			if (copy) {
1679 				if (!PatternMatchMulti(filesToExclude, name)) {
1680 					if (!FileCopy(ff.GetPath(), newFullPath))
1681 						errorList << "\n" << Format(t_("Impossible to copy '%s' to '%s': %s"), ff.GetPath(), newFullPath, GetLastErrorMessage());
1682 				}
1683 			}
1684 		} else if (ff.IsFolder()) {
1685 			if (!DirectoryExists(newFullPath)) {
1686 				if (!DirectoryCreate(newFullPath))
1687 					errorList << "\n" << Format(t_("Impossible to create directory '%s': %s"), newFullPath, GetLastErrorMessage());
1688 			}
1689 			DirectoryCopy_Each(dir, newPlace, AppendFileName(relPath, name), replaceOnlyNew, filesToExclude, errorList);
1690 		}
1691 		ff.Next();
1692 	}
1693 }
1694 
1695 void DirectoryCopyX(const char *dir, const char *newPlace, bool replaceOnlyNew, String filesToExclude, String &errorList) {
1696 	if (!DirectoryExists(dir))
1697 		errorList << "\n" << Format(t_("Directory '%s' does not exist"), dir);
1698 	else
1699 		DirectoryCopy_Each(dir, newPlace, "", replaceOnlyNew, filesToExclude, errorList);
1700 }
1701 
1702 bool FolderIsEmpty(const char *path) {
1703 	FindFile ff(AppendFileName(path, "*.*"));
1704 	while(ff) {
1705 		if(ff.IsFile() || ff.IsFolder())
1706 			return false;
1707 		ff.Next();
1708 	}
1709 	return true;
1710 }
1711 
1712 #if defined(__MINGW32__)
1713 	#define _SH_DENYNO 0x40
1714 #endif
1715 
1716 int FileCompare(const char *path1, const char *path2) {
1717 	int fp1;
1718 #ifdef PLATFORM_POSIX
1719 	fp1 = open(ToSystemCharset(path1), O_RDONLY, S_IWRITE|S_IREAD);
1720 #else
1721 	fp1 = _wsopen(ToSystemCharsetW(path1), O_RDONLY|O_BINARY, _SH_DENYNO, _S_IREAD|_S_IWRITE);
1722 #endif
1723 	if (fp1 == -1)
1724 		return -2;
1725 	int fp2;
1726 #ifdef PLATFORM_POSIX
1727 	fp2 = open(ToSystemCharset(path2), O_RDONLY, S_IWRITE|S_IREAD);
1728 #else
1729 	fp2 = _wsopen(ToSystemCharsetW(path2), O_RDONLY|O_BINARY, _SH_DENYNO, _S_IREAD|_S_IWRITE);
1730 #endif
1731 	if (fp2 == -1) {
1732 		close(fp1);
1733 		return -2;
1734 	}
1735 	Buffer <byte> c1(8192), c2(8192);
1736 	int ret = -1;
1737 	while (true) {
1738 		int n1 = read(fp1, c1, 8192);
1739 		int n2 = read(fp2, c2, 8192);
1740 		if (n1 == -1 || n2 == -1) {
1741 			ret = -2;
1742 			break;
1743 		}
1744 		if (n1 != n2)
1745 			break;
1746 		if (memcmp(c1, c2, n1) != 0)
1747 			break;
1748 		if (n1 == 0) {
1749 			ret = 1;
1750 			break;
1751 		}
1752 	}
1753 #ifdef PLATFORM_POSIX
1754 	if (-1 == close(fp1))
1755 		ret = -2;
1756 	if (-1 == close(fp2))
1757 		ret = -2;
1758 #else
1759 	if (-1 == _close(fp1))
1760 		ret = -2;
1761 	if (-1 == _close(fp2))
1762 		ret = -2;
1763 #endif
1764 	return ret;
1765 }
1766 
1767 int64 FindStringInFile(const char *file, const String text, int64 pos0) {
1768 #ifdef PLATFORM_POSIX
1769 	FILE *fp = fopen(file, "rb");
1770 #else
1771 	FILE *fp = _wfopen(String(file).ToWString(), L"rb");
1772 #endif
1773 	if (fp != NULL) {
1774 		int64 pos = 0;
1775 		if (pos0 > 0) {
1776 			pos = pos0;
1777 			if (0 == fseek(fp, long(pos0), SEEK_SET))
1778 				return -2;
1779 		}
1780 		int i = 0, c;
1781 		for (; (c = fgetc(fp)) != EOF; pos++) {
1782 			if (c == text[i]) {
1783 				++i;
1784 				if (i == text.GetCount())
1785 					return pos - i;
1786 			} else {
1787 				if (i != 0)
1788 					if (0 == fseek(fp, -(i-1), SEEK_CUR))
1789 						return -2;
1790 				i = 0;
1791 			}
1792 		}
1793 		fclose(fp);
1794 	} else
1795 		return -2;
1796 	return -1;
1797 }
1798 
1799 bool MatchPathName(const char *name, const Vector<String> &cond, const Vector<String> &ext) {
1800 	for (int i = 0; i < cond.GetCount(); ++i) {
1801 		if(!PatternMatch(cond[i], name))
1802 			return false;
1803 	}
1804 	for (int i = 0; i < ext.GetCount(); ++i) {
1805 		if(PatternMatch(ext[i], name))
1806 			return false;
1807 	}
1808 	return true;
1809 }
1810 
1811 void SearchFile_Each(String dir, const Vector<String> &condFiles, const Vector<String> &condFolders,
1812 								 const Vector<String> &extFiles,  const Vector<String> &extFolders,
1813 								 const String text, Vector<String> &files, Vector<String> &errorList) {
1814 	FindFile ff;
1815 	if (ff.Search(AppendFileName(dir, "*"))) {
1816 		do {
1817 			if(ff.IsFile()) {
1818 				String name = AppendFileName(dir, ff.GetName());
1819 				if (MatchPathName(ff.GetName(), condFiles, extFiles)) {
1820 					if (text.IsEmpty())
1821 						files.Add(name);
1822 					else {
1823 						switch(FindStringInFile(name, text)) {
1824 						case 1:	files.Add(name);
1825 								break;
1826 						case -1:errorList.Add(AppendFileName(dir, ff.GetName()) + String(": ") +
1827 																	t_("Impossible to open file"));
1828 								break;
1829 						}
1830 					}
1831 				}
1832 			} else if(ff.IsDirectory()) {
1833 				String folder = ff.GetName();
1834 				if (folder != "." && folder != "..") {
1835 					String name = AppendFileName(dir, folder);
1836 					if (MatchPathName(name, condFolders, extFolders))
1837 						SearchFile_Each(name, condFiles, condFolders, extFiles, extFolders, text, files, errorList);
1838 				}
1839 			}
1840 		} while (ff.Next());
1841 	}
1842 }
1843 
1844 Vector<String> SearchFile(String dir, const Vector<String> &condFiles, const Vector<String> &condFolders,
1845 								 const Vector<String> &extFiles,  const Vector<String> &extFolders,
1846 								 const String text, Vector<String> &errorList) {
1847 	Vector<String> files;
1848 	errorList.Clear();
1849 
1850 	SearchFile_Each(dir, condFiles, condFolders, extFiles, extFolders, text, files, errorList);
1851 
1852 	return files;
1853 }
1854 
1855 Vector<String> SearchFile(String dir, String condFile, String text, Vector<String> &errorList)
1856 {
1857 	Vector<String> condFiles, condFolders, extFiles, extFolders, files;
1858 	errorList.Clear();
1859 
1860 	condFiles.Add(condFile);
1861 	SearchFile_Each(dir, condFiles, condFolders, extFiles, extFolders, text, files, errorList);
1862 
1863 	return files;
1864 }
1865 
1866 Vector<String> SearchFile(String dir, String condFile, String text)
1867 {
1868 	Vector<String> errorList;
1869 	Vector<String> condFiles, condFolders, extFiles, extFolders, files;
1870 
1871 	condFiles.Add(condFile);
1872 	SearchFile_Each(dir, condFiles, condFolders, extFiles, extFolders, text, files, errorList);
1873 
1874 	return files;
1875 }
1876 
1877 bool fileDataSortAscending;
1878 char fileDataSortBy;
1879 
1880 FileDataArray::FileDataArray(bool use, int _fileFlags)
1881 {
1882 	Clear();
1883 	fileDataSortAscending = true;
1884 	fileDataSortBy = 'n';
1885 	useId = use;
1886 	fileFlags = _fileFlags;
1887 }
1888 
1889 bool FileDataArray::Init(String , FileDataArray &orig, FileDiffArray &diff)
1890 {
1891 	basePath = orig.basePath;
1892 	fileCount = orig.fileCount;
1893 	folderCount = orig.folderCount;
1894 	fileSize = orig.fileSize;
1895 	useId = orig.useId;
1896 	fileList.SetCount(orig.GetCount());
1897 	for (int i = 0; i < orig.GetCount(); ++i)
1898 		fileList[i] = orig[i];
1899 
1900 	for (int i = 0; i < diff.GetCount(); ++i) {
1901 		long id;
1902 		if (diff[i].action != 'n') {
1903 		 	id = Find(diff[i].relPath, diff[i].fileName, diff[i].isFolder);
1904 			if (id < 0)
1905 				return false;
1906 		}
1907 		switch(diff[i].action) {
1908 		case 'u':
1909 			fileList[id].id = diff[i].idMaster;
1910 			fileList[id].length = diff[i].lengthMaster;
1911 			fileList[id].t = diff[i].tMaster;
1912 			break;
1913 		case 'n':
1914 			fileList.Add(FileData(diff[i].isFolder, diff[i].fileName, diff[i].relPath, diff[i].lengthMaster, diff[i].tMaster, diff[i].idMaster));
1915 			if (diff[i].isFolder)
1916 				folderCount++;
1917 			else
1918 				fileCount++;
1919 			break;
1920 		case 'd':
1921 			fileList.Remove(id);
1922 			if (diff[i].isFolder)
1923 				folderCount--;
1924 			else
1925 				fileCount--;
1926 			break;
1927 			break;
1928 		case 'p':
1929 			SetLastError(t_("Problem found"));		// To Fix
1930 			//return false;
1931 		}
1932 	}
1933 	return true;
1934 }
1935 
1936 void FileDataArray::Clear()
1937 {
1938 	fileList.Clear();
1939 	errorList.Clear();
1940 	fileCount = folderCount = 0;
1941 	fileSize = 0;
1942 	basePath = "";
1943 }
1944 
1945 bool FileDataArray::Search(String dir, String condFile, bool recurse, String text)
1946 {
1947 	Clear();
1948 	if (fileFlags & BROWSE_LINKS) {
1949 		if (IsSymLink(dir))
1950 			dir = basePath = GetSymLinkPath(dir);
1951 		else
1952 			basePath = dir;
1953 	} else
1954 		basePath = dir;
1955 	Search_Each(dir, condFile, recurse, text);
1956 	return errorList.IsEmpty();
1957 }
1958 
1959 void FileDataArray::Search_Each(String dir, String condFile, bool recurse, String text)
1960 {
1961 	FindFile ff;
1962 	if (ff.Search(AppendFileName(dir, condFile))) {
1963 		do {
1964 			if(ff.IsFile()) {
1965 				String p = AppendFileName(dir, ff.GetName());
1966 				//if (ff.IsSymLink()) {
1967 				//	p = p;
1968 				//}
1969 				/*
1970 					fileList.Add(FileData(true, ff.GetName(), GetRelativePath(dir), 0, ff.GetLastWriteTime(), 0));
1971 					folderCount++;
1972 					if (recurse)
1973 						Search_Each(p, condFile, recurse, text);
1974 				} else */ if (text.IsEmpty()) {
1975 					uint64 len = ff.GetLength();
1976 					fileList.Add(FileData(false, ff.GetName(), GetRelativePath(dir), len, ff.GetLastWriteTime(),
1977 											(useId && len > 0) ? GetFileId(p) : 0));
1978 					fileCount++;
1979 					fileSize += len;
1980 				} else {
1981 					FILE *fp = fopen(p, "r");
1982 					if (fp != NULL) {
1983 						int i = 0, c;
1984 						while ((c = fgetc(fp)) != EOF) {
1985 							if (c == text[i]) {
1986 								++i;
1987 								if (i == text.GetCount()) {
1988 									uint64 len = ff.GetLength();
1989 									fileList.Add(FileData(false, ff.GetName(), GetRelativePath(dir), len, ff.GetLastWriteTime(), useId ? GetFileId(p) : 0));
1990 									fileCount++;
1991 									fileSize += len;
1992 									break;
1993 								}
1994 							} else {
1995 								if (i != 0)
1996 									fseek(fp, -(i-1), SEEK_CUR);
1997 								i = 0;
1998 							}
1999 						}
2000 						fclose(fp);
2001 					} else
2002 						errorList.Add(AppendFileName(dir, ff.GetName()) + String(": ") + t_("Impossible to open file"));
2003 				}
2004 			}
2005 		} while (ff.Next());
2006 	}
2007 	ff.Search(AppendFileName(dir, "*"));
2008 	do {
2009 		String name = ff.GetName();
2010 		if(ff.IsDirectory() && name != "." && name != "..") {
2011 			String p = AppendFileName(dir, name);
2012 			fileList.Add(FileData(true, name, GetRelativePath(dir), 0, ff.GetLastWriteTime(), 0));
2013 			folderCount++;
2014 			if (recurse)
2015 				Search_Each(p, condFile, recurse, text);
2016 		}
2017 	} while (ff.Next());
2018 }
2019 
2020 int64 FileDataArray::GetFileId(String fileName)
2021 {
2022 	int64 id = -1;
2023 #ifdef PLATFORM_POSIX
2024 	FILE *fp = fopen(fileName, "rb");
2025 #else
2026 	FILE *fp = _wfopen(fileName.ToWString(), L"rb");
2027 #endif
2028 	if (fp != NULL) {
2029 		int c;
2030 		long i = 0;
2031 		while ((c = fgetc(fp)) != EOF) {
2032 			id += c*i;
2033 			i++;
2034 		}
2035 		fclose(fp);
2036 	}
2037 	return id;
2038 }
2039 
2040 void FileDataArray::SortByName(bool ascending)
2041 {
2042 	fileDataSortBy = 'n';
2043 	fileDataSortAscending = ascending;
2044 	Sort(fileList);
2045 }
2046 void FileDataArray::SortByDate(bool ascending)
2047 {
2048 	fileDataSortBy = 'd';
2049 	fileDataSortAscending = ascending;
2050 	Sort(fileList);
2051 }
2052 void FileDataArray::SortBySize(bool ascending)
2053 {
2054 	fileDataSortBy = 's';
2055 	fileDataSortAscending = ascending;
2056 	Sort(fileList);
2057 }
2058 
2059 bool operator<(const FileData& a, const FileData& b)
2060 {
2061 	if ((a.isFolder && b.isFolder) || (!a.isFolder && !b.isFolder)) {
2062 		switch (fileDataSortBy) {
2063 		case 'n':	{
2064 						bool ais = IsDigit(a.fileName[0]);
2065 						bool bis = IsDigit(b.fileName[0]);
2066 						if (ais && bis)
2067 							return atoi(a.fileName) < atoi(b.fileName);
2068 						if (ais)
2069 							return true;
2070 						if (bis)
2071 							return false;
2072 					}
2073 #ifdef PLATFORM_POSIX
2074 					return (a.fileName < b.fileName)&fileDataSortAscending;
2075 #else
2076 					return (ToLower(a.fileName) < ToLower(b.fileName))&fileDataSortAscending;
2077 #endif
2078 		case 'd':	return (a.t < b.t)&fileDataSortAscending;
2079 		default:	return (a.length < b.length)&fileDataSortAscending;
2080 		}
2081 	} else
2082 		return a.isFolder;
2083 }
2084 
2085 bool CheckFileData(FileData &data, String &, String &, String &lowrelFileName, String &lowfileName, bool isFolder) {
2086 	if (data.isFolder == isFolder) {
2087 		if (ToLower(data.fileName) == lowfileName) {
2088 			if (ToLower(data.relFilename) == lowrelFileName)
2089 	    		return true;
2090 		}
2091 	}
2092 	return false;
2093 }
2094 
2095 int FileDataArray::Find(String &relFileName, String &fileName, bool isFolder) {
2096 	String lowrelFileName = ToLower(relFileName);
2097 	String lowfileName = ToLower(fileName);
2098 	for (int i = 0; i < fileList.GetCount(); ++i) {
2099 		if (CheckFileData(fileList[i], relFileName, fileName, lowrelFileName, lowfileName, isFolder))
2100 		    return i;
2101 	}
2102 	return -1;
2103 }
2104 
2105 /*
2106 int FileDataArray::Find(FileDataArray &data, int id) {
2107 	return Find(data[id].relFilename, data[id].fileName, data[id].isFolder);
2108 }
2109 */
2110 
2111 int FileDataArray::Find(FileDataArray &data, int id) {
2112 	String lowrelFileName = ToLower(data[id].relFilename);
2113 	String lowfileName = ToLower(data[id].fileName);
2114 	bool isFolder = data[id].isFolder;
2115 
2116 	int num = fileList.GetCount();
2117 	if (num == 0)
2118 		return -1;
2119 	if (num == 1) {
2120 		if (CheckFileData(fileList[0], data[id].relFilename, data[id].fileName, lowrelFileName, lowfileName, isFolder))
2121 			return 0;
2122 		else
2123 			return -1;
2124 	}
2125 	int down, up;
2126 	down = id < num-1 ? id : num-2;
2127 	up = down + 1;
2128 	while (down >= 0 || up < num) {
2129 		if (down >= 0) {
2130 			if (CheckFileData(fileList[down], data[id].relFilename, data[id].fileName, lowrelFileName, lowfileName, isFolder))
2131 		    	return down;
2132 			down--;
2133 		}
2134 		if (up < num) {
2135 			if (CheckFileData(fileList[up], data[id].relFilename, data[id].fileName, lowrelFileName, lowfileName, isFolder))
2136 		    	return up;
2137 			up++;
2138 		}
2139 	}
2140 	return -1;
2141 }
2142 
2143 String FileDataArray::GetFileText() {
2144 	String ret;
2145 
2146 	for (int i = 0; i < fileList.GetCount(); ++i) {
2147 		ret << fileList[i].relFilename << "; ";
2148 		ret << fileList[i].fileName << "; ";
2149 		ret << fileList[i].isFolder << "; ";
2150 		ret << fileList[i].length << "; ";
2151 		ret << fileList[i].t << "; ";
2152 		ret << fileList[i].id << "; ";
2153 		ret << "\n";
2154 	}
2155 	return ret;
2156 }
2157 
2158 bool FileDataArray::SaveFile(const char *fileName) {
2159 	return Upp::SaveFile(fileName, GetFileText());
2160 }
2161 
2162 bool FileDataArray::AppendFile(const char *fileName) {
2163 	return Upp::AppendFile(fileName, GetFileText());
2164 }
2165 
2166 bool FileDataArray::LoadFile(const char *fileName)
2167 {
2168 	Clear();
2169 	StringParse in = Upp::LoadFile(fileName);
2170 
2171 	if (in == "")
2172 		return false;
2173 
2174 	int numData = in.Count("\n");
2175 	fileList.SetCount(numData);
2176 	for (int row = 0; row < numData; ++row) {
2177 		fileList[row].relFilename = in.GetText(";");
2178 		fileList[row].fileName = in.GetText(";");
2179 		fileList[row].isFolder = in.GetText(";") == "true" ? true : false;
2180 		if (fileList[row].isFolder)
2181 			folderCount++;
2182 		else
2183 			fileCount++;
2184 		fileList[row].length = in.GetUInt64(";");
2185 		struct Upp::Time t;
2186 		StrToTime(t, in.GetText(";"));
2187 		fileList[row].t = t;
2188 		fileList[row].id = in.GetUInt64(";");
2189 	}
2190 	return true;
2191 }
2192 
2193 String FileDataArray::GetRelativePath(const String &fullPath) {
2194 	if (basePath != fullPath.Left(basePath.GetCount()))
2195 		return "";
2196 	return fullPath.Mid(basePath.GetCount());
2197 }
2198 
2199 int64 GetDirectoryLength(const char *directoryName) {
2200 	FileDataArray files;
2201 	files.Search(directoryName, "*.*", true);
2202 	return files.GetSize();
2203 }
2204 
2205 int64 GetLength(const char *fileName) {
2206 	if (FileExists(fileName))
2207 		return GetFileLength(fileName);
2208 	else
2209 		return GetDirectoryLength(fileName);
2210 }
2211 
2212 FileDiffArray::FileDiffArray() {
2213 	Clear();
2214 }
2215 
2216 void FileDiffArray::Clear()
2217 {
2218 	diffList.Clear();
2219 }
2220 
2221 // True if equal
2222 bool FileDiffArray::Compare(FileDataArray &master, FileDataArray &secondary, const String folderFrom,
2223 						 Vector<String> &excepFolders, Vector<String> &excepFiles, int sensSecs) {
2224 	if (master.GetCount() == 0) {
2225 		if (secondary.GetCount() == 0)
2226 			return true;
2227 		else
2228 			return false;
2229 	} else if (secondary.GetCount() == 0)
2230 		return false;
2231 
2232 	bool equal = true;
2233 	diffList.Clear();
2234 	Vector<bool> secReviewed;
2235 	secReviewed.SetCount(secondary.GetCount(), false);
2236 
2237 	for (int i = 0; i < master.GetCount(); ++i) {
2238 		bool cont = true;
2239 		if (master[i].isFolder) {
2240 			String fullfolder = AppendFileName(AppendFileName(folderFrom, master[i].relFilename), master[i].fileName);
2241 			for (int iex = 0; iex < excepFolders.GetCount(); ++iex)
2242 				if (PatternMatch(excepFolders[iex] + "*", fullfolder)) {// Subfolders included
2243 					cont = false;
2244 					break;
2245 				}
2246 		} else {
2247 			String fullfolder = AppendFileName(folderFrom, master[i].relFilename);
2248 			for (int iex = 0; iex < excepFolders.GetCount(); ++iex)
2249 				if (PatternMatch(excepFolders[iex] + "*", fullfolder)) {
2250 					cont = false;
2251 					break;
2252 				}
2253 			for (int iex = 0; iex < excepFiles.GetCount(); ++iex)
2254 				if (PatternMatch(excepFiles[iex], master[i].fileName)) {
2255 					cont = false;
2256 					break;
2257 				}
2258 		}
2259 		if (cont) {
2260 			int idSec = secondary.Find(master, i);
2261 			if (idSec >= 0) {
2262 				bool useId = master.UseId() && secondary.UseId();
2263 				secReviewed[idSec] = true;
2264 				if (master[i].isFolder)
2265 					;
2266 				else if ((useId && (master[i].id == secondary[idSec].id)) ||
2267 						 (!useId && (master[i].length == secondary[idSec].length) &&
2268 						 			 (abs(master[i].t - secondary[idSec].t) <= sensSecs)))
2269 					;
2270 				else {
2271 					equal = false;
2272 					FileDiffData &f = diffList.Add();
2273 					//bool isf = f.isFolder = master[i].isFolder;
2274 					f.relPath = master[i].relFilename;
2275 					String name = f.fileName = master[i].fileName;
2276 					f.idMaster = master[i].id;
2277 					f.idSecondary = secondary[idSec].id;
2278 					f.tMaster = master[i].t;
2279 					f.tSecondary = secondary[idSec].t;
2280 					f.lengthMaster = master[i].length;
2281 					f.lengthSecondary = secondary[idSec].length;
2282 					if (master[i].t > secondary[idSec].t)
2283 						f.action = 'u';
2284 					else
2285 						f.action = 'p';
2286 				}
2287 			} else {
2288 				equal = false;
2289 				FileDiffData &f = diffList.Add();
2290 				f.isFolder = master[i].isFolder;
2291 				f.relPath = master[i].relFilename;
2292 				f.fileName = master[i].fileName;
2293 				f.idMaster = master[i].id;
2294 				f.idSecondary = 0;
2295 				f.tMaster = master[i].t;
2296 				f.tSecondary = Null;
2297 				f.lengthMaster = master[i].length;
2298 				f.lengthSecondary = 0;
2299 				f.action = 'n';
2300 			}
2301 		}
2302 	}
2303 	for (int i = 0; i < secReviewed.GetCount(); ++i) {
2304 		if (!secReviewed[i]) {
2305 			bool cont = true;
2306 			if (secondary[i].isFolder) {
2307 				String fullfolder = AppendFileName(AppendFileName(folderFrom, secondary[i].relFilename), secondary[i].fileName);
2308 				for (int iex = 0; iex < excepFolders.GetCount(); ++iex)
2309 					if (PatternMatch(excepFolders[iex] + "*", fullfolder)) {
2310 						cont = false;
2311 						break;
2312 					}
2313 			} else {
2314 				String fullfolder = AppendFileName(folderFrom, secondary[i].relFilename);
2315 				for (int iex = 0; iex < excepFolders.GetCount(); ++iex)
2316 					if (PatternMatch(excepFolders[iex] + "*", fullfolder)) {
2317 						cont = false;
2318 						break;
2319 					}
2320 				for (int iex = 0; iex < excepFiles.GetCount(); ++iex)
2321 					if (PatternMatch(excepFiles[iex], secondary[i].fileName)) {
2322 						cont = false;
2323 						break;
2324 					}
2325 			}
2326 			if (cont) {
2327 				equal = false;
2328 				FileDiffData &f = diffList.Add();
2329 				f.isFolder = secondary[i].isFolder;
2330 				f.relPath = secondary[i].relFilename;
2331 				f.fileName = secondary[i].fileName;
2332 				f.idMaster = 0;
2333 				f.idSecondary = secondary[i].id;
2334 				f.tMaster = Null;
2335 				f.tSecondary = secondary[i].t;
2336 				f.lengthMaster = 0;
2337 				f.lengthSecondary = secondary[i].length;
2338 				f.action = 'd';
2339 			}
2340 		}
2341 	}
2342 	return equal;
2343 }
2344 
2345 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
2346 String WinLastError() {
2347 	LPVOID lpMsgBuf;
2348 	String ret;
2349 
2350     FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
2351         		  FORMAT_MESSAGE_IGNORE_INSERTS,
2352         		  NULL, ::GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
2353         		  reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL);
2354   	ret = static_cast<char *>(lpMsgBuf);
2355   	LocalFree(lpMsgBuf);
2356 	return ret;
2357 }
2358 #endif
2359 
2360 bool FileDiffArray::Apply(String toFolder, String fromFolder, EXT_FILE_FLAGS flags)
2361 {
2362 	for (int i = 0; i < diffList.GetCount(); ++i) {
2363 		bool ok = true;
2364 		String dest = AppendFileName(toFolder,
2365 									 AppendFileName(diffList[i].relPath, diffList[i].fileName));
2366 		if (diffList[i].action == 'u' || diffList[i].action == 'd') {
2367 			if (diffList[i].isFolder) {
2368 				if (DirectoryExists(dest)) {
2369 					if (!SetReadOnly(dest, false))
2370 						ok = false;
2371 				}
2372 			} else {
2373 				if (FileExists(dest)) {
2374 					if (!SetReadOnly(dest, false))
2375 						ok = false;
2376 				}
2377 			}
2378 		}
2379 		if (!ok) {
2380 			String strError = t_("Not possible to modify ") + dest;
2381 			SetLastError(strError);
2382 			//return false;
2383 		}
2384 
2385 		switch (diffList[i].action) {
2386 		case 'n': case 'u':
2387 			if (diffList[i].isFolder) {
2388 				if (!DirectoryExists(dest)) {
2389 					ok = DirectoryCreate(dest);	////////////////////////////////////////////////////////////////////////////////////////
2390 				}
2391 			} else {
2392 				if (FileExists(dest)) {
2393 					if (!SetReadOnly(dest, false))
2394 						ok = false;
2395 				}
2396 				if (ok) {
2397 					ok = FileCopy(AppendFileName(fromFolder, FormatInt(i)), dest);
2398 					diffList[i].tSecondary = diffList[i].tMaster;
2399 				}
2400 			}
2401 
2402 			if (!ok) {
2403 				String strError = t_("Not possible to create ") + dest;
2404 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
2405 			  	strError += ". " + WinLastError();
2406 #endif
2407 				SetLastError(strError);
2408 				//return false;
2409 			}
2410 			break;
2411 		case 'd':
2412 			if (diffList[i].isFolder) {
2413 				if (DirectoryExists(dest))
2414 					ok = DeleteFolderDeep(dest);	// Necessary to add the "X"
2415 			} else {
2416 				if (FileExists(dest))
2417 					ok = FileDeleteX(dest, flags);
2418 			}
2419 			if (!ok) {
2420 				String strError = t_("Not possible to delete") + String(" ") + dest;
2421 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
2422 			  	strError += ". " + WinLastError();
2423 #endif
2424 				SetLastError(strError);
2425 				//return false;
2426 			}
2427 			break;
2428 		case 'p':
2429 			SetLastError(t_("There was a problem in the copy"));
2430 			//return false;
2431 			break;
2432 		}
2433 	}
2434 	return true;
2435 }
2436 
2437 bool FileDiffArray::SaveFile(const char *fileName)
2438 {
2439 	return Upp::SaveFile(fileName, ToString());
2440 }
2441 
2442 String FileDiffArray::ToString()
2443 {
2444 	String ret;
2445 
2446 	for (int i = 0; i < diffList.GetCount(); ++i) {
2447 		ret << diffList[i].action << "; ";
2448 		ret << diffList[i].isFolder << "; ";
2449 		ret << diffList[i].relPath << "; ";
2450 		ret << diffList[i].fileName << "; ";
2451 		ret << diffList[i].idMaster << "; ";
2452 		ret << diffList[i].idSecondary << "; ";
2453 		ret << diffList[i].tMaster << "; ";
2454 		ret << diffList[i].tSecondary << "; ";
2455 		ret << diffList[i].lengthMaster << "; ";
2456 		ret << diffList[i].lengthSecondary << "; ";
2457 		ret << "\n";
2458 	}
2459 	return  ret;
2460 }
2461 
2462 bool FileDiffArray::LoadFile(const char *fileName)
2463 {
2464 	Clear();
2465 	StringParse in = Upp::LoadFile(fileName);
2466 
2467 	if (in == "")
2468 		return false;
2469 
2470 	int numData = in.Count("\n");
2471 	diffList.SetCount(numData);
2472 	for (int row = 0; row < numData; ++row) {
2473 		diffList[row].action = TrimLeft(in.GetText(";"))[0];
2474 		diffList[row].isFolder = in.GetText(";") == "true" ? true : false;
2475 		diffList[row].relPath = in.GetText(";");
2476 		diffList[row].fileName = in.GetText(";");
2477 		diffList[row].idMaster = in.GetUInt64(";");
2478 		diffList[row].idSecondary = in.GetUInt64(";");
2479 		struct Upp::Time t;
2480 		StrToTime(t, in.GetText(";"));
2481 		diffList[row].tMaster = t;
2482 		StrToTime(t, in.GetText(";"));
2483 		diffList[row].tSecondary = t;
2484 		diffList[row].lengthMaster = in.GetUInt64(";");
2485 		diffList[row].lengthSecondary = in.GetUInt64(";");
2486 	}
2487 	return true;
2488 }
2489 
2490 
2491 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
2492 Value GetVARIANT(VARIANT &result)
2493 {
2494 	Value ret;
2495 	switch(result.vt) {
2496 	case VT_EMPTY:
2497 	case VT_NULL:
2498 	case VT_BLOB:
2499 		break;
2500 	case VT_BOOL:
2501          ret = result.boolVal;// ? "true" : "false";
2502          break;
2503    	case VT_I2:
2504          ret = result.iVal;
2505          break;
2506 	case VT_I4:
2507 		ret = static_cast<int64>(result.lVal);
2508 		break;
2509 	case VT_R4:
2510 		ret = AsString(result.fltVal);
2511 		break;
2512 	case VT_R8:
2513 		ret = AsString(result.dblVal);
2514 		break;
2515 	case VT_BSTR:
2516 		ret = WideToString(result.bstrVal);
2517 		break;
2518 	case VT_LPSTR:
2519          //ret = result.pszVal;
2520          ret = "Unknown VT_LPSTR";
2521          break;
2522     case VT_DATE:
2523   		SYSTEMTIME stime;
2524      	VariantTimeToSystemTime(result.date, &stime);
2525      	{
2526 	     	Time t;
2527 	     	t.day    = static_cast<Upp::byte>(stime.wDay);
2528 	     	t.month  = static_cast<Upp::byte>(stime.wMonth);
2529 	     	t.year   = stime.wYear;
2530 	     	t.hour   = static_cast<Upp::byte>(stime.wHour);
2531 	     	t.minute = static_cast<Upp::byte>(stime.wMinute);
2532 	     	t.second = static_cast<Upp::byte>(stime.wSecond);
2533 			ret = t;
2534      	}
2535     	break;
2536  	case VT_CF:
2537      	ret = "(Clipboard format)";
2538      	break;
2539 	}
2540 	return ret;
2541 }
2542 
2543 String WideToString(LPCWSTR wcs, int len) {
2544 	if (len == -1) {
2545 		len = WideCharToMultiByte(CP_UTF8, 0, wcs, len, nullptr, 0, nullptr, nullptr);
2546 		if (len == 0)
2547 			return Null;
2548 	}
2549 	Buffer<char> w(len);
2550 	WideCharToMultiByte(CP_UTF8, 0, wcs, len, w, len, nullptr, nullptr);
2551 	return ~w;
2552 }
2553 
2554 #endif
2555 
2556 #if defined(PLATFORM_WIN32) || defined (PLATFORM_WIN64)
2557 
2558 Dl::Dl() {
2559 	hinstLib = 0;
2560 }
2561 
2562 Dl::~Dl() {
2563 	if (hinstLib)
2564 		if (FreeLibrary(hinstLib) == 0)
2565 			LOG(t_("Dl cannot be released"));
2566 }
2567 
2568 #ifndef LOAD_IGNORE_CODE_AUTHZ_LEVEL
2569 	#define LOAD_IGNORE_CODE_AUTHZ_LEVEL	0x00000010
2570 #endif
2571 
2572 bool Dl::Load(const String &fileDll) {
2573 	if (hinstLib)
2574 		if (FreeLibrary(hinstLib) == 0)
2575 			return false;
2576 
2577 	hinstLib = LoadLibraryEx(TEXT(fileDll), nullptr, LOAD_IGNORE_CODE_AUTHZ_LEVEL);
2578 	if (!hinstLib)
2579 		return false;
2580 	return true;
2581 }
2582 
2583 void *Dl::GetFunction(const String &functionName) {
2584 	if (!hinstLib)
2585 		return nullptr;
2586 	return reinterpret_cast<void *>(GetProcAddress(hinstLib, functionName));
2587 }
2588 
2589 #else
2590 
2591 #include <dlfcn.h>
2592 
2593 Dl::Dl() {
2594 	hinstLib = 0;
2595 }
2596 
2597 Dl::~Dl() {
2598 	if (hinstLib)
2599 		if (dlclose(hinstLib) == 0)
2600 			;	// Dl cannot be released
2601 }
2602 
2603 bool Dl::Load(const String &fileDll) {
2604 	if (hinstLib)
2605 		if (dlclose(hinstLib) == 0)
2606 			return false;
2607 
2608 	hinstLib = dlopen(fileDll, RTLD_LAZY);
2609 	if (!hinstLib)
2610 		return false;
2611 	return true;
2612 }
2613 
2614 void *Dl::GetFunction(const String &functionName) {
2615 	if (!hinstLib)
2616 		return nullptr;
2617 	return dlsym(hinstLib, functionName);
2618 }
2619 
2620 #endif
2621 
2622 Color RandomColor() {
2623 	return Color().FromRaw(Random());
2624 }
2625 
2626 Image GetRect(const Image& orig, const Rect &r) {
2627 	if(r.IsEmpty())
2628 		return Image();
2629 	ImageBuffer ib(r.GetSize());
2630 	for(int y = r.top; y < r.bottom; y++) {
2631 		const RGBA *s = orig[y] + r.left;
2632 		const RGBA *e = orig[y] + r.right;
2633 		RGBA *t = ib[y - r.top];
2634 		while(s < e) {
2635 			*t = *s;
2636 			t++;
2637 			s++;
2638 		}
2639 	}
2640 	return ib;
2641 }
2642 
2643 
2644 double tmGetTimeX() {
2645 #ifdef __linux__
2646 	timespec t;
2647 	if (0 != clock_gettime(CLOCK_REALTIME, &t))
2648 		return Null;
2649 	return t.tv_sec + (double)t.tv_nsec/1000000000.;
2650 #elif defined(_WIN32) || defined(WIN32)
2651 	LARGE_INTEGER clock;
2652 	LARGE_INTEGER freq;
2653 	if(!QueryPerformanceCounter(&clock) || !QueryPerformanceFrequency(&freq))
2654 	    return Null;
2655 	return double(clock.QuadPart)/freq.QuadPart;
2656 #else
2657 	return double(time(0));		// Low resolution
2658 #endif
2659 }
2660 
2661 
2662 int SysX(const char *cmd, String& out, String& err, double timeOut,
2663 			Gate3<double, String&, String&> progress, bool convertcharset) {
2664 	out.Clear();
2665 	LocalProcess p;
2666 	p.ConvertCharset(convertcharset);
2667 	double t0 = tmGetTimeX();
2668 	if(!p.Start2(cmd))
2669 		return -1;
2670 	int ret = Null;
2671 	String os, es;
2672 	while(p.IsRunning()) {
2673 		if (p.Read2(os, es)) {
2674 			out.Cat(os);
2675 			err.Cat(es);
2676 		}
2677 		double elapsed = tmGetTimeX() - t0;
2678 		if (!IsNull(timeOut) && elapsed > timeOut) {
2679 			ret = -2;
2680 			break;
2681 		}
2682 		if (progress(elapsed, out, err)) {
2683 			ret = -3;
2684 			break;
2685 		}
2686 		Sleep(1);
2687 	}
2688 	out.Cat(os);
2689 	err.Cat(es);
2690 	if (!IsNull(ret))
2691 		p.Kill();
2692 
2693 	return IsNull(ret) ? 0 : ret;
2694 }
2695 
2696 
2697 int LevenshteinDistance(const char *s, const char *t) {
2698 	int lens = int(strlen(s));
2699 	int lent = int(strlen(t));
2700 
2701     Buffer<int> v0(lent + 1);
2702     Buffer<int> v1(lent + 1);
2703 
2704     for (int i = 0; i <= lent; ++i)
2705         v0[i] = i;
2706 
2707     for (int i = 0; i < lens; ++i) {
2708         v1[0] = i + 1;
2709 
2710         for (int j = 0; j < lent; ++j) {
2711             int deletionCost = v0[j + 1] + 1;
2712             int insertionCost = v1[j] + 1;
2713             int substitutionCost;
2714             if (s[i] == t[j])
2715                 substitutionCost = v0[j];
2716             else
2717                 substitutionCost = v0[j] + 1;
2718 
2719             v1[j + 1] = min(deletionCost, insertionCost, substitutionCost);
2720         }
2721         Swap(v0, v1);
2722    	}
2723     return v0[lent];
2724 }
2725 
2726 int DamerauLevenshteinDistance(const char *s, const char *t, int alphabetLength) {
2727 	int lens = int(strlen(s));
2728 	int lent = int(strlen(t));
2729 	int lent2 = lent + 2;
2730 	Buffer<int> H((lens+2)*lent2);
2731 
2732     int infinity = lens + lent;
2733     H[0] = infinity;
2734     for(int i = 0; i <= lens; i++) {
2735 		H[lent2*(i+1)+1] = i;
2736 		H[lent2*(i+1)+0] = infinity;
2737     }
2738     for(int j = 0; j <= lent; j++) {
2739 		H[lent2*1+(j+1)] = j;
2740 		H[lent2*0+(j+1)] = infinity;
2741     }
2742     Buffer<int> DA(alphabetLength, 0);
2743 
2744     for(int i = 1; i <= lens; i++) {
2745       	int DB = 0;
2746       	for(int j = 1; j <= lent; j++) {
2747 	        int i1 = DA[int(t[j-1])];
2748 	        int j1 = DB;
2749 	        int cost = (s[i-1] == t[j-1]) ? 0 : 1;
2750 	        if(cost == 0)
2751 	        	DB = j;
2752 	        H[lent2*(i+1)+j+1] =
2753 	          min(H[lent2*i     + j] + cost,
2754 	              H[lent2*(i+1) + j] + 1,
2755 	              H[lent2*i     + j+1] + 1,
2756 	              H[lent2*i1    + j1] + (i-i1-1) + 1 + (j-j1-1));
2757 	  	}
2758       	DA[int(s[i-1])] = i;
2759     }
2760     return H[lent2*(lens+1)+lent+1];
2761 }
2762 
2763 int SentenceSimilitude(const char *s, const char *t) {
2764 	int ls = int(strlen(s));
2765 	int lt = int(strlen(t));
2766 	if (ls > lt) {
2767 		Swap(s, t);
2768 		Swap(ls, lt);
2769 	}
2770 	int mind = ls;
2771 	for (int i = 0; i < t - s; ++i) {
2772 		int d = DamerauLevenshteinDistance(s, String(t).Mid(i, ls));
2773 		if (d < mind)
2774 			mind = d;
2775 	}
2776 	return (100*mind)/ls;
2777 }
2778 
2779 // Dummy functions added after TheIDE change
2780 Upp::String GetCurrentMainPackage() {return "dummy";}
2781 Upp::String GetCurrentBuildMethod()	{return "dummy";}
2782 void IdePutErrorLine(const Upp::String& ) {}
2783 
2784 size_t GetNumLines(Stream &stream) {
2785 	size_t res = 0;
2786 	int c;
2787 
2788 	if ((c = stream.Get()) < 0)
2789 		return 0;
2790 	if (c == '\n')
2791 		res++;
2792 	while ((c = stream.Get()) > 0)
2793 		if (c == '\n')
2794 			res++;
2795 	if (c != '\n')
2796 		res++;
2797 	return res;
2798 }
2799 
2800 
2801 bool SetConsoleColor(CONSOLE_COLOR color) {
2802 	static Vector<int> colors;
2803 	if (color == RESET)
2804 		colors.Clear();
2805 	else if(color == PREVIOUS) {
2806 		int num = colors.size();
2807 		if (num == 0)
2808 			color = RESET;
2809 		else {
2810 			colors.Remove(num-1);
2811 			if (num-2 < 0)
2812 				color = RESET;
2813 			else
2814 				color = static_cast<CONSOLE_COLOR>(colors[num-2]);
2815 		}
2816 	} else
2817 		colors << color;
2818 
2819 #ifdef PLATFORM_WIN32
2820 	static HANDLE hstdout = 0;
2821 	static CONSOLE_SCREEN_BUFFER_INFO csbiInfo = {};
2822 	static WORD woldattrs;
2823 
2824 	if (hstdout == 0) {
2825 		hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
2826 		if (!GetConsoleScreenBufferInfo(hstdout, &csbiInfo)) {
2827 			hstdout = 0;
2828 			return false;
2829 		}
2830 		woldattrs = csbiInfo.wAttributes;
2831 	}
2832 	if (color == RESET)
2833 		return SetConsoleTextAttribute(hstdout, woldattrs);
2834 	else
2835 		return SetConsoleTextAttribute(hstdout, color);
2836 #else
2837 	if (color < 100)
2838 		printf("\x1b[%dm", color);
2839 	else
2840 		printf("\x1b[1;%dm", color-100);
2841 	return true;
2842 #endif
2843 }
2844 
2845 void ConsoleOutputDisable(bool disable) {
2846 #ifdef PLATFORM_WIN32
2847 	if (disable)
2848 		freopen("nul", "w", stdout);
2849 	else
2850 		freopen("CONOUT$", "w", stdout);
2851 #else
2852 	static int saved_id = Null;
2853 	static fpos_t saved_pos;
2854 
2855 	if (disable) {
2856 		fflush(stdout);
2857 		fgetpos(stdout, &saved_pos);
2858 		saved_id = dup(fileno(stdout));
2859 		close(fileno(stdout));
2860 	} else {
2861 		if (!IsNull(saved_id)) {
2862 			fflush(stdout);
2863 			dup2(saved_id, fileno(stdout));
2864 			close(saved_id);
2865 			clearerr(stdout);
2866 			fsetpos(stdout, &saved_pos);
2867 		}
2868 	}
2869 #endif
2870 }
2871 
2872 }