diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-01-06 20:12:03 +0000 | 
| commit | 9e6d35490a6542f9c97607f93c2ef8ca8e03cbcc (patch) | |
| tree | dd2a1ddf0476664c2b823409c36cbccd52662ca7 /source/Host/macosx | |
| parent | 3bd2e91faeb9eeec1aae82c64a3253afff551cfd (diff) | |
Notes
Diffstat (limited to 'source/Host/macosx')
| -rw-r--r-- | source/Host/macosx/Host.mm | 1587 | ||||
| -rw-r--r-- | source/Host/macosx/HostInfoMacOSX.mm | 372 | ||||
| -rw-r--r-- | source/Host/macosx/HostThreadMacOSX.mm | 102 | ||||
| -rw-r--r-- | source/Host/macosx/Symbols.cpp | 559 | ||||
| -rw-r--r-- | source/Host/macosx/ThisThread.cpp | 39 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCBundle.cpp | 99 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCBundle.h | 50 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCData.cpp | 82 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCData.h | 35 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableArray.cpp | 166 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableArray.h | 39 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableDictionary.cpp | 529 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableDictionary.h | 79 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableSet.cpp | 114 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCMutableSet.h | 53 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCReleaser.h | 158 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCString.cpp | 195 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CFCString.h | 41 | ||||
| -rw-r--r-- | source/Host/macosx/cfcpp/CoreFoundationCPP.h | 30 | 
19 files changed, 4329 insertions, 0 deletions
| diff --git a/source/Host/macosx/Host.mm b/source/Host/macosx/Host.mm new file mode 100644 index 0000000000000..2db27a276e4ab --- /dev/null +++ b/source/Host/macosx/Host.mm @@ -0,0 +1,1587 @@ +//===-- Host.mm -------------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Host.h" + +#include <AvailabilityMacros.h> + +#if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 +#define NO_XPC_SERVICES 1  +#endif + +#if !defined(NO_XPC_SERVICES) +#define __XPC_PRIVATE_H__ +#include <xpc/xpc.h> + +#define LaunchUsingXPCRightName "com.apple.dt.Xcode.RootDebuggingXPCService" + +// These XPC messaging keys are used for communication between Host.mm and the XPC service. +#define LauncherXPCServiceAuthKey               "auth-key" +#define LauncherXPCServiceArgPrefxKey           "arg" +#define LauncherXPCServiceEnvPrefxKey           "env" +#define LauncherXPCServiceCPUTypeKey            "cpuType" +#define LauncherXPCServicePosixspawnFlagsKey    "posixspawnFlags" +#define LauncherXPCServiceStdInPathKeyKey       "stdInPath" +#define LauncherXPCServiceStdOutPathKeyKey      "stdOutPath" +#define LauncherXPCServiceStdErrPathKeyKey      "stdErrPath" +#define LauncherXPCServiceChildPIDKey           "childPID" +#define LauncherXPCServiceErrorTypeKey          "errorType" +#define LauncherXPCServiceCodeTypeKey           "errorCode" + +#endif + +#include "llvm/Support/Host.h" + +#include <asl.h> +#include <crt_externs.h> +#include <grp.h> +#include <libproc.h> +#include <pwd.h> +#include <spawn.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/proc.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> +#include <unistd.h> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StructuredData.h" +#include "lldb/Host/ConnectionFileDescriptor.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/FileSystem.h" +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/ThreadLauncher.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" +#include "lldb/Utility/CleanUp.h" +#include "lldb/Utility/NameMatches.h" + +#include "cfcpp/CFCBundle.h" +#include "cfcpp/CFCMutableArray.h" +#include "cfcpp/CFCMutableDictionary.h" +#include "cfcpp/CFCReleaser.h" +#include "cfcpp/CFCString.h" + + +#include <objc/objc-auto.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> + +#ifndef _POSIX_SPAWN_DISABLE_ASLR +#define _POSIX_SPAWN_DISABLE_ASLR       0x0100 +#endif + +extern "C"  +{ +    int __pthread_chdir(const char *path); +    int __pthread_fchdir (int fildes); +} + +using namespace lldb; +using namespace lldb_private; + +bool +Host::GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory) +{ +#if defined (__APPLE__) +    if (file.GetFileType () == FileSpec::eFileTypeDirectory) +    { +        char path[PATH_MAX]; +        if (file.GetPath(path, sizeof(path))) +        { +            CFCBundle bundle (path); +            if (bundle.GetPath (path, sizeof(path))) +            { +                bundle_directory.SetFile (path, false); +                return true; +            } +        } +    } +#endif +    bundle_directory.Clear(); +    return false; +} + + +bool +Host::ResolveExecutableInBundle (FileSpec &file) +{ +#if defined (__APPLE__) +    if (file.GetFileType () == FileSpec::eFileTypeDirectory) +    { +        char path[PATH_MAX]; +        if (file.GetPath(path, sizeof(path))) +        { +            CFCBundle bundle (path); +            CFCReleaser<CFURLRef> url(bundle.CopyExecutableURL ()); +            if (url.get()) +            { +                if (::CFURLGetFileSystemRepresentation (url.get(), YES, (UInt8*)path, sizeof(path))) +                { +                    file.SetFile(path, false); +                    return true; +                } +            } +        } +    } +#endif +  return false; +} + +static void * +AcceptPIDFromInferior (void *arg) +{ +    const char *connect_url = (const char *)arg; +    ConnectionFileDescriptor file_conn; +    Error error; +    if (file_conn.Connect (connect_url, &error) == eConnectionStatusSuccess) +    { +        char pid_str[256]; +        ::memset (pid_str, 0, sizeof(pid_str)); +        ConnectionStatus status; +        const size_t pid_str_len = file_conn.Read (pid_str, sizeof(pid_str), 0, status, NULL); +        if (pid_str_len > 0) +        { +            int pid = atoi (pid_str); +            return (void *)(intptr_t)pid; +        } +    } +    return NULL; +} + +static bool +WaitForProcessToSIGSTOP (const lldb::pid_t pid, const int timeout_in_seconds) +{ +    const int time_delta_usecs = 100000; +    const int num_retries = timeout_in_seconds/time_delta_usecs; +    for (int i=0; i<num_retries; i++) +    { +        struct proc_bsdinfo bsd_info; +        int error = ::proc_pidinfo (pid, PROC_PIDTBSDINFO,  +                                    (uint64_t) 0,  +                                    &bsd_info,  +                                    PROC_PIDTBSDINFO_SIZE); +         +        switch (error) +        { +        case EINVAL: +        case ENOTSUP: +        case ESRCH: +        case EPERM: +            return false; +         +        default: +            break; + +        case 0: +            if (bsd_info.pbi_status == SSTOP) +                return true; +        } +        ::usleep (time_delta_usecs); +    } +    return false; +} +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + +//static lldb::pid_t +//LaunchInNewTerminalWithCommandFile  +//( +//    const char **argv,  +//    const char **envp, +//    const char *working_dir, +//    const ArchSpec *arch_spec, +//    bool stop_at_entry, +//    bool disable_aslr +//) +//{ +//    if (!argv || !argv[0]) +//        return LLDB_INVALID_PROCESS_ID; +// +//    OSStatus error = 0; +//     +//    FileSpec program (argv[0], false); +//     +//     +//    std::string unix_socket_name; +// +//    char temp_file_path[PATH_MAX]; +//    const char *tmpdir = ::getenv ("TMPDIR"); +//    if (tmpdir == NULL) +//        tmpdir = "/tmp/"; +//    ::snprintf (temp_file_path, sizeof(temp_file_path), "%s%s-XXXXXX", tmpdir, program.GetFilename().AsCString()); +//     +//    if (::mktemp (temp_file_path) == NULL) +//        return LLDB_INVALID_PROCESS_ID; +// +//    unix_socket_name.assign (temp_file_path); +// +//    ::strlcat (temp_file_path, ".command", sizeof (temp_file_path)); +// +//    StreamFile command_file; +//    command_file.GetFile().Open (temp_file_path,  +//                                 File::eOpenOptionWrite | File::eOpenOptionCanCreate, +//                                 lldb::eFilePermissionsDefault); +//     +//    if (!command_file.GetFile().IsValid()) +//        return LLDB_INVALID_PROCESS_ID; +//     +//    FileSpec darwin_debug_file_spec; +//    if (!HostInfo::GetLLDBPath (ePathTypeSupportExecutableDir, darwin_debug_file_spec)) +//        return LLDB_INVALID_PROCESS_ID; +//    darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); +// +//    if (!darwin_debug_file_spec.Exists()) +//        return LLDB_INVALID_PROCESS_ID; +// +//    char launcher_path[PATH_MAX]; +//    darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); +//    command_file.Printf("\"%s\" ", launcher_path); +// +//    command_file.Printf("--unix-socket=%s ", unix_socket_name.c_str()); +// +//    if (arch_spec && arch_spec->IsValid()) +//    { +//        command_file.Printf("--arch=%s ", arch_spec->GetArchitectureName()); +//    } +// +//    if (disable_aslr) +//    { +//        command_file.PutCString("--disable-aslr "); +//    } +// +//    command_file.PutCString("-- "); +// +//    if (argv) +//    { +//        for (size_t i=0; argv[i] != NULL; ++i) +//        { +//            command_file.Printf("\"%s\" ", argv[i]); +//        } +//    } +//    command_file.PutCString("\necho Process exited with status $?\n"); +//    command_file.GetFile().Close(); +//    if (::chmod (temp_file_path, S_IRWXU | S_IRWXG) != 0) +//        return LLDB_INVALID_PROCESS_ID; +// +//    CFCMutableDictionary cf_env_dict; +// +//    const bool can_create = true; +//    if (envp) +//    { +//        for (size_t i=0; envp[i] != NULL; ++i) +//        { +//            const char *env_entry = envp[i]; +//            const char *equal_pos = strchr(env_entry, '='); +//            if (equal_pos) +//            { +//                std::string env_key (env_entry, equal_pos); +//                std::string env_val (equal_pos + 1); +//                CFCString cf_env_key (env_key.c_str(), kCFStringEncodingUTF8); +//                CFCString cf_env_val (env_val.c_str(), kCFStringEncodingUTF8); +//                cf_env_dict.AddValue (cf_env_key.get(), cf_env_val.get(), can_create); +//            } +//        } +//    } +// +//    LSApplicationParameters app_params; +//    ::memset (&app_params, 0, sizeof (app_params)); +//    app_params.flags = kLSLaunchDontAddToRecents | kLSLaunchAsync; +//    app_params.argv = NULL; +//    app_params.environment = (CFDictionaryRef)cf_env_dict.get(); +// +//    CFCReleaser<CFURLRef> command_file_url (::CFURLCreateFromFileSystemRepresentation (NULL, +//                                                                                       (const UInt8 *)temp_file_path, +//                                                                                       strlen(temp_file_path), +//                                                                                       false)); +// +//    CFCMutableArray urls; +// +//    // Terminal.app will open the ".command" file we have created +//    // and run our process inside it which will wait at the entry point +//    // for us to attach. +//    urls.AppendValue(command_file_url.get()); +// +// +//    lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; +// +//    Error lldb_error; +//    // Sleep and wait a bit for debugserver to start to listen... +//    char connect_url[128]; +//    ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name.c_str()); +// +//    // Spawn a new thread to accept incoming connection on the connect_url +//    // so we can grab the pid from the inferior +//    lldb::thread_t accept_thread = Host::ThreadCreate (unix_socket_name.c_str(), +//                                                       AcceptPIDFromInferior, +//                                                       connect_url, +//                                                       &lldb_error); +// +//    ProcessSerialNumber psn; +//    error = LSOpenURLsWithRole(urls.get(), kLSRolesShell, NULL, &app_params, &psn, 1); +//    if (error == noErr) +//    { +//        thread_result_t accept_thread_result = NULL; +//        if (Host::ThreadJoin (accept_thread, &accept_thread_result, &lldb_error)) +//        { +//            if (accept_thread_result) +//            { +//                pid = (intptr_t)accept_thread_result; +// +//                // Wait for process to be stopped the entry point by watching +//                // for the process status to be set to SSTOP which indicates it it +//                // SIGSTOP'ed at the entry point +//                WaitForProcessToSIGSTOP (pid, 5); +//            } +//        } +//    } +//    else +//    { +//        Host::ThreadCancel (accept_thread, &lldb_error); +//    } +// +//    return pid; +//} + +const char *applscript_in_new_tty =  +"tell application \"Terminal\"\n" +"	do script \"%s\"\n" +"end tell\n"; + + +const char *applscript_in_existing_tty = "\ +set the_shell_script to \"%s\"\n\ +tell application \"Terminal\"\n\ +	repeat with the_window in (get windows)\n\ +		repeat with the_tab in tabs of the_window\n\ +			set the_tty to tty in the_tab\n\ +			if the_tty contains \"%s\" then\n\ +				if the_tab is not busy then\n\ +					set selected of the_tab to true\n\ +					set frontmost of the_window to true\n\ +					do script the_shell_script in the_tab\n\ +					return\n\ +				end if\n\ +			end if\n\ +		end repeat\n\ +	end repeat\n\ +	do script the_shell_script\n\ +end tell\n"; + + +static Error +LaunchInNewTerminalWithAppleScript (const char *exe_path, ProcessLaunchInfo &launch_info) +{ +    Error error; +    char unix_socket_name[PATH_MAX] = "/tmp/XXXXXX";     +    if (::mktemp (unix_socket_name) == NULL) +    { +        error.SetErrorString ("failed to make temporary path for a unix socket"); +        return error; +    } +     +    StreamString command; +    FileSpec darwin_debug_file_spec; +    if (!HostInfo::GetLLDBPath(ePathTypeSupportExecutableDir, darwin_debug_file_spec)) +    { +        error.SetErrorString ("can't locate the 'darwin-debug' executable"); +        return error; +    } + +    darwin_debug_file_spec.GetFilename().SetCString("darwin-debug"); +         +    if (!darwin_debug_file_spec.Exists()) +    { +        error.SetErrorStringWithFormat ("the 'darwin-debug' executable doesn't exists at '%s'",  +                                        darwin_debug_file_spec.GetPath().c_str()); +        return error; +    } +     +    char launcher_path[PATH_MAX]; +    darwin_debug_file_spec.GetPath(launcher_path, sizeof(launcher_path)); + +    const ArchSpec &arch_spec = launch_info.GetArchitecture(); +    // Only set the architecture if it is valid and if it isn't Haswell (x86_64h). +    if (arch_spec.IsValid() && arch_spec.GetCore() != ArchSpec::eCore_x86_64_x86_64h) +        command.Printf("arch -arch %s ", arch_spec.GetArchitectureName()); + +    command.Printf("'%s' --unix-socket=%s", launcher_path, unix_socket_name); + +    if (arch_spec.IsValid()) +        command.Printf(" --arch=%s", arch_spec.GetArchitectureName()); + +    FileSpec working_dir{launch_info.GetWorkingDirectory()}; +    if (working_dir) +        command.Printf(" --working-dir '%s'", working_dir.GetCString()); +    else +    { +        char cwd[PATH_MAX]; +        if (getcwd(cwd, PATH_MAX)) +            command.Printf(" --working-dir '%s'", cwd); +    } +     +    if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR)) +        command.PutCString(" --disable-aslr"); +     +    // We are launching on this host in a terminal. So compare the environment on the host +    // to what is supplied in the launch_info. Any items that aren't in the host environment +    // need to be sent to darwin-debug. If we send all environment entries, we might blow the +    // max command line length, so we only send user modified entries. +    const char **envp = launch_info.GetEnvironmentEntries().GetConstArgumentVector (); + +    StringList host_env; +    const size_t host_env_count = Host::GetEnvironment (host_env); + +    if (envp && envp[0]) +    { +        const char *env_entry; +        for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) +        { +            bool add_entry = true; +            for (size_t i=0; i<host_env_count; ++i) +            { +                const char *host_env_entry = host_env.GetStringAtIndex(i); +                if (strcmp(env_entry, host_env_entry) == 0) +                { +                    add_entry = false; +                    break; +                } +            } +            if (add_entry) +            { +                command.Printf(" --env='%s'", env_entry); +            } +        } +    } + +    command.PutCString(" -- "); + +    const char **argv = launch_info.GetArguments().GetConstArgumentVector (); +    if (argv) +    { +        for (size_t i=0; argv[i] != NULL; ++i) +        { +            if (i==0) +                command.Printf(" '%s'", exe_path); +            else +                command.Printf(" '%s'", argv[i]); +        } +    } +    else +    { +        command.Printf(" '%s'", exe_path); +    } +    command.PutCString (" ; echo Process exited with status $?"); +    if (launch_info.GetFlags().Test(lldb::eLaunchFlagCloseTTYOnExit)) +        command.PutCString (" ; exit"); +     +    StreamString applescript_source; + +    const char *tty_command = command.GetString().c_str(); +//    if (tty_name && tty_name[0]) +//    { +//        applescript_source.Printf (applscript_in_existing_tty,  +//                                   tty_command, +//                                   tty_name); +//    } +//    else +//    { +        applescript_source.Printf (applscript_in_new_tty,  +                                   tty_command); +//    } + +     + +    const char *script_source = applescript_source.GetString().c_str(); +    //puts (script_source); +    NSAppleScript* applescript = [[NSAppleScript alloc] initWithSource:[NSString stringWithCString:script_source encoding:NSUTF8StringEncoding]]; + +    lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + +    Error lldb_error; +    // Sleep and wait a bit for debugserver to start to listen... +    ConnectionFileDescriptor file_conn; +    char connect_url[128]; +    ::snprintf (connect_url, sizeof(connect_url), "unix-accept://%s", unix_socket_name); + +    // Spawn a new thread to accept incoming connection on the connect_url +    // so we can grab the pid from the inferior. We have to do this because we +    // are sending an AppleScript that will launch a process in Terminal.app, +    // in a shell and the shell will fork/exec a couple of times before we get +    // to the process that we wanted to launch. So when our process actually +    // gets launched, we will handshake with it and get the process ID for it. +    HostThread accept_thread = ThreadLauncher::LaunchThread(unix_socket_name, AcceptPIDFromInferior, connect_url, &lldb_error); + +    [applescript executeAndReturnError:nil]; +     +    thread_result_t accept_thread_result = NULL; +    lldb_error = accept_thread.Join(&accept_thread_result); +    if (lldb_error.Success() && accept_thread_result) +    { +        pid = (intptr_t)accept_thread_result; + +        // Wait for process to be stopped at the entry point by watching +        // for the process status to be set to SSTOP which indicates it it +        // SIGSTOP'ed at the entry point +        WaitForProcessToSIGSTOP(pid, 5); +    } + +    FileSystem::Unlink(FileSpec{unix_socket_name, false}); +    [applescript release]; +    if (pid != LLDB_INVALID_PROCESS_ID) +        launch_info.SetProcessID (pid); +    return error; +} + +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) + + +// On MacOSX CrashReporter will display a string for each shared library if +// the shared library has an exported symbol named "__crashreporter_info__". + +static Mutex& +GetCrashReporterMutex () +{ +    static Mutex g_mutex; +    return g_mutex; +} + +extern "C" { +    const char *__crashreporter_info__ = NULL; +} + +asm(".desc ___crashreporter_info__, 0x10"); + +void +Host::SetCrashDescriptionWithFormat (const char *format, ...) +{ +    static StreamString g_crash_description; +    Mutex::Locker locker (GetCrashReporterMutex ()); +     +    if (format) +    { +        va_list args; +        va_start (args, format); +        g_crash_description.GetString().clear(); +        g_crash_description.PrintfVarArg(format, args); +        va_end (args); +        __crashreporter_info__ = g_crash_description.GetData(); +    } +    else +    { +        __crashreporter_info__ = NULL; +    } +} + +void +Host::SetCrashDescription (const char *cstr) +{ +    Mutex::Locker locker (GetCrashReporterMutex ()); +    static std::string g_crash_description; +    if (cstr) +    { +        g_crash_description.assign (cstr); +        __crashreporter_info__ = g_crash_description.c_str(); +    } +    else +    { +        __crashreporter_info__ = NULL; +    } +} + +bool +Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) +{ +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +    return false; +#else +    // We attach this to an 'odoc' event to specify a particular selection +    typedef struct { +        int16_t   reserved0;  // must be zero +        int16_t   fLineNumber; +        int32_t   fSelStart; +        int32_t   fSelEnd; +        uint32_t  reserved1;  // must be zero +        uint32_t  reserved2;  // must be zero +    } BabelAESelInfo; +     +    Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST)); +    char file_path[PATH_MAX]; +    file_spec.GetPath(file_path, PATH_MAX); +    CFCString file_cfstr (file_path, kCFStringEncodingUTF8); +    CFCReleaser<CFURLRef> file_URL (::CFURLCreateWithFileSystemPath (NULL,  +                                                                     file_cfstr.get(),  +                                                                     kCFURLPOSIXPathStyle,  +                                                                     false)); +                                                                      +    if (log) +        log->Printf("Sending source file: \"%s\" and line: %d to external editor.\n", file_path, line_no); +     +    long error;	 +    BabelAESelInfo file_and_line_info =  +    { +        0,                         // reserved0 +        (int16_t)(line_no - 1),    // fLineNumber (zero based line number) +        1,                         // fSelStart +        1024,                      // fSelEnd +        0,                         // reserved1 +        0                          // reserved2 +    }; + +    AEKeyDesc file_and_line_desc; +     +    error = ::AECreateDesc (typeUTF8Text,  +                            &file_and_line_info,  +                            sizeof (file_and_line_info),  +                            &(file_and_line_desc.descContent)); +     +    if (error != noErr) +    { +        if (log) +            log->Printf("Error creating AEDesc: %ld.\n", error); +        return false; +    } +     +    file_and_line_desc.descKey = keyAEPosition; +     +    static std::string g_app_name; +    static FSRef g_app_fsref; + +    LSApplicationParameters app_params; +    ::memset (&app_params, 0, sizeof (app_params)); +    app_params.flags = kLSLaunchDefaults |  +                       kLSLaunchDontAddToRecents |  +                       kLSLaunchDontSwitch; +     +    char *external_editor = ::getenv ("LLDB_EXTERNAL_EDITOR"); +     +    if (external_editor) +    { +        if (log) +            log->Printf("Looking for external editor \"%s\".\n", external_editor); + +        if (g_app_name.empty() || strcmp (g_app_name.c_str(), external_editor) != 0) +        { +            CFCString editor_name (external_editor, kCFStringEncodingUTF8); +            error = ::LSFindApplicationForInfo (kLSUnknownCreator,  +                                                NULL,  +                                                editor_name.get(),  +                                                &g_app_fsref,  +                                                NULL); +             +            // If we found the app, then store away the name so we don't have to re-look it up. +            if (error != noErr) +            { +                if (log) +                    log->Printf("Could not find External Editor application, error: %ld.\n", error); +                return false; +            } +                 +        } +        app_params.application = &g_app_fsref; +    } + +    ProcessSerialNumber psn; +    CFCReleaser<CFArrayRef> file_array(CFArrayCreate (NULL, (const void **) file_URL.ptr_address(false), 1, NULL)); +    error = ::LSOpenURLsWithRole (file_array.get(),  +                                  kLSRolesAll,  +                                  &file_and_line_desc,  +                                  &app_params,  +                                  &psn,  +                                  1); +     +    AEDisposeDesc (&(file_and_line_desc.descContent)); + +    if (error != noErr) +    { +        if (log) +            log->Printf("LSOpenURLsWithRole failed, error: %ld.\n", error); + +        return false; +    } + +    return true; +#endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) +} + +size_t +Host::GetEnvironment (StringList &env) +{ +    char **host_env = *_NSGetEnviron(); +    char *env_entry; +    size_t i; +    for (i=0; (env_entry = host_env[i]) != NULL; ++i) +        env.AppendString(env_entry); +    return i; +         +} + +static bool +GetMacOSXProcessCPUType (ProcessInstanceInfo &process_info) +{ +    if (process_info.ProcessIDIsValid()) +    { +        // Make a new mib to stay thread safe +        int mib[CTL_MAXNAME]={0,}; +        size_t mib_len = CTL_MAXNAME; +        if (::sysctlnametomib("sysctl.proc_cputype", mib, &mib_len))  +            return false; +     +        mib[mib_len] = process_info.GetProcessID(); +        mib_len++; + +        cpu_type_t cpu, sub = 0; +        size_t len = sizeof(cpu); +        if (::sysctl (mib, mib_len, &cpu, &len, 0, 0) == 0) +        { +            switch (cpu) +            { +                case CPU_TYPE_I386:      sub = CPU_SUBTYPE_I386_ALL;     break; +                case CPU_TYPE_X86_64:    sub = CPU_SUBTYPE_X86_64_ALL;   break; + +#if defined (CPU_TYPE_ARM64) && defined (CPU_SUBTYPE_ARM64_ALL) +                case CPU_TYPE_ARM64:     sub = CPU_SUBTYPE_ARM64_ALL;      break; +#endif + +                case CPU_TYPE_ARM: +                    { +                        // Note that we fetched the cpu type from the PROCESS but we can't get a cpusubtype of the  +                        // process -- we can only get the host's cpu subtype. +                        uint32_t cpusubtype = 0; +                        len = sizeof(cpusubtype); +                        if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) == 0) +                            sub = cpusubtype; + +                        bool host_cpu_is_64bit; +                        uint32_t is64bit_capable; +                        size_t is64bit_capable_len = sizeof (is64bit_capable); +                        if (sysctlbyname("hw.cpu64bit_capable", &is64bit_capable, &is64bit_capable_len, NULL, 0) == 0) +                            host_cpu_is_64bit = true; +                        else +                            host_cpu_is_64bit = false; + +                        // if the host is an armv8 device, its cpusubtype will be in CPU_SUBTYPE_ARM64 numbering +                        // and we need to rewrite it to a reasonable CPU_SUBTYPE_ARM value instead. + +                        if (host_cpu_is_64bit) +                        { +                            sub = CPU_SUBTYPE_ARM_V7; +                        } +                    } +                    break; + +                default: +                    break; +            } +            process_info.GetArchitecture ().SetArchitecture (eArchTypeMachO, cpu, sub); +            return true; +        } +    } +    process_info.GetArchitecture().Clear(); +    return false; +} + +static bool +GetMacOSXProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr, +                      ProcessInstanceInfo &process_info) +{ +    if (process_info.ProcessIDIsValid()) +    { +        int proc_args_mib[3] = { CTL_KERN, KERN_PROCARGS2, (int)process_info.GetProcessID() }; + +        size_t arg_data_size = 0; +        if (::sysctl (proc_args_mib, 3, nullptr, &arg_data_size, NULL, 0) || arg_data_size == 0) +            arg_data_size = 8192; + +        // Add a few bytes to the calculated length, I know we need to add at least one byte +        // to this number otherwise we get junk back, so add 128 just in case... +        DataBufferHeap arg_data(arg_data_size+128, 0); +        arg_data_size = arg_data.GetByteSize(); +        if (::sysctl (proc_args_mib, 3, arg_data.GetBytes(), &arg_data_size , NULL, 0) == 0) +        { +            DataExtractor data (arg_data.GetBytes(), arg_data_size, endian::InlHostByteOrder(), sizeof(void *)); +            lldb::offset_t offset = 0; +            uint32_t argc = data.GetU32 (&offset); +            llvm::Triple &triple = process_info.GetArchitecture().GetTriple(); +            const llvm::Triple::ArchType triple_arch = triple.getArch(); +            const bool check_for_ios_simulator = (triple_arch == llvm::Triple::x86 || triple_arch == llvm::Triple::x86_64); +            const char *cstr = data.GetCStr (&offset); +            if (cstr) +            { +                process_info.GetExecutableFile().SetFile(cstr, false); + +                if (match_info_ptr == NULL ||  +                    NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(), +                                 match_info_ptr->GetNameMatchType(), +                                 match_info_ptr->GetProcessInfo().GetName())) +                { +                    // Skip NULLs +                    while (1) +                    { +                        const uint8_t *p = data.PeekData(offset, 1); +                        if ((p == NULL) || (*p != '\0')) +                            break; +                        ++offset; +                    } +                    // Now extract all arguments +                    Args &proc_args = process_info.GetArguments(); +                    for (int i=0; i<static_cast<int>(argc); ++i) +                    { +                        cstr = data.GetCStr(&offset); +                        if (cstr) +                            proc_args.AppendArgument(cstr); +                    } +                     +                    Args &proc_env = process_info.GetEnvironmentEntries (); +                    while ((cstr = data.GetCStr(&offset))) +                    { +                        if (cstr[0] == '\0') +                            break; +                         +                        if (check_for_ios_simulator) +                        { +                            if (strncmp(cstr, "SIMULATOR_UDID=", strlen("SIMULATOR_UDID=")) == 0) +                                process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::IOS); +                            else +                                process_info.GetArchitecture().GetTriple().setOS(llvm::Triple::MacOSX); +                        } + +                        proc_env.AppendArgument(cstr); +                    } +                    return true; +                } +            } +        } +    } +    return false; +} + +static bool +GetMacOSXProcessUserAndGroup (ProcessInstanceInfo &process_info) +{ +    if (process_info.ProcessIDIsValid()) +    { +        int mib[4]; +        mib[0] = CTL_KERN; +        mib[1] = KERN_PROC; +        mib[2] = KERN_PROC_PID; +        mib[3] = process_info.GetProcessID(); +        struct kinfo_proc proc_kinfo; +        size_t proc_kinfo_size = sizeof(struct kinfo_proc); + +        if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) +        { +            if (proc_kinfo_size > 0) +            { +                process_info.SetParentProcessID (proc_kinfo.kp_eproc.e_ppid); +                process_info.SetUserID (proc_kinfo.kp_eproc.e_pcred.p_ruid); +                process_info.SetGroupID (proc_kinfo.kp_eproc.e_pcred.p_rgid); +                process_info.SetEffectiveUserID (proc_kinfo.kp_eproc.e_ucred.cr_uid); +                if (proc_kinfo.kp_eproc.e_ucred.cr_ngroups > 0) +                    process_info.SetEffectiveGroupID (proc_kinfo.kp_eproc.e_ucred.cr_groups[0]); +                else +                    process_info.SetEffectiveGroupID (UINT32_MAX);             +                return true; +            } +        } +    } +    process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID); +    process_info.SetUserID (UINT32_MAX); +    process_info.SetGroupID (UINT32_MAX); +    process_info.SetEffectiveUserID (UINT32_MAX); +    process_info.SetEffectiveGroupID (UINT32_MAX);             +    return false; +} + + +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ +    std::vector<struct kinfo_proc> kinfos; +     +    int mib[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; +     +    size_t pid_data_size = 0; +    if (::sysctl (mib, 4, NULL, &pid_data_size, NULL, 0) != 0) +        return 0; +     +    // Add a few extra in case a few more show up +    const size_t estimated_pid_count = (pid_data_size / sizeof(struct kinfo_proc)) + 10; + +    kinfos.resize (estimated_pid_count); +    pid_data_size = kinfos.size() * sizeof(struct kinfo_proc); +     +    if (::sysctl (mib, 4, &kinfos[0], &pid_data_size, NULL, 0) != 0) +        return 0; + +    const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc)); + +    bool all_users = match_info.GetMatchAllUsers(); +    const lldb::pid_t our_pid = getpid(); +    const uid_t our_uid = getuid(); +    for (size_t i = 0; i < actual_pid_count; i++) +    { +        const struct kinfo_proc &kinfo = kinfos[i]; +         +        bool kinfo_user_matches = false; +        if (all_users) +            kinfo_user_matches = true; +        else +            kinfo_user_matches = kinfo.kp_eproc.e_pcred.p_ruid == our_uid; + +        // Special case, if lldb is being run as root we can attach to anything. +        if (our_uid == 0) +          kinfo_user_matches = true; + +        if (kinfo_user_matches == false         || // Make sure the user is acceptable +            static_cast<lldb::pid_t>(kinfo.kp_proc.p_pid) == our_pid || // Skip this process +            kinfo.kp_proc.p_pid == 0            || // Skip kernel (kernel pid is zero) +            kinfo.kp_proc.p_stat == SZOMB       || // Zombies are bad, they like brains... +            kinfo.kp_proc.p_flag & P_TRACED     || // Being debugged? +            kinfo.kp_proc.p_flag & P_WEXIT      || // Working on exiting? +            kinfo.kp_proc.p_flag & P_TRANSLATED)   // Skip translated ppc (Rosetta) +            continue; + +        ProcessInstanceInfo process_info; +        process_info.SetProcessID (kinfo.kp_proc.p_pid); +        process_info.SetParentProcessID (kinfo.kp_eproc.e_ppid); +        process_info.SetUserID (kinfo.kp_eproc.e_pcred.p_ruid); +        process_info.SetGroupID (kinfo.kp_eproc.e_pcred.p_rgid); +        process_info.SetEffectiveUserID (kinfo.kp_eproc.e_ucred.cr_uid); +        if (kinfo.kp_eproc.e_ucred.cr_ngroups > 0) +            process_info.SetEffectiveGroupID (kinfo.kp_eproc.e_ucred.cr_groups[0]); +        else +            process_info.SetEffectiveGroupID (UINT32_MAX);             + +        // Make sure our info matches before we go fetch the name and cpu type +        if (match_info.Matches (process_info)) +        { +            // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 +            if (GetMacOSXProcessCPUType (process_info)) +            { +                if (GetMacOSXProcessArgs (&match_info, process_info)) +                { +                    if (match_info.Matches (process_info)) +                        process_infos.Append (process_info); +                } +            } +        } +    }     +    return process_infos.GetSize(); +} + +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ +    process_info.SetProcessID(pid); +    bool success = false; + +    // Get CPU type first so we can know to look for iOS simulator is we have x86 or x86_64 +    if (GetMacOSXProcessCPUType (process_info)) +        success = true; +     +    if (GetMacOSXProcessArgs (NULL, process_info)) +        success = true; +     +    if (GetMacOSXProcessUserAndGroup (process_info)) +        success = true; +     +    if (success) +        return true; +     +    process_info.Clear(); +    return false; +} + +#if !NO_XPC_SERVICES +static void +PackageXPCArguments (xpc_object_t message, const char *prefix, const Args& args) +{ +    size_t count = args.GetArgumentCount(); +    char buf[50]; // long enough for 'argXXX' +    memset(buf, 0, 50); +    sprintf(buf, "%sCount", prefix); +	xpc_dictionary_set_int64(message, buf, count); +    for (size_t i=0; i<count; i++) { +        memset(buf, 0, 50); +        sprintf(buf, "%s%zi", prefix, i); +        xpc_dictionary_set_string(message, buf, args.GetArgumentAtIndex(i)); +    } +} + +/* + A valid authorizationRef means that  +    - there is the LaunchUsingXPCRightName rights in the /etc/authorization +    - we have successfully copied the rights to be send over the XPC wire + Once obtained, it will be valid for as long as the process lives. + */ +static AuthorizationRef authorizationRef = NULL; +static Error +getXPCAuthorization (ProcessLaunchInfo &launch_info) +{ +    Error error; +    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); +     +    if ((launch_info.GetUserID() == 0) && !authorizationRef) +    { +        OSStatus createStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); +        if (createStatus != errAuthorizationSuccess) +        { +            error.SetError(1, eErrorTypeGeneric); +            error.SetErrorString("Can't create authorizationRef."); +            if (log) +            { +                error.PutToLog(log, "%s", error.AsCString()); +            } +            return error; +        } +         +        OSStatus rightsStatus = AuthorizationRightGet(LaunchUsingXPCRightName, NULL); +        if (rightsStatus != errAuthorizationSuccess) +        { +            // No rights in the security database, Create it with the right prompt. +            CFStringRef prompt = CFSTR("Xcode is trying to take control of a root process."); +            CFStringRef keys[] = { CFSTR("en") }; +            CFTypeRef values[] = { prompt }; +            CFDictionaryRef promptDict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +             +            CFStringRef keys1[] = { CFSTR("class"), CFSTR("group"), CFSTR("comment"),               CFSTR("default-prompt"), CFSTR("shared") }; +            CFTypeRef values1[] = { CFSTR("user"),  CFSTR("admin"), CFSTR(LaunchUsingXPCRightName), promptDict,              kCFBooleanFalse }; +            CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys1, (const void **)values1, 5, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +            rightsStatus = AuthorizationRightSet(authorizationRef, LaunchUsingXPCRightName, dict, NULL, NULL, NULL); +            CFRelease(promptDict); +            CFRelease(dict); +        } +             +        OSStatus copyRightStatus = errAuthorizationDenied; +        if (rightsStatus == errAuthorizationSuccess) +        { +            AuthorizationItem item1 = { LaunchUsingXPCRightName, 0, NULL, 0 }; +            AuthorizationItem items[] = {item1}; +            AuthorizationRights requestedRights = {1, items }; +            AuthorizationFlags authorizationFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; +            copyRightStatus = AuthorizationCopyRights(authorizationRef, &requestedRights, kAuthorizationEmptyEnvironment, authorizationFlags, NULL); +        } +         +        if (copyRightStatus != errAuthorizationSuccess) +        { +            // Eventually when the commandline supports running as root and the user is not +            // logged in in the current audit session, we will need the trick in gdb where +            // we ask the user to type in the root passwd in the terminal. +            error.SetError(2, eErrorTypeGeneric); +            error.SetErrorStringWithFormat("Launching as root needs root authorization."); +            if (log) +            { +                error.PutToLog(log, "%s", error.AsCString()); +            } +             +            if (authorizationRef) +            { +                AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); +                authorizationRef = NULL; +            } +        } +    } + +    return error; +} +#endif + +static Error +LaunchProcessXPC(const char *exe_path, ProcessLaunchInfo &launch_info, lldb::pid_t &pid) +{ +#if !NO_XPC_SERVICES +    Error error = getXPCAuthorization(launch_info); +    if (error.Fail()) +        return error; +     +    Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); +     +    uid_t requested_uid = launch_info.GetUserID(); +    const char *xpc_service  = nil; +    bool send_auth = false; +    AuthorizationExternalForm extForm; +    if (requested_uid == 0) +    { +        if (AuthorizationMakeExternalForm(authorizationRef, &extForm) == errAuthorizationSuccess) +        { +            send_auth = true; +        } +        else +        { +            error.SetError(3, eErrorTypeGeneric); +            error.SetErrorStringWithFormat("Launching root via XPC needs to externalize authorization reference."); +            if (log) +            { +                error.PutToLog(log, "%s", error.AsCString()); +            } +            return error; +        } +        xpc_service = LaunchUsingXPCRightName; +    } +    else +    { +        error.SetError(4, eErrorTypeGeneric); +        error.SetErrorStringWithFormat("Launching via XPC is only currently available for root."); +        if (log) +        { +            error.PutToLog(log, "%s", error.AsCString()); +        } +        return error; +    } +     +    xpc_connection_t conn = xpc_connection_create(xpc_service, NULL); +     +	xpc_connection_set_event_handler(conn, ^(xpc_object_t event) { +        xpc_type_t	type = xpc_get_type(event); +         +        if (type == XPC_TYPE_ERROR) { +            if (event == XPC_ERROR_CONNECTION_INTERRUPTED) { +                // The service has either canceled itself, crashed, or been terminated. +                // The XPC connection is still valid and sending a message to it will re-launch the service. +                // If the service is state-full, this is the time to initialize the new service. +                return; +            } else if (event == XPC_ERROR_CONNECTION_INVALID) { +                // The service is invalid. Either the service name supplied to xpc_connection_create() is incorrect +                // or we (this process) have canceled the service; we can do any cleanup of application state at this point. +                // printf("Service disconnected"); +                return; +            } else { +                // printf("Unexpected error from service: %s", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); +            } +             +        } else { +            // printf("Received unexpected event in handler"); +        } +    }); +     +    xpc_connection_set_finalizer_f (conn, xpc_finalizer_t(xpc_release)); +	xpc_connection_resume (conn); +    xpc_object_t message = xpc_dictionary_create (nil, nil, 0); +     +    if (send_auth) +    { +        xpc_dictionary_set_data(message, LauncherXPCServiceAuthKey, extForm.bytes, sizeof(AuthorizationExternalForm)); +    } +     +    PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey, launch_info.GetArguments()); +    PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey, launch_info.GetEnvironmentEntries()); +     +    // Posix spawn stuff. +    xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey, launch_info.GetArchitecture().GetMachOCPUType()); +    xpc_dictionary_set_int64(message, LauncherXPCServicePosixspawnFlagsKey, Host::GetPosixspawnFlags(launch_info)); +    const FileAction *file_action = launch_info.GetFileActionForFD(STDIN_FILENO); +    if (file_action && file_action->GetPath()) +    { +        xpc_dictionary_set_string(message, LauncherXPCServiceStdInPathKeyKey, file_action->GetPath()); +    } +    file_action = launch_info.GetFileActionForFD(STDOUT_FILENO); +    if (file_action && file_action->GetPath()) +    { +        xpc_dictionary_set_string(message, LauncherXPCServiceStdOutPathKeyKey, file_action->GetPath()); +    } +    file_action = launch_info.GetFileActionForFD(STDERR_FILENO); +    if (file_action && file_action->GetPath()) +    { +        xpc_dictionary_set_string(message, LauncherXPCServiceStdErrPathKeyKey, file_action->GetPath()); +    } +     +    xpc_object_t reply = xpc_connection_send_message_with_reply_sync(conn, message); +    xpc_type_t returnType = xpc_get_type(reply); +    if (returnType == XPC_TYPE_DICTIONARY) +    { +        pid = xpc_dictionary_get_int64(reply, LauncherXPCServiceChildPIDKey); +        if (pid == 0) +        { +            int errorType = xpc_dictionary_get_int64(reply, LauncherXPCServiceErrorTypeKey); +            int errorCode = xpc_dictionary_get_int64(reply, LauncherXPCServiceCodeTypeKey); +             +            error.SetError(errorCode, eErrorTypeGeneric); +            error.SetErrorStringWithFormat("Problems with launching via XPC. Error type : %i, code : %i", errorType, errorCode); +            if (log) +            { +                error.PutToLog(log, "%s", error.AsCString()); +            } +             +            if (authorizationRef) +            { +                AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); +                authorizationRef = NULL; +            } +        } +    } +    else if (returnType == XPC_TYPE_ERROR) +    { +        error.SetError(5, eErrorTypeGeneric); +        error.SetErrorStringWithFormat("Problems with launching via XPC. XPC error : %s", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION)); +        if (log) +        { +            error.PutToLog(log, "%s", error.AsCString()); +        } +    } +     +    return error; +#else +    Error error; +    return error; +#endif +} + +static bool +ShouldLaunchUsingXPC(ProcessLaunchInfo &launch_info) +{ +    bool result = false; + +#if !NO_XPC_SERVICES     +    bool launchingAsRoot = launch_info.GetUserID() == 0; +    bool currentUserIsRoot = HostInfo::GetEffectiveUserID() == 0; +     +    if (launchingAsRoot && !currentUserIsRoot) +    { +        // If current user is already root, we don't need XPC's help. +        result = true; +    } +#endif +     +    return result; +} + +Error +Host::LaunchProcess (ProcessLaunchInfo &launch_info) +{ +    Error error; +    char exe_path[PATH_MAX]; +    PlatformSP host_platform_sp (Platform::GetHostPlatform ()); + +    ModuleSpec exe_module_spec(launch_info.GetExecutableFile(), launch_info.GetArchitecture()); + +    FileSpec::FileType file_type = exe_module_spec.GetFileSpec().GetFileType(); +    if (file_type != FileSpec::eFileTypeRegular) +    { +        lldb::ModuleSP exe_module_sp; +        error = host_platform_sp->ResolveExecutable (exe_module_spec, +                                                     exe_module_sp, +                                                     NULL); +         +        if (error.Fail()) +            return error; +         +        if (exe_module_sp) +            exe_module_spec.GetFileSpec() = exe_module_sp->GetFileSpec(); +    } +     +    if (exe_module_spec.GetFileSpec().Exists()) +    { +        exe_module_spec.GetFileSpec().GetPath (exe_path, sizeof(exe_path)); +    } +    else +    { +        launch_info.GetExecutableFile().GetPath (exe_path, sizeof(exe_path)); +        error.SetErrorStringWithFormat ("executable doesn't exist: '%s'", exe_path); +        return error; +    } +     +    if (launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) +    { +#if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__) +        return LaunchInNewTerminalWithAppleScript (exe_path, launch_info); +#else +        error.SetErrorString ("launching a process in a new terminal is not supported on iOS devices"); +        return error; +#endif +    } + +    lldb::pid_t pid = LLDB_INVALID_PROCESS_ID; + +    if (ShouldLaunchUsingXPC(launch_info)) +    { +        error = LaunchProcessXPC(exe_path, launch_info, pid); +    } +    else +    { +        error = LaunchProcessPosixSpawn(exe_path, launch_info, pid); +    } +     +    if (pid != LLDB_INVALID_PROCESS_ID) +    { +        // If all went well, then set the process ID into the launch info +        launch_info.SetProcessID(pid); +         +        // Make sure we reap any processes we spawn or we will have zombies. +        if (!launch_info.MonitorProcess()) +        { +            const bool monitor_signals = false; +            Host::MonitorChildProcessCallback callback = nullptr; +             +            if (!launch_info.GetFlags().Test(lldb::eLaunchFlagDontSetExitStatus)) +                callback = Process::SetProcessExitStatus; + +            StartMonitoringChildProcess (callback, +                                         NULL,  +                                         pid,  +                                         monitor_signals); +        } +    } +    else +    { +        // Invalid process ID, something didn't go well +        if (error.Success()) +            error.SetErrorString ("process launch failed for unknown reasons"); +    } +    return error; +} + +Error +Host::ShellExpandArguments (ProcessLaunchInfo &launch_info) +{ +    Error error; +    if (launch_info.GetFlags().Test(eLaunchFlagShellExpandArguments)) +    { +        FileSpec expand_tool_spec; +        if (!HostInfo::GetLLDBPath(lldb::ePathTypeSupportExecutableDir, expand_tool_spec)) +        { +            error.SetErrorString("could not get support executable directory for lldb-argdumper tool"); +            return error; +        } +        expand_tool_spec.AppendPathComponent("lldb-argdumper"); +        if (!expand_tool_spec.Exists()) +        { +            error.SetErrorStringWithFormat("could not find the lldb-argdumper tool: %s", expand_tool_spec.GetPath().c_str()); +            return error; +        } +         +        StreamString expand_tool_spec_stream; +        expand_tool_spec_stream.Printf("\"%s\"",expand_tool_spec.GetPath().c_str()); + +        Args expand_command(expand_tool_spec_stream.GetData()); +        expand_command.AppendArguments (launch_info.GetArguments()); +         +        int status; +        std::string output; +        FileSpec cwd(launch_info.GetWorkingDirectory()); +        if (!cwd.Exists()) +        { +            char *wd = getcwd(nullptr, 0); +            if (wd == nullptr) +            { +                error.SetErrorStringWithFormat("cwd does not exist; cannot launch with shell argument expansion"); +                return error; +            } +            else +            { +                FileSpec working_dir(wd, false); +                free(wd); +                launch_info.SetWorkingDirectory(working_dir); + +            } +        } +        RunShellCommand(expand_command, cwd, &status, nullptr, &output, 10); +         +        if (status != 0) +        { +            error.SetErrorStringWithFormat("lldb-argdumper exited with error %d", status); +            return error; +        } +         +        auto data_sp = StructuredData::ParseJSON(output); +        if (!data_sp) +        { +            error.SetErrorString("invalid JSON"); +            return error; +        } +         +        auto dict_sp = data_sp->GetAsDictionary(); +        if (!data_sp) +        { +            error.SetErrorString("invalid JSON"); +            return error; +        } +         +        auto args_sp = dict_sp->GetObjectForDotSeparatedPath("arguments"); +        if (!args_sp) +        { +            error.SetErrorString("invalid JSON"); +            return error; +        } + +        auto args_array_sp = args_sp->GetAsArray(); +        if (!args_array_sp) +        { +            error.SetErrorString("invalid JSON"); +            return error; +        } +         +        launch_info.GetArguments().Clear(); +         +        for (size_t i = 0; +             i < args_array_sp->GetSize(); +             i++) +        { +            auto item_sp = args_array_sp->GetItemAtIndex(i); +            if (!item_sp) +                continue; +            auto str_sp = item_sp->GetAsString(); +            if (!str_sp) +                continue; +             +            launch_info.GetArguments().AppendArgument(str_sp->GetValue().c_str()); +        } +    } +     +    return error; +} + +HostThread +Host::StartMonitoringChildProcess(Host::MonitorChildProcessCallback callback, void *callback_baton, lldb::pid_t pid, bool monitor_signals) +{ +    unsigned long mask = DISPATCH_PROC_EXIT; +    if (monitor_signals) +        mask |= DISPATCH_PROC_SIGNAL; + +    Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS)); + + +    dispatch_source_t source = ::dispatch_source_create (DISPATCH_SOURCE_TYPE_PROC,  +                                                         pid,  +                                                         mask,  +                                                         ::dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT,0)); + +    if (log) +        log->Printf ("Host::StartMonitoringChildProcess (callback=%p, baton=%p, pid=%i, monitor_signals=%i) source = %p\n",  +                     callback,  +                     callback_baton,  +                     (int)pid,  +                     monitor_signals,  +                     source); + +    if (source) +    { +        ::dispatch_source_set_cancel_handler (source, ^{ +            ::dispatch_release (source); +        }); +        ::dispatch_source_set_event_handler (source, ^{ +             +            int status= 0; +            int wait_pid = 0; +            bool cancel = false; +            bool exited = false; +            do +            { +                wait_pid = ::waitpid (pid, &status, 0); +            } while (wait_pid < 0 && errno == EINTR); + +            if (wait_pid >= 0) +            { +                int signal = 0; +                int exit_status = 0; +                const char *status_cstr = NULL; +                if (WIFSTOPPED(status)) +                { +                    signal = WSTOPSIG(status); +                    status_cstr = "STOPPED"; +                } +                else if (WIFEXITED(status)) +                { +                    exit_status = WEXITSTATUS(status); +                    status_cstr = "EXITED"; +                    exited = true; +                } +                else if (WIFSIGNALED(status)) +                { +                    signal = WTERMSIG(status); +                    status_cstr = "SIGNALED"; +                    exited = true; +                    exit_status = -1; +                } +                else +                { +                    status_cstr = "???"; +                } + +                if (log) +                    log->Printf ("::waitpid (pid = %llu, &status, 0) => pid = %i, status = 0x%8.8x (%s), signal = %i, exit_status = %i", +                                 pid, +                                 wait_pid, +                                 status, +                                 status_cstr, +                                 signal, +                                 exit_status); +                 +                if (callback) +                    cancel = callback (callback_baton, pid, exited, signal, exit_status); +                 +                if (exited || cancel) +                { +                    ::dispatch_source_cancel(source); +                } +            } +        }); + +        ::dispatch_resume (source); +    } +    return HostThread(); +} + +//---------------------------------------------------------------------- +// Log to both stderr and to ASL Logging when running on MacOSX. +//---------------------------------------------------------------------- +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ +    if (format && format[0]) +    { +        static aslmsg g_aslmsg = NULL; +        if (g_aslmsg == NULL) +        { +            g_aslmsg = ::asl_new (ASL_TYPE_MSG); +            char asl_key_sender[PATH_MAX]; +            snprintf(asl_key_sender, sizeof(asl_key_sender), "com.apple.LLDB.framework"); +            ::asl_set (g_aslmsg, ASL_KEY_SENDER, asl_key_sender); +        } +         +        // Copy the va_list so we can log this message twice +        va_list copy_args; +        va_copy (copy_args, args); +        // Log to stderr +        ::vfprintf (stderr, format, copy_args); +        va_end (copy_args); + +        int asl_level; +        switch (type) +        { +            case eSystemLogError: +                asl_level = ASL_LEVEL_ERR; +                break; +                 +            case eSystemLogWarning: +                asl_level = ASL_LEVEL_WARNING; +                break; +        } +         +        // Log to ASL +        ::asl_vlog (NULL, g_aslmsg, asl_level, format, args); +    } +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ +    return lldb::DataBufferSP(); +} diff --git a/source/Host/macosx/HostInfoMacOSX.mm b/source/Host/macosx/HostInfoMacOSX.mm new file mode 100644 index 0000000000000..f5a0540e87749 --- /dev/null +++ b/source/Host/macosx/HostInfoMacOSX.mm @@ -0,0 +1,372 @@ +//===-- HostInfoMacOSX.mm ---------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#if !defined(LLDB_DISABLE_PYTHON) +#include "Plugins/ScriptInterpreter/Python/lldb-python.h" +#endif + +#include "lldb/Host/HostInfo.h" +#include "lldb/Host/macosx/HostInfoMacOSX.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/SafeMachO.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +// C++ Includes +#include <string> + +// C inclues +#include <stdlib.h> +#include <sys/sysctl.h> +#include <sys/syslimits.h> +#include <sys/types.h> + +// Objective C/C++ includes +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> +#include <mach-o/dyld.h> +#include <objc/objc-auto.h> + +// These are needed when compiling on systems +// that do not yet have these definitions +#include <AvailabilityMacros.h> +#ifndef CPU_SUBTYPE_X86_64_H +#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) +#endif +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (CPU_TYPE_ARM|CPU_ARCH_ABI64) +#endif + +#include <TargetConditionals.h> // for TARGET_OS_TV, TARGET_OS_WATCH + +using namespace lldb_private; + +bool +HostInfoMacOSX::GetOSBuildString(std::string &s) +{ +    int mib[2] = {CTL_KERN, KERN_OSVERSION}; +    char cstr[PATH_MAX]; +    size_t cstr_len = sizeof(cstr); +    if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) +    { +        s.assign(cstr, cstr_len); +        return true; +    } + +    s.clear(); +    return false; +} + +bool +HostInfoMacOSX::GetOSKernelDescription(std::string &s) +{ +    int mib[2] = {CTL_KERN, KERN_VERSION}; +    char cstr[PATH_MAX]; +    size_t cstr_len = sizeof(cstr); +    if (::sysctl(mib, 2, cstr, &cstr_len, NULL, 0) == 0) +    { +        s.assign(cstr, cstr_len); +        return true; +    } +    s.clear(); +    return false; +} + +bool +HostInfoMacOSX::GetOSVersion(uint32_t &major, uint32_t &minor, uint32_t &update) +{ +    static uint32_t g_major = 0; +    static uint32_t g_minor = 0; +    static uint32_t g_update = 0; + +    if (g_major == 0) +    { +        @autoreleasepool +        { +            NSDictionary *version_info = [NSDictionary dictionaryWithContentsOfFile:@"/System/Library/CoreServices/SystemVersion.plist"]; +            NSString *version_value = [version_info objectForKey:@"ProductVersion"]; +            const char *version_str = [version_value UTF8String]; +            if (version_str) +                Args::StringToVersion(version_str, g_major, g_minor, g_update); +        } +    } + +    if (g_major != 0) +    { +        major = g_major; +        minor = g_minor; +        update = g_update; +        return true; +    } +    return false; +} + +FileSpec +HostInfoMacOSX::GetProgramFileSpec() +{ +    static FileSpec g_program_filespec; +    if (!g_program_filespec) +    { +        char program_fullpath[PATH_MAX]; +        // If DST is NULL, then return the number of bytes needed. +        uint32_t len = sizeof(program_fullpath); +        int err = _NSGetExecutablePath(program_fullpath, &len); +        if (err == 0) +            g_program_filespec.SetFile(program_fullpath, false); +        else if (err == -1) +        { +            char *large_program_fullpath = (char *)::malloc(len + 1); + +            err = _NSGetExecutablePath(large_program_fullpath, &len); +            if (err == 0) +                g_program_filespec.SetFile(large_program_fullpath, false); + +            ::free(large_program_fullpath); +        } +    } +    return g_program_filespec; +} + +bool +HostInfoMacOSX::ComputeSupportExeDirectory(FileSpec &file_spec) +{ +    FileSpec lldb_file_spec; +    if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) +        return false; + +    std::string raw_path = lldb_file_spec.GetPath(); + +    size_t framework_pos = raw_path.find("LLDB.framework"); +    if (framework_pos != std::string::npos) +    { +        framework_pos += strlen("LLDB.framework"); +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +        // Shallow bundle +        raw_path.resize(framework_pos); +#else +        // Normal bundle +        raw_path.resize(framework_pos); +        raw_path.append("/Resources"); +#endif +    } +    else +    { +        // Find the bin path relative to the lib path where the cmake-based +        // OS X .dylib lives.  This is not going to work if the bin and lib +        // dir are not both in the same dir. +        // +        // It is not going to work to do it by the executable path either, +        // as in the case of a python script, the executable is python, not +        // the lldb driver. +        raw_path.append("/../bin"); +        FileSpec support_dir_spec(raw_path, true); +        if (!support_dir_spec.Exists() || !support_dir_spec.IsDirectory()) +        { +            Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); +            if (log) +                log->Printf("HostInfoMacOSX::%s(): failed to find support directory", +                            __FUNCTION__); +            return false; +        } + +        // Get normalization from support_dir_spec.  Note the FileSpec resolve +        // does not remove '..' in the path. +        char *const dir_realpath = realpath(support_dir_spec.GetPath().c_str(), NULL); +        if (dir_realpath) +        { +            raw_path = dir_realpath; +            free(dir_realpath); +        } +        else +        { +            raw_path = support_dir_spec.GetPath(); +        } +    } + +    file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); +    return (bool)file_spec.GetDirectory(); +} + +bool +HostInfoMacOSX::ComputeHeaderDirectory(FileSpec &file_spec) +{ +    FileSpec lldb_file_spec; +    if (!HostInfo::GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) +        return false; + +    std::string raw_path = lldb_file_spec.GetPath(); + +    size_t framework_pos = raw_path.find("LLDB.framework"); +    if (framework_pos != std::string::npos) +    { +        framework_pos += strlen("LLDB.framework"); +        raw_path.resize(framework_pos); +        raw_path.append("/Headers"); +    } +    file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); +    return true; +} + +bool +HostInfoMacOSX::ComputePythonDirectory(FileSpec &file_spec) +{ +#ifndef LLDB_DISABLE_PYTHON +    FileSpec lldb_file_spec; +    if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) +        return false; + +    std::string raw_path = lldb_file_spec.GetPath(); + +    size_t framework_pos = raw_path.find("LLDB.framework"); +    if (framework_pos != std::string::npos) +    { +        framework_pos += strlen("LLDB.framework"); +        raw_path.resize(framework_pos); +        raw_path.append("/Resources/Python"); +    } +    else +    { +        llvm::SmallString<256> python_version_dir; +        llvm::raw_svector_ostream os(python_version_dir); +        os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + +        // We may get our string truncated. Should we protect this with an assert? +        raw_path.append(python_version_dir.c_str()); +    } +    file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); +    return true; +#else +    return false; +#endif +} + +bool +HostInfoMacOSX::ComputeClangDirectory(FileSpec &file_spec) +{ +    FileSpec lldb_file_spec; +    if (!GetLLDBPath (lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) +        return false; +     +    std::string raw_path = lldb_file_spec.GetPath(); +     +    size_t framework_pos = raw_path.find("LLDB.framework"); +    if (framework_pos != std::string::npos) +    { +        framework_pos += strlen("LLDB.framework"); +        raw_path.resize(framework_pos); +        raw_path.append("/Resources/Clang"); +    } +    file_spec.SetFile (raw_path.c_str(), true); +    return true; +} + +bool +HostInfoMacOSX::ComputeSystemPluginsDirectory(FileSpec &file_spec) +{ +    FileSpec lldb_file_spec; +    if (!GetLLDBPath(lldb::ePathTypeLLDBShlibDir, lldb_file_spec)) +        return false; + +    std::string raw_path = lldb_file_spec.GetPath(); + +    size_t framework_pos = raw_path.find("LLDB.framework"); +    if (framework_pos == std::string::npos) +        return false; + +    framework_pos += strlen("LLDB.framework"); +    raw_path.resize(framework_pos); +    raw_path.append("/Resources/PlugIns"); +    file_spec.GetDirectory().SetString(llvm::StringRef(raw_path.c_str(), raw_path.size())); +    return true; +} + +bool +HostInfoMacOSX::ComputeUserPluginsDirectory(FileSpec &file_spec) +{ +    FileSpec temp_file("~/Library/Application Support/LLDB/PlugIns", true); +    file_spec.GetDirectory().SetCString(temp_file.GetPath().c_str()); +    return true; +} + +void +HostInfoMacOSX::ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64) +{ +    // All apple systems support 32 bit execution. +    uint32_t cputype, cpusubtype; +    uint32_t is_64_bit_capable = false; +    size_t len = sizeof(cputype); +    ArchSpec host_arch; +    // These will tell us about the kernel architecture, which even on a 64 +    // bit machine can be 32 bit... +    if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) +    { +        len = sizeof(cpusubtype); +        if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) +            cpusubtype = CPU_TYPE_ANY; + +        len = sizeof(is_64_bit_capable); +        ::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0); + +        if (is_64_bit_capable) +        { +            if (cputype & CPU_ARCH_ABI64) +            { +                // We have a 64 bit kernel on a 64 bit system +                arch_64.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); +            } +            else +            { +                // We have a 64 bit kernel that is returning a 32 bit cputype, the +                // cpusubtype will be correct as if it were for a 64 bit architecture +                arch_64.SetArchitecture(eArchTypeMachO, cputype | CPU_ARCH_ABI64, cpusubtype); +            } + +            // Now we need modify the cpusubtype for the 32 bit slices. +            uint32_t cpusubtype32 = cpusubtype; +#if defined(__i386__) || defined(__x86_64__) +            if (cpusubtype == CPU_SUBTYPE_486 || cpusubtype == CPU_SUBTYPE_X86_64_H) +                cpusubtype32 = CPU_SUBTYPE_I386_ALL; +#elif defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +            if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) +                cpusubtype32 = CPU_SUBTYPE_ARM_V7S; +#endif +            arch_32.SetArchitecture(eArchTypeMachO, cputype & ~(CPU_ARCH_MASK), cpusubtype32); + +            if (cputype == CPU_TYPE_ARM || cputype == CPU_TYPE_ARM64) +            { +                // When running on a watch or tv, report the host os correctly +#if defined (TARGET_OS_TV) && TARGET_OS_TV == 1 +                arch_32.GetTriple().setOS(llvm::Triple::TvOS); +                arch_64.GetTriple().setOS(llvm::Triple::TvOS); +#else +                arch_32.GetTriple().setOS(llvm::Triple::IOS); +                arch_64.GetTriple().setOS(llvm::Triple::IOS); +#endif +            } +            else +            { +                arch_32.GetTriple().setOS(llvm::Triple::MacOSX); +                arch_64.GetTriple().setOS(llvm::Triple::MacOSX); +            } +        } +        else +        { +            // We have a 32 bit kernel on a 32 bit system +            arch_32.SetArchitecture(eArchTypeMachO, cputype, cpusubtype); +#if defined (TARGET_OS_WATCH) && TARGET_OS_WATCH == 1 +            arch_32.GetTriple().setOS(llvm::Triple::WatchOS); +#else +            arch_32.GetTriple().setOS(llvm::Triple::IOS); +#endif +            arch_64.Clear(); +        } +    } +} diff --git a/source/Host/macosx/HostThreadMacOSX.mm b/source/Host/macosx/HostThreadMacOSX.mm new file mode 100644 index 0000000000000..c84a78efd9e6c --- /dev/null +++ b/source/Host/macosx/HostThreadMacOSX.mm @@ -0,0 +1,102 @@ +//===-- HostThreadMacOSX.cpp ------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/macosx/HostThreadMacOSX.h" +#include "lldb/Host/Host.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <Foundation/Foundation.h> + +#include <objc/objc-auto.h> +#include <pthread.h> + +using namespace lldb_private; + +namespace +{ + +pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +pthread_key_t g_thread_create_key = 0; + +class MacOSXDarwinThread +{ +  public: +    MacOSXDarwinThread() +        : m_pool(nil) +    { +        // Register our thread with the collector if garbage collection is enabled. +        if (objc_collectingEnabled()) +        { +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5 +            // On Leopard and earlier there is no way objc_registerThreadWithCollector +            // function, so we do it manually. +            auto_zone_register_thread(auto_zone()); +#else +            // On SnowLeopard and later we just call the thread registration function. +            objc_registerThreadWithCollector(); +#endif +        } +        else +        { +            m_pool = [[NSAutoreleasePool alloc] init]; +        } +    } + +    ~MacOSXDarwinThread() +    { +        if (m_pool) +        { +            [m_pool drain]; +            m_pool = nil; +        } +    } + +    static void +    PThreadDestructor(void *v) +    { +        if (v) +            delete static_cast<MacOSXDarwinThread *>(v); +        ::pthread_setspecific(g_thread_create_key, NULL); +    } + +  protected: +    NSAutoreleasePool *m_pool; + +  private: +    DISALLOW_COPY_AND_ASSIGN(MacOSXDarwinThread); +}; + +void +InitThreadCreated() +{ +    ::pthread_key_create(&g_thread_create_key, MacOSXDarwinThread::PThreadDestructor); +} +} // namespace + +HostThreadMacOSX::HostThreadMacOSX() +    : HostThreadPosix() +{ +} + +HostThreadMacOSX::HostThreadMacOSX(lldb::thread_t thread) +    : HostThreadPosix(thread) +{ +} + +lldb::thread_result_t +HostThreadMacOSX::ThreadCreateTrampoline(lldb::thread_arg_t arg) +{ +    ::pthread_once(&g_thread_create_once, InitThreadCreated); +    if (g_thread_create_key) +    { +        ::pthread_setspecific(g_thread_create_key, new MacOSXDarwinThread()); +    } + +    return HostThreadPosix::ThreadCreateTrampoline(arg); +} diff --git a/source/Host/macosx/Symbols.cpp b/source/Host/macosx/Symbols.cpp new file mode 100644 index 0000000000000..f6a18febe6da9 --- /dev/null +++ b/source/Host/macosx/Symbols.cpp @@ -0,0 +1,559 @@ +//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Symbols.h" + +// C Includes +#include <dirent.h> +#include <pwd.h> +#include "lldb/Utility/SafeMachO.h" + +// C++ Includes +// Other libraries and framework includes +#include <CoreFoundation/CoreFoundation.h> + +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/CleanUp.h" +#include "Host/macosx/cfcpp/CFCBundle.h" +#include "Host/macosx/cfcpp/CFCData.h" +#include "Host/macosx/cfcpp/CFCReleaser.h" +#include "Host/macosx/cfcpp/CFCString.h" +#include "mach/machine.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::MachO; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices +extern "C" { + +CFURLRef DBGCopyFullDSYMURLForUUID (CFUUIDRef uuid, CFURLRef exec_url); +CFDictionaryRef DBGCopyDSYMPropertyLists (CFURLRef dsym_url); + +} +#endif + +int +LocateMacOSXFilesUsingDebugSymbols +( +    const ModuleSpec &module_spec, +    ModuleSpec &return_module_spec +) +{ +    return_module_spec = module_spec; +    return_module_spec.GetFileSpec().Clear(); +    return_module_spec.GetSymbolFileSpec().Clear(); + +    int items_found = 0; + +#if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) // No DebugSymbols on the iOS devices + +    const UUID *uuid = module_spec.GetUUIDPtr(); +    const ArchSpec *arch = module_spec.GetArchitecturePtr(); + +    if (uuid && uuid->IsValid()) +    { +        // Try and locate the dSYM file using DebugSymbols first +        const UInt8 *module_uuid = (const UInt8 *)uuid->GetBytes(); +        if (module_uuid != NULL) +        { +            CFCReleaser<CFUUIDRef> module_uuid_ref(::CFUUIDCreateWithBytes (NULL, +                                                                            module_uuid[0], +                                                                            module_uuid[1], +                                                                            module_uuid[2], +                                                                            module_uuid[3], +                                                                            module_uuid[4], +                                                                            module_uuid[5], +                                                                            module_uuid[6], +                                                                            module_uuid[7], +                                                                            module_uuid[8], +                                                                            module_uuid[9], +                                                                            module_uuid[10], +                                                                            module_uuid[11], +                                                                            module_uuid[12], +                                                                            module_uuid[13], +                                                                            module_uuid[14], +                                                                            module_uuid[15])); + +            if (module_uuid_ref.get()) +            { +                CFCReleaser<CFURLRef> exec_url; +                const FileSpec *exec_fspec = module_spec.GetFileSpecPtr(); +                Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); +                if (exec_fspec) +                { +                    char exec_cf_path[PATH_MAX]; +                    if (exec_fspec->GetPath(exec_cf_path, sizeof(exec_cf_path))) +                        exec_url.reset(::CFURLCreateFromFileSystemRepresentation (NULL, +                                                                                  (const UInt8 *)exec_cf_path, +                                                                                  strlen(exec_cf_path), +                                                                                  FALSE)); +                } + +                CFCReleaser<CFURLRef> dsym_url (::DBGCopyFullDSYMURLForUUID(module_uuid_ref.get(), exec_url.get())); +                char path[PATH_MAX]; + +                if (dsym_url.get()) +                { +                    if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) +                    { +                        if (log) +                        { +                            log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for the dSYM", path, uuid->GetAsString().c_str()); +                        } +                        FileSpec dsym_filespec(path, path[0] == '~'); + +                        if (dsym_filespec.GetFileType () == FileSpec::eFileTypeDirectory) +                        { +                            dsym_filespec = Symbols::FindSymbolFileInBundle (dsym_filespec, uuid, arch); +                            ++items_found; +                        } +                        else +                        { +                            ++items_found; +                        } +                        return_module_spec.GetSymbolFileSpec() = dsym_filespec; +                    } + +                    bool success = false; +                    if (log) +                    { +                        if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) +                        { +                            log->Printf ("DebugSymbols framework returned dSYM path of %s for UUID %s -- looking for an exec file", path, uuid->GetAsString().c_str()); +                        } + +                    } + +                    CFCReleaser<CFDictionaryRef> dict(::DBGCopyDSYMPropertyLists (dsym_url.get())); +                    CFDictionaryRef uuid_dict = NULL; +                    if (dict.get()) +                    { +                        CFCString uuid_cfstr (uuid->GetAsString().c_str()); +                        uuid_dict = static_cast<CFDictionaryRef>(::CFDictionaryGetValue (dict.get(), uuid_cfstr.get())); +                    } +                    if (uuid_dict) +                    { +                        CFStringRef exec_cf_path = static_cast<CFStringRef>(::CFDictionaryGetValue (uuid_dict, CFSTR("DBGSymbolRichExecutable"))); +                        if (exec_cf_path && ::CFStringGetFileSystemRepresentation (exec_cf_path, path, sizeof(path))) +                        { +                            if (log) +                            { +                                log->Printf ("plist bundle has exec path of %s for UUID %s", path, uuid->GetAsString().c_str()); +                            } +                            ++items_found; +                            FileSpec exec_filespec (path, path[0] == '~'); +                            if (exec_filespec.Exists()) +                            { +                                success = true; +                                return_module_spec.GetFileSpec() = exec_filespec; +                            } +                        } +                    } + +                    if (!success) +                    { +                        // No dictionary, check near the dSYM bundle for an executable that matches... +                        if (::CFURLGetFileSystemRepresentation (dsym_url.get(), true, (UInt8*)path, sizeof(path)-1)) +                        { +                            char *dsym_extension_pos = ::strstr (path, ".dSYM"); +                            if (dsym_extension_pos) +                            { +                                *dsym_extension_pos = '\0'; +                                if (log) +                                { +                                    log->Printf ("Looking for executable binary next to dSYM bundle with name with name %s", path); +                                } +                                FileSpec file_spec (path, true); +                                ModuleSpecList module_specs; +                                ModuleSpec matched_module_spec; +                                switch (file_spec.GetFileType()) +                                { +                                    case FileSpec::eFileTypeDirectory:  // Bundle directory? +                                        { +                                            CFCBundle bundle (path); +                                            CFCReleaser<CFURLRef> bundle_exe_url (bundle.CopyExecutableURL ()); +                                            if (bundle_exe_url.get()) +                                            { +                                                if (::CFURLGetFileSystemRepresentation (bundle_exe_url.get(), true, (UInt8*)path, sizeof(path)-1)) +                                                { +                                                    FileSpec bundle_exe_file_spec (path, true); +                                                    if (ObjectFile::GetModuleSpecifications(bundle_exe_file_spec, 0, 0, module_specs) && +                                                        module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + +                                                    { +                                                        ++items_found; +                                                        return_module_spec.GetFileSpec() = bundle_exe_file_spec; +                                                        if (log) +                                                        { +                                                            log->Printf ("Executable binary %s next to dSYM is compatible; using", path); +                                                        } +                                                    } +                                                } +                                            } +                                        } +                                        break; + +                                    case FileSpec::eFileTypePipe:       // Forget pipes +                                    case FileSpec::eFileTypeSocket:     // We can't process socket files +                                    case FileSpec::eFileTypeInvalid:    // File doesn't exist... +                                        break; + +                                    case FileSpec::eFileTypeUnknown: +                                    case FileSpec::eFileTypeRegular: +                                    case FileSpec::eFileTypeSymbolicLink: +                                    case FileSpec::eFileTypeOther: +                                        if (ObjectFile::GetModuleSpecifications(file_spec, 0, 0, module_specs) && +                                            module_specs.FindMatchingModuleSpec(module_spec, matched_module_spec)) + +                                        { +                                            ++items_found; +                                            return_module_spec.GetFileSpec() = file_spec; +                                            if (log) +                                            { +                                                log->Printf ("Executable binary %s next to dSYM is compatible; using", path); +                                            } +                                        } +                                        break; +                                } +                            } +                        } +                    } +                } +            } +        } +    } +#endif // #if !defined (__arm__) && !defined (__arm64__) && !defined (__aarch64__) + +    return items_found; +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, +                                 const lldb_private::UUID *uuid, +                                 const ArchSpec *arch) +{ +    char path[PATH_MAX]; + +    FileSpec dsym_fspec; + +    if (dsym_bundle_fspec.GetPath(path, sizeof(path))) +    { +        ::strncat (path, "/Contents/Resources/DWARF", sizeof(path) - strlen(path) - 1); + +        lldb_utility::CleanUp <DIR *, int> dirp (opendir(path), NULL, closedir); +        if (dirp.is_valid()) +        { +            dsym_fspec.GetDirectory().SetCString(path); +            struct dirent* dp; +            while ((dp = readdir(dirp.get())) != NULL) +            { +                // Only search directories +                if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) +                { +                    if (dp->d_namlen == 1 && dp->d_name[0] == '.') +                        continue; + +                    if (dp->d_namlen == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') +                        continue; +                } + +                if (dp->d_type == DT_REG || dp->d_type == DT_UNKNOWN) +                { +                    dsym_fspec.GetFilename().SetCString(dp->d_name); +                    ModuleSpecList module_specs; +                    if (ObjectFile::GetModuleSpecifications(dsym_fspec, 0, 0, module_specs)) +                    { +                        ModuleSpec spec; +                        for (size_t i = 0; i < module_specs.GetSize(); ++i) +                        { +                            assert(module_specs.GetModuleSpecAtIndex(i, spec)); +                            if ((uuid == NULL || (spec.GetUUIDPtr() && spec.GetUUID() == *uuid)) && +                                (arch == NULL || (spec.GetArchitecturePtr() && spec.GetArchitecture().IsCompatibleMatch(*arch)))) +                            { +                                return dsym_fspec; +                            } +                        } +                    } +                } +            } +        } +    } +    dsym_fspec.Clear(); +    return dsym_fspec; +} + +static bool +GetModuleSpecInfoFromUUIDDictionary (CFDictionaryRef uuid_dict, ModuleSpec &module_spec) +{ +    Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); +    bool success = false; +    if (uuid_dict != NULL && CFGetTypeID (uuid_dict) == CFDictionaryGetTypeID ()) +    { +        std::string str; +        CFStringRef cf_str; +         +        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSymbolRichExecutable")); +        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) +        { +            if (CFCString::FileSystemRepresentation(cf_str, str)) +            { +                module_spec.GetFileSpec().SetFile (str.c_str(), true); +                if (log) +                { +                    log->Printf ("From dsymForUUID plist: Symbol rich executable is at '%s'", str.c_str()); +                } +            } +        } +         +        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGDSYMPath")); +        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) +        { +            if (CFCString::FileSystemRepresentation(cf_str, str)) +            { +                module_spec.GetSymbolFileSpec().SetFile (str.c_str(), true); +                success = true; +                if (log) +                { +                    log->Printf ("From dsymForUUID plist: dSYM is at '%s'", str.c_str()); +                } +            } +        } +         +        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGArchitecture")); +        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) +        { +            if (CFCString::FileSystemRepresentation(cf_str, str)) +                module_spec.GetArchitecture().SetTriple(str.c_str()); +        } + +        std::string DBGBuildSourcePath; +        std::string DBGSourcePath; + +        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGBuildSourcePath")); +        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) +        { +            CFCString::FileSystemRepresentation(cf_str, DBGBuildSourcePath); +        } + +        cf_str = (CFStringRef)CFDictionaryGetValue ((CFDictionaryRef) uuid_dict, CFSTR("DBGSourcePath")); +        if (cf_str && CFGetTypeID (cf_str) == CFStringGetTypeID ()) +        { +            CFCString::FileSystemRepresentation(cf_str, DBGSourcePath); +        } +         +        if (!DBGBuildSourcePath.empty() && !DBGSourcePath.empty()) +        { +            module_spec.GetSourceMappingList().Append (ConstString(DBGBuildSourcePath.c_str()), ConstString(DBGSourcePath.c_str()), true); +        } +    } +    return success; +} + + +bool +Symbols::DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup) +{ +    bool success = false; +    const UUID *uuid_ptr = module_spec.GetUUIDPtr(); +    const FileSpec *file_spec_ptr = module_spec.GetFileSpecPtr(); + +    // It's expensive to check for the DBGShellCommands defaults setting, only do it once per +    // lldb run and cache the result.   +    static bool g_have_checked_for_dbgshell_command = false; +    static const char *g_dbgshell_command = NULL; +    if (g_have_checked_for_dbgshell_command == false) +    { +        g_have_checked_for_dbgshell_command = true; +        CFTypeRef defaults_setting = CFPreferencesCopyAppValue (CFSTR ("DBGShellCommands"), CFSTR ("com.apple.DebugSymbols")); +        if (defaults_setting && CFGetTypeID (defaults_setting) == CFStringGetTypeID()) +        {  +            char cstr_buf[PATH_MAX]; +            if (CFStringGetCString ((CFStringRef) defaults_setting, cstr_buf, sizeof (cstr_buf), kCFStringEncodingUTF8)) +            { +                g_dbgshell_command = strdup (cstr_buf);  // this malloc'ed memory will never be freed +            } +        } +        if (defaults_setting) +        { +            CFRelease (defaults_setting); +        } +    } + +    // When g_dbgshell_command is NULL, the user has not enabled the use of an external program +    // to find the symbols, don't run it for them. +    if (force_lookup == false && g_dbgshell_command == NULL) +    { +        return false; +    } + +    if (uuid_ptr || (file_spec_ptr && file_spec_ptr->Exists())) +    { +        static bool g_located_dsym_for_uuid_exe = false; +        static bool g_dsym_for_uuid_exe_exists = false; +        static char g_dsym_for_uuid_exe_path[PATH_MAX]; +        if (!g_located_dsym_for_uuid_exe) +        { +            g_located_dsym_for_uuid_exe = true; +            const char *dsym_for_uuid_exe_path_cstr = getenv("LLDB_APPLE_DSYMFORUUID_EXECUTABLE"); +            FileSpec dsym_for_uuid_exe_spec; +            if (dsym_for_uuid_exe_path_cstr) +            { +                dsym_for_uuid_exe_spec.SetFile(dsym_for_uuid_exe_path_cstr, true); +                g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); +            } +             +            if (!g_dsym_for_uuid_exe_exists) +            { +                dsym_for_uuid_exe_spec.SetFile("/usr/local/bin/dsymForUUID", false); +                g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); +                if (!g_dsym_for_uuid_exe_exists) +                { +                    long bufsize; +                    if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) != -1) +                    { +                        char buffer[bufsize]; +                        struct passwd pwd; +                        struct passwd *tilde_rc = NULL; +                        // we are a library so we need to use the reentrant version of getpwnam() +                        if (getpwnam_r ("rc", &pwd, buffer, bufsize, &tilde_rc) == 0  +                            && tilde_rc  +                            && tilde_rc->pw_dir) +                        { +                            std::string dsymforuuid_path(tilde_rc->pw_dir); +                            dsymforuuid_path += "/bin/dsymForUUID"; +                            dsym_for_uuid_exe_spec.SetFile(dsymforuuid_path.c_str(), false); +                            g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); +                        } +                    } +                } +            } +            if (!g_dsym_for_uuid_exe_exists && g_dbgshell_command != NULL) +            { +                dsym_for_uuid_exe_spec.SetFile(g_dbgshell_command, true); +                g_dsym_for_uuid_exe_exists = dsym_for_uuid_exe_spec.Exists(); +            } + +            if (g_dsym_for_uuid_exe_exists) +                dsym_for_uuid_exe_spec.GetPath (g_dsym_for_uuid_exe_path, sizeof(g_dsym_for_uuid_exe_path)); +        } +        if (g_dsym_for_uuid_exe_exists) +        { +            std::string uuid_str; +            char file_path[PATH_MAX]; +            file_path[0] = '\0'; + +            if (uuid_ptr) +                uuid_str = uuid_ptr->GetAsString(); + +            if (file_spec_ptr) +                file_spec_ptr->GetPath(file_path, sizeof(file_path)); +             +            StreamString command; +            if (!uuid_str.empty()) +                command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, uuid_str.c_str()); +            else if (file_path[0] != '\0') +                command.Printf("%s --ignoreNegativeCache --copyExecutable %s", g_dsym_for_uuid_exe_path, file_path); +             +            if (!command.GetString().empty()) +            { +                Log *log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST); +                int exit_status = -1; +                int signo = -1; +                std::string command_output; +                if (log) +                { +                    if (!uuid_str.empty()) +                        log->Printf("Calling %s with UUID %s to find dSYM", g_dsym_for_uuid_exe_path, uuid_str.c_str()); +                    else if (file_path[0] != '\0') +                        log->Printf("Calling %s with file %s to find dSYM", g_dsym_for_uuid_exe_path, file_path); +                } +                Error error = Host::RunShellCommand (command.GetData(), +                                                     NULL,              // current working directory +                                                     &exit_status,      // Exit status +                                                     &signo,            // Signal int * +                                                     &command_output,   // Command output +                                                     30,                // Large timeout to allow for long dsym download times +                                                     false);            // Don't run in a shell (we don't need shell expansion) +                if (error.Success() && exit_status == 0 && !command_output.empty()) +                { +                    CFCData data (CFDataCreateWithBytesNoCopy (NULL, +                                                               (const UInt8 *)command_output.data(), +                                                               command_output.size(), +                                                               kCFAllocatorNull)); +                     +                    CFCReleaser<CFDictionaryRef> plist((CFDictionaryRef)::CFPropertyListCreateFromXMLData (NULL, data.get(), kCFPropertyListImmutable, NULL)); +                     +                    if (plist.get() && CFGetTypeID (plist.get()) == CFDictionaryGetTypeID ()) +                    { +                        if (!uuid_str.empty()) +                        { +                            CFCString uuid_cfstr(uuid_str.c_str()); +                            CFDictionaryRef uuid_dict = (CFDictionaryRef)CFDictionaryGetValue (plist.get(), uuid_cfstr.get()); +                            success = GetModuleSpecInfoFromUUIDDictionary (uuid_dict, module_spec); +                        } +                        else +                        { +                            const CFIndex num_values = ::CFDictionaryGetCount(plist.get()); +                            if (num_values > 0) +                            { +                                std::vector<CFStringRef> keys (num_values, NULL); +                                std::vector<CFDictionaryRef> values (num_values, NULL); +                                ::CFDictionaryGetKeysAndValues(plist.get(), NULL, (const void **)&values[0]); +                                if (num_values == 1) +                                { +                                    return GetModuleSpecInfoFromUUIDDictionary (values[0], module_spec); +                                } +                                else +                                { +                                    for (CFIndex i=0; i<num_values; ++i) +                                    { +                                        ModuleSpec curr_module_spec; +                                        if (GetModuleSpecInfoFromUUIDDictionary (values[i], curr_module_spec)) +                                        { +                                            if (module_spec.GetArchitecture().IsCompatibleMatch(curr_module_spec.GetArchitecture())) +                                            { +                                                module_spec = curr_module_spec; +                                                return true; +                                            } +                                        } +                                    } +                                } +                            } +                        } +                    } +                } +                else +                { +                    if (log) +                    { +                        if (!uuid_str.empty()) +                            log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, uuid_str.c_str()); +                        else if (file_path[0] != '\0') +                            log->Printf("Called %s on %s, no matches", g_dsym_for_uuid_exe_path, file_path); +                    } +                } +            } +        } +    } +    return success; +} + diff --git a/source/Host/macosx/ThisThread.cpp b/source/Host/macosx/ThisThread.cpp new file mode 100644 index 0000000000000..95c7f2bf1e38b --- /dev/null +++ b/source/Host/macosx/ThisThread.cpp @@ -0,0 +1,39 @@ +//===-- ThisThread.cpp ------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/ThisThread.h" + +#include <pthread.h> + +using namespace lldb_private; + +void +ThisThread::SetName(llvm::StringRef name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 +    ::pthread_setname_np(name); +#endif +} + +void +ThisThread::GetName(llvm::SmallVectorImpl<char> &name) +{ +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 +    char pthread_name[1024]; +    dispatch_queue_t current_queue = ::dispatch_get_current_queue(); +    if (current_queue != NULL) +    { +        const char *queue_name = dispatch_queue_get_label(current_queue); +        if (queue_name && queue_name[0]) +        { +            name = queue_name; +        } +    } +#endif +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.cpp b/source/Host/macosx/cfcpp/CFCBundle.cpp new file mode 100644 index 0000000000000..71b074993661d --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.cpp @@ -0,0 +1,99 @@ +//===-- CFCBundle.cpp -------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCBundle.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCBundle constructor +//---------------------------------------------------------------------- +CFCBundle::CFCBundle(const char *path) : +    CFCReleaser<CFBundleRef>() +{ +    if (path && path[0]) +        SetPath(path); +} + +CFCBundle::CFCBundle(CFURLRef url) : +    CFCReleaser<CFBundleRef>(url ? CFBundleCreate(NULL, url) : NULL) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCBundle::~CFCBundle() +{ +} + +//---------------------------------------------------------------------- +// Set the path for a bundle by supplying a +//---------------------------------------------------------------------- +bool +CFCBundle::SetPath (const char *path) +{ +    CFAllocatorRef alloc = kCFAllocatorDefault; +    // Release our old bundle and URL +    reset(); + +    // Make a CFStringRef from the supplied path +    CFCString cf_path; +    cf_path.SetFileSystemRepresentation(path); +    if (cf_path.get()) +    { +        // Make our Bundle URL +        CFCReleaser<CFURLRef> bundle_url (::CFURLCreateWithFileSystemPath (alloc, cf_path.get(), kCFURLPOSIXPathStyle, true)); +        if (bundle_url.get()) +            reset (::CFBundleCreate (alloc, bundle_url.get())); +    } +    return get() != NULL; +} + +bool +CFCBundle::GetPath (char *dst, size_t dst_len) +{ +    CFBundleRef bundle = get(); +    if (bundle) +    { +        CFCReleaser<CFURLRef> bundle_url (CFBundleCopyBundleURL (bundle)); +        if (bundle_url.get()) +        { +            Boolean resolveAgainstBase = 0; +            return ::CFURLGetFileSystemRepresentation (bundle_url.get(), resolveAgainstBase, (UInt8 *)dst, dst_len) != 0; +        } +    } +    return false; +}    + +CFStringRef +CFCBundle::GetIdentifier () const +{ +    CFBundleRef bundle = get(); +    if (bundle != NULL) +        return ::CFBundleGetIdentifier (bundle); +    return NULL; +} + +CFTypeRef +CFCBundle::GetValueForInfoDictionaryKey(CFStringRef key) const +{ +    CFBundleRef bundle = get(); +    if (bundle != NULL) +        return ::CFBundleGetValueForInfoDictionaryKey(bundle, key); +    return NULL; +} + +CFURLRef +CFCBundle::CopyExecutableURL () const +{ +    CFBundleRef bundle = get(); +    if (bundle != NULL) +        return CFBundleCopyExecutableURL(bundle); +    return NULL; +} diff --git a/source/Host/macosx/cfcpp/CFCBundle.h b/source/Host/macosx/cfcpp/CFCBundle.h new file mode 100644 index 0000000000000..1cd1b681af840 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCBundle.h @@ -0,0 +1,50 @@ +//===-- CFCBundle.h ---------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFBundle_h_ +#define CoreFoundationCPP_CFBundle_h_ + +#include "CFCReleaser.h" + +class CFCBundle : public CFCReleaser<CFBundleRef> +{ +public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    CFCBundle (const char *path = NULL); +    CFCBundle (CFURLRef url); + +    virtual +    ~CFCBundle(); + +    CFURLRef +    CopyExecutableURL () const; + +    CFStringRef +    GetIdentifier () const; + +    CFTypeRef +    GetValueForInfoDictionaryKey(CFStringRef key) const; + +    bool +    GetPath (char *dst, size_t dst_len); + +    bool +    SetPath (const char *path); + +private: +    // Disallow copy and assignment constructors +    CFCBundle(const CFCBundle&); + +    const CFCBundle& +    operator=(const CFCBundle&); +}; + +#endif // #ifndef CoreFoundationCPP_CFBundle_h_ diff --git a/source/Host/macosx/cfcpp/CFCData.cpp b/source/Host/macosx/cfcpp/CFCData.cpp new file mode 100644 index 0000000000000..4f49368ad8adf --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCData.cpp @@ -0,0 +1,82 @@ +//===-- CFCData.cpp ---------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCData.h" + +//---------------------------------------------------------------------- +// CFCData constructor +//---------------------------------------------------------------------- +CFCData::CFCData(CFDataRef data) : +    CFCReleaser<CFDataRef>(data) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData::CFCData(const CFCData& rhs) : +    CFCReleaser<CFDataRef>(rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCData copy constructor +//---------------------------------------------------------------------- +CFCData& +CFCData::operator=(const CFCData& rhs) + +{ +    if (this != &rhs) +        *this = rhs; +    return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCData::~CFCData() +{ +} + + +CFIndex +CFCData::GetLength() const +{ +    CFDataRef data = get(); +    if (data) +        return CFDataGetLength (data); +    return 0; +} + + +const uint8_t* +CFCData::GetBytePtr() const +{ +    CFDataRef data = get(); +    if (data) +        return CFDataGetBytePtr (data); +    return NULL; +} + +CFDataRef +CFCData::Serialize(CFPropertyListRef plist, CFPropertyListFormat format) +{ +    CFAllocatorRef alloc = kCFAllocatorDefault; +    reset(); +    CFCReleaser<CFWriteStreamRef> stream (::CFWriteStreamCreateWithAllocatedBuffers (alloc, alloc)); +    ::CFWriteStreamOpen (stream.get()); +    CFIndex len = ::CFPropertyListWriteToStream (plist, stream.get(), format, NULL); +    if (len > 0) +        reset((CFDataRef)::CFWriteStreamCopyProperty (stream.get(), kCFStreamPropertyDataWritten)); +    ::CFWriteStreamClose (stream.get()); +    return get(); +} + diff --git a/source/Host/macosx/cfcpp/CFCData.h b/source/Host/macosx/cfcpp/CFCData.h new file mode 100644 index 0000000000000..6a718f54c0555 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCData.h @@ -0,0 +1,35 @@ +//===-- CFCData.h -----------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFData_h_ +#define CoreFoundationCPP_CFData_h_ + +#include "CFCReleaser.h" + +class CFCData : public CFCReleaser<CFDataRef> +{ +public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    CFCData(CFDataRef data = NULL); +    CFCData(const CFCData& rhs); +    CFCData& operator=(const CFCData& rhs); +    virtual ~CFCData(); + +        CFDataRef Serialize(CFPropertyListRef plist, CFPropertyListFormat format); +        const uint8_t* GetBytePtr () const; +        CFIndex GetLength () const; +protected: +    //------------------------------------------------------------------ +    // Classes that inherit from CFCData can see and modify these +    //------------------------------------------------------------------ +}; + +#endif // #ifndef CoreFoundationCPP_CFData_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.cpp b/source/Host/macosx/cfcpp/CFCMutableArray.cpp new file mode 100644 index 0000000000000..c3c0a11193a7c --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.cpp @@ -0,0 +1,166 @@ +//===-- CFCMutableArray.cpp -------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableArray.h" +#include "CFCString.h" + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(CFMutableArrayRef s) : +    CFCReleaser<CFMutableArrayRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray::CFCMutableArray(const CFCMutableArray& rhs) : +    CFCReleaser<CFMutableArrayRef> (rhs)    // NOTE: this won't make a copy of the array, just add a new reference to it +{ +} + +//---------------------------------------------------------------------- +// CFCMutableArray copy constructor +//---------------------------------------------------------------------- +CFCMutableArray& +CFCMutableArray::operator=(const CFCMutableArray& rhs) +{ +    if (this != &rhs) +        *this = rhs;    // NOTE: this operator won't make a copy of the array, just add a new reference to it +    return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableArray::~CFCMutableArray() +{ +} + + +CFIndex +CFCMutableArray::GetCount() const +{ +    CFMutableArrayRef array = get(); +    if (array) +        return ::CFArrayGetCount (array); +    return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(CFRange range, const void *value) const +{ +    CFMutableArrayRef array = get(); +    if (array) +        return ::CFArrayGetCountOfValue (array, range, value); +    return 0; +} + +CFIndex +CFCMutableArray::GetCountOfValue(const void *value) const +{ +    CFMutableArrayRef array = get(); +    if (array) +        return ::CFArrayGetCountOfValue (array, CFRangeMake(0, GetCount()), value); +    return 0; +} + +const void * +CFCMutableArray::GetValueAtIndex(CFIndex idx) const +{ +    CFMutableArrayRef array = get(); +    if (array) +    { +        const CFIndex num_array_items = ::CFArrayGetCount (array); +        if (0 <= idx && idx < num_array_items) +        { +            return ::CFArrayGetValueAtIndex (array, idx); +        } +    } +    return NULL; +} + +bool +CFCMutableArray::SetValueAtIndex(CFIndex idx, const void *value) +{ +    CFMutableArrayRef array = get(); +    if (array != NULL) +    { +        const CFIndex num_array_items = ::CFArrayGetCount (array); +        if (0 <= idx && idx < num_array_items) +        { +            ::CFArraySetValueAtIndex (array, idx, value); +            return true; +        } +    } +    return false; +} + + +bool +CFCMutableArray::AppendValue(const void *value, bool can_create) +{ +    CFMutableArrayRef array = get(); +    if (array == NULL) +    { +        if (can_create == false) +            return false; +        array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); +        reset ( array ); +    } +    if (array != NULL) +    { +        ::CFArrayAppendValue(array, value); +        return true; +    } +    return false; +} + + +bool +CFCMutableArray::AppendCStringAsCFString (const char *s, CFStringEncoding encoding, bool can_create) +{ +    CFMutableArrayRef array = get(); +    if (array == NULL) +    { +        if (can_create == false) +            return false; +        array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); +        reset ( array ); +    } +    if (array != NULL) +    { +        CFCString cf_str (s, encoding); +        ::CFArrayAppendValue (array, cf_str.get()); +        return true; +    } +    return false; +} + +bool +CFCMutableArray::AppendFileSystemRepresentationAsCFString (const char *s, bool can_create) +{ +    CFMutableArrayRef array = get(); +    if (array == NULL) +    { +        if (can_create == false) +            return false; +        array = ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); +        reset ( array ); +    } +    if (array != NULL) +    { +        CFCString cf_path; +        cf_path.SetFileSystemRepresentation(s); +        ::CFArrayAppendValue (array, cf_path.get()); +        return true; +    } +    return false; +} diff --git a/source/Host/macosx/cfcpp/CFCMutableArray.h b/source/Host/macosx/cfcpp/CFCMutableArray.h new file mode 100644 index 0000000000000..f78cd92ffab16 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableArray.h @@ -0,0 +1,39 @@ +//===-- CFCMutableArray.h ---------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableArray_h_ +#define CoreFoundationCPP_CFMutableArray_h_ + +#include "CFCReleaser.h" + +class CFCMutableArray : public CFCReleaser<CFMutableArrayRef> +{ +public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    CFCMutableArray(CFMutableArrayRef array = NULL); +    CFCMutableArray(const CFCMutableArray& rhs);                // This will copy the array contents into a new array +    CFCMutableArray& operator=(const CFCMutableArray& rhs); // This will re-use the same array and just bump the ref count +    virtual ~CFCMutableArray(); + +    CFIndex         GetCount() const; +    CFIndex         GetCountOfValue(const void *value) const; +    CFIndex         GetCountOfValue(CFRange range, const void *value) const; +    const void *    GetValueAtIndex(CFIndex idx) const; +    bool            SetValueAtIndex(CFIndex idx, const void *value); +    bool            AppendValue(const void *value, bool can_create = true); // Appends value and optionally creates a CFCMutableArray if this class doesn't contain one +    bool            AppendCStringAsCFString (const char *cstr,  +                                             CFStringEncoding encoding = kCFStringEncodingUTF8,  +                                             bool can_create = true); +    bool            AppendFileSystemRepresentationAsCFString (const char *s,  +                                                              bool can_create = true); +}; + +#endif // #ifndef CoreFoundationCPP_CFMutableArray_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp new file mode 100644 index 0000000000000..bce023bfd616e --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.cpp @@ -0,0 +1,529 @@ +//===-- CFCMutableDictionary.cpp --------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableDictionary.h" +#include "CFCString.h" +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(CFMutableDictionaryRef s) : +    CFCReleaser<CFMutableDictionaryRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +CFCMutableDictionary::CFCMutableDictionary(const CFCMutableDictionary& rhs) : +    CFCReleaser<CFMutableDictionaryRef> (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableDictionary copy constructor +//---------------------------------------------------------------------- +const CFCMutableDictionary& +CFCMutableDictionary::operator=(const CFCMutableDictionary& rhs) +{ +    if (this != &rhs) +        *this = rhs; +    return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableDictionary::~CFCMutableDictionary() +{ +} + + +CFIndex +CFCMutableDictionary::GetCount() const +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        return ::CFDictionaryGetCount (dict); +    return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfKey(const void *key) const + +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        return ::CFDictionaryGetCountOfKey (dict, key); +    return 0; +} + +CFIndex +CFCMutableDictionary::GetCountOfValue(const void *value) const + +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        return ::CFDictionaryGetCountOfValue (dict, value); +    return 0; +} + +void +CFCMutableDictionary::GetKeysAndValues(const void **keys, const void **values) const +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        ::CFDictionaryGetKeysAndValues (dict, keys, values); +} + + +const void * +CFCMutableDictionary::GetValue(const void *key) const + +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        return ::CFDictionaryGetValue (dict, key); +    return NULL; +} + +Boolean +CFCMutableDictionary::GetValueIfPresent(const void *key, const void **value_handle) const +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        return ::CFDictionaryGetValueIfPresent (dict, key, value_handle); +    return false; +} + + +CFMutableDictionaryRef +CFCMutableDictionary::Dictionary(bool can_create) +{ +    CFMutableDictionaryRef dict = get(); +    if (can_create && dict == NULL) +    { +        dict = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); +        reset ( dict ); +    } +    return dict; +} + +bool +CFCMutableDictionary::AddValue(CFStringRef key, const void *value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Let the dictionary own the CFNumber +        ::CFDictionaryAddValue (dict, key, value); +        return true; +    } +    return false; +} + +bool +CFCMutableDictionary::SetValue(CFStringRef key, const void *value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Let the dictionary own the CFNumber +        ::CFDictionarySetValue (dict, key, value); +        return true; +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueSInt8(CFStringRef key, int8_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt8Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueSInt16(CFStringRef key, int16_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueSInt32(CFStringRef key, int32_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueSInt64(CFStringRef key, int64_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Have to promote to the next size type so things don't appear negative of the MSBit is set... +        int16_t sval = value; +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueUInt8(CFStringRef key, uint8_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Have to promote to the next size type so things don't appear negative of the MSBit is set... +        int16_t sval = value; +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt16Type, &sval)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + + +bool +CFCMutableDictionary::AddValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Have to promote to the next size type so things don't appear negative of the MSBit is set... +        int32_t sval = value; +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueUInt16(CFStringRef key, uint16_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Have to promote to the next size type so things don't appear negative of the MSBit is set... +        int32_t sval = value; +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt32Type, &sval)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Have to promote to the next size type so things don't appear negative of the MSBit is set... +        int64_t sval = value; +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueUInt32(CFStringRef key, uint32_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // Have to promote to the next size type so things don't appear negative of the MSBit is set... +        int64_t sval = value; +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &sval)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + + +bool +CFCMutableDictionary::AddValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // The number may appear negative if the MSBit is set in "value". Due to a limitation of +        // CFNumber, there isn't a way to have it show up otherwise as of this writing. +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + + +bool +CFCMutableDictionary::SetValueUInt64(CFStringRef key, uint64_t value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // The number may appear negative if the MSBit is set in "value". Due to a limitation of +        // CFNumber, there isn't a way to have it show up otherwise as of this writing. +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberSInt64Type, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueDouble(CFStringRef key, double value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // The number may appear negative if the MSBit is set in "value". Due to a limitation of +        // CFNumber, there isn't a way to have it show up otherwise as of this writing. +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueDouble(CFStringRef key, double value, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        // The number may appear negative if the MSBit is set in "value". Due to a limitation of +        // CFNumber, there isn't a way to have it show up otherwise as of this writing. +        CFCReleaser<CFNumberRef> cf_number(::CFNumberCreate (kCFAllocatorDefault, kCFNumberDoubleType, &value)); +        if (cf_number.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_number.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::AddValueCString(CFStringRef key, const char *cstr, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCString cf_str(cstr, kCFStringEncodingUTF8); +        if (cf_str.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionaryAddValue (dict, key, cf_str.get()); +            return true; +        } +    } +    return false; +} + +bool +CFCMutableDictionary::SetValueCString(CFStringRef key, const char *cstr, bool can_create) +{ +    CFMutableDictionaryRef dict = Dictionary(can_create); +    if (dict != NULL) +    { +        CFCString cf_str(cstr, kCFStringEncodingUTF8); +        if (cf_str.get()) +        { +            // Let the dictionary own the CFNumber +            ::CFDictionarySetValue (dict, key, cf_str.get()); +            return true; +        } +    } +    return false; +} + + +void +CFCMutableDictionary::RemoveAllValues() +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        ::CFDictionaryRemoveAllValues(dict); +} + +void +CFCMutableDictionary::RemoveValue(const void *value) +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        ::CFDictionaryRemoveValue(dict, value); +} +void +CFCMutableDictionary::ReplaceValue(const void *key, const void *value) +{ +    CFMutableDictionaryRef dict = get(); +    if (dict) +        ::CFDictionaryReplaceValue (dict, key, value); +} + diff --git a/source/Host/macosx/cfcpp/CFCMutableDictionary.h b/source/Host/macosx/cfcpp/CFCMutableDictionary.h new file mode 100644 index 0000000000000..a1cfb68f569e1 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableDictionary.h @@ -0,0 +1,79 @@ +//===-- CFCMutableDictionary.h ----------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableDictionary_h_ +#define CoreFoundationCPP_CFMutableDictionary_h_ + +#include "CFCReleaser.h" + +class CFCMutableDictionary : public CFCReleaser<CFMutableDictionaryRef> +{ +public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    CFCMutableDictionary(CFMutableDictionaryRef s = NULL); +    CFCMutableDictionary(const CFCMutableDictionary& rhs); +    virtual ~CFCMutableDictionary(); + +    //------------------------------------------------------------------ +    // Operators +    //------------------------------------------------------------------ +    const CFCMutableDictionary& +    operator=(const CFCMutableDictionary& rhs); + + +    CFIndex GetCount() const; +    CFIndex GetCountOfKey(const void *value) const; +    CFIndex GetCountOfValue(const void *value) const; +    void    GetKeysAndValues(const void **keys, const void **values) const; +    const void * GetValue(const void *key) const; +    Boolean GetValueIfPresent(const void *key, const void **value_handle) const; +    bool    AddValue(CFStringRef key, const void *value, bool can_create = false); +    bool    SetValue(CFStringRef key, const void *value, bool can_create = false); +    bool    AddValueSInt8(CFStringRef key, int8_t value, bool can_create = false); +    bool    SetValueSInt8(CFStringRef key, int8_t value, bool can_create = false); +    bool    AddValueSInt16(CFStringRef key, int16_t value, bool can_create = false); +    bool    SetValueSInt16(CFStringRef key, int16_t value, bool can_create = false); +    bool    AddValueSInt32(CFStringRef key, int32_t value, bool can_create = false); +    bool    SetValueSInt32(CFStringRef key, int32_t value, bool can_create = false); +    bool    AddValueSInt64(CFStringRef key, int64_t value, bool can_create = false); +    bool    SetValueSInt64(CFStringRef key, int64_t value, bool can_create = false); +    bool    AddValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); +    bool    SetValueUInt8(CFStringRef key, uint8_t value, bool can_create = false); +    bool    AddValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); +    bool    SetValueUInt16(CFStringRef key, uint16_t value, bool can_create = false); +    bool    AddValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); +    bool    SetValueUInt32(CFStringRef key, uint32_t value, bool can_create = false); +    bool    AddValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); +    bool    SetValueUInt64(CFStringRef key, uint64_t value, bool can_create = false); +    bool    AddValueDouble(CFStringRef key, double value, bool can_create = false); +    bool    SetValueDouble(CFStringRef key, double value, bool can_create = false); +    bool    AddValueCString(CFStringRef key, const char *cstr, bool can_create = false); +    bool    SetValueCString(CFStringRef key, const char *cstr, bool can_create = false); +    void    RemoveValue(const void *value); +    void    ReplaceValue(const void *key, const void *value); +    void    RemoveAllValues(); +    CFMutableDictionaryRef Dictionary(bool can_create); + + +protected: +    //------------------------------------------------------------------ +    // Classes that inherit from CFCMutableDictionary can see and modify these +    //------------------------------------------------------------------ + +private: +    //------------------------------------------------------------------ +    // For CFCMutableDictionary only +    //------------------------------------------------------------------ + +}; + + +#endif  // CoreFoundationCPP_CFMutableDictionary_h_ diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.cpp b/source/Host/macosx/cfcpp/CFCMutableSet.cpp new file mode 100644 index 0000000000000..afc09e180b6b6 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableSet.cpp @@ -0,0 +1,114 @@ +//===-- CFCMutableSet.cpp ---------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCMutableSet.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(CFMutableSetRef s) : +    CFCReleaser<CFMutableSetRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +CFCMutableSet::CFCMutableSet(const CFCMutableSet& rhs) : +    CFCReleaser<CFMutableSetRef> (rhs) +{ +} + +//---------------------------------------------------------------------- +// CFCMutableSet copy constructor +//---------------------------------------------------------------------- +const CFCMutableSet& +CFCMutableSet::operator=(const CFCMutableSet& rhs) +{ +    if (this != &rhs) +        *this = rhs; +    return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCMutableSet::~CFCMutableSet() +{ +} + + +CFIndex +CFCMutableSet::GetCount() const +{ +    CFMutableSetRef set = get(); +    if (set) +        return ::CFSetGetCount (set); +    return 0; +} + +CFIndex +CFCMutableSet::GetCountOfValue(const void *value) const +{ +    CFMutableSetRef set = get(); +    if (set) +        return ::CFSetGetCountOfValue (set, value); +    return 0; +} + +const void * +CFCMutableSet::GetValue(const void *value) const +{ +    CFMutableSetRef set = get(); +    if (set) +        return ::CFSetGetValue(set, value); +    return NULL; +} + + +const void * +CFCMutableSet::AddValue(const void *value, bool can_create) +{ +    CFMutableSetRef set = get(); +    if (set == NULL) +    { +        if (can_create == false) +            return NULL; +        set = ::CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks); +        reset ( set ); +    } +    if (set != NULL) +    { +        ::CFSetAddValue(set, value); +        return value; +    } +    return NULL; +} + +void +CFCMutableSet::RemoveValue(const void *value) +{ +    CFMutableSetRef set = get(); +    if (set) +        ::CFSetRemoveValue(set, value); +} + +void +CFCMutableSet::RemoveAllValues() +{ +    CFMutableSetRef set = get(); +    if (set) +        ::CFSetRemoveAllValues(set); +} + diff --git a/source/Host/macosx/cfcpp/CFCMutableSet.h b/source/Host/macosx/cfcpp/CFCMutableSet.h new file mode 100644 index 0000000000000..78f7a8be81d20 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCMutableSet.h @@ -0,0 +1,53 @@ +//===-- CFCMutableSet.h -----------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFMutableSet_h_ +#define CoreFoundationCPP_CFMutableSet_h_ + +#include "CFCReleaser.h" + +class CFCMutableSet : public CFCReleaser<CFMutableSetRef> +{ +public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +    CFCMutableSet(CFMutableSetRef s = NULL); +    CFCMutableSet(const CFCMutableSet& rhs); +    virtual ~CFCMutableSet(); + +    //------------------------------------------------------------------ +    // Operators +    //------------------------------------------------------------------ +    const CFCMutableSet& +    operator=(const CFCMutableSet& rhs); + + +    CFIndex GetCount() const; +    CFIndex GetCountOfValue(const void *value) const; +    const void * GetValue(const void *value) const; +    const void * AddValue(const void *value, bool can_create); +    void RemoveValue(const void *value); +    void RemoveAllValues(); + + + +protected: +    //------------------------------------------------------------------ +    // Classes that inherit from CFCMutableSet can see and modify these +    //------------------------------------------------------------------ + +private: +    //------------------------------------------------------------------ +    // For CFCMutableSet only +    //------------------------------------------------------------------ + +}; + +#endif  // CoreFoundationCPP_CFMutableSet_h_ diff --git a/source/Host/macosx/cfcpp/CFCReleaser.h b/source/Host/macosx/cfcpp/CFCReleaser.h new file mode 100644 index 0000000000000..67dd2ead57990 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCReleaser.h @@ -0,0 +1,158 @@ +//===-- CFCReleaser.h -------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFReleaser_h_ +#define CoreFoundationCPP_CFReleaser_h_ + +#include <CoreFoundation/CoreFoundation.h> + +#ifdef __cplusplus + +#include <assert.h> + +//---------------------------------------------------------------------- +// Templatized CF helper class that can own any CF pointer and will +// call CFRelease() on any valid pointer it owns unless that pointer is +// explicitly released using the release() member function. This class +// is designed to mimic the std::auto_ptr<T> class and has all of the +// same functions. The one thing to watch out for is the +// CFCReleaser<T>::release() function won't actually CFRelease any owned +// pointer, it is designed to relinquish ownership of the pointer just +// like std:auto_ptr<T>::release() does. +//---------------------------------------------------------------------- +template <class T> +class CFCReleaser +{ +public: +    //---------------------------------------------------------- +    // Constructor that takes a pointer to a CF object that is +    // to be released when this object goes out of scope +    //---------------------------------------------------------- +    CFCReleaser(T ptr = NULL) : +        _ptr(ptr) +    { +    } + +    //---------------------------------------------------------- +    // Copy constructor +    // +    // Note that copying a CFCReleaser will not transfer +    // ownership of the contained pointer, but it will bump its +    // reference count. This is where this class differs from +    // std::auto_ptr. +    //---------------------------------------------------------- +    CFCReleaser(const CFCReleaser& rhs) : +        _ptr(rhs.get()) +    { +        if (get()) +            ::CFRetain(get()); +    } + + +    //---------------------------------------------------------- +    // The destructor will release the pointer that it contains +    // if it has a valid pointer. +    //---------------------------------------------------------- +    virtual ~CFCReleaser() +    { +        reset(); +    } + +    //---------------------------------------------------------- +    // Assignment operator. +    // +    // Note that assigning one CFCReleaser to another will +    // not transfer ownership of the contained pointer, but it +    // will bump its reference count. This is where this class +    // differs from std::auto_ptr. +    //---------------------------------------------------------- +    CFCReleaser& +    operator= (const CFCReleaser<T>& rhs) +    { +        if (this != &rhs) +        { +            // Replace our owned pointer with the new one +            reset(rhs.get()); +            // Retain the current pointer that we own +            if (get()) +                ::CFRetain(get()); +        } +        return *this; +    } + +    //---------------------------------------------------------- +    // Get the address of the contained type in case it needs +    // to be passed to a function that will fill in a pointer +    // value. The function currently will assert if _ptr is not +    // NULL because the only time this method should be used is +    // if another function will modify the contents, and we +    // could leak a pointer if this is not NULL. If the +    // assertion fires, check the offending code, or call +    // reset() prior to using the "ptr_address()" member to make +    // sure any owned objects has CFRelease called on it. +    // I had to add the "enforce_null" bool here because some +    // API's require the pointer address even though they don't change it. +    //---------------------------------------------------------- +    T* +    ptr_address(bool enforce_null = true) +    { +        if (enforce_null) +            assert (_ptr == NULL); +        return &_ptr; +    } + +    //---------------------------------------------------------- +    // Access the pointer itself +    //---------------------------------------------------------- +    T +    get() +    { +        return _ptr; +    } + +    const T +    get() const +    { +        return _ptr; +    } + + +    //---------------------------------------------------------- +    // Set a new value for the pointer and CFRelease our old +    // value if we had a valid one. +    //---------------------------------------------------------- +    void +    reset(T ptr = NULL) +    { +        if ((_ptr != NULL) && (ptr != _ptr)) +            ::CFRelease(_ptr); +        _ptr = ptr; +    } + +    //---------------------------------------------------------- +    // Release ownership without calling CFRelease. This class +    // is designed to mimic std::auto_ptr<T>, so the release +    // method releases ownership of the contained pointer +    // and does NOT call CFRelease. +    //---------------------------------------------------------- +    T +    release() +    { +        T tmp = _ptr; +        _ptr = NULL; +        return tmp; +    } + +private: +    T _ptr; +}; + +#endif  // #ifdef __cplusplus +#endif  // #ifndef CoreFoundationCPP_CFReleaser_h_ + diff --git a/source/Host/macosx/cfcpp/CFCString.cpp b/source/Host/macosx/cfcpp/CFCString.cpp new file mode 100644 index 0000000000000..81a96b824999c --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCString.cpp @@ -0,0 +1,195 @@ +//===-- CFCString.cpp -------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CFCString.h" +#include <string> +#include <glob.h> + +//---------------------------------------------------------------------- +// CFCString constructor +//---------------------------------------------------------------------- +CFCString::CFCString(CFStringRef s) : +    CFCReleaser<CFStringRef> (s) +{ +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString::CFCString(const CFCString& rhs) : +    CFCReleaser<CFStringRef> (rhs) +{ + +} + +//---------------------------------------------------------------------- +// CFCString copy constructor +//---------------------------------------------------------------------- +CFCString& +CFCString::operator=(const CFCString& rhs) +{ +    if (this != &rhs) +        *this = rhs; +    return *this; +} + +CFCString::CFCString (const char *cstr, CFStringEncoding cstr_encoding) : +    CFCReleaser<CFStringRef> () +{ +    if (cstr && cstr[0]) +    { +        reset(::CFStringCreateWithCString(kCFAllocatorDefault, cstr, cstr_encoding)); +    } +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CFCString::~CFCString() +{ +} + +const char * +CFCString::GetFileSystemRepresentation(std::string& s) +{ +    return CFCString::FileSystemRepresentation(get(), s); +} + +CFStringRef +CFCString::SetFileSystemRepresentation (const char *path) +{ +    CFStringRef new_value = NULL; +    if (path && path[0]) +        new_value = ::CFStringCreateWithFileSystemRepresentation (kCFAllocatorDefault, path); +    reset(new_value); +    return get(); +} + + +CFStringRef +CFCString::SetFileSystemRepresentationFromCFType (CFTypeRef cf_type) +{ +    CFStringRef new_value = NULL; +    if (cf_type != NULL) +    { +        CFTypeID cf_type_id = ::CFGetTypeID(cf_type); + +        if (cf_type_id == ::CFStringGetTypeID()) +        { +            // Retain since we are using the existing object +            new_value = (CFStringRef)::CFRetain(cf_type); +        } +        else if (cf_type_id == ::CFURLGetTypeID()) +        { +            new_value = ::CFURLCopyFileSystemPath((CFURLRef)cf_type, kCFURLPOSIXPathStyle); +        } +    } +    reset(new_value); +    return get(); +} + +CFStringRef +CFCString::SetFileSystemRepresentationAndExpandTilde (const char *path) +{ +    std::string expanded_path; +    if (CFCString::ExpandTildeInPath(path, expanded_path)) +        SetFileSystemRepresentation(expanded_path.c_str()); +    else +        reset(); +    return get(); +} + +const char * +CFCString::UTF8(std::string& str) +{ +    return CFCString::UTF8(get(), str); +} + +// Static function that puts a copy of the UTF8 contents of CF_STR into STR +// and returns the C string pointer that is contained in STR when successful, else +// NULL is returned. This allows the std::string parameter to own the extracted string, +// and also allows that string to be returned as a C string pointer that can be used. + +const char * +CFCString::UTF8 (CFStringRef cf_str, std::string& str) +{ +    if (cf_str) +    { +        const CFStringEncoding encoding = kCFStringEncodingUTF8; +        CFIndex max_utf8_str_len = CFStringGetLength (cf_str); +        max_utf8_str_len = CFStringGetMaximumSizeForEncoding (max_utf8_str_len, encoding); +        if (max_utf8_str_len > 0) +        { +            str.resize(max_utf8_str_len); +            if (!str.empty()) +            { +                if (CFStringGetCString (cf_str, &str[0], str.size(), encoding)) +                { +                    str.resize(strlen(str.c_str())); +                    return str.c_str(); +                } +            } +        } +    } +    return NULL; +} + +const char* +CFCString::ExpandTildeInPath(const char* path, std::string &expanded_path) +{ +    glob_t globbuf; +    if (::glob (path, GLOB_TILDE, NULL, &globbuf) == 0) +    { +        expanded_path = globbuf.gl_pathv[0]; +        ::globfree (&globbuf); +    } +    else +        expanded_path.clear(); + +    return expanded_path.c_str(); +} + +// Static function that puts a copy of the file system representation of CF_STR +// into STR and returns the C string pointer that is contained in STR when +// successful, else NULL is returned. This allows the std::string parameter +// to own the extracted string, and also allows that string to be returned as +// a C string pointer that can be used. + +const char * +CFCString::FileSystemRepresentation (CFStringRef cf_str, std::string& str) +{ +    if (cf_str) +    { +        CFIndex max_length = ::CFStringGetMaximumSizeOfFileSystemRepresentation (cf_str); +        if (max_length > 0) +        { +            str.resize(max_length); +            if (!str.empty()) +            { +                if (::CFStringGetFileSystemRepresentation (cf_str, &str[0], str.size())) +                { +                    str.erase(::strlen(str.c_str())); +                    return str.c_str(); +                } +            } +        } +    } +    str.erase(); +    return NULL; +} + + +CFIndex +CFCString::GetLength() const +{ +    CFStringRef str = get(); +    if (str) +        return CFStringGetLength (str); +    return 0; +} diff --git a/source/Host/macosx/cfcpp/CFCString.h b/source/Host/macosx/cfcpp/CFCString.h new file mode 100644 index 0000000000000..27c090313ece6 --- /dev/null +++ b/source/Host/macosx/cfcpp/CFCString.h @@ -0,0 +1,41 @@ +//===-- CFCString.h ---------------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CoreFoundationCPP_CFString_h_ +#define CoreFoundationCPP_CFString_h_ + +#include <iosfwd> + +#include "CFCReleaser.h" + +class CFCString : public CFCReleaser<CFStringRef> +{ +public: +    //------------------------------------------------------------------ +    // Constructors and Destructors +    //------------------------------------------------------------------ +                        CFCString (CFStringRef cf_str = NULL); +                        CFCString (const char *s, CFStringEncoding encoding = kCFStringEncodingUTF8); +                        CFCString (const CFCString& rhs); +                        CFCString& operator= (const CFCString& rhs); +                        virtual ~CFCString (); + +        const char *    GetFileSystemRepresentation (std::string& str); +        CFStringRef     SetFileSystemRepresentation (const char *path); +        CFStringRef     SetFileSystemRepresentationFromCFType (CFTypeRef cf_type); +        CFStringRef     SetFileSystemRepresentationAndExpandTilde (const char *path); +        const char *    UTF8 (std::string& str); +        CFIndex         GetLength() const; +        static const char *UTF8 (CFStringRef cf_str, std::string& str); +        static const char *FileSystemRepresentation (CFStringRef cf_str, std::string& str); +        static const char *ExpandTildeInPath(const char* path, std::string &expanded_path); + +}; + +#endif // #ifndef CoreFoundationCPP_CFString_h_ diff --git a/source/Host/macosx/cfcpp/CoreFoundationCPP.h b/source/Host/macosx/cfcpp/CoreFoundationCPP.h new file mode 100644 index 0000000000000..6843e2649cdab --- /dev/null +++ b/source/Host/macosx/cfcpp/CoreFoundationCPP.h @@ -0,0 +1,30 @@ +//===-- CoreFoundationCPP.h -------------------------------------*- C++ -*-===// +// +//                     The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//---------------------------------------------------------------------- +// +//  CoreFoundationCPP.h +//  CoreFoundationCPP +// +//  Created by Greg Clayton on 4/23/09. +// +// +//---------------------------------------------------------------------- + +#ifndef CoreFoundationCPP_CoreFoundationCPP_H_ +#define CoreFoundationCPP_CoreFoundationCPP_H_ + +#include <CoreFoundationCPP/CFCBundle.h> +#include <CoreFoundationCPP/CFCData.h> +#include <CoreFoundationCPP/CFCReleaser.h> +#include <CoreFoundationCPP/CFCMutableArray.h> +#include <CoreFoundationCPP/CFCMutableDictionary.h> +#include <CoreFoundationCPP/CFCMutableSet.h> +#include <CoreFoundationCPP/CFCString.h> + +#endif  // CoreFoundationCPP_CoreFoundationCPP_H_ | 
