/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2020 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. The code included in this file is provided under the terms of the ISC license http://www.isc.org/downloads/software-support-policy/isc-license. Permission To use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted provided that the above copyright notice and this permission notice appear in all copies. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #if defined(__FreeBSD__) || defined(__DragonFly__) extern char **environ; #endif namespace juce { enum { U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h }; bool File::isOnCDRomDrive() const { struct statfs buf; return statfs (getFullPathName().toUTF8(), &buf) == 0 && buf.f_type == (short) U_ISOFS_SUPER_MAGIC; } bool File::isOnHardDisk() const { struct statfs buf; if (statfs (getFullPathName().toUTF8(), &buf) == 0) { switch (buf.f_type) { case U_ISOFS_SUPER_MAGIC: // CD-ROM case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem) case U_NFS_SUPER_MAGIC: // Network NFS case U_SMB_SUPER_MAGIC: // Network Samba return false; default: break; } } // Assume so if this fails for some reason return true; } bool File::isOnRemovableDrive() const { jassertfalse; // xxx not implemented for linux! return false; } String File::getVersion() const { return {}; // xxx not yet implemented } //============================================================================== static File resolveXDGFolder (const char* const type, const char* const fallbackFolder) { StringArray confLines; File ("~/.config/user-dirs.dirs").readLines (confLines); for (int i = 0; i < confLines.size(); ++i) { const String line (confLines[i].trimStart()); if (line.startsWith (type)) { // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music const File f (line.replace ("$HOME", File ("~").getFullPathName()) .fromFirstOccurrenceOf ("=", false, false) .trim().unquoted()); if (f.isDirectory()) return f; } } return File (fallbackFolder); } const char* const* juce_argv = nullptr; int juce_argc = 0; File File::getSpecialLocation (const SpecialLocationType type) { switch (type) { case userHomeDirectory: { if (const char* homeDir = getenv ("HOME")) return File (CharPointer_UTF8 (homeDir)); if (auto* pw = getpwuid (getuid())) return File (CharPointer_UTF8 (pw->pw_dir)); return {}; } case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents"); case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~/Music"); case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~/Videos"); case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~/Pictures"); case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~/.config"); case commonDocumentsDirectory: case commonApplicationDataDirectory: return File ("/opt"); case globalApplicationsDirectory: return File ("/usr"); case tempDirectory: { if (const char* tmpDir = getenv ("TMPDIR")) return File (CharPointer_UTF8 (tmpDir)); return File ("/tmp"); } case invokedExecutableFile: if (juce_argv != nullptr && juce_argc > 0) return File (CharPointer_UTF8 (juce_argv[0])); // Falls through JUCE_FALLTHROUGH case currentExecutableFile: case currentApplicationFile: #if ! JUCE_STANDALONE_APPLICATION return juce_getExecutableFile(); #endif // deliberate fall-through if this is not a shared-library JUCE_FALLTHROUGH case hostApplicationPath: { const File f ("/proc/self/exe"); return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile(); } default: jassertfalse; // unknown type? break; } return {}; } //============================================================================== bool File::moveToTrash() const { if (! exists()) return true; File trashCan ("~/.Trash"); if (! trashCan.isDirectory()) trashCan = "~/.local/share/Trash/files"; if (! trashCan.isDirectory()) return false; return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(), getFileExtension())); } //============================================================================== static bool isFileExecutable (const String& filename) { juce_statStruct info; return juce_stat (filename, info) && S_ISREG (info.st_mode) && access (filename.toUTF8(), X_OK) == 0; } bool Process::openDocument (const String& fileName, const String& parameters) { auto cmdString = fileName.replace (" ", "\\ ", false); cmdString << " " << parameters; if (cmdString.startsWithIgnoreCase ("file:") || File::createFileWithoutCheckingPath (fileName).isDirectory() || ! isFileExecutable (fileName)) { StringArray cmdLines; for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", "google-chrome", "chromium-browser", "opera", "konqueror" }) { cmdLines.add (String (browserName) + " " + cmdString.trim().quoted()); } cmdString = cmdLines.joinIntoString (" || "); } const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr }; auto cpid = fork(); if (cpid == 0) { setsid(); // Child process execve (argv[0], (char**) argv, environ); exit (0); } return cpid >= 0; } void File::revealToUser() const { if (isDirectory()) startAsProcess(); else if (getParentDirectory().exists()) getParentDirectory().startAsProcess(); } } // namespace juce