]> Dogcows Code - chaz/yoink/blobdiff - src/stlplus/portability/subprocesses.cpp
import stlplus 3.7
[chaz/yoink] / src / stlplus / portability / subprocesses.cpp
index b6c791b3149cfad9370c64b1b015a586a4432585..782b994f5647f624156d7fff8b9677981c2f53f9 100644 (file)
-////////////////////////////////////////////////////////////////////////////////
-
-//   Author:    Andy Rushton
-//   Copyright: (c) Southampton University 1999-2004
-//              (c) Andy Rushton           2004-2009
-//   License:   BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Bug fix by Alistair Low: kill on Windows now kills grandchild processes as
-// well as the child process. This is done using jobs - which has to be
-// enabled by stating that the version of Windows is at least 5.0
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#define _WIN32_WINNT 0x0500
-#endif
-
-#include "subprocesses.hpp"
-#include "file_system.hpp"
-#include "dprintf.hpp"
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-
-#ifdef MSWINDOWS
-#else
-#include <signal.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <fcntl.h>
-extern char** environ;
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // argument-vector related stuff
-
-  static void skip_white (const std::string& command, unsigned& i)
-  {
-    while(i < command.size() && isspace(command[i]))
-      i++;
-  }
-
-  // get_argument is the main function for breaking a string down into separate command arguments
-  // it mimics the way shells break down a command into an argv[] and unescapes the escaped characters on the way
-
-  static std::string get_argument (const std::string& command, unsigned& i)
-  {
-    std::string result;
-#ifdef MSWINDOWS
-
-  // as far as I know, there is only double-quoting and no escape character in DOS
-  // so, how do you include a double-quote in an argument???
-
-    bool dquote = false;
-    for ( ; i < command.size(); i++)
-    {
-      char ch = command[i];
-      if (!dquote && isspace(ch)) break;
-      if (dquote)
-      {
-        if (ch == '\"')
-          dquote = false;
-        else
-          result += ch;
-      }
-      else if (ch == '\"')
-        dquote = true;
-      else
-        result += ch;
-    }
-#else
-    bool squote = false;
-    bool dquote = false;
-    bool escaped = false;
-    for ( ; i < command.size(); i++)
-    {
-      char ch = command[i];
-      if (!squote && !dquote && !escaped && isspace(ch)) break;
-      if (escaped)
-      {
-        result += ch;
-        escaped = false;
-      }
-      else if (squote)
-      {
-        if (ch == '\'')
-          squote = false;
-        else
-          result += ch;
-      }
-      else if (ch == '\\')
-        escaped = true;
-      else if (dquote)
-      {
-        if (ch == '\"')
-          dquote = false;
-        else
-          result += ch;
-      }
-      else if (ch == '\'')
-        squote = true;
-      else if (ch == '\"')
-        dquote = true;
-      else
-        result += ch;
-    }
-#endif
-
-    return result;
-  }
-
-
-  // this function performs the reverse of the above on a single argument
-  // it escapes special characters and quotes the argument if necessary ready for shell interpretation
-
-  static std::string make_argument (const std::string& arg)
-  {
-    std::string result;
-    bool needs_quotes = false;
-
-    for (unsigned i = 0; i < arg.size(); i++)
-    {
-      switch (arg[i])
-      {
-        // set of characters requiring escapes
-#ifdef MSWINDOWS
-#else
-      case '\\': case '\'': case '\"': case '`': case '(': case ')': 
-      case '&': case '|': case '<': case '>': case '*': case '?': case '!':
-        result += '\\';
-        result += arg[i];
-        break;
-#endif
-        // set of whitespace characters that force quoting
-      case ' ':
-        result += arg[i];
-        needs_quotes = true;
-        break;
-      default:
-        result += arg[i];
-        break;
-      }
-    }
-
-    if (needs_quotes)
-    {
-      result.insert(result.begin(), '"');
-      result += '"';
-    }
-    return result;
-  }
-
-  static char* copy_string (const char* str)
-  {
-    char* result = new char[strlen(str)+1];
-    strcpy(result,str);
-    return result;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-
-  arg_vector::arg_vector (void)
-  {
-    m_argv = 0;
-  }
-
-  arg_vector::arg_vector (const arg_vector& a)
-  {
-    m_argv = 0;
-    *this = a;
-  }
-
-  arg_vector::arg_vector (char** a)
-  {
-    m_argv = 0;
-    *this = a;
-  }
-
-  arg_vector::arg_vector (const std::string& command)
-  {
-    m_argv = 0;
-    *this = command;
-  }
-
-  arg_vector::arg_vector (const char* command)
-  {
-    m_argv = 0;
-    *this = command;
-  }
-
-  arg_vector::~arg_vector (void)
-  {
-    clear();
-  }
-
-  arg_vector& arg_vector::operator = (const arg_vector& a)
-  {
-    return *this = a.m_argv;
-  }
-
-  arg_vector& arg_vector::operator = (char** argv)
-  {
-    clear();
-    for (unsigned i = 0; argv[i]; i++)
-      operator += (argv[i]);
-    return *this;
-  }
-
-  arg_vector& arg_vector::operator = (const std::string& command)
-  {
-    clear();
-    for (unsigned i = 0; i < command.size(); )
-    {
-      std::string argument = get_argument(command, i);
-      operator += (argument);
-      skip_white(command, i);
-    }
-    return *this;
-  }
-
-  arg_vector& arg_vector::operator = (const char* command)
-  {
-    return operator = (std::string(command));
-  }
-
-  arg_vector& arg_vector::operator += (const std::string& str)
-  {
-    insert(size(), str);
-    return *this;
-  }
-
-  arg_vector& arg_vector::operator -= (const std::string& str)
-  {
-    insert(0, str);
-    return *this;
-  }
-
-  void arg_vector::insert (unsigned index, const std::string& str) throw(std::out_of_range)
-  {
-    if (index > size()) throw std::out_of_range("arg_vector::insert");
-    // copy up to but not including index, then add the new argument, then copy the rest
-    char** new_argv = new char*[size()+2];
-    unsigned i = 0;
-    for ( ; i < index; i++)
-      new_argv[i] = copy_string(m_argv[i]);
-    new_argv[index] = copy_string(str.c_str());
-    for ( ; i < size(); i++)
-      new_argv[i+1] = copy_string(m_argv[i]);
-    new_argv[i+1] = 0;
-    clear();
-    m_argv = new_argv;
-  }
-
-  void arg_vector::clear (unsigned index) throw(std::out_of_range)
-  {
-    if (index >= size()) throw std::out_of_range("arg_vector::clear");
-    // copy up to index, skip it, then copy the rest
-    char** new_argv = new char*[size()];
-    unsigned i = 0;
-    for ( ; i < index; i++)
-      new_argv[i] = copy_string(m_argv[i]);
-    i++;
-    for ( ; i < size(); i++)
-      new_argv[i-1] = copy_string(m_argv[i]);
-    new_argv[i-1] = 0;
-    clear();
-    m_argv = new_argv;
-  }
-
-  void arg_vector::clear(void)
-  {
-    if (m_argv)
-    {
-      for (unsigned i = 0; m_argv[i]; i++)
-        delete[] m_argv[i];
-      delete[] m_argv;
-      m_argv = 0;
-    }
-  }
-
-  unsigned arg_vector::size (void) const
-  {
-    unsigned i = 0;
-    if (m_argv)
-      while (m_argv[i])
-        i++;
-    return i;
-  }
-
-  arg_vector::operator char** (void) const
-  {
-    return m_argv;
-  }
-
-  char** arg_vector::argv (void) const
-  {
-    return m_argv;
-  }
-
-  char* arg_vector::operator [] (unsigned index) const throw(std::out_of_range)
-  {
-    if (index >= size()) throw std::out_of_range("arg_vector::operator[]");
-    return m_argv[index];
-  }
-
-  char* arg_vector::argv0 (void) const throw(std::out_of_range)
-  {
-    return operator [] (0);
-  }
-
-  std::string arg_vector::image (void) const
-  {
-    std::string result;
-    for (unsigned i = 0; i < size(); i++)
-    {
-      if (i) result += ' ';
-      result += make_argument(m_argv[i]);
-    }
-    return result;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // environment-vector
-
-  // Windoze environment is a single string containing null-terminated
-  // name=value strings and the whole terminated by a null
-
-  // Unix environment is a null-terminated vector of pointers to null-terminated strings
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // platform specifics
-
-#ifdef MSWINDOWS
-  // Windows utilities
-
-  // Windows environment variables are case-insensitive and I do comparisons by converting to lowercase
-  static std::string lowercase(const std::string& val)
-  {
-    std::string text = val;
-    for (unsigned i = 0; i < text.size(); i++)
-      text[i] = tolower(text[i]);
-    return text;
-  }
-
-  static unsigned envp_size(const char* envp)
-  {
-    unsigned size = 0;
-    while (envp[size] || (size > 0 && envp[size-1])) size++;
-    size++;
-    return size;
-  }
-
-  static void envp_extract(std::string& name, std::string& value, const char* envp, unsigned& envi)
-  {
-    name.erase();
-    value.erase();
-    if (!envp[envi]) return;
-    // some special variables start with '=' so ensure at least one character in the name
-    name += envp[envi++];
-    while(envp[envi] != '=')
-      name += envp[envi++];
-    envi++;
-    while(envp[envi])
-      value += envp[envi++];
-    envi++;
-  }
-
-  static void envp_append(const std::string& name, const std::string& value, char* envp, unsigned& envi)
-  {
-    for (unsigned i = 0; i < name.size(); i++)
-      envp[envi++] = name[i];
-    envp[envi++] = '=';
-    for (unsigned j = 0; j < value.size(); j++)
-      envp[envi++] = value[j];
-    envp[envi++] = '\0';
-    envp[envi] = '\0';
-  }
-
-  static char* envp_copy(const char* envp)
-  {
-    unsigned size = envp_size(envp);
-    char* result = new char[size];
-    result[0] = '\0';
-    unsigned i = 0;
-    unsigned j = 0;
-    while(envp[i])
-    {
-      std::string name;
-      std::string value;
-      envp_extract(name, value, envp, i);
-      envp_append(name, value, result, j);
-    }
-    return result;
-  }
-
-  static void envp_clear(char*& envp)
-  {
-    if (envp)
-    {
-      delete[] envp;
-      envp = 0;
-    }
-  }
-
-  static bool envp_equal(const std::string& left, const std::string& right)
-  {
-    return lowercase(left) == lowercase(right);
-  }
-
-  static bool envp_less(const std::string& left, const std::string& right)
-  {
-    return lowercase(left) < lowercase(right);
-  }
-
-#else
-  // Unix utilities
-
-  static unsigned envp_size(char* const* envp)
-  {
-    unsigned size = 0;
-    while(envp[size]) size++;
-    size++;
-    return size;
-  }
-
-  static void envp_extract(std::string& name, std::string& value, char* const* envp, unsigned& envi)
-  {
-    name = "";
-    value = "";
-    if (!envp[envi]) return;
-    unsigned i = 0;
-    while(envp[envi][i] != '=')
-      name += envp[envi][i++];
-    i++;
-    while(envp[envi][i])
-      value += envp[envi][i++];
-    envi++;
-  }
-
-  static void envp_append(const std::string& name, const std::string& value, char** envp, unsigned& envi)
-  {
-    std::string entry = name + "=" + value;
-    envp[envi] = copy_string(entry.c_str());
-    envi++;
-    envp[envi] = 0;
-  }
-
-  static char** envp_copy(char* const* envp)
-  {
-    unsigned size = envp_size(envp);
-    char** result = new char*[size];
-    unsigned i = 0;
-    unsigned j = 0;
-    while(envp[i])
-    {
-      std::string name;
-      std::string value;
-      envp_extract(name, value, envp, i);
-      envp_append(name, value, result, j);
-    }
-    return result;
-  }
-
-  static void envp_clear(char**& envp)
-  {
-    if (envp)
-    {
-      for (unsigned i = 0; envp[i]; i++)
-        delete[] envp[i];
-      delete[] envp;
-      envp = 0;
-    }
-  }
-
-  static bool envp_equal(const std::string& left, const std::string& right)
-  {
-    return left == right;
-  }
-
-  static bool envp_less(const std::string& left, const std::string& right)
-  {
-    return left < right;
-  }
-
-#endif
-  ////////////////////////////////////////////////////////////////////////////////
-
-  env_vector::env_vector(void)
-  {
-#ifdef MSWINDOWS
-    char* env = (char*)GetEnvironmentStringsA();
-    m_env = envp_copy(env);
-    FreeEnvironmentStringsA(env);
-#else
-    m_env = envp_copy(::environ);
-#endif
-  }
-
-  env_vector::env_vector (const env_vector& a)
-  {
-    m_env = 0;
-    *this = a;
-  }
-
-  env_vector::~env_vector (void)
-  {
-    clear();
-  }
-
-  env_vector& env_vector::operator = (const env_vector& a)
-  {
-    clear();
-    m_env = envp_copy(a.m_env);
-    return *this;
-  }
-
-  void env_vector::clear(void)
-  {
-    envp_clear(m_env);
-  }
-
-  void env_vector::add(const std::string& name, const std::string& value)
-  {
-    // the trick is to add the value in alphabetic order
-    // this is done by copying the existing environment string to a new
-    // string, inserting the new value when a name greater than it is found
-    unsigned size = envp_size(m_env);
-#ifdef MSWINDOWS
-    unsigned new_size = size + name.size() + value.size() + 2;
-    char* new_v = new char[new_size];
-    new_v[0] = '\0';
-#else
-    unsigned new_size = size + 1;
-    char** new_v = new char*[new_size];
-    new_v[0] = 0;
-#endif
-    // now extract each name=value pair and check the ordering
-    bool added = false;
-    unsigned i = 0;
-    unsigned j = 0;
-    while(m_env[i])
-    {
-      std::string current_name;
-      std::string current_value;
-      envp_extract(current_name, current_value, m_env, i);
-      if (envp_equal(name,current_name))
-      {
-        // replace an existing value
-        envp_append(name, value, new_v, j);
-      }
-      else if (!added && envp_less(name,current_name))
-      {
-        // add the new value first, then the existing one
-        envp_append(name, value, new_v, j);
-        envp_append(current_name, current_value, new_v, j);
-        added = true;
-      }
-      else
-      {
-        // just add the existing value
-        envp_append(current_name, current_value, new_v, j);
-      }
-    }
-    if (!added)
-      envp_append(name, value, new_v, j);
-    envp_clear(m_env);
-    m_env = new_v;
-  }
-
-
-  bool env_vector::remove (const std::string& name)
-  {
-    bool result = false;
-    // this is done by copying the existing environment string to a new string, but excluding the specified name
-    unsigned size = envp_size(m_env);
-#ifdef MSWINDOWS
-    char* new_v = new char[size];
-    new_v[0] = '\0';
-#else
-    char** new_v = new char*[size];
-    new_v[0] = 0;
-#endif
-    unsigned i = 0;
-    unsigned j = 0;
-    while(m_env[i])
-    {
-      std::string current_name;
-      std::string current_value;
-      envp_extract(current_name, current_value, m_env, i);
-      if (envp_equal(name,current_name))
-        result = true;
-      else
-        envp_append(current_name, current_value, new_v, j);
-    }
-    envp_clear(m_env);
-    m_env = new_v;
-    return result;
-  }
-
-  std::string env_vector::operator [] (const std::string& name) const
-  {
-    return get(name);
-  }
-
-  std::string env_vector::get (const std::string& name) const
-  {
-    unsigned i = 0;
-    while(m_env[i])
-    {
-      std::string current_name;
-      std::string current_value;
-      envp_extract(current_name, current_value, m_env, i);
-      if (envp_equal(name,current_name))
-        return current_value;
-    }
-    return std::string();
-  }
-
-  unsigned env_vector::size (void) const
-  {
-    unsigned i = 0;
-#ifdef MSWINDOWS
-    unsigned offset = 0;
-    while(m_env[offset])
-    {
-      std::string current_name;
-      std::string current_value;
-      envp_extract(current_name, current_value, m_env, offset);
-      i++;
-    }
-#else
-    while(m_env[i])
-      i++;
-#endif
-
-    return i;
-  }
-
-  std::pair<std::string,std::string> env_vector::operator [] (unsigned index) const throw(std::out_of_range)
-  {
-    return get(index);
-  }
-
-  std::pair<std::string,std::string> env_vector::get (unsigned index) const throw(std::out_of_range)
-  {
-    if (index >= size()) throw std::out_of_range("arg_vector::get");
-    unsigned j = 0;
-    for (unsigned i = 0; i < index; i++)
-    {
-      std::string current_name;
-      std::string current_value;
-      envp_extract(current_name, current_value, m_env, j);
-    }
-    std::string name;
-    std::string value;
-    envp_extract(name, value, m_env, j);
-    return std::make_pair(name,value);
-  }
-
-  ENVIRON_TYPE env_vector::envp (void) const
-  {
-    return m_env;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // Synchronous subprocess
-  // Win32 implementation mostly cribbed from MSDN examples and then made (much) more readable
-  // Unix implementation mostly from man pages and bitter experience
-  ////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSWINDOWS
-
-  subprocess::subprocess(void)
-  {
-    m_pid.hProcess = 0;
-    m_job = 0;
-    m_child_in = 0;
-    m_child_out = 0;
-    m_child_err = 0;
-    m_err = 0;
-    m_status = 0;
-  }
-
-#else
-
-  subprocess::subprocess(void)
-  {
-    m_pid = -1;
-    m_child_in = -1;
-    m_child_out = -1;
-    m_child_err = -1;
-    m_err = 0;
-    m_status = 0;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  subprocess::~subprocess(void)
-  {
-    if (m_pid.hProcess != 0)
-    {
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      kill();
-      WaitForSingleObject(m_pid.hProcess, INFINITE);
-      CloseHandle(m_pid.hThread);
-      CloseHandle(m_pid.hProcess);
-      CloseHandle(m_job);
-    }
-  }
-
-#else
-
-  subprocess::~subprocess(void)
-  {
-    if (m_pid != -1)
-    {
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      kill();
-      for (;;)
-      {
-        int wait_status = 0;
-        int wait_ret_val = waitpid(m_pid, &wait_status, 0);
-        if (wait_ret_val != -1 || errno != EINTR) break;
-      }
-    }
-  }
-
-#endif
-
-  void subprocess::add_variable(const std::string& name, const std::string& value)
-  {
-    m_env.add(name, value);
-  }
-
-  bool subprocess::remove_variable(const std::string& name)
-  {
-    return m_env.remove(name);
-  }
-
-#ifdef MSWINDOWS
-
-  bool subprocess::spawn(const std::string& path, const arg_vector& argv,
-                         bool connect_stdin, bool connect_stdout, bool connect_stderr)
-  {
-    bool result = true;
-    // first create the pipes to be used to connect to the child stdin/out/err
-    // If no pipes requested, then connect to the parent stdin/out/err
-    // for some reason you have to create a pipe handle, then duplicate it
-    // This is not well explained in MSDN but seems to work
-    PIPE_TYPE parent_stdin = 0;
-    if (!connect_stdin)
-      parent_stdin = GetStdHandle(STD_INPUT_HANDLE);
-    else
-    {
-      PIPE_TYPE tmp = 0;
-      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
-      CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);
-      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-    }
-
-    PIPE_TYPE parent_stdout = 0;
-    if (!connect_stdout)
-      parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
-    else
-    {
-      PIPE_TYPE tmp = 0;
-      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
-      CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);
-      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-    }
-
-    PIPE_TYPE parent_stderr = 0;
-    if (!connect_stderr)
-      parent_stderr = GetStdHandle(STD_ERROR_HANDLE);
-    else
-    {
-      PIPE_TYPE tmp = 0;
-      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
-      CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);
-      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-    }
-
-    // Now create the subprocess
-    // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work
-    // Note that the child will inherit a copy of the pipe handles
-    STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,
-                            STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,
-                            parent_stdin,parent_stdout,parent_stderr};
-    bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;
-    // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them
-    if (connect_stdin) CloseHandle(parent_stdin);
-    if (connect_stdout) CloseHandle(parent_stdout);
-    if (connect_stderr) CloseHandle(parent_stderr);
-    if (!created)
-    {
-      m_err = GetLastError();
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      result = false;
-    }
-    else
-    {
-      m_job = CreateJobObject(NULL, NULL);
-      AssignProcessToJobObject(m_job, m_pid.hProcess);
-      ResumeThread(m_pid.hThread);
-
-      // The child process is now running so call the user's callback
-      // The convention is that the callback can return false, in which case this will kill the child (if its still running)
-      if (!callback())
-      {
-        result = false;
-        kill();
-      }
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      // wait for the child to finish
-      // TODO - kill the child if a timeout happens
-      WaitForSingleObject(m_pid.hProcess, INFINITE);
-      DWORD exit_status = 0;
-      if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))
-      {
-        m_err = GetLastError();
-        result = false;
-      }
-      else if (exit_status != 0)
-        result = false;
-      m_status = (int)exit_status;
-      CloseHandle(m_pid.hThread);
-      CloseHandle(m_pid.hProcess);
-      CloseHandle(m_job);
-    }
-    m_pid.hProcess = 0;
-    return result;
-  }
-
-#else
-
-  bool subprocess::spawn(const std::string& path, const arg_vector& argv,
-                         bool connect_stdin, bool connect_stdout, bool connect_stderr)
-  {
-    bool result = true;
-    // first create the pipes to be used to connect to the child stdin/out/err
-
-    int stdin_pipe [2] = {-1, -1};
-    if (connect_stdin)
-      pipe(stdin_pipe);
-
-    int stdout_pipe [2] = {-1, -1};
-    if (connect_stdout)
-      pipe(stdout_pipe);
-
-    int stderr_pipe [2] = {-1, -1};
-    if (connect_stderr)
-      pipe(stderr_pipe);
-
-    // now create the subprocess
-    // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec
-    m_pid = ::fork();
-    switch(m_pid)
-    {
-    case -1:   // failed to fork
-      m_err = errno;
-      if (connect_stdin)
-      {
-        ::close(stdin_pipe[0]);
-        ::close(stdin_pipe[1]);
-      }
-      if (connect_stdout)
-      {
-        ::close(stdout_pipe[0]);
-        ::close(stdout_pipe[1]);
-      }
-      if (connect_stderr)
-      {
-        ::close(stderr_pipe[0]);
-        ::close(stderr_pipe[1]);
-      }
-      result = false;
-      break;
-    case 0:  // in child;
-    {
-      // for each pipe, close the end of the duplicated pipe that is being used by the parent
-      // and connect the child's end of the pipe to the appropriate standard I/O device
-      if (connect_stdin)
-      {
-        ::close(stdin_pipe[1]);
-        dup2(stdin_pipe[0],STDIN_FILENO);
-      }
-      if (connect_stdout)
-      {
-        ::close(stdout_pipe[0]);
-        dup2(stdout_pipe[1],STDOUT_FILENO);
-      }
-      if (connect_stderr)
-      {
-        ::close(stderr_pipe[0]);
-        dup2(stderr_pipe[1],STDERR_FILENO);
-      }
-      execve(path.c_str(), argv.argv(), m_env.envp());
-      // will only ever get here if the exec() failed completely - *must* now exit the child process
-      // by using errno, the parent has some chance of diagnosing the cause of the problem
-      exit(errno);
-    }
-    break;
-    default:  // in parent
-    {
-      // for each pipe, close the end of the duplicated pipe that is being used by the child
-      // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback
-      if (connect_stdin)
-      {
-        ::close(stdin_pipe[0]);
-        m_child_in = stdin_pipe[1];
-      }
-      if (connect_stdout)
-      {
-        ::close(stdout_pipe[1]);
-        m_child_out = stdout_pipe[0];
-      }
-      if (connect_stderr)
-      {
-        ::close(stderr_pipe[1]);
-        m_child_err = stderr_pipe[0];
-      }
-      // call the user's callback
-      if (!callback())
-      {
-        result = false;
-        kill();
-      }
-      // close the pipes and wait for the child to finish
-      // wait exits on a signal which may be the child signalling its exit or may be an interrupt
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      int wait_status = 0;
-      for (;;)
-      {
-        int wait_ret_val = waitpid(m_pid, &wait_status, 0);
-        if (wait_ret_val != -1 || errno != EINTR) break;
-      }
-      // establish whether an error occurred
-      if (WIFSIGNALED(wait_status))
-      {
-        // set_error(errno);
-        m_status = WTERMSIG(wait_status);
-        result = false;
-      }
-      else if (WIFEXITED(wait_status))
-      {
-        m_status = WEXITSTATUS(wait_status);
-        if (m_status != 0)
-          result = false;
-      }
-      m_pid = -1;
-    }
-    break;
-    }
-    return result;
-  }
-
-#endif
-
-  bool subprocess::spawn(const std::string& command_line,
-                                  bool connect_stdin, bool connect_stdout, bool connect_stderr)
-  {
-    arg_vector arguments = command_line;
-    if (arguments.size() == 0) return false;
-    std::string path = path_lookup(arguments.argv0());
-    if (path.empty()) return false;
-    return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);
-  }
-
-  bool subprocess::callback(void)
-  {
-    return true;
-  }
-
-#ifdef MSWINDOWS
-
-  bool subprocess::kill (void)
-  {
-    if (!m_pid.hProcess) return false;
-    close_stdin();
-    close_stdout();
-    close_stderr();
-    if (!TerminateJobObject(m_job, (UINT)-1))
-    {
-      m_err = GetLastError();
-      return false;
-    }
-    return true;
-  }
-
-#else
-
-  bool subprocess::kill (void)
-  {
-    if (m_pid == -1) return false;
-    close_stdin();
-    close_stdout();
-    close_stderr();
-    if (::kill(m_pid, SIGINT) == -1)
-    {
-      m_err = errno;
-      return false;
-    }
-    return true;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  int subprocess::write_stdin (std::string& buffer)
-  {
-    if (m_child_in == 0) return -1;
-    // do a blocking write of the whole buffer
-    DWORD bytes = 0;
-    if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))
-    {
-      m_err = GetLastError();
-      close_stdin();
-      return -1;
-    }
-    // now discard that part of the buffer that was written
-    if (bytes > 0)
-      buffer.erase(0, bytes);
-    return bytes;
-  }
-
-#else
-
-  int subprocess::write_stdin (std::string& buffer)
-  {
-    if (m_child_in == -1) return -1;
-    // do a blocking write of the whole buffer
-    int bytes = write(m_child_in, buffer.c_str(), buffer.size());
-    if (bytes == -1)
-    {
-      m_err = errno;
-      close_stdin();
-      return -1;
-    }
-    // now discard that part of the buffer that was written
-    if (bytes > 0)
-      buffer.erase(0, bytes);
-    return bytes;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  int subprocess::read_stdout (std::string& buffer)
-  {
-    if (m_child_out == 0) return -1;
-    DWORD bytes = 0;
-    DWORD buffer_size = 256;
-    char* tmp = new char[buffer_size];
-    if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))
-    {
-      if (GetLastError() != ERROR_BROKEN_PIPE)
-        m_err = GetLastError();
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return (int)bytes;
-  }
-
-#else
-
-  int subprocess::read_stdout (std::string& buffer)
-  {
-    if (m_child_out == -1) return -1;
-    int buffer_size = 256;
-    char* tmp = new char[buffer_size];
-    int bytes = read(m_child_out, tmp, buffer_size);
-    if (bytes == -1)
-    {
-      m_err = errno;
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return bytes;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  int subprocess::read_stderr(std::string& buffer)
-  {
-    if (m_child_err == 0) return -1;
-    DWORD bytes = 0;
-    DWORD buffer_size = 256;
-    char* tmp = new char[buffer_size];
-    if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))
-    {
-      if (GetLastError() != ERROR_BROKEN_PIPE)
-        m_err = GetLastError();
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return (int)bytes;
-  }
-
-#else
-
-  int subprocess::read_stderr (std::string& buffer)
-  {
-    if (m_child_err == -1) return -1;
-    int buffer_size = 256;
-    char* tmp = new char[buffer_size];
-    int bytes = read(m_child_err, tmp, buffer_size);
-    if (bytes == -1)
-    {
-      m_err = errno;
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return bytes;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  void subprocess::close_stdin (void)
-  {
-    if (m_child_in)
-    {
-      CloseHandle(m_child_in);
-      m_child_in = 0;
-    }
-  }
-
-#else
-
-  void subprocess::close_stdin (void)
-  {
-    if (m_child_in != -1)
-    {
-      ::close(m_child_in);
-      m_child_in = -1;
-    }
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  void subprocess::close_stdout (void)
-  {
-    if (m_child_out)
-    {
-      CloseHandle(m_child_out);
-      m_child_out = 0;
-    }
-  }
-
-#else
-
-  void subprocess::close_stdout (void)
-  {
-    if (m_child_out != -1)
-    {
-      ::close(m_child_out);
-      m_child_out = -1;
-    }
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  void subprocess::close_stderr (void)
-  {
-    if (m_child_err)
-    {
-      CloseHandle(m_child_err);
-      m_child_err = 0;
-    }
-  }
-
-#else
-
-  void subprocess::close_stderr (void)
-  {
-    if (m_child_err != -1)
-    {
-      ::close(m_child_err);
-      m_child_err = -1;
-    }
-  }
-
-#endif
-
-  bool subprocess::error(void) const
-  {
-    return m_err != 0;
-  }
-
-  int subprocess::error_number(void) const
-  {
-    return m_err;
-  }
-
-#ifdef MSWINDOWS
-
-  std::string subprocess::error_text(void) const
-  {
-    if (m_err == 0) return std::string();
-    char* message;
-    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
-                  0,
-                  m_err,
-                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                  (LPTSTR)&message,
-                  0,0);
-    std::string result = message;
-    LocalFree(message);
-    // the error message is for some perverse reason newline terminated - remove this
-    if (result[result.size()-1] == '\n')
-      result.erase(result.end()-1);
-    if (result[result.size()-1] == '\r')
-      result.erase(result.end()-1);
-    return result;
-  }
-
-#else
-
-  std::string subprocess::error_text(void) const
-  {
-    if (m_err == 0) return std::string();
-    char* text = strerror(m_err);
-    if (text) return std::string(text);
-    return "error number " + dformat("%d",m_err);
-  }
-
-#endif
-
-  int subprocess::exit_status(void) const
-  {
-    return m_status;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // backtick subprocess and operations
-
-  backtick_subprocess::backtick_subprocess(void) : subprocess()
-  {
-  }
-
-  bool backtick_subprocess::callback(void)
-  {
-    for (;;)
-    {
-      std::string buffer;
-      int read_size = read_stdout(buffer);
-      if (read_size < 0) break;
-      m_text += buffer;
-    }
-    return !error();
-  }
-
-  bool backtick_subprocess::spawn(const std::string& path, const arg_vector& argv)
-  {
-    return subprocess::spawn(path, argv, false, true, false);
-  }
-
-  bool backtick_subprocess::spawn(const std::string& command_line)
-  {
-    return subprocess::spawn(command_line, false, true, false);
-  }
-
-  std::vector<std::string> backtick_subprocess::text(void) const
-  {
-    std::vector<std::string> result;
-    // convert the raw text into a vector of strings, each corresponding to a line
-    // in the process, strip out platform-specific line-endings
-    for (unsigned i = 0; i < m_text.size(); i++)
-    {
-      // handle any kind of line-ending - Dos, Unix or MacOS
-      switch(m_text[i])
-      {
-      case '\xd': // carriage-return - optionally followed by linefeed
-      {
-        // discard optional following linefeed
-        if ((i+1 < m_text.size()) && (m_text[i+1] == '\xa'))
-          i++;
-        // add a new line to the end of the vector
-        result.push_back(std::string());
-        break;
-      }
-      case '\xa': // linefeed
-      {
-        // add a new line to the end of the vector
-        result.push_back(std::string());
-        break;
-      }
-      default:
-      {
-        result.back() += m_text[i];
-        break;
-      }
-      }
-    }
-    // tidy up - if the last line ended with a newline, the vector will end with an empty string - discard this
-    if ((result.size()) > 0 && result.back().empty())
-      result.erase(result.end()-1);
-    return result;
-  }
-
-  std::vector<std::string> backtick(const std::string& path, const arg_vector& argv)
-  {
-    backtick_subprocess sub;
-    sub.spawn(path, argv);
-    return sub.text();
-  }
-
-  std::vector<std::string> backtick(const std::string& command_line)
-  {
-    backtick_subprocess sub;
-    sub.spawn(command_line);
-    return sub.text();
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-  // Asynchronous subprocess
-
-#ifdef MSWINDOWS
-
-  async_subprocess::async_subprocess(void)
-  {
-    m_pid.hProcess = 0;
-    m_job = 0;
-    m_child_in = 0;
-    m_child_out = 0;
-    m_child_err = 0;
-    m_err = 0;
-    m_status = 0;
-  }
-
-#else
-
-  async_subprocess::async_subprocess(void)
-  {
-    m_pid = -1;
-    m_child_in = -1;
-    m_child_out = -1;
-    m_child_err = -1;
-    m_err = 0;
-    m_status = 0;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  async_subprocess::~async_subprocess(void)
-  {
-    if (m_pid.hProcess != 0)
-    {
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      kill();
-      WaitForSingleObject(m_pid.hProcess, INFINITE);
-      CloseHandle(m_pid.hThread);
-      CloseHandle(m_pid.hProcess);
-      CloseHandle(m_job);
-    }
-  }
-
-#else
-
-  async_subprocess::~async_subprocess(void)
-  {
-    if (m_pid != -1)
-    {
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      kill();
-      for (;;)
-      {
-        int wait_status = 0;
-        int wait_ret_val = waitpid(m_pid, &wait_status, 0);
-        if (wait_ret_val != -1 || errno != EINTR) break;
-      }
-    }
-  }
-
-#endif
-
-  void async_subprocess::set_error(int e)
-  {
-    m_err = e;
-  }
-
-  void async_subprocess::add_variable(const std::string& name, const std::string& value)
-  {
-    m_env.add(name, value);
-  }
-
-  bool async_subprocess::remove_variable(const std::string& name)
-  {
-    return m_env.remove(name);
-  }
-
-#ifdef MSWINDOWS
-
-  bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,
-                                        bool connect_stdin, bool connect_stdout, bool connect_stderr)
-  {
-    bool result = true;
-    // first create the pipes to be used to connect to the child stdin/out/err
-    // If no pipes requested, then connect to the parent stdin/out/err
-    // for some reason you have to create a pipe handle, then duplicate it
-    // This is not well explained in MSDN but seems to work
-    PIPE_TYPE parent_stdin = 0;
-    if (!connect_stdin)
-      parent_stdin = GetStdHandle(STD_INPUT_HANDLE);
-    else
-    {
-      PIPE_TYPE tmp = 0;
-      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
-      CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);
-      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-    }
-
-    PIPE_TYPE parent_stdout = 0;
-    if (!connect_stdout)
-      parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
-    else
-    {
-      PIPE_TYPE tmp = 0;
-      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
-      CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);
-      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-    }
-
-    PIPE_TYPE parent_stderr = 0;
-    if (!connect_stderr)
-      parent_stderr = GetStdHandle(STD_ERROR_HANDLE);
-    else
-    {
-      PIPE_TYPE tmp = 0;
-      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
-      CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);
-      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
-    }
-
-    // Now create the subprocess
-    // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work
-    // Note that the child will inherit a copy of the pipe handles
-    STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,
-                            STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,
-                            parent_stdin,parent_stdout,parent_stderr};
-    bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;
-    // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them
-    if (connect_stdin) CloseHandle(parent_stdin);
-    if (connect_stdout) CloseHandle(parent_stdout);
-    if (connect_stderr) CloseHandle(parent_stderr);
-    if (!created)
-    {
-      set_error(GetLastError());
-      close_stdin();
-      close_stdout();
-      close_stderr();
-      result = false;
-    }
-    else
-    {
-      m_job = CreateJobObject(NULL, NULL);
-      AssignProcessToJobObject(m_job, m_pid.hProcess);
-      ResumeThread(m_pid.hThread);
-    }
-    return result;
-  }
-
-#else
-
-  bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,
-                               bool connect_stdin, bool connect_stdout, bool connect_stderr)
-  {
-    bool result = true;
-    // first create the pipes to be used to connect to the child stdin/out/err
-
-    int stdin_pipe [2] = {-1, -1};
-    if (connect_stdin)
-      pipe(stdin_pipe);
-
-    int stdout_pipe [2] = {-1, -1};
-    if (connect_stdout)
-      pipe(stdout_pipe);
-
-    int stderr_pipe [2] = {-1, -1};
-    if (connect_stderr)
-      pipe(stderr_pipe);
-
-    // now create the subprocess
-    // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec
-    m_pid = ::fork();
-    switch(m_pid)
-    {
-    case -1:   // failed to fork
-      set_error(errno);
-      if (connect_stdin)
-      {
-        ::close(stdin_pipe[0]);
-        ::close(stdin_pipe[1]);
-      }
-      if (connect_stdout)
-      {
-        ::close(stdout_pipe[0]);
-        ::close(stdout_pipe[1]);
-      }
-      if (connect_stderr)
-      {
-        ::close(stderr_pipe[0]);
-        ::close(stderr_pipe[1]);
-      }
-      result = false;
-      break;
-    case 0:  // in child;
-    {
-      // for each pipe, close the end of the duplicated pipe that is being used by the parent
-      // and connect the child's end of the pipe to the appropriate standard I/O device
-      if (connect_stdin)
-      {
-        ::close(stdin_pipe[1]);
-        dup2(stdin_pipe[0],STDIN_FILENO);
-      }
-      if (connect_stdout)
-      {
-        ::close(stdout_pipe[0]);
-        dup2(stdout_pipe[1],STDOUT_FILENO);
-      }
-      if (connect_stderr)
-      {
-        ::close(stderr_pipe[0]);
-        dup2(stderr_pipe[1],STDERR_FILENO);
-      }
-      execve(path.c_str(), argv.argv(), m_env.envp());
-      // will only ever get here if the exec() failed completely - *must* now exit the child process
-      // by using errno, the parent has some chance of diagnosing the cause of the problem
-      exit(errno);
-    }
-    break;
-    default:  // in parent
-    {
-      // for each pipe, close the end of the duplicated pipe that is being used by the child
-      // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback
-      if (connect_stdin)
-      {
-        ::close(stdin_pipe[0]);
-        m_child_in = stdin_pipe[1];
-        if (fcntl(m_child_in, F_SETFL, O_NONBLOCK) == -1)
-        {
-          set_error(errno);
-          result = false;
-        }
-      }
-      if (connect_stdout)
-      {
-        ::close(stdout_pipe[1]);
-        m_child_out = stdout_pipe[0];
-        if (fcntl(m_child_out, F_SETFL, O_NONBLOCK) == -1)
-        {
-          set_error(errno);
-          result = false;
-        }
-      }
-      if (connect_stderr)
-      {
-        ::close(stderr_pipe[1]);
-        m_child_err = stderr_pipe[0];
-        if (fcntl(m_child_err, F_SETFL, O_NONBLOCK) == -1)
-        {
-          set_error(errno);
-          result = false;
-        }
-      }
-    }
-    break;
-    }
-    return result;
-  }
-
-#endif
-
-  bool async_subprocess::spawn(const std::string& command_line,
-                               bool connect_stdin, bool connect_stdout, bool connect_stderr)
-  {
-    arg_vector arguments = command_line;
-    if (arguments.size() == 0) return false;
-    std::string path = path_lookup(arguments.argv0());
-    if (path.empty()) return false;
-    return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);
-  }
-
-  bool async_subprocess::callback(void)
-  {
-    return true;
-  }
-
-#ifdef MSWINDOWS
-
-  bool async_subprocess::tick(void)
-  {
-    bool result = true;
-    if (!callback())
-      kill();
-    DWORD exit_status = 0;
-    if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))
-    {
-      set_error(GetLastError());
-      result = false;
-    }
-    else if (exit_status != STILL_ACTIVE)
-    {
-      CloseHandle(m_pid.hThread);
-      CloseHandle(m_pid.hProcess);
-      CloseHandle(m_job);
-      m_pid.hProcess = 0;
-      result = false;
-    }
-    m_status = (int)exit_status;
-    return result;
-  }
-
-#else
-
-  bool async_subprocess::tick(void)
-  {
-    bool result = true;
-    if (!callback())
-      kill();
-    int wait_status = 0;
-    int wait_ret_val = waitpid(m_pid, &wait_status, WNOHANG);
-    if (wait_ret_val == -1 && errno != EINTR)
-    {
-      set_error(errno);
-      result = false;
-    }
-    else if (wait_ret_val != 0)
-    {
-      // the only states that indicate a terminated child are WIFSIGNALLED and WIFEXITED
-      if (WIFSIGNALED(wait_status))
-      {
-        // set_error(errno);
-        m_status = WTERMSIG(wait_status);
-        result = false;
-      }
-      else if (WIFEXITED(wait_status))
-      {
-        // child has exited
-        m_status = WEXITSTATUS(wait_status);
-        result = false;
-      }
-    }
-    if (!result)
-      m_pid = -1;
-    return result;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  bool async_subprocess::kill(void)
-  {
-    if (!m_pid.hProcess) return false;
-    close_stdin();
-    close_stdout();
-    close_stderr();
-    if (!TerminateJobObject(m_job, (UINT)-1))
-    {
-      set_error(GetLastError());
-      return false;
-    }
-    return true;
-  }
-
-#else
-
-  bool async_subprocess::kill(void)
-  {
-    if (m_pid == -1) return false;
-    close_stdin();
-    close_stdout();
-    close_stderr();
-    if (::kill(m_pid, SIGINT) == -1)
-    {
-      set_error(errno);
-      return false;
-    }
-    return true;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  int async_subprocess::write_stdin (std::string& buffer)
-  {
-    if (m_child_in == 0) return -1;
-    // there doesn't seem to be a way of doing non-blocking writes under Windoze
-    DWORD bytes = 0;
-    if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))
-    {
-      set_error(GetLastError());
-      close_stdin();
-      return -1;
-    }
-    // now discard that part of the buffer that was written
-    if (bytes > 0)
-      buffer.erase(0, bytes);
-    return (int)bytes;
-  }
-
-#else
-
-  int async_subprocess::write_stdin (std::string& buffer)
-  {
-    if (m_child_in == -1) return -1;
-    // relies on the pipe being non-blocking
-    // This does block under Windoze
-    int bytes = write(m_child_in, buffer.c_str(), buffer.size());
-    if (bytes == -1 && errno == EAGAIN)
-    {
-      // not ready
-      return 0;
-    }
-    if (bytes == -1)
-    {
-      // error on write - close the pipe and give up
-      set_error(errno);
-      close_stdin();
-      return -1;
-    }
-    // successful write
-    // now discard that part of the buffer that was written
-    if (bytes > 0)
-      buffer.erase(0, bytes);
-    return bytes;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  int async_subprocess::read_stdout (std::string& buffer)
-  {
-    if (m_child_out == 0) return -1;
-    // peek at the buffer to see how much data there is in the first place
-    DWORD buffer_size = 0;
-    if (!PeekNamedPipe(m_child_out, 0, 0, 0, &buffer_size, 0))
-    {
-      if (GetLastError() != ERROR_BROKEN_PIPE)
-        set_error(GetLastError());
-      close_stdout();
-      return -1;
-    }
-    if (buffer_size == 0) return 0;
-    DWORD bytes = 0;
-    char* tmp = new char[buffer_size];
-    if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))
-    {
-      set_error(GetLastError());
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return (int)bytes;
-  }
-
-#else
-
-  int async_subprocess::read_stdout (std::string& buffer)
-  {
-    if (m_child_out == -1) return -1;
-    // rely on the pipe being non-blocking
-    int buffer_size = 256;
-    char* tmp = new char[buffer_size];
-    int bytes = read(m_child_out, tmp, buffer_size);
-    if (bytes == -1 && errno == EAGAIN)
-    {
-      // not ready
-      delete[] tmp;
-      return 0;
-    }
-    if (bytes == -1)
-    {
-      // error
-      set_error(errno);
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stdout();
-      delete[] tmp;
-      return -1;
-    }
-    // successful read
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return bytes;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  int async_subprocess::read_stderr (std::string& buffer)
-  {
-    if (m_child_err == 0) return -1;
-    // peek at the buffer to see how much data there is in the first place
-    DWORD buffer_size = 0;
-    if (!PeekNamedPipe(m_child_err, 0, 0, 0, &buffer_size, 0))
-    {
-      if (GetLastError() != ERROR_BROKEN_PIPE)
-        set_error(GetLastError());
-      close_stderr();
-      return -1;
-    }
-    if (buffer_size == 0) return 0;
-    DWORD bytes = 0;
-    char* tmp = new char[buffer_size];
-    if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))
-    {
-      set_error(GetLastError());
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return (int)bytes;
-  }
-
-#else
-
-  int async_subprocess::read_stderr (std::string& buffer)
-  {
-    if (m_child_err == -1) return -1;
-    // rely on the pipe being non-blocking
-    int buffer_size = 256;
-    char* tmp = new char[buffer_size];
-    int bytes = read(m_child_err, tmp, buffer_size);
-    if (bytes == -1 && errno == EAGAIN)
-    {
-      // not ready
-      delete[] tmp;
-      return 0;
-    }
-    if (bytes == -1)
-    {
-      // error
-      set_error(errno);
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    if (bytes == 0)
-    {
-      // EOF
-      close_stderr();
-      delete[] tmp;
-      return -1;
-    }
-    // successful read
-    buffer.append(tmp, bytes);
-    delete[] tmp;
-    return bytes;
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  void async_subprocess::close_stdin (void)
-  {
-    if (m_child_in)
-    {
-      CloseHandle(m_child_in);
-      m_child_in = 0;
-    }
-  }
-
-#else
-
-  void async_subprocess::close_stdin (void)
-  {
-    if (m_child_in != -1)
-    {
-      ::close(m_child_in);
-      m_child_in = -1;
-    }
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  void async_subprocess::close_stdout (void)
-  {
-    if (m_child_out)
-    {
-      CloseHandle(m_child_out);
-      m_child_out = 0;
-    }
-  }
-
-#else
-
-  void async_subprocess::close_stdout (void)
-  {
-    if (m_child_out != -1)
-    {
-      ::close(m_child_out);
-      m_child_out = -1;
-    }
-  }
-
-#endif
-
-#ifdef MSWINDOWS
-
-  void async_subprocess::close_stderr (void)
-  {
-    if (m_child_err)
-    {
-      CloseHandle(m_child_err);
-      m_child_err = 0;
-    }
-  }
-
-#else
-
-  void async_subprocess::close_stderr (void)
-  {
-    if (m_child_err != -1)
-    {
-      ::close(m_child_err);
-      m_child_err = -1;
-    }
-  }
-
-#endif
-
-  bool async_subprocess::error(void) const
-  {
-    return m_err != 0;
-  }
-
-  int async_subprocess::error_number(void) const
-  {
-    return m_err;
-  }
-
-#ifdef MSWINDOWS
-
-  std::string async_subprocess::error_text(void) const
-  {
-    if (m_err == 0) return std::string();
-    char* message;
-    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
-                  0,
-                  m_err,
-                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
-                  (LPTSTR)&message,
-                  0,0);
-    std::string result = message;
-    LocalFree(message);
-    // the error message is for some perverse reason newline terminated - remove this
-    if (result[result.size()-1] == '\n')
-      result.erase(result.end()-1);
-    if (result[result.size()-1] == '\r')
-      result.erase(result.end()-1);
-    return result;
-  }
-
-#else
-
-  std::string async_subprocess::error_text(void) const
-  {
-    if (m_err == 0) return std::string();
-    char* text = strerror(m_err);
-    if (text) return std::string(text);
-    return "error number " + dformat("%d",m_err);
-  }
-
-#endif
-
-  int async_subprocess::exit_status(void) const
-  {
-    return m_status;
-  }
-
-  ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+//   Author:    Andy Rushton\r
+//   Copyright: (c) Southampton University 1999-2004\r
+//              (c) Andy Rushton           2004 onwards\r
+//   License:   BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Bug fix by Alistair Low: kill on Windows now kills grandchild processes as\r
+// well as the child process. This is done using jobs - which has to be\r
+// enabled by stating that the version of Windows is at least 5.0\r
+#if defined(_WIN32) || defined(_WIN32_WCE)\r
+#define _WIN32_WINNT 0x0500\r
+#endif\r
+\r
+#include "subprocesses.hpp"\r
+#include "file_system.hpp"\r
+#include "dprintf.hpp"\r
+#include <ctype.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+\r
+#ifdef MSWINDOWS\r
+#ifdef __BORLANDC__\r
+// missing declaration in Borland headers\r
+LPTCH WINAPI GetEnvironmentStringsA(void);\r
+#endif\r
+#else\r
+extern char** environ;\r
+#include <signal.h>\r
+#include <errno.h>\r
+#include <sys/wait.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // argument-vector related stuff\r
+\r
+  static void skip_white (const std::string& command, unsigned& i)\r
+  {\r
+    while(i < command.size() && isspace(command[i]))\r
+      i++;\r
+  }\r
+\r
+  // get_argument is the main function for breaking a string down into separate command arguments\r
+  // it mimics the way shells break down a command into an argv[] and unescapes the escaped characters on the way\r
+\r
+  static std::string get_argument (const std::string& command, unsigned& i)\r
+  {\r
+    std::string result;\r
+#ifdef MSWINDOWS\r
+\r
+  // as far as I know, there is only double-quoting and no escape character in DOS\r
+  // so, how do you include a double-quote in an argument???\r
+\r
+    bool dquote = false;\r
+    for ( ; i < command.size(); i++)\r
+    {\r
+      char ch = command[i];\r
+      if (!dquote && isspace(ch)) break;\r
+      if (dquote)\r
+      {\r
+        if (ch == '\"')\r
+          dquote = false;\r
+        else\r
+          result += ch;\r
+      }\r
+      else if (ch == '\"')\r
+        dquote = true;\r
+      else\r
+        result += ch;\r
+    }\r
+#else\r
+    bool squote = false;\r
+    bool dquote = false;\r
+    bool escaped = false;\r
+    for ( ; i < command.size(); i++)\r
+    {\r
+      char ch = command[i];\r
+      if (!squote && !dquote && !escaped && isspace(ch)) break;\r
+      if (escaped)\r
+      {\r
+        result += ch;\r
+        escaped = false;\r
+      }\r
+      else if (squote)\r
+      {\r
+        if (ch == '\'')\r
+          squote = false;\r
+        else\r
+          result += ch;\r
+      }\r
+      else if (ch == '\\')\r
+        escaped = true;\r
+      else if (dquote)\r
+      {\r
+        if (ch == '\"')\r
+          dquote = false;\r
+        else\r
+          result += ch;\r
+      }\r
+      else if (ch == '\'')\r
+        squote = true;\r
+      else if (ch == '\"')\r
+        dquote = true;\r
+      else\r
+        result += ch;\r
+    }\r
+#endif\r
+\r
+    return result;\r
+  }\r
+\r
+\r
+  // this function performs the reverse of the above on a single argument\r
+  // it escapes special characters and quotes the argument if necessary ready for shell interpretation\r
+\r
+  static std::string make_argument (const std::string& arg)\r
+  {\r
+    std::string result;\r
+    bool needs_quotes = false;\r
+\r
+    for (unsigned i = 0; i < arg.size(); i++)\r
+    {\r
+      switch (arg[i])\r
+      {\r
+        // set of characters requiring escapes\r
+#ifdef MSWINDOWS\r
+#else\r
+      case '\\': case '\'': case '\"': case '`': case '(': case ')':\r
+      case '&': case '|': case '<': case '>': case '*': case '?': case '!':\r
+        result += '\\';\r
+        result += arg[i];\r
+        break;\r
+#endif\r
+        // set of whitespace characters that force quoting\r
+      case ' ':\r
+        result += arg[i];\r
+        needs_quotes = true;\r
+        break;\r
+      default:\r
+        result += arg[i];\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (needs_quotes)\r
+    {\r
+      result.insert(result.begin(), '"');\r
+      result += '"';\r
+    }\r
+    return result;\r
+  }\r
+\r
+  static char* copy_string (const char* str)\r
+  {\r
+    char* result = new char[strlen(str)+1];\r
+    strcpy(result,str);\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  arg_vector::arg_vector (void)\r
+  {\r
+    m_argv = 0;\r
+  }\r
+\r
+  arg_vector::arg_vector (const arg_vector& a)\r
+  {\r
+    m_argv = 0;\r
+    *this = a;\r
+  }\r
+\r
+  arg_vector::arg_vector (char** a)\r
+  {\r
+    m_argv = 0;\r
+    *this = a;\r
+  }\r
+\r
+  arg_vector::arg_vector (const std::string& command)\r
+  {\r
+    m_argv = 0;\r
+    *this = command;\r
+  }\r
+\r
+  arg_vector::arg_vector (const char* command)\r
+  {\r
+    m_argv = 0;\r
+    *this = command;\r
+  }\r
+\r
+  arg_vector::~arg_vector (void)\r
+  {\r
+    clear();\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (const arg_vector& a)\r
+  {\r
+    return *this = a.m_argv;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (char** argv)\r
+  {\r
+    clear();\r
+    for (unsigned i = 0; argv[i]; i++)\r
+      operator += (argv[i]);\r
+    return *this;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (const std::string& command)\r
+  {\r
+    clear();\r
+    for (unsigned i = 0; i < command.size(); )\r
+    {\r
+      std::string argument = get_argument(command, i);\r
+      operator += (argument);\r
+      skip_white(command, i);\r
+    }\r
+    return *this;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator = (const char* command)\r
+  {\r
+    return operator = (std::string(command));\r
+  }\r
+\r
+  arg_vector& arg_vector::operator += (const std::string& str)\r
+  {\r
+    insert(size(), str);\r
+    return *this;\r
+  }\r
+\r
+  arg_vector& arg_vector::operator -= (const std::string& str)\r
+  {\r
+    insert(0, str);\r
+    return *this;\r
+  }\r
+\r
+  void arg_vector::insert (unsigned index, const std::string& str) throw(std::out_of_range)\r
+  {\r
+    if (index > size()) throw std::out_of_range("arg_vector::insert");\r
+    // copy up to but not including index, then add the new argument, then copy the rest\r
+    char** new_argv = new char*[size()+2];\r
+    unsigned i = 0;\r
+    for ( ; i < index; i++)\r
+      new_argv[i] = copy_string(m_argv[i]);\r
+    new_argv[index] = copy_string(str.c_str());\r
+    for ( ; i < size(); i++)\r
+      new_argv[i+1] = copy_string(m_argv[i]);\r
+    new_argv[i+1] = 0;\r
+    clear();\r
+    m_argv = new_argv;\r
+  }\r
+\r
+  void arg_vector::clear (unsigned index) throw(std::out_of_range)\r
+  {\r
+    if (index >= size()) throw std::out_of_range("arg_vector::clear");\r
+    // copy up to index, skip it, then copy the rest\r
+    char** new_argv = new char*[size()];\r
+    unsigned i = 0;\r
+    for ( ; i < index; i++)\r
+      new_argv[i] = copy_string(m_argv[i]);\r
+    i++;\r
+    for ( ; i < size(); i++)\r
+      new_argv[i-1] = copy_string(m_argv[i]);\r
+    new_argv[i-1] = 0;\r
+    clear();\r
+    m_argv = new_argv;\r
+  }\r
+\r
+  void arg_vector::clear(void)\r
+  {\r
+    if (m_argv)\r
+    {\r
+      for (unsigned i = 0; m_argv[i]; i++)\r
+        delete[] m_argv[i];\r
+      delete[] m_argv;\r
+      m_argv = 0;\r
+    }\r
+  }\r
+\r
+  unsigned arg_vector::size (void) const\r
+  {\r
+    unsigned i = 0;\r
+    if (m_argv)\r
+      while (m_argv[i])\r
+        i++;\r
+    return i;\r
+  }\r
+\r
+  arg_vector::operator char** (void) const\r
+  {\r
+    return m_argv;\r
+  }\r
+\r
+  char** arg_vector::argv (void) const\r
+  {\r
+    return m_argv;\r
+  }\r
+\r
+  char* arg_vector::operator [] (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    if (index >= size()) throw std::out_of_range("arg_vector::operator[]");\r
+    return m_argv[index];\r
+  }\r
+\r
+  char* arg_vector::argv0 (void) const throw(std::out_of_range)\r
+  {\r
+    return operator [] (0);\r
+  }\r
+\r
+  std::string arg_vector::image (void) const\r
+  {\r
+    std::string result;\r
+    for (unsigned i = 0; i < size(); i++)\r
+    {\r
+      if (i) result += ' ';\r
+      result += make_argument(m_argv[i]);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // environment-vector\r
+\r
+  // Windoze environment is a single string containing null-terminated\r
+  // name=value strings and the whole terminated by a null\r
+\r
+  // Unix environment is a null-terminated vector of pointers to null-terminated strings\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // platform specifics\r
+\r
+#ifdef MSWINDOWS\r
+  // Windows utilities\r
+\r
+  // Windows environment variables are case-insensitive and I do comparisons by converting to lowercase\r
+  static std::string lowercase(const std::string& val)\r
+  {\r
+    std::string text = val;\r
+    for (unsigned i = 0; i < text.size(); i++)\r
+      text[i] = tolower(text[i]);\r
+    return text;\r
+  }\r
+\r
+  static unsigned envp_size(const char* envp)\r
+  {\r
+    unsigned size = 0;\r
+    while (envp[size] || (size > 0 && envp[size-1])) size++;\r
+    size++;\r
+    return size;\r
+  }\r
+\r
+  static void envp_extract(std::string& name, std::string& value, const char* envp, unsigned& envi)\r
+  {\r
+    name.erase();\r
+    value.erase();\r
+    if (!envp[envi]) return;\r
+    // some special variables start with '=' so ensure at least one character in the name\r
+    name += envp[envi++];\r
+    while(envp[envi] != '=')\r
+      name += envp[envi++];\r
+    envi++;\r
+    while(envp[envi])\r
+      value += envp[envi++];\r
+    envi++;\r
+  }\r
+\r
+  static void envp_append(const std::string& name, const std::string& value, char* envp, unsigned& envi)\r
+  {\r
+    for (unsigned i = 0; i < name.size(); i++)\r
+      envp[envi++] = name[i];\r
+    envp[envi++] = '=';\r
+    for (unsigned j = 0; j < value.size(); j++)\r
+      envp[envi++] = value[j];\r
+    envp[envi++] = '\0';\r
+    envp[envi] = '\0';\r
+  }\r
+\r
+  static char* envp_copy(const char* envp)\r
+  {\r
+    unsigned size = envp_size(envp);\r
+    char* result = new char[size];\r
+    result[0] = '\0';\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(envp[i])\r
+    {\r
+      std::string name;\r
+      std::string value;\r
+      envp_extract(name, value, envp, i);\r
+      envp_append(name, value, result, j);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  static void envp_clear(char*& envp)\r
+  {\r
+    if (envp)\r
+    {\r
+      delete[] envp;\r
+      envp = 0;\r
+    }\r
+  }\r
+\r
+  static bool envp_equal(const std::string& left, const std::string& right)\r
+  {\r
+    return lowercase(left) == lowercase(right);\r
+  }\r
+\r
+  static bool envp_less(const std::string& left, const std::string& right)\r
+  {\r
+    return lowercase(left) < lowercase(right);\r
+  }\r
+\r
+#else\r
+  // Unix utilities\r
+\r
+  static unsigned envp_size(char* const* envp)\r
+  {\r
+    unsigned size = 0;\r
+    while(envp[size]) size++;\r
+    size++;\r
+    return size;\r
+  }\r
+\r
+  static void envp_extract(std::string& name, std::string& value, char* const* envp, unsigned& envi)\r
+  {\r
+    name = "";\r
+    value = "";\r
+    if (!envp[envi]) return;\r
+    unsigned i = 0;\r
+    while(envp[envi][i] != '=')\r
+      name += envp[envi][i++];\r
+    i++;\r
+    while(envp[envi][i])\r
+      value += envp[envi][i++];\r
+    envi++;\r
+  }\r
+\r
+  static void envp_append(const std::string& name, const std::string& value, char** envp, unsigned& envi)\r
+  {\r
+    std::string entry = name + "=" + value;\r
+    envp[envi] = copy_string(entry.c_str());\r
+    envi++;\r
+    envp[envi] = 0;\r
+  }\r
+\r
+  static char** envp_copy(char* const* envp)\r
+  {\r
+    unsigned size = envp_size(envp);\r
+    char** result = new char*[size];\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(envp[i])\r
+    {\r
+      std::string name;\r
+      std::string value;\r
+      envp_extract(name, value, envp, i);\r
+      envp_append(name, value, result, j);\r
+    }\r
+    return result;\r
+  }\r
+\r
+  static void envp_clear(char**& envp)\r
+  {\r
+    if (envp)\r
+    {\r
+      for (unsigned i = 0; envp[i]; i++)\r
+        delete[] envp[i];\r
+      delete[] envp;\r
+      envp = 0;\r
+    }\r
+  }\r
+\r
+  static bool envp_equal(const std::string& left, const std::string& right)\r
+  {\r
+    return left == right;\r
+  }\r
+\r
+  static bool envp_less(const std::string& left, const std::string& right)\r
+  {\r
+    return left < right;\r
+  }\r
+\r
+#endif\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+  env_vector::env_vector(void)\r
+  {\r
+#ifdef MSWINDOWS\r
+    char* env = (char*)GetEnvironmentStringsA();\r
+    m_env = envp_copy(env);\r
+    FreeEnvironmentStringsA(env);\r
+#else\r
+    m_env = envp_copy(::environ);\r
+#endif\r
+  }\r
+\r
+  env_vector::env_vector (const env_vector& a)\r
+  {\r
+    m_env = 0;\r
+    *this = a;\r
+  }\r
+\r
+  env_vector::~env_vector (void)\r
+  {\r
+    clear();\r
+  }\r
+\r
+  env_vector& env_vector::operator = (const env_vector& a)\r
+  {\r
+    clear();\r
+    m_env = envp_copy(a.m_env);\r
+    return *this;\r
+  }\r
+\r
+  void env_vector::clear(void)\r
+  {\r
+    envp_clear(m_env);\r
+  }\r
+\r
+  void env_vector::add(const std::string& name, const std::string& value)\r
+  {\r
+    // the trick is to add the value in alphabetic order\r
+    // this is done by copying the existing environment string to a new\r
+    // string, inserting the new value when a name greater than it is found\r
+    unsigned size = envp_size(m_env);\r
+#ifdef MSWINDOWS\r
+    unsigned new_size = size + name.size() + value.size() + 2;\r
+    char* new_v = new char[new_size];\r
+    new_v[0] = '\0';\r
+#else\r
+    unsigned new_size = size + 1;\r
+    char** new_v = new char*[new_size];\r
+    new_v[0] = 0;\r
+#endif\r
+    // now extract each name=value pair and check the ordering\r
+    bool added = false;\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+      {\r
+        // replace an existing value\r
+        envp_append(name, value, new_v, j);\r
+      }\r
+      else if (!added && envp_less(name,current_name))\r
+      {\r
+        // add the new value first, then the existing one\r
+        envp_append(name, value, new_v, j);\r
+        envp_append(current_name, current_value, new_v, j);\r
+        added = true;\r
+      }\r
+      else\r
+      {\r
+        // just add the existing value\r
+        envp_append(current_name, current_value, new_v, j);\r
+      }\r
+    }\r
+    if (!added)\r
+      envp_append(name, value, new_v, j);\r
+    envp_clear(m_env);\r
+    m_env = new_v;\r
+  }\r
+\r
+\r
+  bool env_vector::remove (const std::string& name)\r
+  {\r
+    bool result = false;\r
+    // this is done by copying the existing environment string to a new string, but excluding the specified name\r
+    unsigned size = envp_size(m_env);\r
+#ifdef MSWINDOWS\r
+    char* new_v = new char[size];\r
+    new_v[0] = '\0';\r
+#else\r
+    char** new_v = new char*[size];\r
+    new_v[0] = 0;\r
+#endif\r
+    unsigned i = 0;\r
+    unsigned j = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+        result = true;\r
+      else\r
+        envp_append(current_name, current_value, new_v, j);\r
+    }\r
+    envp_clear(m_env);\r
+    m_env = new_v;\r
+    return result;\r
+  }\r
+\r
+  bool env_vector::present (const std::string& name) const\r
+  {\r
+    unsigned i = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+        return true;\r
+    }\r
+    return false;\r
+  }\r
+\r
+  std::string env_vector::operator [] (const std::string& name) const\r
+  {\r
+    return get(name);\r
+  }\r
+\r
+  std::string env_vector::get (const std::string& name) const\r
+  {\r
+    unsigned i = 0;\r
+    while(m_env[i])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, i);\r
+      if (envp_equal(name,current_name))\r
+        return current_value;\r
+    }\r
+    return std::string();\r
+  }\r
+\r
+  unsigned env_vector::size (void) const\r
+  {\r
+    unsigned i = 0;\r
+#ifdef MSWINDOWS\r
+    unsigned offset = 0;\r
+    while(m_env[offset])\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, offset);\r
+      i++;\r
+    }\r
+#else\r
+    while(m_env[i])\r
+      i++;\r
+#endif\r
+\r
+    return i;\r
+  }\r
+\r
+  std::pair<std::string,std::string> env_vector::operator [] (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    return get(index);\r
+  }\r
+\r
+  std::pair<std::string,std::string> env_vector::get (unsigned index) const throw(std::out_of_range)\r
+  {\r
+    if (index >= size()) throw std::out_of_range("arg_vector::get");\r
+    unsigned j = 0;\r
+    for (unsigned i = 0; i < index; i++)\r
+    {\r
+      std::string current_name;\r
+      std::string current_value;\r
+      envp_extract(current_name, current_value, m_env, j);\r
+    }\r
+    std::string name;\r
+    std::string value;\r
+    envp_extract(name, value, m_env, j);\r
+    return std::make_pair(name,value);\r
+  }\r
+\r
+  ENVIRON_TYPE env_vector::envp (void) const\r
+  {\r
+    return m_env;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Synchronous subprocess\r
+  // Win32 implementation mostly cribbed from MSDN examples and then made (much) more readable\r
+  // Unix implementation mostly from man pages and bitter experience\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  subprocess::subprocess(void)\r
+  {\r
+    m_pid.hProcess = 0;\r
+    m_job = 0;\r
+    m_child_in = 0;\r
+    m_child_out = 0;\r
+    m_child_err = 0;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#else\r
+\r
+  subprocess::subprocess(void)\r
+  {\r
+    m_pid = -1;\r
+    m_child_in = -1;\r
+    m_child_out = -1;\r
+    m_child_err = -1;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  subprocess::~subprocess(void)\r
+  {\r
+    if (m_pid.hProcess != 0)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  subprocess::~subprocess(void)\r
+  {\r
+    if (m_pid != -1)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      for (;;)\r
+      {\r
+        int wait_status = 0;\r
+        int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+        if (wait_ret_val != -1 || errno != EINTR) break;\r
+      }\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  void subprocess::set_error(int e)\r
+  {\r
+    m_err = e;\r
+  }\r
+\r
+  void subprocess::add_variable(const std::string& name, const std::string& value)\r
+  {\r
+    m_env.add(name, value);\r
+  }\r
+\r
+  bool subprocess::remove_variable(const std::string& name)\r
+  {\r
+    return m_env.remove(name);\r
+  }\r
+\r
+  const env_vector& subprocess::get_variables(void) const\r
+  {\r
+       return m_env;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                         bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+    // If no pipes requested, then connect to the parent stdin/out/err\r
+    // for some reason you have to create a pipe handle, then duplicate it\r
+    // This is not well explained in MSDN but seems to work\r
+    PIPE_TYPE parent_stdin = 0;\r
+    if (!connect_stdin)\r
+      parent_stdin = GetStdHandle(STD_INPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stdout = 0;\r
+    if (!connect_stdout)\r
+      parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stderr = 0;\r
+    if (!connect_stderr)\r
+      parent_stderr = GetStdHandle(STD_ERROR_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    // Now create the subprocess\r
+    // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work\r
+    // Note that the child will inherit a copy of the pipe handles\r
+    STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,\r
+                            STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,\r
+                            parent_stdin,parent_stdout,parent_stderr};\r
+    bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;\r
+    // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them\r
+    if (connect_stdin) CloseHandle(parent_stdin);\r
+    if (connect_stdout) CloseHandle(parent_stdout);\r
+    if (connect_stderr) CloseHandle(parent_stderr);\r
+    if (!created)\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      result = false;\r
+    }\r
+    else\r
+    {\r
+      m_job = CreateJobObject(NULL, NULL);\r
+      AssignProcessToJobObject(m_job, m_pid.hProcess);\r
+      ResumeThread(m_pid.hThread);\r
+\r
+      // The child process is now running so call the user's callback\r
+      // The convention is that the callback can return false, in which case this will kill the child (if its still running)\r
+      if (!callback())\r
+      {\r
+        result = false;\r
+        kill();\r
+      }\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      // wait for the child to finish\r
+      // TODO - kill the child if a timeout happens\r
+      WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+      DWORD exit_status = 0;\r
+      if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))\r
+      {\r
+        set_error(GetLastError());\r
+        result = false;\r
+      }\r
+      else if (exit_status != 0)\r
+        result = false;\r
+      m_status = (int)exit_status;\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+    }\r
+    m_pid.hProcess = 0;\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  bool subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                         bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+\r
+    int stdin_pipe [2] = {-1, -1};\r
+    if (connect_stdin)\r
+      if (::pipe(stdin_pipe) != 0)\r
+        set_error(errno);\r
+\r
+    int stdout_pipe [2] = {-1, -1};\r
+    if (connect_stdout)\r
+      if (::pipe(stdout_pipe) != 0)\r
+        set_error(errno);\r
+\r
+    int stderr_pipe [2] = {-1, -1};\r
+    if (connect_stderr)\r
+      if (::pipe(stderr_pipe) != 0)\r
+        set_error(errno);\r
+\r
+    // now create the subprocess\r
+    // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec\r
+    m_pid = ::fork();\r
+    switch(m_pid)\r
+    {\r
+    case -1:   // failed to fork\r
+      set_error(errno);\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        ::close(stdin_pipe[1]);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        ::close(stdout_pipe[1]);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        ::close(stderr_pipe[1]);\r
+      }\r
+      result = false;\r
+      break;\r
+    case 0:  // in child;\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the parent\r
+      // and connect the child's end of the pipe to the appropriate standard I/O device\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[1]);\r
+        ::dup2(stdin_pipe[0],STDIN_FILENO);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        ::dup2(stdout_pipe[1],STDOUT_FILENO);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        ::dup2(stderr_pipe[1],STDERR_FILENO);\r
+      }\r
+      execve(path.c_str(), argv.argv(), m_env.envp());\r
+      // will only ever get here if the exec() failed completely - *must* now exit the child process\r
+      // by using errno, the parent has some chance of diagnosing the cause of the problem\r
+      exit(errno);\r
+    }\r
+    break;\r
+    default:  // in parent\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the child\r
+      // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        m_child_in = stdin_pipe[1];\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[1]);\r
+        m_child_out = stdout_pipe[0];\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[1]);\r
+        m_child_err = stderr_pipe[0];\r
+      }\r
+      // call the user's callback\r
+      if (!callback())\r
+      {\r
+        result = false;\r
+        kill();\r
+      }\r
+      // close the pipes and wait for the child to finish\r
+      // wait exits on a signal which may be the child signalling its exit or may be an interrupt\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      int wait_status = 0;\r
+      for (;;)\r
+      {\r
+        int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+        if (wait_ret_val != -1 || errno != EINTR) break;\r
+      }\r
+      // establish whether an error occurred\r
+      if (WIFSIGNALED(wait_status))\r
+      {\r
+        // set_error(errno);\r
+        m_status = WTERMSIG(wait_status);\r
+        result = false;\r
+      }\r
+      else if (WIFEXITED(wait_status))\r
+      {\r
+        m_status = WEXITSTATUS(wait_status);\r
+        if (m_status != 0)\r
+          result = false;\r
+      }\r
+      m_pid = -1;\r
+    }\r
+    break;\r
+    }\r
+    return result;\r
+  }\r
+\r
+#endif\r
+\r
+  bool subprocess::spawn(const std::string& command_line,\r
+                                  bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    arg_vector arguments = command_line;\r
+    if (arguments.size() == 0) return false;\r
+    std::string path = path_lookup(arguments.argv0());\r
+    if (path.empty()) return false;\r
+    return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);\r
+  }\r
+\r
+  bool subprocess::callback(void)\r
+  {\r
+    return true;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool subprocess::kill (void)\r
+  {\r
+    if (!m_pid.hProcess) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (!TerminateJobObject(m_job, (UINT)-1))\r
+    {\r
+      set_error(GetLastError());\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#else\r
+\r
+  bool subprocess::kill (void)\r
+  {\r
+    if (m_pid == -1) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (::kill(m_pid, SIGINT) == -1)\r
+    {\r
+      set_error(errno);\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == 0) return -1;\r
+    // do a blocking write of the whole buffer\r
+    DWORD bytes = 0;\r
+    if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == -1) return -1;\r
+    // do a blocking write of the whole buffer\r
+    int bytes = write(m_child_in, buffer.c_str(), buffer.size());\r
+    if (bytes == -1)\r
+    {\r
+      set_error(errno);\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == 0) return -1;\r
+    DWORD bytes = 0;\r
+    DWORD buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        set_error(GetLastError());\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == -1) return -1;\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_out, tmp, buffer_size);\r
+    if (bytes == -1)\r
+    {\r
+      set_error(errno);\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int subprocess::read_stderr(std::string& buffer)\r
+  {\r
+    if (m_child_err == 0) return -1;\r
+    DWORD bytes = 0;\r
+    DWORD buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        set_error(GetLastError());\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int subprocess::read_stderr (std::string& buffer)\r
+  {\r
+    if (m_child_err == -1) return -1;\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_err, tmp, buffer_size);\r
+    if (bytes == -1)\r
+    {\r
+      set_error(errno);\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in)\r
+    {\r
+      CloseHandle(m_child_in);\r
+      m_child_in = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in != -1)\r
+    {\r
+      ::close(m_child_in);\r
+      m_child_in = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out)\r
+    {\r
+      CloseHandle(m_child_out);\r
+      m_child_out = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out != -1)\r
+    {\r
+      ::close(m_child_out);\r
+      m_child_out = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err)\r
+    {\r
+      CloseHandle(m_child_err);\r
+      m_child_err = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err != -1)\r
+    {\r
+      ::close(m_child_err);\r
+      m_child_err = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  bool subprocess::error(void) const\r
+  {\r
+    return m_err != 0;\r
+  }\r
+\r
+  int subprocess::error_number(void) const\r
+  {\r
+    return m_err;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  std::string subprocess::error_text(void) const\r
+  {\r
+    if (!error()) return std::string();\r
+    char* message;\r
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                  0,\r
+                  m_err,\r
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+                  (LPTSTR)&message,\r
+                  0,0);\r
+    std::string result = message;\r
+    LocalFree(message);\r
+    // the error message is for some perverse reason newline terminated - remove this\r
+    if (result[result.size()-1] == '\n')\r
+      result.erase(result.end()-1);\r
+    if (result[result.size()-1] == '\r')\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  std::string subprocess::error_text(void) const\r
+  {\r
+    if (!error()) return std::string();\r
+    char* text = strerror(m_err);\r
+    if (text) return std::string(text);\r
+    return "error number " + dformat("%d",m_err);\r
+  }\r
+\r
+#endif\r
+\r
+  int subprocess::exit_status(void) const\r
+  {\r
+    return m_status;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // backtick subprocess and operations\r
+\r
+  backtick_subprocess::backtick_subprocess(void) : subprocess()\r
+  {\r
+  }\r
+\r
+  bool backtick_subprocess::callback(void)\r
+  {\r
+    for (;;)\r
+    {\r
+      std::string buffer;\r
+      int read_size = read_stdout(buffer);\r
+      if (read_size < 0) break;\r
+      m_text += buffer;\r
+    }\r
+    return !error();\r
+  }\r
+\r
+  bool backtick_subprocess::spawn(const std::string& path, const arg_vector& argv)\r
+  {\r
+    return subprocess::spawn(path, argv, false, true, false);\r
+  }\r
+\r
+  bool backtick_subprocess::spawn(const std::string& command_line)\r
+  {\r
+    return subprocess::spawn(command_line, false, true, false);\r
+  }\r
+\r
+  std::vector<std::string> backtick_subprocess::text(void) const\r
+  {\r
+    std::vector<std::string> result;\r
+    // convert the raw text into a vector of strings, each corresponding to a line\r
+    // in the process, strip out platform-specific line-endings\r
+    result.push_back(std::string());\r
+    for (unsigned i = 0; i < m_text.size(); i++)\r
+    {\r
+      // handle any kind of line-ending - Dos, Unix or MacOS\r
+      switch(m_text[i])\r
+      {\r
+      case '\xd': // carriage-return - optionally followed by linefeed\r
+      {\r
+        // discard optional following linefeed\r
+        if ((i+1 < m_text.size()) && (m_text[i+1] == '\xa'))\r
+          i++;\r
+        // add a new line to the end of the vector\r
+        result.push_back(std::string());\r
+        break;\r
+      }\r
+      case '\xa': // linefeed\r
+      {\r
+        // add a new line to the end of the vector\r
+        result.push_back(std::string());\r
+        break;\r
+      }\r
+      default:\r
+      {\r
+        result.back() += m_text[i];\r
+        break;\r
+      }\r
+      }\r
+    }\r
+    // tidy up - if the last line ended with a newline, the vector will end with an empty string - discard this\r
+    if ((result.size()) > 0 && result.back().empty())\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+  std::vector<std::string> backtick(const std::string& path, const arg_vector& argv)\r
+  {\r
+    backtick_subprocess sub;\r
+    sub.spawn(path, argv);\r
+    return sub.text();\r
+  }\r
+\r
+  std::vector<std::string> backtick(const std::string& command_line)\r
+  {\r
+    backtick_subprocess sub;\r
+    sub.spawn(command_line);\r
+    return sub.text();\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+  // Asynchronous subprocess\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  async_subprocess::async_subprocess(void)\r
+  {\r
+    m_pid.hProcess = 0;\r
+    m_job = 0;\r
+    m_child_in = 0;\r
+    m_child_out = 0;\r
+    m_child_err = 0;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#else\r
+\r
+  async_subprocess::async_subprocess(void)\r
+  {\r
+    m_pid = -1;\r
+    m_child_in = -1;\r
+    m_child_out = -1;\r
+    m_child_err = -1;\r
+    m_err = 0;\r
+    m_status = 0;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  async_subprocess::~async_subprocess(void)\r
+  {\r
+    if (m_pid.hProcess != 0)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  async_subprocess::~async_subprocess(void)\r
+  {\r
+    if (m_pid != -1)\r
+    {\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      kill();\r
+      for (;;)\r
+      {\r
+        int wait_status = 0;\r
+        int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+        if (wait_ret_val != -1 || errno != EINTR) break;\r
+      }\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  void async_subprocess::set_error(int e)\r
+  {\r
+    m_err = e;\r
+  }\r
+\r
+  void async_subprocess::add_variable(const std::string& name, const std::string& value)\r
+  {\r
+    m_env.add(name, value);\r
+  }\r
+\r
+  bool async_subprocess::remove_variable(const std::string& name)\r
+  {\r
+    return m_env.remove(name);\r
+  }\r
+\r
+  const env_vector& async_subprocess::get_variables(void) const\r
+  {\r
+       return m_env;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                                        bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+    // If no pipes requested, then connect to the parent stdin/out/err\r
+    // for some reason you have to create a pipe handle, then duplicate it\r
+    // This is not well explained in MSDN but seems to work\r
+    PIPE_TYPE parent_stdin = 0;\r
+    if (!connect_stdin)\r
+      parent_stdin = GetStdHandle(STD_INPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stdout = 0;\r
+    if (!connect_stdout)\r
+      parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    PIPE_TYPE parent_stderr = 0;\r
+    if (!connect_stderr)\r
+      parent_stderr = GetStdHandle(STD_ERROR_HANDLE);\r
+    else\r
+    {\r
+      PIPE_TYPE tmp = 0;\r
+      SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+      CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);\r
+      DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+    }\r
+\r
+    // Now create the subprocess\r
+    // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work\r
+    // Note that the child will inherit a copy of the pipe handles\r
+    STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,\r
+                            STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,\r
+                            parent_stdin,parent_stdout,parent_stderr};\r
+    bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;\r
+    // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them\r
+    if (connect_stdin) CloseHandle(parent_stdin);\r
+    if (connect_stdout) CloseHandle(parent_stdout);\r
+    if (connect_stderr) CloseHandle(parent_stderr);\r
+    if (!created)\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdin();\r
+      close_stdout();\r
+      close_stderr();\r
+      result = false;\r
+    }\r
+    else\r
+    {\r
+      m_job = CreateJobObject(NULL, NULL);\r
+      AssignProcessToJobObject(m_job, m_pid.hProcess);\r
+      ResumeThread(m_pid.hThread);\r
+    }\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+                               bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    bool result = true;\r
+    // first create the pipes to be used to connect to the child stdin/out/err\r
+\r
+    int stdin_pipe [2] = {-1, -1};\r
+    if (connect_stdin)\r
+      if (::pipe(stdin_pipe) != 0)\r
+        set_error(errno);\r
+\r
+    int stdout_pipe [2] = {-1, -1};\r
+    if (connect_stdout)\r
+      if (::pipe(stdout_pipe) != 0)\r
+        set_error(errno);\r
+\r
+    int stderr_pipe [2] = {-1, -1};\r
+    if (connect_stderr)\r
+      if (::pipe(stderr_pipe) != 0)\r
+        set_error(errno);\r
+\r
+    // now create the subprocess\r
+    // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec\r
+    m_pid = ::fork();\r
+    switch(m_pid)\r
+    {\r
+    case -1:   // failed to fork\r
+      set_error(errno);\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        ::close(stdin_pipe[1]);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        ::close(stdout_pipe[1]);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        ::close(stderr_pipe[1]);\r
+      }\r
+      result = false;\r
+      break;\r
+    case 0:  // in child;\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the parent\r
+      // and connect the child's end of the pipe to the appropriate standard I/O device\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[1]);\r
+        ::dup2(stdin_pipe[0],STDIN_FILENO);\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[0]);\r
+        ::dup2(stdout_pipe[1],STDOUT_FILENO);\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[0]);\r
+        ::dup2(stderr_pipe[1],STDERR_FILENO);\r
+      }\r
+      ::execve(path.c_str(), argv.argv(), m_env.envp());\r
+      // will only ever get here if the exec() failed completely - *must* now exit the child process\r
+      // by using errno, the parent has some chance of diagnosing the cause of the problem\r
+      ::exit(errno);\r
+    }\r
+    break;\r
+    default:  // in parent\r
+    {\r
+      // for each pipe, close the end of the duplicated pipe that is being used by the child\r
+      // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback\r
+      if (connect_stdin)\r
+      {\r
+        ::close(stdin_pipe[0]);\r
+        m_child_in = stdin_pipe[1];\r
+        if (fcntl(m_child_in, F_SETFL, O_NONBLOCK) == -1)\r
+        {\r
+          set_error(errno);\r
+          result = false;\r
+        }\r
+      }\r
+      if (connect_stdout)\r
+      {\r
+        ::close(stdout_pipe[1]);\r
+        m_child_out = stdout_pipe[0];\r
+        if (fcntl(m_child_out, F_SETFL, O_NONBLOCK) == -1)\r
+        {\r
+          set_error(errno);\r
+          result = false;\r
+        }\r
+      }\r
+      if (connect_stderr)\r
+      {\r
+        ::close(stderr_pipe[1]);\r
+        m_child_err = stderr_pipe[0];\r
+        if (fcntl(m_child_err, F_SETFL, O_NONBLOCK) == -1)\r
+        {\r
+          set_error(errno);\r
+          result = false;\r
+        }\r
+      }\r
+    }\r
+    break;\r
+    }\r
+    return result;\r
+  }\r
+\r
+#endif\r
+\r
+  bool async_subprocess::spawn(const std::string& command_line,\r
+                               bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+  {\r
+    arg_vector arguments = command_line;\r
+    if (arguments.size() == 0) return false;\r
+    std::string path = path_lookup(arguments.argv0());\r
+    if (path.empty()) return false;\r
+    return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);\r
+  }\r
+\r
+  bool async_subprocess::callback(void)\r
+  {\r
+    return true;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool async_subprocess::tick(void)\r
+  {\r
+    bool result = true;\r
+    if (!callback())\r
+      kill();\r
+    DWORD exit_status = 0;\r
+    if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))\r
+    {\r
+      set_error(GetLastError());\r
+      result = false;\r
+    }\r
+    else if (exit_status != STILL_ACTIVE)\r
+    {\r
+      CloseHandle(m_pid.hThread);\r
+      CloseHandle(m_pid.hProcess);\r
+      CloseHandle(m_job);\r
+      m_pid.hProcess = 0;\r
+      result = false;\r
+    }\r
+    m_status = (int)exit_status;\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  bool async_subprocess::tick(void)\r
+  {\r
+    bool result = true;\r
+    if (!callback())\r
+      kill();\r
+    int wait_status = 0;\r
+    int wait_ret_val = waitpid(m_pid, &wait_status, WNOHANG);\r
+    if (wait_ret_val == -1 && errno != EINTR)\r
+    {\r
+      set_error(errno);\r
+      result = false;\r
+    }\r
+    else if (wait_ret_val != 0)\r
+    {\r
+      // the only states that indicate a terminated child are WIFSIGNALLED and WIFEXITED\r
+      if (WIFSIGNALED(wait_status))\r
+      {\r
+        // set_error(errno);\r
+        m_status = WTERMSIG(wait_status);\r
+        result = false;\r
+      }\r
+      else if (WIFEXITED(wait_status))\r
+      {\r
+        // child has exited\r
+        m_status = WEXITSTATUS(wait_status);\r
+        result = false;\r
+      }\r
+    }\r
+    if (!result)\r
+      m_pid = -1;\r
+    return result;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  bool async_subprocess::kill(void)\r
+  {\r
+    if (!m_pid.hProcess) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (!TerminateJobObject(m_job, (UINT)-1))\r
+    {\r
+      set_error(GetLastError());\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#else\r
+\r
+  bool async_subprocess::kill(void)\r
+  {\r
+    if (m_pid == -1) return false;\r
+    close_stdin();\r
+    close_stdout();\r
+    close_stderr();\r
+    if (::kill(m_pid, SIGINT) == -1)\r
+    {\r
+      set_error(errno);\r
+      return false;\r
+    }\r
+    return true;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int async_subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == 0) return -1;\r
+    // there doesn't seem to be a way of doing non-blocking writes under Windoze\r
+    DWORD bytes = 0;\r
+    if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int async_subprocess::write_stdin (std::string& buffer)\r
+  {\r
+    if (m_child_in == -1) return -1;\r
+    // relies on the pipe being non-blocking\r
+    // This does block under Windoze\r
+    int bytes = write(m_child_in, buffer.c_str(), buffer.size());\r
+    if (bytes == -1 && errno == EAGAIN)\r
+    {\r
+      // not ready\r
+      return 0;\r
+    }\r
+    if (bytes == -1)\r
+    {\r
+      // error on write - close the pipe and give up\r
+      set_error(errno);\r
+      close_stdin();\r
+      return -1;\r
+    }\r
+    // successful write\r
+    // now discard that part of the buffer that was written\r
+    if (bytes > 0)\r
+      buffer.erase(0, bytes);\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int async_subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == 0) return -1;\r
+    // peek at the buffer to see how much data there is in the first place\r
+    DWORD buffer_size = 0;\r
+    if (!PeekNamedPipe(m_child_out, 0, 0, 0, &buffer_size, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        set_error(GetLastError());\r
+      close_stdout();\r
+      return -1;\r
+    }\r
+    if (buffer_size == 0) return 0;\r
+    DWORD bytes = 0;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int async_subprocess::read_stdout (std::string& buffer)\r
+  {\r
+    if (m_child_out == -1) return -1;\r
+    // rely on the pipe being non-blocking\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_out, tmp, buffer_size);\r
+    if (bytes == -1 && errno == EAGAIN)\r
+    {\r
+      // not ready\r
+      delete[] tmp;\r
+      return 0;\r
+    }\r
+    if (bytes == -1)\r
+    {\r
+      // error\r
+      set_error(errno);\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stdout();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    // successful read\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  int async_subprocess::read_stderr (std::string& buffer)\r
+  {\r
+    if (m_child_err == 0) return -1;\r
+    // peek at the buffer to see how much data there is in the first place\r
+    DWORD buffer_size = 0;\r
+    if (!PeekNamedPipe(m_child_err, 0, 0, 0, &buffer_size, 0))\r
+    {\r
+      if (GetLastError() != ERROR_BROKEN_PIPE)\r
+        set_error(GetLastError());\r
+      close_stderr();\r
+      return -1;\r
+    }\r
+    if (buffer_size == 0) return 0;\r
+    DWORD bytes = 0;\r
+    char* tmp = new char[buffer_size];\r
+    if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))\r
+    {\r
+      set_error(GetLastError());\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return (int)bytes;\r
+  }\r
+\r
+#else\r
+\r
+  int async_subprocess::read_stderr (std::string& buffer)\r
+  {\r
+    if (m_child_err == -1) return -1;\r
+    // rely on the pipe being non-blocking\r
+    int buffer_size = 256;\r
+    char* tmp = new char[buffer_size];\r
+    int bytes = read(m_child_err, tmp, buffer_size);\r
+    if (bytes == -1 && errno == EAGAIN)\r
+    {\r
+      // not ready\r
+      delete[] tmp;\r
+      return 0;\r
+    }\r
+    if (bytes == -1)\r
+    {\r
+      // error\r
+      set_error(errno);\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    if (bytes == 0)\r
+    {\r
+      // EOF\r
+      close_stderr();\r
+      delete[] tmp;\r
+      return -1;\r
+    }\r
+    // successful read\r
+    buffer.append(tmp, bytes);\r
+    delete[] tmp;\r
+    return bytes;\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void async_subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in)\r
+    {\r
+      CloseHandle(m_child_in);\r
+      m_child_in = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void async_subprocess::close_stdin (void)\r
+  {\r
+    if (m_child_in != -1)\r
+    {\r
+      ::close(m_child_in);\r
+      m_child_in = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void async_subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out)\r
+    {\r
+      CloseHandle(m_child_out);\r
+      m_child_out = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void async_subprocess::close_stdout (void)\r
+  {\r
+    if (m_child_out != -1)\r
+    {\r
+      ::close(m_child_out);\r
+      m_child_out = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  void async_subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err)\r
+    {\r
+      CloseHandle(m_child_err);\r
+      m_child_err = 0;\r
+    }\r
+  }\r
+\r
+#else\r
+\r
+  void async_subprocess::close_stderr (void)\r
+  {\r
+    if (m_child_err != -1)\r
+    {\r
+      ::close(m_child_err);\r
+      m_child_err = -1;\r
+    }\r
+  }\r
+\r
+#endif\r
+\r
+  bool async_subprocess::error(void) const\r
+  {\r
+    return m_err != 0;\r
+  }\r
+\r
+  int async_subprocess::error_number(void) const\r
+  {\r
+    return m_err;\r
+  }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+  std::string async_subprocess::error_text(void) const\r
+  {\r
+    if (!error()) return std::string();\r
+    char* message;\r
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+                  0,\r
+                  m_err,\r
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+                  (LPTSTR)&message,\r
+                  0,0);\r
+    std::string result = message;\r
+    LocalFree(message);\r
+    // the error message is for some perverse reason newline terminated - remove this\r
+    if (result[result.size()-1] == '\n')\r
+      result.erase(result.end()-1);\r
+    if (result[result.size()-1] == '\r')\r
+      result.erase(result.end()-1);\r
+    return result;\r
+  }\r
+\r
+#else\r
+\r
+  std::string async_subprocess::error_text(void) const\r
+  {\r
+    if (!error()) return std::string();\r
+    char* text = strerror(m_err);\r
+    if (text) return std::string(text);\r
+    return "error number " + dformat("%d",m_err);\r
+  }\r
+\r
+#endif\r
+\r
+  int async_subprocess::exit_status(void) const\r
+  {\r
+    return m_status;\r
+  }\r
+\r
+  ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
This page took 0.085178 seconds and 4 git commands to generate.