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);