The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

604 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-7 by Raw Material Software ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the
  7. GNU General Public License, as published by the Free Software Foundation;
  8. either version 2 of the License, or (at your option) any later version.
  9. JUCE is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with JUCE; if not, visit www.gnu.org/licenses or write to the
  15. Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  16. Boston, MA 02111-1307 USA
  17. ------------------------------------------------------------------------------
  18. If you'd like to release a closed-source product which uses JUCE, commercial
  19. licenses are also available: visit www.rawmaterialsoftware.com/juce for
  20. more information.
  21. ==============================================================================
  22. */
  23. #include "linuxincludes.h"
  24. #include "../../../src/juce_core/basics/juce_StandardHeader.h"
  25. #include <sys/stat.h>
  26. #include <sys/dir.h>
  27. #include <sys/ptrace.h>
  28. #include <sys/vfs.h> // for statfs
  29. #include <sys/wait.h>
  30. #include <unistd.h>
  31. #include <fnmatch.h>
  32. #include <utime.h>
  33. #include <pwd.h>
  34. #include <fcntl.h>
  35. #define U_ISOFS_SUPER_MAGIC (short) 0x9660 // linux/iso_fs.h
  36. #define U_MSDOS_SUPER_MAGIC (short) 0x4d44 // linux/msdos_fs.h
  37. #define U_NFS_SUPER_MAGIC (short) 0x6969 // linux/nfs_fs.h
  38. #define U_SMB_SUPER_MAGIC (short) 0x517B // linux/smb_fs.h
  39. BEGIN_JUCE_NAMESPACE
  40. #include "../../../src/juce_core/io/files/juce_FileInputStream.h"
  41. #include "../../../src/juce_core/io/files/juce_FileOutputStream.h"
  42. #include "../../../src/juce_core/basics/juce_SystemStats.h"
  43. #include "../../../src/juce_core/basics/juce_Time.h"
  44. #include "../../../src/juce_core/io/network/juce_URL.h"
  45. #include "../../../src/juce_core/io/files/juce_NamedPipe.h"
  46. static File executableFile;
  47. //==============================================================================
  48. bool juce_isDirectory (const String& fileName) throw()
  49. {
  50. if (fileName.isEmpty())
  51. return true;
  52. struct stat info;
  53. const int res = stat (fileName.toUTF8(), &info);
  54. if (res == 0)
  55. return (info.st_mode & S_IFDIR) != 0;
  56. return false;
  57. }
  58. bool juce_fileExists (const String& fileName, const bool dontCountDirectories) throw()
  59. {
  60. if (fileName.isEmpty())
  61. return false;
  62. bool exists = access (fileName.toUTF8(), F_OK) == 0;
  63. if (exists && dontCountDirectories && juce_isDirectory (fileName))
  64. exists = false;
  65. return exists;
  66. }
  67. int64 juce_getFileSize (const String& fileName) throw()
  68. {
  69. struct stat info;
  70. const int res = stat (fileName.toUTF8(), &info);
  71. if (res == 0)
  72. return info.st_size;
  73. return 0;
  74. }
  75. void juce_getFileTimes (const String& fileName,
  76. int64& modificationTime,
  77. int64& accessTime,
  78. int64& creationTime) throw()
  79. {
  80. modificationTime = 0;
  81. accessTime = 0;
  82. creationTime = 0;
  83. struct stat info;
  84. const int res = stat (fileName.toUTF8(), &info);
  85. if (res == 0)
  86. {
  87. /*
  88. * Note: On Linux the st_ctime field is defined as last change time
  89. * rather than creation.
  90. */
  91. modificationTime = (int64) info.st_mtime * 1000;
  92. accessTime = (int64) info.st_atime * 1000;
  93. creationTime = (int64) info.st_ctime * 1000;
  94. }
  95. }
  96. bool juce_setFileTimes (const String& fileName,
  97. int64 modificationTime,
  98. int64 accessTime,
  99. int64 creationTime) throw()
  100. {
  101. struct utimbuf times;
  102. times.actime = (time_t) (accessTime / 1000);
  103. times.modtime = (time_t) (modificationTime / 1000);
  104. return utime (fileName.toUTF8(), &times) == 0;
  105. }
  106. bool juce_canWriteToFile (const String& fileName) throw()
  107. {
  108. return access (fileName.toUTF8(), W_OK) == 0;
  109. }
  110. bool juce_setFileReadOnly (const String& fileName, bool isReadOnly) throw()
  111. {
  112. struct stat info;
  113. const int res = stat (fileName.toUTF8(), &info);
  114. if (res != 0)
  115. return false;
  116. info.st_mode &= 0777; // Just permissions
  117. if( isReadOnly )
  118. info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
  119. else
  120. // Give everybody write permission?
  121. info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
  122. return chmod (fileName.toUTF8(), info.st_mode) == 0;
  123. }
  124. bool juce_deleteFile (const String& fileName) throw()
  125. {
  126. if (juce_isDirectory (fileName))
  127. return rmdir (fileName.toUTF8()) == 0;
  128. else
  129. return remove (fileName.toUTF8()) == 0;
  130. }
  131. bool juce_copyFile (const String& s, const String& d) throw()
  132. {
  133. const File source (s), dest (d);
  134. FileInputStream* in = source.createInputStream();
  135. bool ok = false;
  136. if (in != 0)
  137. {
  138. if (dest.deleteFile())
  139. {
  140. FileOutputStream* const out = dest.createOutputStream();
  141. if (out != 0)
  142. {
  143. const int bytesCopied = out->writeFromInputStream (*in, -1);
  144. delete out;
  145. ok = (bytesCopied == source.getSize());
  146. if (! ok)
  147. dest.deleteFile();
  148. }
  149. }
  150. delete in;
  151. }
  152. return ok;
  153. }
  154. bool juce_moveFile (const String& source, const String& dest) throw()
  155. {
  156. if (rename (source.toUTF8(), dest.toUTF8()) == 0)
  157. return true;
  158. if (! juce_canWriteToFile (source))
  159. return false;
  160. if (juce_copyFile (source, dest))
  161. {
  162. if (juce_deleteFile (source))
  163. return true;
  164. juce_deleteFile (dest);
  165. }
  166. return false;
  167. }
  168. void juce_createDirectory (const String& fileName) throw()
  169. {
  170. mkdir (fileName.toUTF8(), 0777);
  171. }
  172. void* juce_fileOpen (const String& fileName, bool forWriting) throw()
  173. {
  174. const char* mode = "rb";
  175. if (forWriting)
  176. {
  177. if (juce_fileExists (fileName, false))
  178. {
  179. FILE* f = fopen (fileName.toUTF8(), "r+b");
  180. if (f != 0)
  181. fseek (f, 0, SEEK_END);
  182. return (void*) f;
  183. }
  184. else
  185. {
  186. mode = "w+b";
  187. }
  188. }
  189. return (void*)fopen (fileName.toUTF8(), mode);
  190. }
  191. void juce_fileClose (void* handle) throw()
  192. {
  193. if (handle != 0)
  194. fclose ((FILE*) handle);
  195. }
  196. int juce_fileRead (void* handle, void* buffer, int size) throw()
  197. {
  198. if (handle != 0)
  199. return fread (buffer, 1, size, (FILE*) handle);
  200. return 0;
  201. }
  202. int juce_fileWrite (void* handle, const void* buffer, int size) throw()
  203. {
  204. if (handle != 0)
  205. return fwrite (buffer, 1, size, (FILE*) handle);
  206. return 0;
  207. }
  208. int64 juce_fileSetPosition (void* handle, int64 pos) throw()
  209. {
  210. if (handle != 0 && fseek ((FILE*) handle, (long) pos, SEEK_SET) == 0)
  211. return pos;
  212. return -1;
  213. }
  214. int64 juce_fileGetPosition (void* handle) throw()
  215. {
  216. if (handle != 0)
  217. return ftell ((FILE*) handle);
  218. else
  219. return -1;
  220. }
  221. void juce_fileFlush (void* handle) throw()
  222. {
  223. if (handle != 0)
  224. fflush ((FILE*) handle);
  225. }
  226. const StringArray juce_getFileSystemRoots() throw()
  227. {
  228. StringArray s;
  229. s.add (T("/"));
  230. return s;
  231. }
  232. const String juce_getVolumeLabel (const String& filenameOnVolume,
  233. int& volumeSerialNumber) throw()
  234. {
  235. // There is no equivalent on Linux
  236. volumeSerialNumber = 0;
  237. return String::empty;
  238. }
  239. int64 File::getBytesFreeOnVolume() const throw()
  240. {
  241. struct statfs buf;
  242. int64 free_space = 0;
  243. if (statfs (getFullPathName().toUTF8(), &buf) == 0)
  244. {
  245. // Note: this returns space available to non-super user
  246. free_space = (int64) buf.f_bsize * (int64) buf.f_bavail;
  247. }
  248. return free_space;
  249. }
  250. bool File::isOnCDRomDrive() const throw()
  251. {
  252. struct statfs buf;
  253. if (statfs (getFullPathName().toUTF8(), &buf) == 0)
  254. return (buf.f_type == U_ISOFS_SUPER_MAGIC);
  255. // Assume not if this fails for some reason
  256. return false;
  257. }
  258. bool File::isOnHardDisk() const throw()
  259. {
  260. struct statfs buf;
  261. if (statfs (getFullPathName().toUTF8(), &buf) == 0)
  262. {
  263. switch (buf.f_type)
  264. {
  265. case U_ISOFS_SUPER_MAGIC: // CD-ROM
  266. case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem)
  267. case U_NFS_SUPER_MAGIC: // Network NFS
  268. case U_SMB_SUPER_MAGIC: // Network Samba
  269. return false;
  270. default:
  271. // Assume anything else is a hard-disk (but note it could
  272. // be a RAM disk. There isn't a good way of determining
  273. // this for sure)
  274. return true;
  275. }
  276. }
  277. // Assume so if this fails for some reason
  278. return true;
  279. }
  280. //==============================================================================
  281. const File File::getSpecialLocation (const SpecialLocationType type)
  282. {
  283. switch (type)
  284. {
  285. case userHomeDirectory:
  286. {
  287. const char* homeDir = getenv ("HOME");
  288. if (homeDir == 0)
  289. {
  290. struct passwd* const pw = getpwuid (getuid());
  291. if (pw != 0)
  292. homeDir = pw->pw_dir;
  293. }
  294. return File (String::fromUTF8 ((const uint8*) homeDir));
  295. }
  296. case userDocumentsDirectory:
  297. case userMusicDirectory:
  298. case userMoviesDirectory:
  299. case userApplicationDataDirectory:
  300. return File ("~");
  301. case userDesktopDirectory:
  302. return File ("~/Desktop");
  303. case commonApplicationDataDirectory:
  304. return File ("/var");
  305. case globalApplicationsDirectory:
  306. return File ("/usr");
  307. case tempDirectory:
  308. {
  309. File tmp ("/var/tmp");
  310. if (! tmp.isDirectory())
  311. {
  312. tmp = T("/tmp");
  313. if (! tmp.isDirectory())
  314. tmp = File::getCurrentWorkingDirectory();
  315. }
  316. return tmp;
  317. }
  318. case currentExecutableFile:
  319. case currentApplicationFile:
  320. // if this fails, it's probably because juce_setCurrentExecutableFileName()
  321. // was never called to set the filename - this should be done by the juce
  322. // main() function, so maybe you've hacked it to use your own custom main()?
  323. jassert (executableFile.exists());
  324. return executableFile;
  325. default:
  326. jassertfalse // unknown type?
  327. break;
  328. }
  329. return File::nonexistent;
  330. }
  331. //==============================================================================
  332. void juce_setCurrentExecutableFileName (const String& filename) throw()
  333. {
  334. executableFile = File::getCurrentWorkingDirectory().getChildFile (filename);
  335. }
  336. //==============================================================================
  337. const File File::getCurrentWorkingDirectory() throw()
  338. {
  339. char buf [2048];
  340. getcwd (buf, sizeof(buf));
  341. return File (String::fromUTF8 ((const uint8*) buf));
  342. }
  343. bool File::setAsCurrentWorkingDirectory() const throw()
  344. {
  345. return chdir (getFullPathName().toUTF8()) == 0;
  346. }
  347. //==============================================================================
  348. const tchar File::separator = T('/');
  349. const tchar* File::separatorString = T("/");
  350. //==============================================================================
  351. struct FindFileStruct
  352. {
  353. String parentDir, wildCard;
  354. DIR* dir;
  355. bool getNextMatch (String& result, bool* const isDir, bool* const isHidden, int64* const fileSize,
  356. Time* const modTime, Time* const creationTime, bool* const isReadOnly) throw()
  357. {
  358. const char* const wildcardUTF8 = wildCard.toUTF8();
  359. for (;;)
  360. {
  361. struct dirent* const de = readdir (dir);
  362. if (de == 0)
  363. break;
  364. if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0)
  365. {
  366. result = String::fromUTF8 ((const uint8*) de->d_name);
  367. const String path (parentDir + result);
  368. if (isDir != 0 || fileSize != 0)
  369. {
  370. struct stat info;
  371. const bool statOk = (stat (path.toUTF8(), &info) == 0);
  372. if (isDir != 0)
  373. *isDir = path.isEmpty() || (statOk && ((info.st_mode & S_IFDIR) != 0));
  374. if (isHidden != 0)
  375. *isHidden = (de->d_name[0] == '.');
  376. if (fileSize != 0)
  377. *fileSize = statOk ? info.st_size : 0;
  378. }
  379. if (modTime != 0 || creationTime != 0)
  380. {
  381. int64 m, a, c;
  382. juce_getFileTimes (path, m, a, c);
  383. if (modTime != 0)
  384. *modTime = m;
  385. if (creationTime != 0)
  386. *creationTime = c;
  387. }
  388. if (isReadOnly != 0)
  389. *isReadOnly = ! juce_canWriteToFile (path);
  390. return true;
  391. }
  392. }
  393. return false;
  394. }
  395. };
  396. // returns 0 on failure
  397. void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile,
  398. bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw()
  399. {
  400. DIR* d = opendir (directory.toUTF8());
  401. if (d != 0)
  402. {
  403. FindFileStruct* ff = new FindFileStruct();
  404. ff->parentDir = directory;
  405. if (!ff->parentDir.endsWithChar (File::separator))
  406. ff->parentDir += File::separator;
  407. ff->wildCard = wildCard;
  408. if (wildCard == T("*.*"))
  409. ff->wildCard = T("*");
  410. ff->dir = d;
  411. if (ff->getNextMatch (firstResultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly))
  412. {
  413. return ff;
  414. }
  415. else
  416. {
  417. firstResultFile = String::empty;
  418. isDir = false;
  419. isHidden = false;
  420. closedir (d);
  421. delete ff;
  422. }
  423. }
  424. return 0;
  425. }
  426. bool juce_findFileNext (void* handle, String& resultFile,
  427. bool* isDir, bool* isHidden, int64* fileSize, Time* modTime, Time* creationTime, bool* isReadOnly) throw()
  428. {
  429. FindFileStruct* const ff = (FindFileStruct*) handle;
  430. if (ff != 0)
  431. return ff->getNextMatch (resultFile, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly);
  432. return false;
  433. }
  434. void juce_findFileClose (void* handle) throw()
  435. {
  436. FindFileStruct* const ff = (FindFileStruct*) handle;
  437. if (ff != 0)
  438. {
  439. closedir (ff->dir);
  440. delete ff;
  441. }
  442. }
  443. bool juce_launchFile (const String& fileName,
  444. const String& parameters) throw()
  445. {
  446. String cmdString (fileName);
  447. cmdString << " " << parameters;
  448. if (URL::isProbablyAWebsiteURL (cmdString) || URL::isProbablyAnEmailAddress (cmdString))
  449. {
  450. // create a command that tries to launch a bunch of likely browsers
  451. const char* const browserNames[] = { "/etc/alternatives/x-www-browser", "firefox", "mozilla", "konqueror", "opera" };
  452. StringArray cmdLines;
  453. for (int i = 0; i < numElementsInArray (browserNames); ++i)
  454. cmdLines.add (String (browserNames[i]) + T(" ") + cmdString.trim().quoted());
  455. cmdString = cmdLines.joinIntoString (T(" || "));
  456. }
  457. char* const argv[4] = { "/bin/sh", "-c", (char*) cmdString.toUTF8(), 0 };
  458. const int cpid = fork();
  459. if (cpid == 0)
  460. {
  461. setsid();
  462. // Child process
  463. execve (argv[0], argv, environ);
  464. exit (0);
  465. }
  466. return cpid >= 0;
  467. }
  468. END_JUCE_NAMESPACE