1 #ifdef _WIN32
2 #ifndef UNICODE
3 #define UNICODE
4 #endif
5 #endif
6
7 #include <memory>
8
9 #include "tsystem.h"
10 //#include "tunicode.h"
11 #include "tfilepath_io.h"
12 #include "tconvert.h"
13
14 #include <time.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <errno.h>
18 #include <set>
19 #include <tenv.h>
20
21 #undef PLATFORM
22
23 #ifdef _MSC_VER
24 #pragma warning(disable : 4996)
25 #endif
26
27 #ifdef _WIN32
28 #define PLATFORM WIN32
29 #include <process.h>
30 #include <psapi.h>
31 #include <io.h>
32 #include <stdlib.h>
33 #include <direct.h>
34 #include <shellapi.h>
35 // gmt: sulla mia macchina cosi' non compila!!!
36 // #include "winsock2.h"
37 // #include "lmcons.h"
38 #include <sys/utime.h>
39 #include <lm.h>
40 #endif
41
42 #ifdef LINUX
43 #define PLATFORM LINUX
44 #include <grp.h>
45 #include <utime.h>
46 #include <sys/param.h>
47 #include <unistd.h>
48 #include <sys/types.h>
49 #include <stdio.h>
50 #include <unistd.h>
51 #include <dirent.h>
52 #include <dirent.h>
53 #include <sys/sysinfo.h>
54 #include <sys/swap.h>
55 #include <sys/statfs.h>
56 #include <pwd.h>
57 #include <mntent.h>
58 #include <dlfcn.h>
59 #include <utime.h>
60 #include <sys/time.h>
61 #include <QDir>
62 #include <QFileInfo>
63 #include <QStorageInfo>
64 #include <QTextStream>
65 #include <QUrl>
66 #endif
67
68 #ifdef FREEBSD
69 #define PLATFORM FREEBSD
70 #include <sys/param.h>
71 #include <sys/sched.h>
72 #include <sys/sysctl.h>
73 #include <string.h>
74 #include <unistd.h>
75 #ifdef __DragonFly__
76 #include <sys/user.h>
77 #else
78 #include <sys/proc.h>
79 #endif
80 #include <sys/vmmeter.h>
81 #include <vm/vm_param.h>
82 #include <grp.h>
83 #include <utime.h>
84 #include <stdio.h>
85 #include <dirent.h>
86 #include <sys/mount.h>
87 #include <pwd.h>
88 #include <dlfcn.h>
89 #define pagetok(__nb) ((__nb) * (getpagesize()))
90 #endif
91
92
93 #if defined(MACOSX)
94 #define PLATFORM MACOSX
95 #include <grp.h>
96 #include <utime.h>
97 #include <sys/param.h>
98 #include <unistd.h>
99 #include <sys/types.h>
100 #include <stdio.h>
101 #include <unistd.h>
102 #include <dirent.h>
103 #include <dirent.h>
104 #include <sys/param.h> // for getfsstat
105 #include <sys/ucred.h>
106 #include <sys/mount.h>
107 #include <pwd.h>
108 #include <dlfcn.h>
109
110 #include "Carbon/Carbon.h"
111
112 #endif
113
114 #ifdef __sgi
115 #define PLATFORM SGI
116 #include <sys/param.h>
117 #include <unistd.h>
118 #include <grp.h>
119 #include <dirent.h> // dirent.h
120 #include <sys/utime.h>
121 #include <sys/swap.h>
122 #include <sys/statfs.h>
123 #include <pwd.h>
124 #include <mntent.h>
125
126 #include <dlfcn.h>
127
128 #endif
129
130 #ifndef PLATFORM
131 PLATFORM_NOT_SUPPORTED
132 #endif
133
134 using namespace std;
135
136 #ifdef _WIN32
137
getFormattedMessage(DWORD lastError)138 wstring getFormattedMessage(DWORD lastError) {
139 LPVOID lpMsgBuf;
140 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
141 FORMAT_MESSAGE_IGNORE_INSERTS,
142 NULL, lastError,
143 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
144 (LPTSTR)&lpMsgBuf, 0, NULL);
145
146 int wSize = MultiByteToWideChar(0, 0, (char *)lpMsgBuf, -1, 0, 0);
147 if (!wSize) return wstring();
148
149 std::unique_ptr<wchar_t[]> wBuffer(new wchar_t[wSize + 1]);
150 MultiByteToWideChar(0, 0, (char *)lpMsgBuf, -1, wBuffer.get(), wSize);
151 wBuffer[wSize] = '\0';
152 wstring wmsg(wBuffer.get());
153
154 LocalFree(lpMsgBuf);
155 return wmsg;
156 }
157
158 #endif
159 //------------------------------------------------------------
160
outputDebug(string s)161 void TSystem::outputDebug(string s) {
162 #ifdef TNZCORE_LIGHT
163 #ifdef _WIN32
164 OutputDebugString((LPCWSTR)s.c_str());
165 #else
166 cerr << s << endl;
167 #endif
168 #else
169 qDebug("%s", s.c_str());
170 #endif
171 }
172
173 //------------------------------------------------------------
174
getProcessId()175 int TSystem::getProcessId() { return getpid(); }
176
177 //------------------------------------------------------------
178
memoryShortage()179 bool TSystem::memoryShortage() {
180 #ifdef _WIN32
181
182 MEMORYSTATUSEX memStatus;
183 memStatus.dwLength = sizeof(MEMORYSTATUSEX);
184 GlobalMemoryStatusEx(&memStatus);
185
186 assert(memStatus.ullAvailPhys <= memStatus.ullTotalPhys);
187
188 if (memStatus.ullAvailPhys <
189 memStatus.ullTotalPhys *
190 0.20) // if available memory is less then 20% of total memory
191 return true;
192
193 PROCESS_MEMORY_COUNTERS c;
194 c.cb = sizeof(PROCESS_MEMORY_COUNTERS);
195 BOOL ret = GetProcessMemoryInfo(GetCurrentProcess(), &c,
196 sizeof(PROCESS_MEMORY_COUNTERS));
197 assert(ret);
198
199 return c.WorkingSetSize >
200 memStatus.ullTotalVirtual *
201 0.6; // if total memory used by this process(WorkingSetSize) is
202 // half of max allocatable memory
203 //(ullTotalVirtual: on 32bits machines, tipically it's 2GB)
204 // It's better "to stay large"; for values >0.6 this function may
205 // returns that there is memory, but for fragmentation the malloc fails the
206 // same!
207
208 #elif defined(MACOSX)
209
210 // to be done...
211 return false;
212
213 #elif defined(LINUX)
214
215 // to be done...
216 return false;
217
218 #elif defined(FREEBSD)
219
220 // to be done...
221 return false;
222
223 #else
224
225 @ @ @ERROR : PLATFORM NOT SUPPORTED
226
227 #endif
228 }
229
230 //------------------------------------------------------------
231
getFreeMemorySize(bool onlyPhisicalMemory)232 TINT64 TSystem::getFreeMemorySize(bool onlyPhisicalMemory) {
233 TINT64 totalFree = 0;
234
235 #ifdef _WIN32
236
237 MEMORYSTATUSEX buff;
238 buff.dwLength = sizeof(MEMORYSTATUSEX);
239 GlobalMemoryStatusEx(&buff);
240
241 if (onlyPhisicalMemory)
242 return buff.ullAvailPhys >> 10;
243 else
244 return buff.ullAvailPageFile >> 10;
245
246 #elif defined(__sgi)
247
248 // check for virtual memory
249 int numberOfResources =
250 swapctl(SC_GETNSWP, 0); /* get number of swapping resources configued */
251
252 if (numberOfResources == 0) return 0;
253
254 // avrei voluto fare: struct swaptable *table = new struct swaptable[...]
255 struct swaptable *table = (struct swaptable *)calloc(
256 1, sizeof(struct swapent) * numberOfResources + sizeof(int));
257
258 table->swt_n = numberOfResources;
259 swapctl(SC_LIST, table); /* list all the swapping resources */
260
261 TINT64 virtualFree = 0;
262 TINT64 physicalFree = 0;
263
264 for (int i = 0; i < table->swt_n; i++) {
265 virtualFree += table->swt_ent[i].ste_free;
266 }
267
268 free(table);
269 totalFree = virtualFree << 4 + physicalFree;
270
271 #elif defined(LINUX)
272
273 struct sysinfo *sysInfo = (struct sysinfo *)calloc(1, sizeof(struct sysinfo));
274
275 if (!sysinfo(sysInfo)) {
276 if (onlyPhisicalMemory)
277 totalFree = sysInfo->freeram;
278 else
279 totalFree = sysInfo->freeram + sysInfo->freeswap;
280 } else {
281 assert(!"sysinfo function failed");
282 }
283 free(sysInfo);
284
285 #elif defined(__DragonFly__)
286
287 // to be done...
288 totalFree = 512 * 1024;
289
290 #elif defined(FREEBSD)
291
292 TINT64 ret = 0;
293 size_t size;
294 #ifdef __OpenBSD__
295 int mib[] = {CTL_VM, VM_UVMEXP};
296 struct uvmexp uvmexp;
297 #else
298 int mib[] = {CTL_VM, VM_TOTAL};
299 struct vmtotal vmtotal;
300 #endif
301
302 #ifdef __OpenBSD__
303 size = sizeof(uvmexp);
304 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0)
305 return (ret);
306 ret = pagetok((guint64)uvmexp.free);
307 #else
308 size = sizeof(vmtotal);
309 if (sysctl(mib, 2, &vmtotal, &size, NULL, 0) < 0)
310 return (ret);
311 ret = pagetok(vmtotal.t_free);
312 #endif
313 return ret;
314
315 #elif defined(MACOSX)
316
317 // to be done...
318 totalFree = 512 * 1024;
319
320 #else
321 @ @ @ERROR : PLATFORM NOT SUPPORTED
322 #endif
323
324 #ifndef _WIN32
325 #else
326 #endif
327
328 return totalFree;
329 }
330
331 //------------------------------------------------------------
332 /*
333 ostream& operator<<(ostream&out, const TTime &t)
334 {
335 return out<<t.getDate()<<" "<<t.getTime();
336 }
337 */
338
339 //------------------------------------------------------------
340
getDiskSize(const TFilePath & diskName)341 TINT64 TSystem::getDiskSize(const TFilePath &diskName) {
342 TINT64 size = 0;
343 if (!diskName.isAbsolute()) {
344 assert(0);
345 return 0;
346 }
347 #ifndef _WIN32
348 struct statfs buf;
349 #ifdef __sgi
350 statfs(::to_string(diskName).c_str(), &buf, sizeof(struct statfs), 0);
351 #else
352 statfs(::to_string(diskName).c_str(), &buf);
353 #endif
354 size = (TINT64)((buf.f_blocks * buf.f_bsize) >> 10);
355 #else
356 DWORD sectorsPerCluster; // sectors per cluster
357 DWORD bytesPerSector; // bytes per sector
358 DWORD numberOfFreeClusters; // free clusters
359 DWORD totalNumberOfClusters;
360
361 BOOL rc = GetDiskFreeSpaceW(diskName.getWideString().c_str(), // root path
362 §orsPerCluster, // sectors per cluster
363 &bytesPerSector, // bytes per sector
364 &numberOfFreeClusters, // free clusters
365 &totalNumberOfClusters // total clusters
366 );
367
368 if (!rc)
369 throw TSystemException(diskName, getFormattedMessage(GetLastError()));
370 else
371 size = (totalNumberOfClusters * sectorsPerCluster * bytesPerSector) >> 10;
372 #endif
373 return size;
374 }
375
376 //------------------------------------------------------------
377
getFreeDiskSize(const TFilePath & diskName)378 TINT64 TSystem::getFreeDiskSize(const TFilePath &diskName) {
379 TINT64 size = 0;
380 if (!diskName.isAbsolute()) {
381 assert(0);
382 return 0;
383 }
384 #ifndef _WIN32
385 struct statfs buf;
386 #ifdef __sgi
387 statfs(diskName.getWideString().c_str(), &buf, sizeof(struct statfs), 0);
388 #else
389 statfs(::to_string(diskName).c_str(), &buf);
390 #endif
391 size = (TINT64)(buf.f_bfree * buf.f_bsize) >> 10;
392 #else
393 DWORD sectorsPerCluster; // sectors per cluster
394 DWORD bytesPerSector; // bytes per sector
395 DWORD numberOfFreeClusters; // free clusters
396 DWORD totalNumberOfClusters;
397
398 BOOL rc = GetDiskFreeSpaceW(diskName.getWideString().c_str(), // root path
399 §orsPerCluster, // sectors per cluster
400 &bytesPerSector, // bytes per sector
401 &numberOfFreeClusters, // free clusters
402 &totalNumberOfClusters // total clusters
403 );
404
405 if (!rc) // eccezione... getLastError etc...
406 throw TSystemException(diskName, "cannot get disk info!");
407 else
408 size = (numberOfFreeClusters * sectorsPerCluster * bytesPerSector) >> 10;
409 #endif
410 return size;
411 }
412
413 //------------------------------------------------------------
414
getMemorySize(bool onlyPhisicalMemory)415 TINT64 TSystem::getMemorySize(bool onlyPhisicalMemory) {
416 #ifdef _WIN32
417
418 MEMORYSTATUS buff;
419 GlobalMemoryStatus(&buff);
420 if (onlyPhisicalMemory)
421 return buff.dwTotalPhys >> 10;
422 else
423 return buff.dwTotalPageFile >> 10;
424
425 #elif defined(__sgi)
426
427 int physicalMemory;
428
429 if (swapctl(SC_GETSWAPMAX, &physicalMemory))
430 return ((size_t)0);
431 else
432 return logSwapLibero >> 1;
433 #elif defined(LINUX)
434
435 struct sysinfo *sysInfo = (struct sysinfo *)calloc(1, sizeof(struct sysinfo));
436 TINT64 ret = 0;
437
438 if (!sysinfo(sysInfo))
439 ret = sysInfo->totalram;
440 else
441 assert(!"sysinfo function failed");
442
443 free(sysInfo);
444 return ret;
445
446 #elif defined(__DragonFly__)
447
448 // to be done...
449 return 512 * 1024;
450
451 #elif defined(FREEBSD)
452
453 TINT64 ret = 0;
454 size_t size;
455 #ifdef __OpenBSD__
456 int mib[] = {CTL_VM, VM_UVMEXP};
457 struct uvmexp uvmexp;
458 #else
459 int mib[] = {CTL_VM, VM_TOTAL};
460 struct vmtotal vmtotal;
461 #endif
462
463 #ifdef __OpenBSD__
464 size = sizeof(uvmexp);
465 if (sysctl(mib, 2, &uvmexp, &size, NULL, 0) < 0)
466 return (ret);
467 ret = pagetok((guint64)uvmexp.npages);
468 #else
469 size = sizeof(vmtotal);
470 if (sysctl(mib, 2, &vmtotal, &size, NULL, 0) < 0)
471 return (ret);
472 /* cheat : rm = tot used, add free to get total */
473 ret = pagetok(vmtotal.t_rm + vmtotal.t_free);
474 #endif
475 return ret;
476
477 #elif defined(MACOSX)
478
479 // to be done...
480 return 512 * 1024;
481
482 #else
483 @ @ @ERROR : PLATFORM NOT SUPPORTED
484 #endif
485
486 #ifndef _WIN32
487 #else
488 #endif
489 }
490
491 //------------------------------------------------------------
492
moveFileToRecycleBin(const TFilePath & fp)493 void TSystem::moveFileToRecycleBin(const TFilePath &fp) {
494 #if defined(_WIN32)
495 //
496 // from http://msdn.microsoft.com/msdnmag/issues/01/04/c/default.aspx
497 //
498 // Copy pathname to double-NULL-terminated string.
499 //
500 wchar_t buf[_MAX_PATH + 1]; // allow one more character
501 wcscpy(buf, fp.getWideString().c_str()); // copy caller's path name
502 buf[wcslen(buf) + 1] = 0; // need two NULLs at end
503
504 SHFILEOPSTRUCTW data;
505 memset(&data, 0, sizeof(SHFILEOPSTRUCTW));
506 data.fFlags |= FOF_SILENT; // don't report progress
507 data.fFlags |= FOF_NOERRORUI; // don't report errors
508 data.fFlags |= FOF_NOCONFIRMATION; // don't confirm delete
509
510 data.wFunc = FO_DELETE; // REQUIRED: delete operation
511 data.pFrom = buf; // REQUIRED: which file(s)
512 data.pTo = NULL; // MUST be NULL
513 data.fFlags |= FOF_ALLOWUNDO; // ..send to Recycle Bin
514 int ret = SHFileOperationW(&data); // do it!
515
516 #elif defined(MACOSX)
517 FSRef foundRef;
518 OSErr err = FSFindFolder(kOnSystemDisk, kTrashFolderType, kDontCreateFolder,
519 &foundRef);
520
521 if (err) {
522 assert(false);
523 deleteFile(fp);
524 return;
525 }
526 UInt8 path[255];
527 err = FSRefMakePath(&foundRef, path, 254);
528 if (err) {
529 assert(false);
530 deleteFile(fp);
531 return;
532 }
533 // TFilePath dest = TFilePath(path)+(fp.getName()+fp.getDottedType());
534 string fullNameWithExt = ::to_string(fp);
535 int i = fullNameWithExt.rfind("/");
536 string nameWithExt = fullNameWithExt.substr(i + 1);
537 TFilePath dest = TFilePath((char *)path) + nameWithExt;
538
539 try {
540 renameFile(dest, fp);
541 } catch (...) {
542 try {
543 copyFile(dest, fp);
544 deleteFile(fp);
545 } catch (...) {
546 }
547 }
548 #elif defined(LINUX)
549 //
550 // From https://stackoverflow.com/questions/17964439/move-files-to-trash-recycle-bin-in-qt
551 //
552 QString fileToRecycle = fp.getQString();
553 QFileInfo FileName(fileToRecycle);
554
555 QDateTime currentTime(QDateTime::currentDateTime()); // get system time
556
557 // check if the file is on the local drive
558 const QStorageInfo fileStorageInfo(fileToRecycle);
559 const QStorageInfo homeStorageInfo(QDir::homePath());
560 const bool isOnHomeDrive = fileStorageInfo == homeStorageInfo;
561
562 QString trashFilePath = QDir::homePath() + "/.local/share/Trash/files/"; // this folder contains deleted files
563 QString trashInfoPath = QDir::homePath() + "/.local/share/Trash/info/"; // this folder contains information about the deleted files
564
565 // different paths are used for external drives
566 if (!isOnHomeDrive) {
567 //trashFilePath = fileStorageInfo.rootPath() + "/.Trash-1000/files/";
568 //trashInfoPath = fileStorageInfo.rootPath() + "/.Trash-1000/info/";
569
570 // TODO: Implement this... The standard is /.Trash-<UID>/...
571 outputDebug("Deleting files on external drives in Linux is not implemented yet.");
572 return;
573 }
574
575 // check paths exist
576 if( !QDir(trashFilePath).exists() || !QDir(trashInfoPath).exists() ) {
577 outputDebug("Could not find the right paths to send the file to the recycle bin.");
578 return;
579 }
580
581 // create file for the "Trash/info" folder
582 QFile infoFile(trashInfoPath + FileName.completeBaseName() + "." + FileName.completeSuffix() + ".trashinfo"); // filename+extension+.trashinfo
583
584 infoFile.open(QIODevice::ReadWrite);
585
586 QTextStream stream(&infoFile);
587
588 stream << "[Trash Info]" << endl;
589 stream << "Path=" + QString(QUrl::toPercentEncoding(FileName.absoluteFilePath(), "~_-./")) << endl; // convert path to percentage encoded string
590 stream << "DeletionDate=" + currentTime.toString("yyyy-MM-dd") + "T" + currentTime.toString("hh:mm:ss") << endl; // get date and time in format YYYY-MM-DDThh:mm:ss
591
592 infoFile.close();
593
594 // move the original file to the "Trash/files" folder
595 QDir file;
596 file.rename(FileName.absoluteFilePath(), trashFilePath+FileName.completeBaseName() + "." + FileName.completeSuffix()); // rename(original path, trash path)
597 #else
598 assert(!"Not implemented yet");
599 #endif
600 }
601
602 //------------------------------------------------------------
603
getMessage() const604 TString TSystemException::getMessage() const {
605 wstring msg;
606 switch (m_err) {
607 case -1:
608 msg = m_msg;
609 break; // // nothing
610 case EEXIST:
611 msg =
612 L": Directory was not created because filename is the name of an "
613 L"existing file, directory, or device";
614 break;
615 case ENOENT:
616 msg =
617 L": Path was not found, or the named file does not exist or is a null "
618 L"pathname.";
619 break;
620 case ENOTEMPTY:
621 msg =
622 L": Given path is not a directory; directory is not empty; or "
623 L"directory is either current working directory or root directory";
624 break;
625 case EACCES:
626 msg =
627 L": Search permission is denied by a component of the path prefix, or "
628 L"write permission on the file named by path is denied, or times is "
629 L"NULL, and write access is denied";
630 break;
631 case EFAULT:
632 msg =
633 L": Times is not NULL and, or points outside the process's allocated "
634 L"address space.";
635 break;
636 case EINTR:
637 msg = L": A signal was caught during the utime system call.";
638 break;
639 case ENAMETOOLONG:
640 msg =
641 L": The length of the path argument exceeds {PATH_MAX}, or the length "
642 L"of a path component exceeds {NAME_MAX} while _POSIX_NO_TRUNC is in "
643 L"effect.";
644 break;
645 case ENOTDIR:
646 msg = L": A component of the path prefix is not a directory.";
647 break;
648 case EPERM:
649 msg =
650 L": The calling process does not have the super-user privilege, the "
651 L"effective user ID is not the owner of the file, and times is not "
652 L"NULL, or the file system containing the file is mounted read-only";
653 break;
654 case EROFS:
655 msg =
656 L": The current file system level range does not envelop the level of "
657 L"the file named by path, and the calling process does not have the "
658 L"super-user privilege.";
659 break;
660 case ENOSYS:
661 msg =
662 L": When the named file cannot have its time reset. The file is on a "
663 L"file system that doesn't have this operation.";
664 break;
665 case EMFILE:
666 msg = L": The maximum number of file descriptors are currently open.";
667 break;
668 case ENFILE:
669 msg = L": The system file table is full.";
670 break;
671 case EBADF:
672 msg =
673 L": The file descriptor determined by the DIR stream is no longer "
674 L"valid. This result occurs if the DIR stream has been closed.";
675 break;
676 case EINVAL:
677 msg = L": 64-bit and non-64-bit calls were mixed in a sequence of calls.";
678 break;
679 default:
680 msg = L": Unknown error";
681 break;
682 #ifndef _WIN32
683 case ELOOP:
684 msg = L": Too many symbolic links were encountered in translating path.";
685 break;
686 #ifndef MACOSX
687 case EMULTIHOP:
688 msg =
689 L": Components of path require hopping to multiple remote machines and "
690 L"the file system does not allow it.";
691 break;
692 case ENOLINK:
693 msg =
694 L": Path points to a remote machine and the link to that machine is no "
695 L"longer active.";
696 break;
697 #endif
698 #if defined(__sgi)
699 case EDIRCORRUPTED:
700 msg = L": The directory is corrupted on disk.";
701 break;
702 #endif
703 case EOVERFLOW:
704 msg =
705 L": One of the inode number values or offset values did not fit in 32 "
706 L"bits, and the 64-bit interfaces were not used.";
707 break;
708 #endif
709 }
710 return m_fname.getWideString() + L"\n" + msg;
711 }
712
713 //------------------------------------------------------------
714
touchFile(const TFilePath & path)715 void TSystem::touchFile(const TFilePath &path) {
716 #ifndef TNZCORE_LIGHT
717
718 // string filename = path.getFullPath();
719 if (TFileStatus(path).doesExist()) {
720 int ret;
721 #ifdef _WIN32
722 ret = _wutime(path.getWideString().c_str(), 0);
723 #else
724 ret = utimes(::to_string(path).c_str(), 0);
725 #endif
726 if (0 != ret) throw TSystemException(path, errno);
727 } else {
728 Tofstream file(path);
729 if (!file) {
730 throw TSystemException(path, errno);
731 }
732 file.close(); // altrimenti il compilatore da' un warning:
733 // variabile non utilizzata
734 }
735
736 #endif
737 }
738
739 //------------------------------------------------------------
740