diff --git a/src/CoreServices/CMakeLists.txt b/src/CoreServices/CMakeLists.txt index 980f8d9d5..b28a3062a 100644 --- a/src/CoreServices/CMakeLists.txt +++ b/src/CoreServices/CMakeLists.txt @@ -34,6 +34,7 @@ set(CoreServices_SRCS DriverServices.cpp Components.cpp Processes.cpp + LaunchServices.cpp ) if (WITH_COREAUDIO) diff --git a/src/CoreServices/FileManager.cpp b/src/CoreServices/FileManager.cpp index 50d486a91..3e198b18f 100644 --- a/src/CoreServices/FileManager.cpp +++ b/src/CoreServices/FileManager.cpp @@ -44,7 +44,7 @@ along with Darling. If not, see . // Doesn't resolve the last symlink // Mallocates a new buffer static char* realpath_ns(const char* path); -static bool FSRefMakePath(const FSRef* ref, std::string& out); +/*static*/ bool FSRefMakePath(const FSRef* ref, std::string& out); static bool FSRefParamMakePath(const FSRefParam* param, std::string& out); // Is the current user member of the specified group? static bool hasgid(gid_t gid); diff --git a/src/CoreServices/LaunchServices.cpp b/src/CoreServices/LaunchServices.cpp new file mode 100644 index 000000000..a49ab9b0d --- /dev/null +++ b/src/CoreServices/LaunchServices.cpp @@ -0,0 +1,167 @@ +#include "LaunchServices.h" +#include "MacErrors.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "UniChar.h" + +// in FileManager.cpp +bool FSRefMakePath(const FSRef* ref, std::string& out); + +OSStatus LSInit(LSInitializeFlags flags) +{ + return noErr; +} + +OSStatus LSTerm(void) +{ + return noErr; +} + +OSStatus LSOpenApplication(const LSApplicationParameters *appParams, ProcessSerialNumber *outPSN) +{ + if (!appParams) + return paramErr; + + std::string exePath; + std::vector argv; + std::unique_ptr> envp; + int pipefds[2]; + pid_t pid; + OSStatus rv = noErr; + + if (!FSRefMakePath(appParams->application, exePath)) + return fnfErr; + + if (appParams->argv != nullptr) + { + CFIndex count = CFArrayGetCount(appParams->argv); + + for (CFIndex i = 0; i < count; i++) + { + CFStringRef ref = (CFStringRef) CFArrayGetValueAtIndex(appParams->argv, i); + if (CFGetTypeID(ref) != CFStringGetTypeID()) + return paramErr; + + argv.push_back((char*) CFStringGetCStringPtr((CFStringRef) CFArrayGetValueAtIndex(appParams->argv, i), kCFStringEncodingUTF8)); + } + argv.push_back(nullptr); + } + else + { + argv.push_back((char*) exePath.c_str()); + argv.push_back(nullptr); + } + + if (appParams->environment != nullptr) + { + CFIndex count = CFDictionaryGetCount(appParams->environment); + envp.reset(new std::vector); + + CFDictionaryApplyFunction(appParams->environment, [](const void* key, const void* value, void* context) { + CFStringRef skey = (CFStringRef) key; + CFStringRef svalue = (CFStringRef) value; + std::vector* envp = (std::vector*) context; + + if (CFGetTypeID(skey) == CFStringGetTypeID() && CFGetTypeID(svalue) == CFStringGetTypeID()) + { + char* str = new char[CFStringGetLength(skey) + CFStringGetLength(svalue) + 1]; + + strcpy(str, CFStringGetCStringPtr(skey, kCFStringEncodingUTF8)); + strcat(str, "="); + strcat(str, CFStringGetCStringPtr(svalue, kCFStringEncodingUTF8)); + + envp->push_back(str); + } + }, envp.get()); + } + + // https://stackoverflow.com/questions/1584956/how-to-handle-execvp-errors-after-fork + + if (pipe(pipefds)) + { + if (envp) + std::for_each(envp->begin(), envp->end(), [](char* p) { delete [] p; }); + + return makeOSStatus(errno); + } + + fcntl(pipefds[1], F_SETFD, fcntl(pipefds[1], F_GETFD) | FD_CLOEXEC); + + switch (pid = fork()) + { + case -1: + rv = makeOSStatus(errno); + break; + case 0: + close(pipefds[0]); + execvpe(exePath.c_str(), &argv[0], envp ? &(*envp)[0] : nullptr); + write(pipefds[1], &errno, sizeof(int)); + _exit(1); + break; + default: + { + int err, count; + close(pipefds[1]); + + while ((count = read(pipefds[0], &err, sizeof(errno))) == -1) + { + if (errno != EAGAIN && errno != EINTR) + break; + } + + if (count > 0) + rv = makeOSStatus(err); + close(pipefds[0]); + } + } + + if (envp) + std::for_each(envp->begin(), envp->end(), [](char* p) { delete [] p; }); + + return rv; +} + +OSStatus LSSetExtensionHiddenForRef(const FSRef *inRef, Boolean hide) +{ + return noErr; // dummy operation +} + +OSStatus LSSetExtensionHiddenForURL(CFURLRef inURL, Boolean hide) +{ + return noErr; // dummy operation +} + +OSStatus LSOpenCFURLRef(CFURLRef inURL, CFURLRef *outLaunchedURL) +{ + // TODO: use 'xdg-mime query' and 'xdg-mime default' to determine the app? + // TODO: use xdg-open to start the app + return unimpErr; +} + +OSStatus LSOpenFSRef(const FSRef *inRef, FSRef *outLaunchedRef) +{ + return unimpErr; +} + +OSStatus LSGetExtensionInfo(UniCharCount inNameLen, const UniChar* inNameBuffer, UniCharCount *outExtStartIndex) +{ + if (!outExtStartIndex) + return paramErr; + + UnicodeString str((const char*) inNameBuffer, inNameLen*2, UNICHAR_ENCODING); + int32_t pos = str.lastIndexOf('.'); + + if (pos == -1) + *outExtStartIndex = inNameLen; + else + *outExtStartIndex = pos; + + return noErr; +} + diff --git a/src/CoreServices/LaunchServices.h b/src/CoreServices/LaunchServices.h new file mode 100644 index 000000000..012efe2bc --- /dev/null +++ b/src/CoreServices/LaunchServices.h @@ -0,0 +1,65 @@ +#ifndef LAUNCHSERVICES_H +#define LAUNCHSERVICES_H +#include "MacTypes.h" +#include "FileManager.h" +#include "Processes.h" +#include +#include +#include + +extern "C" { + +typedef OptionBits LSInitializeFlags; +typedef OptionBits LSLaunchFlags; +typedef void AppleEvent; + +enum +{ + kLSLaunchDefaults = 0x00000001, + kLSLaunchAndPrint = 0x00000002, + kLSLaunchReserved2 = 0x00000004, + kLSLaunchReserved3 = 0x00000008, + kLSLaunchReserved4 = 0x00000010, + kLSLaunchReserved5 = 0x00000020, + kLSLaunchAndDisplayErrors = 0x00000040, + kLSLaunchInhibitBGOnly = 0x00000080, + kLSLaunchDontAddToRecents = 0x00000100, + kLSLaunchDontSwitch = 0x00000200, + kLSLaunchNoParams = 0x00000800, + kLSLaunchAsync = 0x00010000, + kLSLaunchStartClassic = 0x00020000, + kLSLaunchInClassic = 0x00040000, + kLSLaunchNewInstance = 0x00080000, + kLSLaunchAndHide = 0x00100000, + kLSLaunchAndHideOthers = 0x00200000, + kLSLaunchHasUntrustedContents = 0x00400000 +}; + +struct LSApplicationParameters +{ + CFIndex version; + LSLaunchFlags flags; + const FSRef * application; + void * asyncLaunchRefCon; + CFDictionaryRef environment; + CFArrayRef argv; + AppleEvent * initialEvent; +}; + +OSStatus LSInit(LSInitializeFlags flags); +OSStatus LSTerm(void); + +OSStatus LSOpenApplication(const LSApplicationParameters *appParams, ProcessSerialNumber *outPSN); +OSStatus LSOpenCFURLRef(CFURLRef inURL, CFURLRef *outLaunchedURL); +OSStatus LSOpenFSRef(const FSRef *inRef, FSRef *outLaunchedRef); + +OSStatus LSGetExtensionInfo(UniCharCount inNameLen, const UniChar* inNameBuffer, UniCharCount *outExtStartIndex); +OSStatus LSSetExtensionHiddenForRef(const FSRef *inRef, Boolean hide); +OSStatus LSSetExtensionHiddenForURL(CFURLRef inURL, Boolean hide); + +// TODO: many other functions + +} + +#endif + diff --git a/src/CoreServices/UniChar.h b/src/CoreServices/UniChar.h new file mode 100644 index 000000000..d67b4760d --- /dev/null +++ b/src/CoreServices/UniChar.h @@ -0,0 +1,16 @@ +#ifndef UNICHAR_H +#define UNICHAR_H +#include "MacTypes.h" +#include + +// UniChar uses platform endianness, whereas ICU expects BE by default +#if __BYTE_ORDER == __BIG_ENDIAN +# define UNICHAR_ENCODING "UTF-16BE" +#elif __BYTE_ORDER == __LITTLE_ENDIAN +# define UNICHAR_ENCODING "UTF-16LE" +#else +# error Cannot determine endianness! +#endif + +#endif + diff --git a/src/CoreServices/UnicodeUtilities.cpp b/src/CoreServices/UnicodeUtilities.cpp index da06d9abb..84fee08b4 100644 --- a/src/CoreServices/UnicodeUtilities.cpp +++ b/src/CoreServices/UnicodeUtilities.cpp @@ -23,19 +23,10 @@ along with Darling. If not, see . #include #include #include -#include +#include "UniChar.h" #include "MacErrors.h" #include "util/log.h" -// UniChar uses platform endianness, whereas ICU expects BE by default -#if __BYTE_ORDER == __BIG_ENDIAN -# define UNICHAR_ENCODING "UTF-16BE" -#elif __BYTE_ORDER == __LITTLE_ENDIAN -# define UNICHAR_ENCODING "UTF-16LE" -#else -# error Cannot determine endianness! -#endif - namespace Darling { int getLocaleUID(const std::string& str);