1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
15
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
19
20 ==============================================================================
21 */
22
23 #if defined(__FreeBSD__) || defined(__DragonFly__)
24 extern char **environ;
25 #endif
26
27 namespace juce
28 {
29
30 enum
31 {
32 U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h
33 U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h
34 U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h
35 U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h
36 };
37
isOnCDRomDrive() const38 bool File::isOnCDRomDrive() const
39 {
40 struct statfs buf;
41
42 return statfs (getFullPathName().toUTF8(), &buf) == 0
43 && buf.f_type == (short) U_ISOFS_SUPER_MAGIC;
44 }
45
isOnHardDisk() const46 bool File::isOnHardDisk() const
47 {
48 struct statfs buf;
49
50 if (statfs (getFullPathName().toUTF8(), &buf) == 0)
51 {
52 switch (buf.f_type)
53 {
54 case U_ISOFS_SUPER_MAGIC: // CD-ROM
55 case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem)
56 case U_NFS_SUPER_MAGIC: // Network NFS
57 case U_SMB_SUPER_MAGIC: // Network Samba
58 return false;
59
60 default: break;
61 }
62 }
63
64 // Assume so if this fails for some reason
65 return true;
66 }
67
isOnRemovableDrive() const68 bool File::isOnRemovableDrive() const
69 {
70 jassertfalse; // xxx not implemented for linux!
71 return false;
72 }
73
getVersion() const74 String File::getVersion() const
75 {
76 return {}; // xxx not yet implemented
77 }
78
79 //==============================================================================
resolveXDGFolder(const char * const type,const char * const fallbackFolder)80 static File resolveXDGFolder (const char* const type, const char* const fallbackFolder)
81 {
82 StringArray confLines;
83 File ("~/.config/user-dirs.dirs").readLines (confLines);
84
85 for (int i = 0; i < confLines.size(); ++i)
86 {
87 const String line (confLines[i].trimStart());
88
89 if (line.startsWith (type))
90 {
91 // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music
92 const File f (line.replace ("$HOME", File ("~").getFullPathName())
93 .fromFirstOccurrenceOf ("=", false, false)
94 .trim().unquoted());
95
96 if (f.isDirectory())
97 return f;
98 }
99 }
100
101 return File (fallbackFolder);
102 }
103
104 const char* const* juce_argv = nullptr;
105 int juce_argc = 0;
106
getSpecialLocation(const SpecialLocationType type)107 File File::getSpecialLocation (const SpecialLocationType type)
108 {
109 switch (type)
110 {
111 case userHomeDirectory:
112 {
113 if (const char* homeDir = getenv ("HOME"))
114 return File (CharPointer_UTF8 (homeDir));
115
116 if (auto* pw = getpwuid (getuid()))
117 return File (CharPointer_UTF8 (pw->pw_dir));
118
119 return {};
120 }
121
122 case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents");
123 case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~/Music");
124 case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~/Videos");
125 case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~/Pictures");
126 case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop");
127 case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~/.config");
128 case commonDocumentsDirectory:
129 case commonApplicationDataDirectory: return File ("/opt");
130 case globalApplicationsDirectory: return File ("/usr");
131
132 case tempDirectory:
133 {
134 if (const char* tmpDir = getenv ("TMPDIR"))
135 return File (CharPointer_UTF8 (tmpDir));
136
137 return File ("/tmp");
138 }
139
140 case invokedExecutableFile:
141 if (juce_argv != nullptr && juce_argc > 0)
142 return File (CharPointer_UTF8 (juce_argv[0]));
143 // Falls through
144 JUCE_FALLTHROUGH
145
146 case currentExecutableFile:
147 case currentApplicationFile:
148 #if ! JUCE_STANDALONE_APPLICATION
149 return juce_getExecutableFile();
150 #endif
151 // deliberate fall-through if this is not a shared-library
152 JUCE_FALLTHROUGH
153
154 case hostApplicationPath:
155 {
156 const File f ("/proc/self/exe");
157 return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
158 }
159
160 default:
161 jassertfalse; // unknown type?
162 break;
163 }
164
165 return {};
166 }
167
168 //==============================================================================
moveToTrash() const169 bool File::moveToTrash() const
170 {
171 if (! exists())
172 return true;
173
174 File trashCan ("~/.Trash");
175
176 if (! trashCan.isDirectory())
177 trashCan = "~/.local/share/Trash/files";
178
179 if (! trashCan.isDirectory())
180 return false;
181
182 return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(),
183 getFileExtension()));
184 }
185
186 //==============================================================================
isFileExecutable(const String & filename)187 static bool isFileExecutable (const String& filename)
188 {
189 juce_statStruct info;
190
191 return juce_stat (filename, info)
192 && S_ISREG (info.st_mode)
193 && access (filename.toUTF8(), X_OK) == 0;
194 }
195
openDocument(const String & fileName,const String & parameters)196 bool Process::openDocument (const String& fileName, const String& parameters)
197 {
198 auto cmdString = fileName.replace (" ", "\\ ", false);
199 cmdString << " " << parameters;
200
201 if (cmdString.startsWithIgnoreCase ("file:")
202 || File::createFileWithoutCheckingPath (fileName).isDirectory()
203 || ! isFileExecutable (fileName))
204 {
205 StringArray cmdLines;
206
207 for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
208 "google-chrome", "chromium-browser", "opera", "konqueror" })
209 {
210 cmdLines.add (String (browserName) + " " + cmdString.trim().quoted());
211 }
212
213 cmdString = cmdLines.joinIntoString (" || ");
214 }
215
216 const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr };
217
218 auto cpid = fork();
219
220 if (cpid == 0)
221 {
222 setsid();
223
224 // Child process
225 execve (argv[0], (char**) argv, environ);
226 exit (0);
227 }
228
229 return cpid >= 0;
230 }
231
revealToUser() const232 void File::revealToUser() const
233 {
234 if (isDirectory())
235 startAsProcess();
236 else if (getParentDirectory().exists())
237 getParentDirectory().startAsProcess();
238 }
239
240 } // namespace juce
241