D9600: Add scan-build python implementation

llvm-svn: 257533
diff --git a/clang/tools/scan-build-py/libear/__init__.py b/clang/tools/scan-build-py/libear/__init__.py
new file mode 100644
index 0000000..3e1c13c
--- /dev/null
+++ b/clang/tools/scan-build-py/libear/__init__.py
@@ -0,0 +1,260 @@
+# -*- coding: utf-8 -*-
+#                     The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+""" This module compiles the intercept library. """
+
+import sys
+import os
+import os.path
+import re
+import tempfile
+import shutil
+import contextlib
+import logging
+
+__all__ = ['build_libear']
+
+
+def build_libear(compiler, dst_dir):
+    """ Returns the full path to the 'libear' library. """
+
+    try:
+        src_dir = os.path.dirname(os.path.realpath(__file__))
+        toolset = make_toolset(src_dir)
+        toolset.set_compiler(compiler)
+        toolset.set_language_standard('c99')
+        toolset.add_definitions(['-D_GNU_SOURCE'])
+
+        configure = do_configure(toolset)
+        configure.check_function_exists('execve', 'HAVE_EXECVE')
+        configure.check_function_exists('execv', 'HAVE_EXECV')
+        configure.check_function_exists('execvpe', 'HAVE_EXECVPE')
+        configure.check_function_exists('execvp', 'HAVE_EXECVP')
+        configure.check_function_exists('execvP', 'HAVE_EXECVP2')
+        configure.check_function_exists('exect', 'HAVE_EXECT')
+        configure.check_function_exists('execl', 'HAVE_EXECL')
+        configure.check_function_exists('execlp', 'HAVE_EXECLP')
+        configure.check_function_exists('execle', 'HAVE_EXECLE')
+        configure.check_function_exists('posix_spawn', 'HAVE_POSIX_SPAWN')
+        configure.check_function_exists('posix_spawnp', 'HAVE_POSIX_SPAWNP')
+        configure.check_symbol_exists('_NSGetEnviron', 'crt_externs.h',
+                                      'HAVE_NSGETENVIRON')
+        configure.write_by_template(
+            os.path.join(src_dir, 'config.h.in'),
+            os.path.join(dst_dir, 'config.h'))
+
+        target = create_shared_library('ear', toolset)
+        target.add_include(dst_dir)
+        target.add_sources('ear.c')
+        target.link_against(toolset.dl_libraries())
+        target.link_against(['pthread'])
+        target.build_release(dst_dir)
+
+        return os.path.join(dst_dir, target.name)
+
+    except Exception:
+        logging.info("Could not build interception library.", exc_info=True)
+        return None
+
+
+def execute(cmd, *args, **kwargs):
+    """ Make subprocess execution silent. """
+
+    import subprocess
+    kwargs.update({'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT})
+    return subprocess.check_call(cmd, *args, **kwargs)
+
+
+@contextlib.contextmanager
+def TemporaryDirectory(**kwargs):
+    name = tempfile.mkdtemp(**kwargs)
+    try:
+        yield name
+    finally:
+        shutil.rmtree(name)
+
+
+class Toolset(object):
+    """ Abstract class to represent different toolset. """
+
+    def __init__(self, src_dir):
+        self.src_dir = src_dir
+        self.compiler = None
+        self.c_flags = []
+
+    def set_compiler(self, compiler):
+        """ part of public interface """
+        self.compiler = compiler
+
+    def set_language_standard(self, standard):
+        """ part of public interface """
+        self.c_flags.append('-std=' + standard)
+
+    def add_definitions(self, defines):
+        """ part of public interface """
+        self.c_flags.extend(defines)
+
+    def dl_libraries(self):
+        raise NotImplementedError()
+
+    def shared_library_name(self, name):
+        raise NotImplementedError()
+
+    def shared_library_c_flags(self, release):
+        extra = ['-DNDEBUG', '-O3'] if release else []
+        return extra + ['-fPIC'] + self.c_flags
+
+    def shared_library_ld_flags(self, release, name):
+        raise NotImplementedError()
+
+
+class DarwinToolset(Toolset):
+    def __init__(self, src_dir):
+        Toolset.__init__(self, src_dir)
+
+    def dl_libraries(self):
+        return []
+
+    def shared_library_name(self, name):
+        return 'lib' + name + '.dylib'
+
+    def shared_library_ld_flags(self, release, name):
+        extra = ['-dead_strip'] if release else []
+        return extra + ['-dynamiclib', '-install_name', '@rpath/' + name]
+
+
+class UnixToolset(Toolset):
+    def __init__(self, src_dir):
+        Toolset.__init__(self, src_dir)
+
+    def dl_libraries(self):
+        return []
+
+    def shared_library_name(self, name):
+        return 'lib' + name + '.so'
+
+    def shared_library_ld_flags(self, release, name):
+        extra = [] if release else []
+        return extra + ['-shared', '-Wl,-soname,' + name]
+
+
+class LinuxToolset(UnixToolset):
+    def __init__(self, src_dir):
+        UnixToolset.__init__(self, src_dir)
+
+    def dl_libraries(self):
+        return ['dl']
+
+
+def make_toolset(src_dir):
+    platform = sys.platform
+    if platform in {'win32', 'cygwin'}:
+        raise RuntimeError('not implemented on this platform')
+    elif platform == 'darwin':
+        return DarwinToolset(src_dir)
+    elif platform in {'linux', 'linux2'}:
+        return LinuxToolset(src_dir)
+    else:
+        return UnixToolset(src_dir)
+
+
+class Configure(object):
+    def __init__(self, toolset):
+        self.ctx = toolset
+        self.results = {'APPLE': sys.platform == 'darwin'}
+
+    def _try_to_compile_and_link(self, source):
+        try:
+            with TemporaryDirectory() as work_dir:
+                src_file = 'check.c'
+                with open(os.path.join(work_dir, src_file), 'w') as handle:
+                    handle.write(source)
+
+                execute([self.ctx.compiler, src_file] + self.ctx.c_flags,
+                        cwd=work_dir)
+                return True
+        except Exception:
+            return False
+
+    def check_function_exists(self, function, name):
+        template = "int FUNCTION(); int main() { return FUNCTION(); }"
+        source = template.replace("FUNCTION", function)
+
+        logging.debug('Checking function %s', function)
+        found = self._try_to_compile_and_link(source)
+        logging.debug('Checking function %s -- %s', function,
+                      'found' if found else 'not found')
+        self.results.update({name: found})
+
+    def check_symbol_exists(self, symbol, include, name):
+        template = """#include <INCLUDE>
+                      int main() { return ((int*)(&SYMBOL))[0]; }"""
+        source = template.replace('INCLUDE', include).replace("SYMBOL", symbol)
+
+        logging.debug('Checking symbol %s', symbol)
+        found = self._try_to_compile_and_link(source)
+        logging.debug('Checking symbol %s -- %s', symbol,
+                      'found' if found else 'not found')
+        self.results.update({name: found})
+
+    def write_by_template(self, template, output):
+        def transform(line, definitions):
+
+            pattern = re.compile(r'^#cmakedefine\s+(\S+)')
+            m = pattern.match(line)
+            if m:
+                key = m.group(1)
+                if key not in definitions or not definitions[key]:
+                    return '/* #undef {} */\n'.format(key)
+                else:
+                    return '#define {}\n'.format(key)
+            return line
+
+        with open(template, 'r') as src_handle:
+            logging.debug('Writing config to %s', output)
+            with open(output, 'w') as dst_handle:
+                for line in src_handle:
+                    dst_handle.write(transform(line, self.results))
+
+
+def do_configure(toolset):
+    return Configure(toolset)
+
+
+class SharedLibrary(object):
+    def __init__(self, name, toolset):
+        self.name = toolset.shared_library_name(name)
+        self.ctx = toolset
+        self.inc = []
+        self.src = []
+        self.lib = []
+
+    def add_include(self, directory):
+        self.inc.extend(['-I', directory])
+
+    def add_sources(self, source):
+        self.src.append(source)
+
+    def link_against(self, libraries):
+        self.lib.extend(['-l' + lib for lib in libraries])
+
+    def build_release(self, directory):
+        for src in self.src:
+            logging.debug('Compiling %s', src)
+            execute(
+                [self.ctx.compiler, '-c', os.path.join(self.ctx.src_dir, src),
+                 '-o', src + '.o'] + self.inc +
+                self.ctx.shared_library_c_flags(True),
+                cwd=directory)
+        logging.debug('Linking %s', self.name)
+        execute(
+            [self.ctx.compiler] + [src + '.o' for src in self.src] +
+            ['-o', self.name] + self.lib +
+            self.ctx.shared_library_ld_flags(True, self.name),
+            cwd=directory)
+
+
+def create_shared_library(name, toolset):
+    return SharedLibrary(name, toolset)
diff --git a/clang/tools/scan-build-py/libear/config.h.in b/clang/tools/scan-build-py/libear/config.h.in
new file mode 100644
index 0000000..6643d89
--- /dev/null
+++ b/clang/tools/scan-build-py/libear/config.h.in
@@ -0,0 +1,23 @@
+/* -*- coding: utf-8 -*-
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+*/
+
+#pragma once
+
+#cmakedefine HAVE_EXECVE
+#cmakedefine HAVE_EXECV
+#cmakedefine HAVE_EXECVPE
+#cmakedefine HAVE_EXECVP
+#cmakedefine HAVE_EXECVP2
+#cmakedefine HAVE_EXECT
+#cmakedefine HAVE_EXECL
+#cmakedefine HAVE_EXECLP
+#cmakedefine HAVE_EXECLE
+#cmakedefine HAVE_POSIX_SPAWN
+#cmakedefine HAVE_POSIX_SPAWNP
+#cmakedefine HAVE_NSGETENVIRON
+
+#cmakedefine APPLE
diff --git a/clang/tools/scan-build-py/libear/ear.c b/clang/tools/scan-build-py/libear/ear.c
new file mode 100644
index 0000000..0e7093a
--- /dev/null
+++ b/clang/tools/scan-build-py/libear/ear.c
@@ -0,0 +1,605 @@
+/* -*- coding: utf-8 -*-
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+*/
+
+/**
+ * This file implements a shared library. This library can be pre-loaded by
+ * the dynamic linker of the Operating System (OS). It implements a few function
+ * related to process creation. By pre-load this library the executed process
+ * uses these functions instead of those from the standard library.
+ *
+ * The idea here is to inject a logic before call the real methods. The logic is
+ * to dump the call into a file. To call the real method this library is doing
+ * the job of the dynamic linker.
+ *
+ * The only input for the log writing is about the destination directory.
+ * This is passed as environment variable.
+ */
+
+#include "config.h"
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <pthread.h>
+
+#if defined HAVE_POSIX_SPAWN || defined HAVE_POSIX_SPAWNP
+#include <spawn.h>
+#endif
+
+#if defined HAVE_NSGETENVIRON
+# include <crt_externs.h>
+#else
+extern char **environ;
+#endif
+
+#define ENV_OUTPUT "INTERCEPT_BUILD_TARGET_DIR"
+#ifdef APPLE
+# define ENV_FLAT    "DYLD_FORCE_FLAT_NAMESPACE"
+# define ENV_PRELOAD "DYLD_INSERT_LIBRARIES"
+# define ENV_SIZE 3
+#else
+# define ENV_PRELOAD "LD_PRELOAD"
+# define ENV_SIZE 2
+#endif
+
+#define DLSYM(TYPE_, VAR_, SYMBOL_)                                            \
+    union {                                                                    \
+        void *from;                                                            \
+        TYPE_ to;                                                              \
+    } cast;                                                                    \
+    if (0 == (cast.from = dlsym(RTLD_NEXT, SYMBOL_))) {                        \
+        perror("bear: dlsym");                                                 \
+        exit(EXIT_FAILURE);                                                    \
+    }                                                                          \
+    TYPE_ const VAR_ = cast.to;
+
+
+typedef char const * bear_env_t[ENV_SIZE];
+
+static int bear_capture_env_t(bear_env_t *env);
+static int bear_reset_env_t(bear_env_t *env);
+static void bear_release_env_t(bear_env_t *env);
+static char const **bear_update_environment(char *const envp[], bear_env_t *env);
+static char const **bear_update_environ(char const **in, char const *key, char const *value);
+static char **bear_get_environment();
+static void bear_report_call(char const *fun, char const *const argv[]);
+static char const **bear_strings_build(char const *arg, va_list *ap);
+static char const **bear_strings_copy(char const **const in);
+static char const **bear_strings_append(char const **in, char const *e);
+static size_t bear_strings_length(char const *const *in);
+static void bear_strings_release(char const **);
+
+
+static bear_env_t env_names =
+    { ENV_OUTPUT
+    , ENV_PRELOAD
+#ifdef ENV_FLAT
+    , ENV_FLAT
+#endif
+    };
+
+static bear_env_t initial_env =
+    { 0
+    , 0
+#ifdef ENV_FLAT
+    , 0
+#endif
+    };
+
+static int initialized = 0;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static void on_load(void) __attribute__((constructor));
+static void on_unload(void) __attribute__((destructor));
+
+
+#ifdef HAVE_EXECVE
+static int call_execve(const char *path, char *const argv[],
+                       char *const envp[]);
+#endif
+#ifdef HAVE_EXECVP
+static int call_execvp(const char *file, char *const argv[]);
+#endif
+#ifdef HAVE_EXECVPE
+static int call_execvpe(const char *file, char *const argv[],
+                        char *const envp[]);
+#endif
+#ifdef HAVE_EXECVP2
+static int call_execvP(const char *file, const char *search_path,
+                       char *const argv[]);
+#endif
+#ifdef HAVE_EXECT
+static int call_exect(const char *path, char *const argv[],
+                      char *const envp[]);
+#endif
+#ifdef HAVE_POSIX_SPAWN
+static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
+                            const posix_spawn_file_actions_t *file_actions,
+                            const posix_spawnattr_t *restrict attrp,
+                            char *const argv[restrict],
+                            char *const envp[restrict]);
+#endif
+#ifdef HAVE_POSIX_SPAWNP
+static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
+                             const posix_spawn_file_actions_t *file_actions,
+                             const posix_spawnattr_t *restrict attrp,
+                             char *const argv[restrict],
+                             char *const envp[restrict]);
+#endif
+
+
+/* Initialization method to Captures the relevant environment variables.
+ */
+
+static void on_load(void) {
+    pthread_mutex_lock(&mutex);
+    if (!initialized)
+        initialized = bear_capture_env_t(&initial_env);
+    pthread_mutex_unlock(&mutex);
+}
+
+static void on_unload(void) {
+    pthread_mutex_lock(&mutex);
+    bear_release_env_t(&initial_env);
+    initialized = 0;
+    pthread_mutex_unlock(&mutex);
+}
+
+
+/* These are the methods we are try to hijack.
+ */
+
+#ifdef HAVE_EXECVE
+int execve(const char *path, char *const argv[], char *const envp[]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_execve(path, argv, envp);
+}
+#endif
+
+#ifdef HAVE_EXECV
+#ifndef HAVE_EXECVE
+#error can not implement execv without execve
+#endif
+int execv(const char *path, char *const argv[]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    char * const * envp = bear_get_environment();
+    return call_execve(path, argv, envp);
+}
+#endif
+
+#ifdef HAVE_EXECVPE
+int execvpe(const char *file, char *const argv[], char *const envp[]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_execvpe(file, argv, envp);
+}
+#endif
+
+#ifdef HAVE_EXECVP
+int execvp(const char *file, char *const argv[]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_execvp(file, argv);
+}
+#endif
+
+#ifdef HAVE_EXECVP2
+int execvP(const char *file, const char *search_path, char *const argv[]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_execvP(file, search_path, argv);
+}
+#endif
+
+#ifdef HAVE_EXECT
+int exect(const char *path, char *const argv[], char *const envp[]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_exect(path, argv, envp);
+}
+#endif
+
+#ifdef HAVE_EXECL
+# ifndef HAVE_EXECVE
+#  error can not implement execl without execve
+# endif
+int execl(const char *path, const char *arg, ...) {
+    va_list args;
+    va_start(args, arg);
+    char const **argv = bear_strings_build(arg, &args);
+    va_end(args);
+
+    bear_report_call(__func__, (char const *const *)argv);
+    char * const * envp = bear_get_environment();
+    int const result = call_execve(path, (char *const *)argv, envp);
+
+    bear_strings_release(argv);
+    return result;
+}
+#endif
+
+#ifdef HAVE_EXECLP
+# ifndef HAVE_EXECVP
+#  error can not implement execlp without execvp
+# endif
+int execlp(const char *file, const char *arg, ...) {
+    va_list args;
+    va_start(args, arg);
+    char const **argv = bear_strings_build(arg, &args);
+    va_end(args);
+
+    bear_report_call(__func__, (char const *const *)argv);
+    int const result = call_execvp(file, (char *const *)argv);
+
+    bear_strings_release(argv);
+    return result;
+}
+#endif
+
+#ifdef HAVE_EXECLE
+# ifndef HAVE_EXECVE
+#  error can not implement execle without execve
+# endif
+// int execle(const char *path, const char *arg, ..., char * const envp[]);
+int execle(const char *path, const char *arg, ...) {
+    va_list args;
+    va_start(args, arg);
+    char const **argv = bear_strings_build(arg, &args);
+    char const **envp = va_arg(args, char const **);
+    va_end(args);
+
+    bear_report_call(__func__, (char const *const *)argv);
+    int const result =
+        call_execve(path, (char *const *)argv, (char *const *)envp);
+
+    bear_strings_release(argv);
+    return result;
+}
+#endif
+
+#ifdef HAVE_POSIX_SPAWN
+int posix_spawn(pid_t *restrict pid, const char *restrict path,
+                const posix_spawn_file_actions_t *file_actions,
+                const posix_spawnattr_t *restrict attrp,
+                char *const argv[restrict], char *const envp[restrict]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_posix_spawn(pid, path, file_actions, attrp, argv, envp);
+}
+#endif
+
+#ifdef HAVE_POSIX_SPAWNP
+int posix_spawnp(pid_t *restrict pid, const char *restrict file,
+                 const posix_spawn_file_actions_t *file_actions,
+                 const posix_spawnattr_t *restrict attrp,
+                 char *const argv[restrict], char *const envp[restrict]) {
+    bear_report_call(__func__, (char const *const *)argv);
+    return call_posix_spawnp(pid, file, file_actions, attrp, argv, envp);
+}
+#endif
+
+/* These are the methods which forward the call to the standard implementation.
+ */
+
+#ifdef HAVE_EXECVE
+static int call_execve(const char *path, char *const argv[],
+                       char *const envp[]) {
+    typedef int (*func)(const char *, char *const *, char *const *);
+
+    DLSYM(func, fp, "execve");
+
+    char const **const menvp = bear_update_environment(envp, &initial_env);
+    int const result = (*fp)(path, argv, (char *const *)menvp);
+    bear_strings_release(menvp);
+    return result;
+}
+#endif
+
+#ifdef HAVE_EXECVPE
+static int call_execvpe(const char *file, char *const argv[],
+                        char *const envp[]) {
+    typedef int (*func)(const char *, char *const *, char *const *);
+
+    DLSYM(func, fp, "execvpe");
+
+    char const **const menvp = bear_update_environment(envp, &initial_env);
+    int const result = (*fp)(file, argv, (char *const *)menvp);
+    bear_strings_release(menvp);
+    return result;
+}
+#endif
+
+#ifdef HAVE_EXECVP
+static int call_execvp(const char *file, char *const argv[]) {
+    typedef int (*func)(const char *file, char *const argv[]);
+
+    DLSYM(func, fp, "execvp");
+
+    bear_env_t current_env;
+    bear_capture_env_t(&current_env);
+    bear_reset_env_t(&initial_env);
+    int const result = (*fp)(file, argv);
+    bear_reset_env_t(&current_env);
+    bear_release_env_t(&current_env);
+
+    return result;
+}
+#endif
+
+#ifdef HAVE_EXECVP2
+static int call_execvP(const char *file, const char *search_path,
+                       char *const argv[]) {
+    typedef int (*func)(const char *, const char *, char *const *);
+
+    DLSYM(func, fp, "execvP");
+
+    bear_env_t current_env;
+    bear_capture_env_t(&current_env);
+    bear_reset_env_t(&initial_env);
+    int const result = (*fp)(file, search_path, argv);
+    bear_reset_env_t(&current_env);
+    bear_release_env_t(&current_env);
+
+    return result;
+}
+#endif
+
+#ifdef HAVE_EXECT
+static int call_exect(const char *path, char *const argv[],
+                      char *const envp[]) {
+    typedef int (*func)(const char *, char *const *, char *const *);
+
+    DLSYM(func, fp, "exect");
+
+    char const **const menvp = bear_update_environment(envp, &initial_env);
+    int const result = (*fp)(path, argv, (char *const *)menvp);
+    bear_strings_release(menvp);
+    return result;
+}
+#endif
+
+#ifdef HAVE_POSIX_SPAWN
+static int call_posix_spawn(pid_t *restrict pid, const char *restrict path,
+                            const posix_spawn_file_actions_t *file_actions,
+                            const posix_spawnattr_t *restrict attrp,
+                            char *const argv[restrict],
+                            char *const envp[restrict]) {
+    typedef int (*func)(pid_t *restrict, const char *restrict,
+                        const posix_spawn_file_actions_t *,
+                        const posix_spawnattr_t *restrict,
+                        char *const *restrict, char *const *restrict);
+
+    DLSYM(func, fp, "posix_spawn");
+
+    char const **const menvp = bear_update_environment(envp, &initial_env);
+    int const result =
+        (*fp)(pid, path, file_actions, attrp, argv, (char *const *restrict)menvp);
+    bear_strings_release(menvp);
+    return result;
+}
+#endif
+
+#ifdef HAVE_POSIX_SPAWNP
+static int call_posix_spawnp(pid_t *restrict pid, const char *restrict file,
+                             const posix_spawn_file_actions_t *file_actions,
+                             const posix_spawnattr_t *restrict attrp,
+                             char *const argv[restrict],
+                             char *const envp[restrict]) {
+    typedef int (*func)(pid_t *restrict, const char *restrict,
+                        const posix_spawn_file_actions_t *,
+                        const posix_spawnattr_t *restrict,
+                        char *const *restrict, char *const *restrict);
+
+    DLSYM(func, fp, "posix_spawnp");
+
+    char const **const menvp = bear_update_environment(envp, &initial_env);
+    int const result =
+        (*fp)(pid, file, file_actions, attrp, argv, (char *const *restrict)menvp);
+    bear_strings_release(menvp);
+    return result;
+}
+#endif
+
+/* this method is to write log about the process creation. */
+
+static void bear_report_call(char const *fun, char const *const argv[]) {
+    static int const GS = 0x1d;
+    static int const RS = 0x1e;
+    static int const US = 0x1f;
+
+    if (!initialized)
+        return;
+
+    pthread_mutex_lock(&mutex);
+    const char *cwd = getcwd(NULL, 0);
+    if (0 == cwd) {
+        perror("bear: getcwd");
+        exit(EXIT_FAILURE);
+    }
+    char const * const out_dir = initial_env[0];
+    size_t const path_max_length = strlen(out_dir) + 32;
+    char filename[path_max_length];
+    if (-1 == snprintf(filename, path_max_length, "%s/%d.cmd", out_dir, getpid())) {
+        perror("bear: snprintf");
+        exit(EXIT_FAILURE);
+    }
+    FILE * fd = fopen(filename, "a+");
+    if (0 == fd) {
+        perror("bear: fopen");
+        exit(EXIT_FAILURE);
+    }
+    fprintf(fd, "%d%c", getpid(), RS);
+    fprintf(fd, "%d%c", getppid(), RS);
+    fprintf(fd, "%s%c", fun, RS);
+    fprintf(fd, "%s%c", cwd, RS);
+    size_t const argc = bear_strings_length(argv);
+    for (size_t it = 0; it < argc; ++it) {
+        fprintf(fd, "%s%c", argv[it], US);
+    }
+    fprintf(fd, "%c", GS);
+    if (fclose(fd)) {
+        perror("bear: fclose");
+        exit(EXIT_FAILURE);
+    }
+    free((void *)cwd);
+    pthread_mutex_unlock(&mutex);
+}
+
+/* update environment assure that chilren processes will copy the desired
+ * behaviour */
+
+static int bear_capture_env_t(bear_env_t *env) {
+    int status = 1;
+    for (size_t it = 0; it < ENV_SIZE; ++it) {
+        char const * const env_value = getenv(env_names[it]);
+        char const * const env_copy = (env_value) ? strdup(env_value) : env_value;
+        (*env)[it] = env_copy;
+        status &= (env_copy) ? 1 : 0;
+    }
+    return status;
+}
+
+static int bear_reset_env_t(bear_env_t *env) {
+    int status = 1;
+    for (size_t it = 0; it < ENV_SIZE; ++it) {
+        if ((*env)[it]) {
+            setenv(env_names[it], (*env)[it], 1);
+        } else {
+            unsetenv(env_names[it]);
+        }
+    }
+    return status;
+}
+
+static void bear_release_env_t(bear_env_t *env) {
+    for (size_t it = 0; it < ENV_SIZE; ++it) {
+        free((void *)(*env)[it]);
+        (*env)[it] = 0;
+    }
+}
+
+static char const **bear_update_environment(char *const envp[], bear_env_t *env) {
+    char const **result = bear_strings_copy((char const **)envp);
+    for (size_t it = 0; it < ENV_SIZE && (*env)[it]; ++it)
+        result = bear_update_environ(result, env_names[it], (*env)[it]);
+    return result;
+}
+
+static char const **bear_update_environ(char const *envs[], char const *key, char const * const value) {
+    // find the key if it's there
+    size_t const key_length = strlen(key);
+    char const **it = envs;
+    for (; (it) && (*it); ++it) {
+        if ((0 == strncmp(*it, key, key_length)) &&
+            (strlen(*it) > key_length) && ('=' == (*it)[key_length]))
+            break;
+    }
+    // allocate a environment entry
+    size_t const value_length = strlen(value);
+    size_t const env_length = key_length + value_length + 2;
+    char *env = malloc(env_length);
+    if (0 == env) {
+        perror("bear: malloc [in env_update]");
+        exit(EXIT_FAILURE);
+    }
+    if (-1 == snprintf(env, env_length, "%s=%s", key, value)) {
+        perror("bear: snprintf");
+        exit(EXIT_FAILURE);
+    }
+    // replace or append the environment entry
+    if (it && *it) {
+        free((void *)*it);
+        *it = env;
+	return envs;
+    }
+    return bear_strings_append(envs, env);
+}
+
+static char **bear_get_environment() {
+#if defined HAVE_NSGETENVIRON
+    return *_NSGetEnviron();
+#else
+    return environ;
+#endif
+}
+
+/* util methods to deal with string arrays. environment and process arguments
+ * are both represented as string arrays. */
+
+static char const **bear_strings_build(char const *const arg, va_list *args) {
+    char const **result = 0;
+    size_t size = 0;
+    for (char const *it = arg; it; it = va_arg(*args, char const *)) {
+        result = realloc(result, (size + 1) * sizeof(char const *));
+        if (0 == result) {
+            perror("bear: realloc");
+            exit(EXIT_FAILURE);
+        }
+        char const *copy = strdup(it);
+        if (0 == copy) {
+            perror("bear: strdup");
+            exit(EXIT_FAILURE);
+        }
+        result[size++] = copy;
+    }
+    result = realloc(result, (size + 1) * sizeof(char const *));
+    if (0 == result) {
+        perror("bear: realloc");
+        exit(EXIT_FAILURE);
+    }
+    result[size++] = 0;
+
+    return result;
+}
+
+static char const **bear_strings_copy(char const **const in) {
+    size_t const size = bear_strings_length(in);
+
+    char const **const result = malloc((size + 1) * sizeof(char const *));
+    if (0 == result) {
+        perror("bear: malloc");
+        exit(EXIT_FAILURE);
+    }
+
+    char const **out_it = result;
+    for (char const *const *in_it = in; (in_it) && (*in_it);
+         ++in_it, ++out_it) {
+        *out_it = strdup(*in_it);
+        if (0 == *out_it) {
+            perror("bear: strdup");
+            exit(EXIT_FAILURE);
+        }
+    }
+    *out_it = 0;
+    return result;
+}
+
+static char const **bear_strings_append(char const **const in,
+                                        char const *const e) {
+    size_t size = bear_strings_length(in);
+    char const **result = realloc(in, (size + 2) * sizeof(char const *));
+    if (0 == result) {
+        perror("bear: realloc");
+        exit(EXIT_FAILURE);
+    }
+    result[size++] = e;
+    result[size++] = 0;
+    return result;
+}
+
+static size_t bear_strings_length(char const *const *const in) {
+    size_t result = 0;
+    for (char const *const *it = in; (it) && (*it); ++it)
+        ++result;
+    return result;
+}
+
+static void bear_strings_release(char const **in) {
+    for (char const *const *it = in; (it) && (*it); ++it) {
+        free((void *)*it);
+    }
+    free((void *)in);
+}