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                               &sectorsPerCluster,     // 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                               &sectorsPerCluster,     // 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