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 namespace juce
24 {
25 
CriticalSection()26 CriticalSection::CriticalSection() noexcept
27 {
28     pthread_mutexattr_t atts;
29     pthread_mutexattr_init (&atts);
30     pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE);
31    #if ! JUCE_ANDROID
32     pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);
33    #endif
34     pthread_mutex_init (&lock, &atts);
35     pthread_mutexattr_destroy (&atts);
36 }
37 
~CriticalSection()38 CriticalSection::~CriticalSection() noexcept        { pthread_mutex_destroy (&lock); }
enter()39 void CriticalSection::enter() const noexcept        { pthread_mutex_lock (&lock); }
tryEnter()40 bool CriticalSection::tryEnter() const noexcept     { return pthread_mutex_trylock (&lock) == 0; }
exit()41 void CriticalSection::exit() const noexcept         { pthread_mutex_unlock (&lock); }
42 
43 //==============================================================================
sleep(int millisecs)44 void JUCE_CALLTYPE Thread::sleep (int millisecs)
45 {
46     struct timespec time;
47     time.tv_sec = millisecs / 1000;
48     time.tv_nsec = (millisecs % 1000) * 1000000;
49     nanosleep (&time, nullptr);
50 }
51 
terminate()52 void JUCE_CALLTYPE Process::terminate()
53 {
54    #if JUCE_ANDROID
55     _exit (EXIT_FAILURE);
56    #else
57     std::_Exit (EXIT_FAILURE);
58    #endif
59 }
60 
61 
62 #if JUCE_MAC || JUCE_LINUX || JUCE_BSD
setMaxNumberOfFileHandles(int newMaxNumber)63 bool Process::setMaxNumberOfFileHandles (int newMaxNumber) noexcept
64 {
65     rlimit lim;
66 
67     if (getrlimit (RLIMIT_NOFILE, &lim) == 0)
68     {
69         if (newMaxNumber <= 0 && lim.rlim_cur == RLIM_INFINITY && lim.rlim_max == RLIM_INFINITY)
70             return true;
71 
72         if (newMaxNumber > 0 && lim.rlim_cur >= (rlim_t) newMaxNumber)
73             return true;
74     }
75 
76     lim.rlim_cur = lim.rlim_max = newMaxNumber <= 0 ? RLIM_INFINITY : (rlim_t) newMaxNumber;
77     return setrlimit (RLIMIT_NOFILE, &lim) == 0;
78 }
79 
80 struct MaxNumFileHandlesInitialiser
81 {
MaxNumFileHandlesInitialiserMaxNumFileHandlesInitialiser82     MaxNumFileHandlesInitialiser() noexcept
83     {
84        #ifndef JUCE_PREFERRED_MAX_FILE_HANDLES
85         enum { JUCE_PREFERRED_MAX_FILE_HANDLES = 8192 };
86        #endif
87 
88         // Try to give our app a decent number of file handles by default
89         if (! Process::setMaxNumberOfFileHandles (0))
90         {
91             for (int num = JUCE_PREFERRED_MAX_FILE_HANDLES; num > 256; num -= 1024)
92                 if (Process::setMaxNumberOfFileHandles (num))
93                     break;
94         }
95     }
96 };
97 
98 static MaxNumFileHandlesInitialiser maxNumFileHandlesInitialiser;
99 #endif
100 
101 //==============================================================================
102 JUCE_DECLARE_DEPRECATED_STATIC (const juce_wchar File::separator = '/';)
103 JUCE_DECLARE_DEPRECATED_STATIC (const StringRef File::separatorString ("/");)
104 
getSeparatorChar()105 juce_wchar File::getSeparatorChar()    { return '/'; }
getSeparatorString()106 StringRef File::getSeparatorString()   { return "/"; }
107 
108 
109 //==============================================================================
getCurrentWorkingDirectory()110 File File::getCurrentWorkingDirectory()
111 {
112     HeapBlock<char> heapBuffer;
113 
114     char localBuffer[1024];
115     auto cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
116     size_t bufferSize = 4096;
117 
118     while (cwd == nullptr && errno == ERANGE)
119     {
120         heapBuffer.malloc (bufferSize);
121         cwd = getcwd (heapBuffer, bufferSize - 1);
122         bufferSize += 1024;
123     }
124 
125     return File (CharPointer_UTF8 (cwd));
126 }
127 
setAsCurrentWorkingDirectory()128 bool File::setAsCurrentWorkingDirectory() const
129 {
130     return chdir (getFullPathName().toUTF8()) == 0;
131 }
132 
133 //==============================================================================
134 // The unix siginterrupt function is deprecated - this does the same job.
juce_siginterrupt(int sig,int flag)135 int juce_siginterrupt (int sig, int flag)
136 {
137    #if JUCE_WASM
138     ignoreUnused (sig, flag);
139     return 0;
140    #else
141     #if JUCE_ANDROID
142      using juce_sigactionflags_type = unsigned long;
143     #else
144      using juce_sigactionflags_type = int;
145     #endif
146 
147     struct ::sigaction act;
148     (void) ::sigaction (sig, nullptr, &act);
149 
150     if (flag != 0)
151         act.sa_flags &= static_cast<juce_sigactionflags_type> (~SA_RESTART);
152     else
153         act.sa_flags |= static_cast<juce_sigactionflags_type> (SA_RESTART);
154 
155     return ::sigaction (sig, &act, nullptr);
156    #endif
157 }
158 
159 //==============================================================================
160 namespace
161 {
162    #if JUCE_LINUX || (JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T) // (this iOS stuff is to avoid a simulator bug)
163     using juce_statStruct = struct stat64;
164     #define JUCE_STAT  stat64
165    #elif JUCE_BSD
166     using juce_statStruct = struct stat;
167     #define JUCE_STAT  stat
168    #else
169     using juce_statStruct = struct stat;
170     #define JUCE_STAT  stat
171    #endif
172 
juce_stat(const String & fileName,juce_statStruct & info)173     bool juce_stat (const String& fileName, juce_statStruct& info)
174     {
175         return fileName.isNotEmpty()
176                  && JUCE_STAT (fileName.toUTF8(), &info) == 0;
177     }
178 
179    #if ! JUCE_WASM
180     // if this file doesn't exist, find a parent of it that does..
juce_doStatFS(File f,struct statfs & result)181     bool juce_doStatFS (File f, struct statfs& result)
182     {
183         for (int i = 5; --i >= 0;)
184         {
185             if (f.exists())
186                 break;
187 
188             f = f.getParentDirectory();
189         }
190 
191         return statfs (f.getFullPathName().toUTF8(), &result) == 0;
192     }
193 
194    #if JUCE_MAC || JUCE_IOS
getCreationTime(const juce_statStruct & s)195     static int64 getCreationTime (const juce_statStruct& s) noexcept     { return (int64) s.st_birthtime; }
196    #else
getCreationTime(const juce_statStruct & s)197     static int64 getCreationTime (const juce_statStruct& s) noexcept     { return (int64) s.st_ctime; }
198    #endif
199 
updateStatInfoForFile(const String & path,bool * isDir,int64 * fileSize,Time * modTime,Time * creationTime,bool * isReadOnly)200     void updateStatInfoForFile (const String& path, bool* isDir, int64* fileSize,
201                                 Time* modTime, Time* creationTime, bool* isReadOnly)
202     {
203         if (isDir != nullptr || fileSize != nullptr || modTime != nullptr || creationTime != nullptr)
204         {
205             juce_statStruct info;
206             const bool statOk = juce_stat (path, info);
207 
208             if (isDir != nullptr)         *isDir        = statOk && ((info.st_mode & S_IFDIR) != 0);
209             if (fileSize != nullptr)      *fileSize     = statOk ? (int64) info.st_size : 0;
210             if (modTime != nullptr)       *modTime      = Time (statOk ? (int64) info.st_mtime  * 1000 : 0);
211             if (creationTime != nullptr)  *creationTime = Time (statOk ? getCreationTime (info) * 1000 : 0);
212         }
213 
214         if (isReadOnly != nullptr)
215             *isReadOnly = access (path.toUTF8(), W_OK) != 0;
216     }
217    #endif
218 
getResultForErrno()219     Result getResultForErrno()
220     {
221         return Result::fail (String (strerror (errno)));
222     }
223 
getResultForReturnValue(int value)224     Result getResultForReturnValue (int value)
225     {
226         return value == -1 ? getResultForErrno() : Result::ok();
227     }
228 
getFD(void * handle)229     int getFD (void* handle) noexcept        { return (int) (pointer_sized_int) handle; }
fdToVoidPointer(int fd)230     void* fdToVoidPointer (int fd) noexcept  { return (void*) (pointer_sized_int) fd; }
231 }
232 
isDirectory()233 bool File::isDirectory() const
234 {
235     juce_statStruct info;
236 
237     return fullPath.isNotEmpty()
238              && (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0));
239 }
240 
exists()241 bool File::exists() const
242 {
243     return fullPath.isNotEmpty()
244              && access (fullPath.toUTF8(), F_OK) == 0;
245 }
246 
existsAsFile()247 bool File::existsAsFile() const
248 {
249     return exists() && ! isDirectory();
250 }
251 
getSize()252 int64 File::getSize() const
253 {
254     juce_statStruct info;
255     return juce_stat (fullPath, info) ? info.st_size : 0;
256 }
257 
getFileIdentifier()258 uint64 File::getFileIdentifier() const
259 {
260     juce_statStruct info;
261     return juce_stat (fullPath, info) ? (uint64) info.st_ino : 0;
262 }
263 
hasEffectiveRootFilePermissions()264 static bool hasEffectiveRootFilePermissions()
265 {
266    #if JUCE_LINUX || JUCE_BSD
267     return geteuid() == 0;
268    #else
269     return false;
270    #endif
271 }
272 
273 //==============================================================================
hasWriteAccess()274 bool File::hasWriteAccess() const
275 {
276     if (exists())
277         return (hasEffectiveRootFilePermissions()
278              || access (fullPath.toUTF8(), W_OK) == 0);
279 
280     if ((! isDirectory()) && fullPath.containsChar (getSeparatorChar()))
281         return getParentDirectory().hasWriteAccess();
282 
283     return false;
284 }
285 
setFileModeFlags(const String & fullPath,mode_t flags,bool shouldSet)286 static bool setFileModeFlags (const String& fullPath, mode_t flags, bool shouldSet) noexcept
287 {
288     juce_statStruct info;
289 
290     if (! juce_stat (fullPath, info))
291         return false;
292 
293     info.st_mode &= 0777;
294 
295     if (shouldSet)
296         info.st_mode |= flags;
297     else
298         info.st_mode &= ~flags;
299 
300     return chmod (fullPath.toUTF8(), (mode_t) info.st_mode) == 0;
301 }
302 
setFileReadOnlyInternal(bool shouldBeReadOnly)303 bool File::setFileReadOnlyInternal (bool shouldBeReadOnly) const
304 {
305     // Hmm.. should we give global write permission or just the current user?
306     return setFileModeFlags (fullPath, S_IWUSR | S_IWGRP | S_IWOTH, ! shouldBeReadOnly);
307 }
308 
setFileExecutableInternal(bool shouldBeExecutable)309 bool File::setFileExecutableInternal (bool shouldBeExecutable) const
310 {
311     return setFileModeFlags (fullPath, S_IXUSR | S_IXGRP | S_IXOTH, shouldBeExecutable);
312 }
313 
getFileTimesInternal(int64 & modificationTime,int64 & accessTime,int64 & creationTime)314 void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const
315 {
316     modificationTime = 0;
317     accessTime = 0;
318     creationTime = 0;
319 
320     juce_statStruct info;
321 
322     if (juce_stat (fullPath, info))
323     {
324       #if JUCE_MAC || (JUCE_IOS && __DARWIN_ONLY_64_BIT_INO_T)
325         modificationTime  = (int64) info.st_mtimespec.tv_sec * 1000 + info.st_mtimespec.tv_nsec / 1000000;
326         accessTime        = (int64) info.st_atimespec.tv_sec * 1000 + info.st_atimespec.tv_nsec / 1000000;
327         creationTime      = (int64) info.st_birthtimespec.tv_sec * 1000 + info.st_birthtimespec.tv_nsec / 1000000;
328       #else
329         modificationTime  = (int64) info.st_mtime * 1000;
330         accessTime        = (int64) info.st_atime * 1000;
331        #if JUCE_IOS
332         creationTime      = (int64) info.st_birthtime * 1000;
333        #else
334         creationTime      = (int64) info.st_ctime * 1000;
335        #endif
336       #endif
337     }
338 }
339 
setFileTimesInternal(int64 modificationTime,int64 accessTime,int64)340 bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 /*creationTime*/) const
341 {
342    #if ! JUCE_WASM
343     juce_statStruct info;
344 
345     if ((modificationTime != 0 || accessTime != 0) && juce_stat (fullPath, info))
346     {
347        #if JUCE_MAC || (JUCE_IOS && __DARWIN_ONLY_64_BIT_INO_T)
348         struct timeval times[2];
349 
350         bool setModificationTime = (modificationTime != 0);
351         bool setAccessTime       = (accessTime != 0);
352 
353         times[0].tv_sec  = setAccessTime ? static_cast<__darwin_time_t> (accessTime / 1000)
354                                          : info.st_atimespec.tv_sec;
355 
356         times[0].tv_usec = setAccessTime ? static_cast<__darwin_suseconds_t> ((accessTime % 1000) * 1000)
357                                          : static_cast<__darwin_suseconds_t> (info.st_atimespec.tv_nsec / 1000);
358 
359         times[1].tv_sec  = setModificationTime ? static_cast<__darwin_time_t> (modificationTime / 1000)
360                                                : info.st_mtimespec.tv_sec;
361 
362         times[1].tv_usec = setModificationTime ? static_cast<__darwin_suseconds_t> ((modificationTime % 1000) * 1000)
363                                                : static_cast<__darwin_suseconds_t> (info.st_mtimespec.tv_nsec / 1000);
364 
365         return utimes (fullPath.toUTF8(), times) == 0;
366        #else
367         struct utimbuf times;
368         times.actime  = accessTime != 0       ? static_cast<time_t> (accessTime / 1000)       : static_cast<time_t> (info.st_atime);
369         times.modtime = modificationTime != 0 ? static_cast<time_t> (modificationTime / 1000) : static_cast<time_t> (info.st_mtime);
370 
371         return utime (fullPath.toUTF8(), &times) == 0;
372        #endif
373     }
374    #endif
375 
376     return false;
377 }
378 
deleteFile()379 bool File::deleteFile() const
380 {
381     if (! isSymbolicLink())
382     {
383         if (! exists())
384             return true;
385 
386         if (isDirectory())
387             return rmdir (fullPath.toUTF8()) == 0;
388     }
389 
390     return remove (fullPath.toUTF8()) == 0;
391 }
392 
moveInternal(const File & dest)393 bool File::moveInternal (const File& dest) const
394 {
395     if (rename (fullPath.toUTF8(), dest.getFullPathName().toUTF8()) == 0)
396         return true;
397 
398     if (hasWriteAccess() && copyInternal (dest))
399     {
400         if (deleteFile())
401             return true;
402 
403         dest.deleteFile();
404     }
405 
406     return false;
407 }
408 
replaceInternal(const File & dest)409 bool File::replaceInternal (const File& dest) const
410 {
411     return moveInternal (dest);
412 }
413 
createDirectoryInternal(const String & fileName)414 Result File::createDirectoryInternal (const String& fileName) const
415 {
416     return getResultForReturnValue (mkdir (fileName.toUTF8(), 0777));
417 }
418 
419 //==============================================================================
juce_fileSetPosition(void * handle,int64 pos)420 int64 juce_fileSetPosition (void* handle, int64 pos)
421 {
422     if (handle != nullptr && lseek (getFD (handle), (off_t) pos, SEEK_SET) == pos)
423         return pos;
424 
425     return -1;
426 }
427 
openHandle()428 void FileInputStream::openHandle()
429 {
430     auto f = open (file.getFullPathName().toUTF8(), O_RDONLY);
431 
432     if (f != -1)
433         fileHandle = fdToVoidPointer (f);
434     else
435         status = getResultForErrno();
436 }
437 
~FileInputStream()438 FileInputStream::~FileInputStream()
439 {
440     if (fileHandle != nullptr)
441         close (getFD (fileHandle));
442 }
443 
readInternal(void * buffer,size_t numBytes)444 size_t FileInputStream::readInternal (void* buffer, size_t numBytes)
445 {
446     ssize_t result = 0;
447 
448     if (fileHandle != nullptr)
449     {
450         result = ::read (getFD (fileHandle), buffer, numBytes);
451 
452         if (result < 0)
453         {
454             status = getResultForErrno();
455             result = 0;
456         }
457     }
458 
459     return (size_t) result;
460 }
461 
462 //==============================================================================
openHandle()463 void FileOutputStream::openHandle()
464 {
465     if (file.exists())
466     {
467         auto f = open (file.getFullPathName().toUTF8(), O_RDWR);
468 
469         if (f != -1)
470         {
471             currentPosition = lseek (f, 0, SEEK_END);
472 
473             if (currentPosition >= 0)
474             {
475                 fileHandle = fdToVoidPointer (f);
476             }
477             else
478             {
479                 status = getResultForErrno();
480                 close (f);
481             }
482         }
483         else
484         {
485             status = getResultForErrno();
486         }
487     }
488     else
489     {
490         auto f = open (file.getFullPathName().toUTF8(), O_RDWR | O_CREAT, 00644);
491 
492         if (f != -1)
493             fileHandle = fdToVoidPointer (f);
494         else
495             status = getResultForErrno();
496     }
497 }
498 
closeHandle()499 void FileOutputStream::closeHandle()
500 {
501     if (fileHandle != nullptr)
502     {
503         close (getFD (fileHandle));
504         fileHandle = nullptr;
505     }
506 }
507 
writeInternal(const void * data,size_t numBytes)508 ssize_t FileOutputStream::writeInternal (const void* data, size_t numBytes)
509 {
510     if (fileHandle == nullptr)
511         return 0;
512 
513     auto result = ::write (getFD (fileHandle), data, numBytes);
514 
515     if (result == -1)
516         status = getResultForErrno();
517 
518     return (ssize_t) result;
519 }
520 
521 #ifndef JUCE_ANDROID
flushInternal()522 void FileOutputStream::flushInternal()
523 {
524     if (fileHandle != nullptr && fsync (getFD (fileHandle)) == -1)
525         status = getResultForErrno();
526 }
527 #endif
528 
truncate()529 Result FileOutputStream::truncate()
530 {
531     if (fileHandle == nullptr)
532         return status;
533 
534     flush();
535     return getResultForReturnValue (ftruncate (getFD (fileHandle), (off_t) currentPosition));
536 }
537 
538 //==============================================================================
getEnvironmentVariable(const String & name,const String & defaultValue)539 String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue)
540 {
541     if (auto s = ::getenv (name.toUTF8()))
542         return String::fromUTF8 (s);
543 
544     return defaultValue;
545 }
546 
547 //==============================================================================
548 #if ! JUCE_WASM
openInternal(const File & file,AccessMode mode,bool exclusive)549 void MemoryMappedFile::openInternal (const File& file, AccessMode mode, bool exclusive)
550 {
551     jassert (mode == readOnly || mode == readWrite);
552 
553     if (range.getStart() > 0)
554     {
555         auto pageSize = sysconf (_SC_PAGE_SIZE);
556         range.setStart (range.getStart() - (range.getStart() % pageSize));
557     }
558 
559     auto filename = file.getFullPathName().toUTF8();
560 
561     if (mode == readWrite)
562         fileHandle = open (filename, O_CREAT | O_RDWR, 00644);
563     else
564         fileHandle = open (filename, O_RDONLY);
565 
566     if (fileHandle != -1)
567     {
568         auto m = mmap (nullptr, (size_t) range.getLength(),
569                        mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
570                        exclusive ? MAP_PRIVATE : MAP_SHARED, fileHandle,
571                        (off_t) range.getStart());
572 
573         if (m != MAP_FAILED)
574         {
575             address = m;
576             madvise (m, (size_t) range.getLength(), MADV_SEQUENTIAL);
577         }
578         else
579         {
580             range = Range<int64>();
581         }
582     }
583 }
584 
~MemoryMappedFile()585 MemoryMappedFile::~MemoryMappedFile()
586 {
587     if (address != nullptr)
588         munmap (address, (size_t) range.getLength());
589 
590     if (fileHandle != 0)
591         close (fileHandle);
592 }
593 
594 //==============================================================================
595 File juce_getExecutableFile();
juce_getExecutableFile()596 File juce_getExecutableFile()
597 {
598     struct DLAddrReader
599     {
600         static String getFilename()
601         {
602             Dl_info exeInfo;
603 
604             auto localSymbol = (void*) juce_getExecutableFile;
605             dladdr (localSymbol, &exeInfo);
606             return CharPointer_UTF8 (exeInfo.dli_fname);
607         }
608     };
609 
610     static String filename = DLAddrReader::getFilename();
611     return File::getCurrentWorkingDirectory().getChildFile (filename);
612 }
613 
614 //==============================================================================
getBytesFreeOnVolume()615 int64 File::getBytesFreeOnVolume() const
616 {
617     struct statfs buf;
618 
619     if (juce_doStatFS (*this, buf))
620         return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user
621 
622     return 0;
623 }
624 
getVolumeTotalSize()625 int64 File::getVolumeTotalSize() const
626 {
627     struct statfs buf;
628 
629     if (juce_doStatFS (*this, buf))
630         return (int64) buf.f_bsize * (int64) buf.f_blocks;
631 
632     return 0;
633 }
634 
getVolumeLabel()635 String File::getVolumeLabel() const
636 {
637    #if JUCE_MAC
638     struct VolAttrBuf
639     {
640         u_int32_t       length;
641         attrreference_t mountPointRef;
642         char            mountPointSpace[MAXPATHLEN];
643     } attrBuf;
644 
645     struct attrlist attrList;
646     zerostruct (attrList); // (can't use "= {}" on this object because it's a C struct)
647     attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
648     attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
649 
650     File f (*this);
651 
652     for (;;)
653     {
654         if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0)
655             return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset,
656                                      (int) attrBuf.mountPointRef.attr_length);
657 
658         auto parent = f.getParentDirectory();
659 
660         if (f == parent)
661             break;
662 
663         f = parent;
664     }
665    #endif
666 
667     return {};
668 }
669 
getVolumeSerialNumber()670 int File::getVolumeSerialNumber() const
671 {
672     return 0;
673 }
674 
675 #endif
676 
677 //==============================================================================
678 #if ! JUCE_IOS
679 void juce_runSystemCommand (const String&);
juce_runSystemCommand(const String & command)680 void juce_runSystemCommand (const String& command)
681 {
682     int result = system (command.toUTF8());
683     ignoreUnused (result);
684 }
685 
686 String juce_getOutputFromCommand (const String&);
juce_getOutputFromCommand(const String & command)687 String juce_getOutputFromCommand (const String& command)
688 {
689     // slight bodge here, as we just pipe the output into a temp file and read it...
690     auto tempFile = File::getSpecialLocation (File::tempDirectory)
691                       .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false);
692 
693     juce_runSystemCommand (command + " > " + tempFile.getFullPathName());
694 
695     auto result = tempFile.loadFileAsString();
696     tempFile.deleteFile();
697     return result;
698 }
699 #endif
700 
701 //==============================================================================
702 #if JUCE_IOS
703 class InterProcessLock::Pimpl
704 {
705 public:
Pimpl(const String &,int)706     Pimpl (const String&, int)  {}
707 
708     int handle = 1, refCount = 1;  // On iOS just fake success..
709 };
710 
711 #else
712 
713 class InterProcessLock::Pimpl
714 {
715 public:
Pimpl(const String & lockName,int timeOutMillisecs)716     Pimpl (const String& lockName, int timeOutMillisecs)
717     {
718        #if JUCE_MAC
719         if (! createLockFile (File ("~/Library/Caches/com.juce.locks").getChildFile (lockName), timeOutMillisecs))
720             // Fallback if the user's home folder is on a network drive with no ability to lock..
721             createLockFile (File ("/tmp/com.juce.locks").getChildFile (lockName), timeOutMillisecs);
722 
723        #else
724         File tempFolder ("/var/tmp");
725 
726         if (! tempFolder.isDirectory())
727             tempFolder = "/tmp";
728 
729         createLockFile (tempFolder.getChildFile (lockName), timeOutMillisecs);
730        #endif
731     }
732 
~Pimpl()733     ~Pimpl()
734     {
735         closeFile();
736     }
737 
createLockFile(const File & file,int timeOutMillisecs)738     bool createLockFile (const File& file, int timeOutMillisecs)
739     {
740         file.create();
741         handle = open (file.getFullPathName().toUTF8(), O_RDWR);
742 
743         if (handle != 0)
744         {
745             struct flock fl;
746             zerostruct (fl);
747 
748             fl.l_whence = SEEK_SET;
749             fl.l_type = F_WRLCK;
750 
751             auto endTime = Time::currentTimeMillis() + timeOutMillisecs;
752 
753             for (;;)
754             {
755                 auto result = fcntl (handle, F_SETLK, &fl);
756 
757                 if (result >= 0)
758                     return true;
759 
760                 auto error = errno;
761 
762                 if (error != EINTR)
763                 {
764                     if (error == EBADF || error == ENOTSUP)
765                         return false;
766 
767                     if (timeOutMillisecs == 0
768                          || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime))
769                         break;
770 
771                     Thread::sleep (10);
772                 }
773             }
774         }
775 
776         closeFile();
777         return true; // only false if there's a file system error. Failure to lock still returns true.
778     }
779 
closeFile()780     void closeFile()
781     {
782         if (handle != 0)
783         {
784             struct flock fl;
785             zerostruct (fl);
786 
787             fl.l_whence = SEEK_SET;
788             fl.l_type = F_UNLCK;
789 
790             while (! (fcntl (handle, F_SETLKW, &fl) >= 0 || errno != EINTR))
791             {}
792 
793             close (handle);
794             handle = 0;
795         }
796     }
797 
798     int handle = 0, refCount = 1;
799 };
800 #endif
801 
InterProcessLock(const String & nm)802 InterProcessLock::InterProcessLock (const String& nm)  : name (nm)
803 {
804 }
805 
~InterProcessLock()806 InterProcessLock::~InterProcessLock()
807 {
808 }
809 
enter(int timeOutMillisecs)810 bool InterProcessLock::enter (int timeOutMillisecs)
811 {
812     const ScopedLock sl (lock);
813 
814     if (pimpl == nullptr)
815     {
816         pimpl.reset (new Pimpl (name, timeOutMillisecs));
817 
818         if (pimpl->handle == 0)
819             pimpl.reset();
820     }
821     else
822     {
823         pimpl->refCount++;
824     }
825 
826     return pimpl != nullptr;
827 }
828 
exit()829 void InterProcessLock::exit()
830 {
831     const ScopedLock sl (lock);
832 
833     // Trying to release the lock too many times!
834     jassert (pimpl != nullptr);
835 
836     if (pimpl != nullptr && --(pimpl->refCount) == 0)
837         pimpl.reset();
838 }
839 
840 //==============================================================================
841 void JUCE_API juce_threadEntryPoint (void*);
842 
843 #if JUCE_ANDROID
844 extern JavaVM* androidJNIJavaVM;
845 #endif
846 
threadEntryProc(void * userData)847 static void* threadEntryProc (void* userData)
848 {
849     auto* myself = static_cast<Thread*> (userData);
850 
851     JUCE_AUTORELEASEPOOL
852     {
853         juce_threadEntryPoint (myself);
854     }
855 
856    #if JUCE_ANDROID
857     if (androidJNIJavaVM != nullptr)
858     {
859         void* env = nullptr;
860         androidJNIJavaVM->GetEnv(&env, JNI_VERSION_1_2);
861 
862         // only detach if we have actually been attached
863         if (env != nullptr)
864             androidJNIJavaVM->DetachCurrentThread();
865     }
866    #endif
867 
868     return nullptr;
869 }
870 
871 #if JUCE_ANDROID && JUCE_MODULE_AVAILABLE_juce_audio_devices && \
872    ((JUCE_USE_ANDROID_OPENSLES || (! defined(JUCE_USE_ANDROID_OPENSLES) && JUCE_ANDROID_API_VERSION > 8)) \
873  || (JUCE_USE_ANDROID_OBOE || (! defined(JUCE_USE_ANDROID_OBOE) && JUCE_ANDROID_API_VERSION > 15)))
874 
875   #define JUCE_ANDROID_REALTIME_THREAD_AVAILABLE 1
876 #endif
877 
878 #if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE
879 extern pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr);
880 #endif
881 
launchThread()882 void Thread::launchThread()
883 {
884    #if JUCE_ANDROID
885     if (isAndroidRealtimeThread)
886     {
887        #if JUCE_ANDROID_REALTIME_THREAD_AVAILABLE
888         threadHandle = (void*) juce_createRealtimeAudioThread (threadEntryProc, this);
889         threadId = (ThreadID) threadHandle.get();
890 
891         return;
892        #else
893         jassertfalse;
894        #endif
895     }
896    #endif
897 
898     threadHandle = {};
899     pthread_t handle = {};
900     pthread_attr_t attr;
901     pthread_attr_t* attrPtr = nullptr;
902 
903     if (pthread_attr_init (&attr) == 0)
904     {
905         attrPtr = &attr;
906         pthread_attr_setstacksize (attrPtr, threadStackSize);
907     }
908 
909 
910     if (pthread_create (&handle, attrPtr, threadEntryProc, this) == 0)
911     {
912         pthread_detach (handle);
913         threadHandle = (void*) handle;
914         threadId = (ThreadID) threadHandle.get();
915     }
916 
917     if (attrPtr != nullptr)
918         pthread_attr_destroy (attrPtr);
919 }
920 
closeThreadHandle()921 void Thread::closeThreadHandle()
922 {
923     threadId = {};
924     threadHandle = {};
925 }
926 
killThread()927 void Thread::killThread()
928 {
929     if (threadHandle.get() != nullptr)
930     {
931        #if JUCE_ANDROID
932         jassertfalse; // pthread_cancel not available!
933        #else
934         pthread_cancel ((pthread_t) threadHandle.get());
935        #endif
936     }
937 }
938 
setCurrentThreadName(const String & name)939 void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
940 {
941    #if JUCE_IOS || JUCE_MAC
942     JUCE_AUTORELEASEPOOL
943     {
944         [[NSThread currentThread] setName: juceStringToNS (name)];
945     }
946    #elif JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
947     #if ((JUCE_LINUX && (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012) \
948           || JUCE_ANDROID && __ANDROID_API__ >= 9) || JUCE_BSD
949      pthread_setname_np (pthread_self(), name.toRawUTF8());
950     #else
951      prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0);
952     #endif
953    #endif
954 }
955 
setThreadPriority(void * handle,int priority)956 bool Thread::setThreadPriority (void* handle, int priority)
957 {
958     struct sched_param param;
959     int policy;
960     priority = jlimit (0, 10, priority);
961 
962     if (handle == nullptr)
963         handle = (void*) pthread_self();
964 
965     if (pthread_getschedparam ((pthread_t) handle, &policy, &param) != 0)
966         return false;
967 
968     policy = priority == 0 ? SCHED_OTHER : SCHED_RR;
969 
970     const int minPriority = sched_get_priority_min (policy);
971     const int maxPriority = sched_get_priority_max (policy);
972 
973     param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
974     return pthread_setschedparam ((pthread_t) handle, policy, &param) == 0;
975 }
976 
getCurrentThreadId()977 Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
978 {
979     return (ThreadID) pthread_self();
980 }
981 
yield()982 void JUCE_CALLTYPE Thread::yield()
983 {
984     sched_yield();
985 }
986 
987 //==============================================================================
988 /* Remove this macro if you're having problems compiling the cpu affinity
989    calls (the API for these has changed about quite a bit in various Linux
990    versions, and a lot of distros seem to ship with obsolete versions)
991 */
992 #if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES)
993  #define SUPPORT_AFFINITIES 1
994 #endif
995 
setCurrentThreadAffinityMask(uint32 affinityMask)996 void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (uint32 affinityMask)
997 {
998    #if SUPPORT_AFFINITIES
999     cpu_set_t affinity;
1000     CPU_ZERO (&affinity);
1001 
1002     for (int i = 0; i < 32; ++i)
1003         if ((affinityMask & (uint32) (1 << i)) != 0)
1004             CPU_SET ((size_t) i, &affinity);
1005 
1006    #if (! JUCE_ANDROID) && ((! (JUCE_LINUX || JUCE_BSD)) || ((__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2004))
1007     pthread_setaffinity_np (pthread_self(), sizeof (cpu_set_t), &affinity);
1008    #elif JUCE_ANDROID
1009     sched_setaffinity (gettid(), sizeof (cpu_set_t), &affinity);
1010    #else
1011     // NB: this call isn't really correct because it sets the affinity of the process,
1012     // (getpid) not the thread (not gettid). But it's included here as a fallback for
1013     // people who are using ridiculously old versions of glibc
1014     sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
1015    #endif
1016 
1017     sched_yield();
1018 
1019    #else
1020     // affinities aren't supported because either the appropriate header files weren't found,
1021     // or the SUPPORT_AFFINITIES macro was turned off
1022     jassertfalse;
1023     ignoreUnused (affinityMask);
1024    #endif
1025 }
1026 
1027 //==============================================================================
1028 #if ! JUCE_WASM
open(const String & name)1029 bool DynamicLibrary::open (const String& name)
1030 {
1031     close();
1032     handle = dlopen (name.isEmpty() ? nullptr : name.toUTF8().getAddress(), RTLD_LOCAL | RTLD_NOW);
1033     return handle != nullptr;
1034 }
1035 
close()1036 void DynamicLibrary::close()
1037 {
1038     if (handle != nullptr)
1039     {
1040         dlclose (handle);
1041         handle = nullptr;
1042     }
1043 }
1044 
getFunction(const String & functionName)1045 void* DynamicLibrary::getFunction (const String& functionName) noexcept
1046 {
1047     return handle != nullptr ? dlsym (handle, functionName.toUTF8()) : nullptr;
1048 }
1049 
1050 //==============================================================================
1051 #if JUCE_LINUX || JUCE_BSD || JUCE_ANDROID
readPosixConfigFileValue(const char * file,const char * key)1052 static String readPosixConfigFileValue (const char* file, const char* key)
1053 {
1054     StringArray lines;
1055     File (file).readLines (lines);
1056 
1057     for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order)
1058         if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase (key))
1059             return lines[i].fromFirstOccurrenceOf (":", false, false).trim();
1060 
1061     return {};
1062 }
1063 #endif
1064 
1065 
1066 //==============================================================================
1067 class ChildProcess::ActiveProcess
1068 {
1069 public:
ActiveProcess(const StringArray & arguments,int streamFlags)1070     ActiveProcess (const StringArray& arguments, int streamFlags)
1071     {
1072         auto exe = arguments[0].unquoted();
1073 
1074         // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX
1075         // you're trying to launch the .app folder rather than the actual binary inside it?)
1076         jassert (File::getCurrentWorkingDirectory().getChildFile (exe).existsAsFile()
1077                   || ! exe.containsChar (File::getSeparatorChar()));
1078 
1079         int pipeHandles[2] = {};
1080 
1081         if (pipe (pipeHandles) == 0)
1082         {
1083             auto result = fork();
1084 
1085             if (result < 0)
1086             {
1087                 close (pipeHandles[0]);
1088                 close (pipeHandles[1]);
1089             }
1090             else if (result == 0)
1091             {
1092                 // we're the child process..
1093                 close (pipeHandles[0]);   // close the read handle
1094 
1095                 if ((streamFlags & wantStdOut) != 0)
1096                     dup2 (pipeHandles[1], STDOUT_FILENO); // turns the pipe into stdout
1097                 else
1098                     dup2 (open ("/dev/null", O_WRONLY), STDOUT_FILENO);
1099 
1100                 if ((streamFlags & wantStdErr) != 0)
1101                     dup2 (pipeHandles[1], STDERR_FILENO);
1102                 else
1103                     dup2 (open ("/dev/null", O_WRONLY), STDERR_FILENO);
1104 
1105                 close (pipeHandles[1]);
1106 
1107                 Array<char*> argv;
1108 
1109                 for (auto& arg : arguments)
1110                     if (arg.isNotEmpty())
1111                         argv.add (const_cast<char*> (arg.toRawUTF8()));
1112 
1113                 argv.add (nullptr);
1114 
1115                 execvp (exe.toRawUTF8(), argv.getRawDataPointer());
1116                 _exit (-1);
1117             }
1118             else
1119             {
1120                 // we're the parent process..
1121                 childPID = result;
1122                 pipeHandle = pipeHandles[0];
1123                 close (pipeHandles[1]); // close the write handle
1124             }
1125         }
1126     }
1127 
~ActiveProcess()1128     ~ActiveProcess()
1129     {
1130         if (readHandle != nullptr)
1131             fclose (readHandle);
1132 
1133         if (pipeHandle != 0)
1134             close (pipeHandle);
1135     }
1136 
isRunning()1137     bool isRunning() noexcept
1138     {
1139         if (childPID == 0)
1140             return false;
1141 
1142         int childState = 0;
1143         auto pid = waitpid (childPID, &childState, WNOHANG);
1144 
1145         if (pid == 0)
1146             return true;
1147 
1148         if (WIFEXITED (childState))
1149         {
1150             exitCode = WEXITSTATUS (childState);
1151             return false;
1152         }
1153 
1154         return ! WIFSIGNALED (childState);
1155     }
1156 
read(void * dest,int numBytes)1157     int read (void* dest, int numBytes) noexcept
1158     {
1159         jassert (dest != nullptr && numBytes > 0);
1160 
1161         #ifdef fdopen
1162          #error // some crazy 3rd party headers (e.g. zlib) define this function as NULL!
1163         #endif
1164 
1165         if (readHandle == nullptr && childPID != 0)
1166             readHandle = fdopen (pipeHandle, "r");
1167 
1168         if (readHandle != nullptr)
1169         {
1170             for (;;)
1171             {
1172                 auto numBytesRead = (int) fread (dest, 1, (size_t) numBytes, readHandle);
1173 
1174                 if (numBytesRead > 0 || feof (readHandle))
1175                     return numBytesRead;
1176 
1177                 // signal occurred during fread() so try again
1178                 if (ferror (readHandle) && errno == EINTR)
1179                     continue;
1180 
1181                 break;
1182             }
1183         }
1184 
1185         return 0;
1186     }
1187 
killProcess()1188     bool killProcess() const noexcept
1189     {
1190         return ::kill (childPID, SIGKILL) == 0;
1191     }
1192 
getExitCode()1193     uint32 getExitCode() noexcept
1194     {
1195         if (exitCode >= 0)
1196             return (uint32) exitCode;
1197 
1198         if (childPID != 0)
1199         {
1200             int childState = 0;
1201             auto pid = waitpid (childPID, &childState, WNOHANG);
1202 
1203             if (pid >= 0 && WIFEXITED (childState))
1204             {
1205                 exitCode = WEXITSTATUS (childState);
1206                 return (uint32) exitCode;
1207             }
1208         }
1209 
1210         return 0;
1211     }
1212 
1213     int childPID = 0;
1214     int pipeHandle = 0;
1215     int exitCode = -1;
1216     FILE* readHandle = {};
1217 
1218     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess)
1219 };
1220 
start(const String & command,int streamFlags)1221 bool ChildProcess::start (const String& command, int streamFlags)
1222 {
1223     return start (StringArray::fromTokens (command, true), streamFlags);
1224 }
1225 
start(const StringArray & args,int streamFlags)1226 bool ChildProcess::start (const StringArray& args, int streamFlags)
1227 {
1228     if (args.size() == 0)
1229         return false;
1230 
1231     activeProcess.reset (new ActiveProcess (args, streamFlags));
1232 
1233     if (activeProcess->childPID == 0)
1234         activeProcess.reset();
1235 
1236     return activeProcess != nullptr;
1237 }
1238 
1239 #endif
1240 
1241 //==============================================================================
1242 struct HighResolutionTimer::Pimpl
1243 {
PimplPimpl1244     explicit Pimpl (HighResolutionTimer& t)
1245         : owner (t)
1246     {}
1247 
~PimplPimpl1248     ~Pimpl()
1249     {
1250         jassert (periodMs == 0);
1251         stop();
1252     }
1253 
startPimpl1254     void start (int newPeriod)
1255     {
1256         if (periodMs == newPeriod)
1257             return;
1258 
1259         if (thread.get_id() == std::this_thread::get_id())
1260         {
1261             periodMs = newPeriod;
1262             return;
1263         }
1264 
1265         stop();
1266 
1267         periodMs = newPeriod;
1268 
1269         thread = std::thread ([this, newPeriod]
1270         {
1271             setThisThreadToRealtime ((uint64) newPeriod);
1272 
1273             auto lastPeriod = periodMs.load();
1274             Clock clock (lastPeriod);
1275 
1276             std::unique_lock<std::mutex> unique_lock (timerMutex);
1277 
1278             while (periodMs != 0)
1279             {
1280                 clock.next();
1281                 while (periodMs != 0 && clock.wait (stopCond, unique_lock));
1282 
1283                 if (periodMs == 0)
1284                     break;
1285 
1286                 owner.hiResTimerCallback();
1287 
1288                 auto nextPeriod = periodMs.load();
1289 
1290                 if (lastPeriod != nextPeriod)
1291                 {
1292                     lastPeriod = nextPeriod;
1293                     clock = Clock (lastPeriod);
1294                 }
1295             }
1296 
1297             periodMs = 0;
1298         });
1299     }
1300 
stopPimpl1301     void stop()
1302     {
1303         periodMs = 0;
1304 
1305         const auto thread_id = thread.get_id();
1306 
1307         if (thread_id == std::thread::id() || thread_id == std::this_thread::get_id())
1308             return;
1309 
1310         {
1311             std::unique_lock<std::mutex> unique_lock (timerMutex);
1312             stopCond.notify_one();
1313         }
1314 
1315         thread.join();
1316     }
1317 
1318     HighResolutionTimer& owner;
1319     std::atomic<int> periodMs { 0 };
1320 
1321 private:
1322     std::thread thread;
1323     std::condition_variable stopCond;
1324     std::mutex timerMutex;
1325 
1326     class Clock
1327     {
1328     public:
ClockPimpl1329         explicit Clock (std::chrono::steady_clock::rep millis) noexcept
1330             : time (std::chrono::steady_clock::now()),
1331               delta (std::chrono::milliseconds (millis))
1332         {}
1333 
waitPimpl1334         bool wait (std::condition_variable& cond, std::unique_lock<std::mutex>& lock) noexcept
1335         {
1336             return cond.wait_until (lock, time) != std::cv_status::timeout;
1337         }
1338 
nextPimpl1339         void next() noexcept
1340         {
1341             time += delta;
1342         }
1343 
1344     private:
1345         std::chrono::time_point<std::chrono::steady_clock> time;
1346         std::chrono::steady_clock::duration delta;
1347     };
1348 
setThisThreadToRealtimePimpl1349     static bool setThisThreadToRealtime (uint64 periodMs)
1350     {
1351         const auto thread = pthread_self();
1352 
1353        #if JUCE_MAC || JUCE_IOS
1354         mach_timebase_info_data_t timebase;
1355         mach_timebase_info (&timebase);
1356 
1357         const auto ticksPerMs = ((double) timebase.denom * 1000000.0) / (double) timebase.numer;
1358         const auto periodTicks = (uint32_t) (ticksPerMs * periodMs);
1359 
1360         thread_time_constraint_policy_data_t policy;
1361         policy.period      = periodTicks;
1362         policy.computation = jmin ((uint32_t) 50000, policy.period);
1363         policy.constraint  = policy.period;
1364         policy.preemptible = true;
1365 
1366         return thread_policy_set (pthread_mach_thread_np (thread),
1367                                   THREAD_TIME_CONSTRAINT_POLICY,
1368                                   (thread_policy_t) &policy,
1369                                   THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS;
1370 
1371        #else
1372         ignoreUnused (periodMs);
1373         struct sched_param param;
1374         param.sched_priority = sched_get_priority_max (SCHED_RR);
1375         return pthread_setschedparam (thread, SCHED_RR, &param) == 0;
1376        #endif
1377     }
1378 
1379     JUCE_DECLARE_NON_COPYABLE (Pimpl)
1380 };
1381 
1382 } // namespace juce
1383