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(¤t_env);
+ bear_reset_env_t(&initial_env);
+ int const result = (*fp)(file, argv);
+ bear_reset_env_t(¤t_env);
+ bear_release_env_t(¤t_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(¤t_env);
+ bear_reset_env_t(&initial_env);
+ int const result = (*fp)(file, search_path, argv);
+ bear_reset_env_t(¤t_env);
+ bear_release_env_t(¤t_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);
+}