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 }