1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
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 namespace juce
24 {
25
26 enum
27 {
28 U_ISOFS_SUPER_MAGIC = 0x9660, // linux/iso_fs.h
29 U_MSDOS_SUPER_MAGIC = 0x4d44, // linux/msdos_fs.h
30 U_NFS_SUPER_MAGIC = 0x6969, // linux/nfs_fs.h
31 U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h
32 };
33
isOnCDRomDrive() const34 bool File::isOnCDRomDrive() const
35 {
36 struct statfs buf;
37
38 #if JUCE_BSD
39 if (statfs(getFullPathName().toUTF8(), &buf) == 0) {
40 String s(buf.f_fstypename);
41 return s.compare("cd9660") == 0 || s.compare("udf") == 0;
42 }
43 return false;
44 #else
45 return statfs (getFullPathName().toUTF8(), &buf) == 0
46 && buf.f_type == (short) U_ISOFS_SUPER_MAGIC;
47 #endif
48 }
49
isOnHardDisk() const50 bool File::isOnHardDisk() const
51 {
52 struct statfs buf;
53
54 if (statfs (getFullPathName().toUTF8(), &buf) == 0)
55 {
56 #if JUCE_BSD
57 String s(buf.f_fstypename);
58 if (s.compare("cd9660") == 0 || s.compare("udf") == 0 ||
59 s.compare("nfs") == 0 || s.compare("smbfs") == 0) {
60 return false;
61 }
62 #else
63 switch (buf.f_type)
64 {
65 case U_ISOFS_SUPER_MAGIC: // CD-ROM
66 case U_MSDOS_SUPER_MAGIC: // Probably floppy (but could be mounted FAT filesystem)
67 case U_NFS_SUPER_MAGIC: // Network NFS
68 case U_SMB_SUPER_MAGIC: // Network Samba
69 return false;
70
71 default: break;
72 }
73 #endif
74 }
75
76 // Assume so if this fails for some reason
77 return true;
78 }
79
isOnRemovableDrive() const80 bool File::isOnRemovableDrive() const
81 {
82 jassertfalse; // xxx not implemented for linux!
83 return false;
84 }
85
getVersion() const86 String File::getVersion() const
87 {
88 return {}; // xxx not yet implemented
89 }
90
91 //==============================================================================
resolveXDGFolder(const char * const type,const char * const fallbackFolder)92 static File resolveXDGFolder (const char* const type, const char* const fallbackFolder)
93 {
94 StringArray confLines;
95 File ("~/.config/user-dirs.dirs").readLines (confLines);
96
97 for (int i = 0; i < confLines.size(); ++i)
98 {
99 const String line (confLines[i].trimStart());
100
101 if (line.startsWith (type))
102 {
103 // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music
104 const File f (line.replace ("$HOME", File ("~").getFullPathName())
105 .fromFirstOccurrenceOf ("=", false, false)
106 .trim().unquoted());
107
108 if (f.isDirectory())
109 return f;
110 }
111 }
112
113 return File (fallbackFolder);
114 }
115
116 const char* const* juce_argv = nullptr;
117 int juce_argc = 0;
118
getSpecialLocation(const SpecialLocationType type)119 File File::getSpecialLocation (const SpecialLocationType type)
120 {
121 switch (type)
122 {
123 case userHomeDirectory:
124 {
125 if (const char* homeDir = getenv ("HOME"))
126 return File (CharPointer_UTF8 (homeDir));
127
128 if (auto* pw = getpwuid (getuid()))
129 return File (CharPointer_UTF8 (pw->pw_dir));
130
131 return {};
132 }
133
134 case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~/Documents");
135 case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~/Music");
136 case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~/Videos");
137 case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~/Pictures");
138 case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop");
139 case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~/.config");
140 case commonDocumentsDirectory:
141 case commonApplicationDataDirectory: return File ("/opt");
142 case globalApplicationsDirectory: return File ("/usr");
143
144 case tempDirectory:
145 {
146 if (const char* tmpDir = getenv ("TMPDIR"))
147 return File (CharPointer_UTF8 (tmpDir));
148
149 return File ("/tmp");
150 }
151
152 case invokedExecutableFile:
153 if (juce_argv != nullptr && juce_argc > 0)
154 return File (CharPointer_UTF8 (juce_argv[0]));
155 // deliberate fall-through...
156
157 case currentExecutableFile:
158 case currentApplicationFile:
159 #if ! JUCE_STANDALONE_APPLICATION
160 return juce_getExecutableFile();
161 #endif
162 // deliberate fall-through if this is not a shared-library
163
164 case hostApplicationPath:
165 {
166 const File f ("/proc/self/exe");
167 return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
168 }
169
170 default:
171 jassertfalse; // unknown type?
172 break;
173 }
174
175 return {};
176 }
177
178 //==============================================================================
moveToTrash() const179 bool File::moveToTrash() const
180 {
181 if (! exists())
182 return true;
183
184 File trashCan ("~/.Trash");
185
186 if (! trashCan.isDirectory())
187 trashCan = "~/.local/share/Trash/files";
188
189 if (! trashCan.isDirectory())
190 return false;
191
192 return moveFileTo (trashCan.getNonexistentChildFile (getFileNameWithoutExtension(),
193 getFileExtension()));
194 }
195
196 //==============================================================================
isFileExecutable(const String & filename)197 static bool isFileExecutable (const String& filename)
198 {
199 juce_statStruct info;
200
201 return juce_stat (filename, info)
202 && S_ISREG (info.st_mode)
203 && access (filename.toUTF8(), X_OK) == 0;
204 }
205
openDocument(const String & fileName,const String & parameters)206 bool Process::openDocument (const String& fileName, const String& parameters)
207 {
208 auto cmdString = fileName.replace (" ", "\\ ", false);
209 cmdString << " " << parameters;
210
211 if (cmdString.startsWithIgnoreCase ("file:")
212 || File::createFileWithoutCheckingPath (fileName).isDirectory()
213 || ! isFileExecutable (fileName))
214 {
215 StringArray cmdLines;
216
217 for (auto browserName : { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla",
218 "google-chrome", "chromium-browser", "opera", "konqueror" })
219 {
220 cmdLines.add (String (browserName) + " " + cmdString.trim());
221 }
222
223 cmdString = cmdLines.joinIntoString (" || ");
224 }
225
226 const char* const argv[4] = { "/bin/sh", "-c", cmdString.toUTF8(), nullptr };
227
228 auto cpid = fork();
229
230 if (cpid == 0)
231 {
232 setsid();
233
234 // Child process
235 execv (argv[0], (char**) argv);
236 exit (0);
237 }
238
239 return cpid >= 0;
240 }
241
revealToUser() const242 void File::revealToUser() const
243 {
244 if (isDirectory())
245 startAsProcess();
246 else if (getParentDirectory().exists())
247 getParentDirectory().startAsProcess();
248 }
249
250 } // namespace juce
251