process 1.2.2.0 → 1.2.3.0
raw patch · 7 files changed
+191/−122 lines, 7 filesdep +processdep ~basedep ~filepath
Dependencies added: process
Dependency ranges changed: base, filepath
Files
- System/Process.hsc +51/−12
- System/Process/Internals.hs +9/−0
- cbits/runProcess.c +3/−1
- changelog.md +7/−0
- configure +97/−107
- process.cabal +10/−2
- test/main.hs +14/−0
System/Process.hsc view
@@ -40,7 +40,9 @@ callCommand, spawnProcess, spawnCommand,+ readCreateProcess, readProcess,+ readCreateProcessWithExitCode, readProcessWithExitCode, -- ** Related utilities@@ -298,7 +300,7 @@ -- /Since: 1.2.0.0/ callProcess :: FilePath -> [String] -> IO () callProcess cmd args = do- exit_code <- withCreateProcess_ "callCommand"+ exit_code <- withCreateProcess_ "callProcess" (proc cmd args) { delegate_ctlc = True } $ \_ _ _ p -> waitForProcess p case exit_code of@@ -413,13 +415,28 @@ -> [String] -- ^ any arguments -> String -- ^ standard input -> IO String -- ^ stdout-readProcess cmd args input = do- let cp_opts = (proc cmd args) {+readProcess cmd args = readCreateProcess $ proc cmd args++-- | @readCreateProcess@ works exactly like 'readProcess' except that it+-- lets you pass 'CreateProcess' giving better flexibility.+--+-- > > readCreateProcess (shell "pwd" { cwd = "/etc/" }) ""+-- > "/etc\n"+--+-- Note that @Handle@s provided for @std_in@ or @std_out@ via the CreateProcess+-- record will be ignored.+-- /Since: 1.2.3.0/++readCreateProcess+ :: CreateProcess+ -> String -- ^ standard input+ -> IO String -- ^ stdout+readCreateProcess cp input = do+ let cp_opts = cp { std_in = CreatePipe,- std_out = CreatePipe,- std_err = Inherit+ std_out = CreatePipe }- (ex, output) <- withCreateProcess_ "readProcess" cp_opts $+ (ex, output) <- withCreateProcess_ "readCreateProcess" cp_opts $ \(Just inh) (Just outh) _ ph -> do -- fork off a thread to start consuming the output@@ -442,16 +459,24 @@ case ex of ExitSuccess -> return output- ExitFailure r -> processFailedException "readProcess" cmd args r+ ExitFailure r -> processFailedException "readCreateProcess" cmd args r+ where+ cmd = case cp of+ CreateProcess { cmdspec = ShellCommand sc } -> sc+ CreateProcess { cmdspec = RawCommand fp _ } -> fp+ args = case cp of+ CreateProcess { cmdspec = ShellCommand _ } -> []+ CreateProcess { cmdspec = RawCommand _ args' } -> args' + -- | @readProcessWithExitCode@ is like @readProcess@ but with two differences:--- +-- -- * it returns the 'ExitCode' of the process, and does not throw any -- exception if the code is not 'ExitSuccess'. -- -- * it reads and returns the output from process' standard error handle, -- rather than the process inheriting the standard error handle.--- +-- -- On Unix systems, see 'waitForProcess' for the meaning of exit codes -- when the process died as the result of a signal. --@@ -460,13 +485,27 @@ -> [String] -- ^ any arguments -> String -- ^ standard input -> IO (ExitCode,String,String) -- ^ exitcode, stdout, stderr-readProcessWithExitCode cmd args input = do- let cp_opts = (proc cmd args) {+readProcessWithExitCode cmd args =+ readCreateProcessWithExitCode $ proc cmd args++-- | @readCreateProcessWithExitCode@ works exactly like 'readProcessWithExitCode' except that it+-- lets you pass 'CreateProcess' giving better flexibility.+--+-- Note that @Handle@s provided for @std_in@, @std_out@, or @std_err@ via the CreateProcess+-- record will be ignored.+--+-- /Since: 1.2.3.0/+readCreateProcessWithExitCode+ :: CreateProcess+ -> String -- ^ standard input+ -> IO (ExitCode,String,String) -- ^ exitcode, stdout, stderr+readCreateProcessWithExitCode cp input = do+ let cp_opts = cp { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe }- withCreateProcess_ "readProcessWithExitCode" cp_opts $+ withCreateProcess_ "readCreateProcessWithExitCode" cp_opts $ \(Just inh) (Just outh) (Just errh) ph -> do out <- hGetContents outh
System/Process/Internals.hs view
@@ -463,11 +463,20 @@ runInteractiveProcess_lock :: MVar () runInteractiveProcess_lock = unsafePerformIO $ newMVar () +-- The following functions are always present in the export list. For+-- compatibility with the non-Windows code, we provide the same functions with+-- matching type signatures, but implemented as no-ops. For details, see:+-- <https://github.com/haskell/process/pull/21> startDelegateControlC :: IO () startDelegateControlC = return () endDelegateControlC :: ExitCode -> IO () endDelegateControlC _ = return ()++stopDelegateControlC :: IO ()+stopDelegateControlC = return ()++-- End no-op functions foreign import ccall unsafe "runInteractiveProcess" c_runInteractiveProcess
cbits/runProcess.c view
@@ -200,7 +200,9 @@ } // XXX Not the pipe for (i = 3; i < max_fd; i++) {- close(i);+ if (i != forkCommunicationFds[1]) {+ close(i);+ } } }
changelog.md view
@@ -1,5 +1,12 @@ # Changelog for [`process` package](http://hackage.haskell.org/package/process) +## 1.2.3.0 *March 2015*++ * [Meaningful error message when exe not found on close\_fds is+ True](https://ghc.haskell.org/trac/ghc/ticket/3649#comment:10)++ * New functions `readCreateProcess` and `readCreateProcessWithExitCode`+ ## 1.2.2.0 *Jan 2015* * Fix delegated CTRL-C handling in `createProcess` in case of failed
configure view
@@ -1,11 +1,13 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.69 for Haskell process package 1.0.+# Generated by GNU Autoconf 2.68 for Haskell process package 1.0. # # Report bugs to <libraries@haskell.org>. # #-# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,+# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software+# Foundation, Inc. # # # This configure script is free software; the Free Software Foundation@@ -134,31 +136,6 @@ # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# Use a proper internal environment variable to ensure we don't fall- # into an infinite loop, continuously re-executing ourselves.- if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then- _as_can_reexec=no; export _as_can_reexec;- # We cannot yet assume a decent shell, so we have to provide a-# neutralization value for shells without unset; and this also-# works around shells that cannot unset nonexistent variables.-# Preserve -v and -x to the replacement shell.-BASH_ENV=/dev/null-ENV=/dev/null-(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV-case $- in # ((((- *v*x* | *x*v* ) as_opts=-vx ;;- *v* ) as_opts=-v ;;- *x* ) as_opts=-x ;;- * ) as_opts= ;;-esac-exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}-# Admittedly, this is quite paranoid, since all the known shells bail-# out after a failed `exec'.-$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2-as_fn_exit 255- fi- # We don't want this to propagate to other subprocesses.- { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh@@ -192,8 +169,7 @@ else exitcode=1; echo positional parameters were not saved. fi-test x\$exitcode = x0 || exit 1-test -x / || exit 1"+test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&@@ -238,25 +214,21 @@ if test "x$CONFIG_SHELL" != x; then :- export CONFIG_SHELL- # We cannot yet assume a decent shell, so we have to provide a-# neutralization value for shells without unset; and this also-# works around shells that cannot unset nonexistent variables.-# Preserve -v and -x to the replacement shell.-BASH_ENV=/dev/null-ENV=/dev/null-(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV-case $- in # ((((- *v*x* | *x*v* ) as_opts=-vx ;;- *v* ) as_opts=-v ;;- *x* ) as_opts=-x ;;- * ) as_opts= ;;-esac-exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}-# Admittedly, this is quite paranoid, since all the known shells bail-# out after a failed `exec'.-$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2-exit 255+ # We cannot yet assume a decent shell, so we have to provide a+ # neutralization value for shells without unset; and this also+ # works around shells that cannot unset nonexistent variables.+ # Preserve -v and -x to the replacement shell.+ BASH_ENV=/dev/null+ ENV=/dev/null+ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV+ export CONFIG_SHELL+ case $- in # ((((+ *v*x* | *x*v* ) as_opts=-vx ;;+ *v* ) as_opts=-v ;;+ *x* ) as_opts=-x ;;+ * ) as_opts= ;;+ esac+ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then :@@ -359,14 +331,6 @@ } # as_fn_mkdir_p--# as_fn_executable_p FILE-# ------------------------# Test if FILE is an executable regular file.-as_fn_executable_p ()-{- test -f "$1" && test -x "$1"-} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take@@ -488,10 +452,6 @@ chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have- # already done that, so ensure we don't try to do so again and fall- # in an infinite loop. This has already happened in practice.- _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this).@@ -526,16 +486,16 @@ # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.- # In both cases, we have to default to `cp -pR'.+ # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||- as_ln_s='cp -pR'+ as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else- as_ln_s='cp -pR'+ as_ln_s='cp -p' fi else- as_ln_s='cp -pR'+ as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null@@ -547,8 +507,28 @@ as_mkdir_p=false fi -as_test_x='test -x'-as_executable_p=as_fn_executable_p+if test -x / >/dev/null 2>&1; then+ as_test_x='test -x'+else+ if ls -dL / >/dev/null 2>&1; then+ as_ls_L_option=L+ else+ as_ls_L_option=+ fi+ as_test_x='+ eval sh -c '\''+ if test -d "$1"; then+ test -d "$1/.";+ else+ case $1 in #(+ -*)set "./$1";;+ esac;+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((+ ???[sx]*):;;*)false;;esac;fi+ '\'' sh+ '+fi+as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"@@ -1141,6 +1121,8 @@ if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe+ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host.+ If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi@@ -1373,9 +1355,9 @@ if $ac_init_version; then cat <<\_ACEOF Haskell process package configure 1.0-generated by GNU Autoconf 2.69+generated by GNU Autoconf 2.68 -Copyright (C) 2012 Free Software Foundation, Inc.+Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF@@ -1706,7 +1688,7 @@ test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes ||- test -x conftest$ac_exeext+ $as_test_x conftest$ac_exeext }; then : ac_retval=0 else@@ -1809,8 +1791,7 @@ main () { static int test_array [1 - 2 * !(($2) >= 0)];-test_array [0] = 0;-return test_array [0];+test_array [0] = 0 ; return 0;@@ -1826,8 +1807,7 @@ main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)];-test_array [0] = 0;-return test_array [0];+test_array [0] = 0 ; return 0;@@ -1853,8 +1833,7 @@ main () { static int test_array [1 - 2 * !(($2) < 0)];-test_array [0] = 0;-return test_array [0];+test_array [0] = 0 ; return 0;@@ -1870,8 +1849,7 @@ main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)];-test_array [0] = 0;-return test_array [0];+test_array [0] = 0 ; return 0;@@ -1905,8 +1883,7 @@ main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)];-test_array [0] = 0;-return test_array [0];+test_array [0] = 0 ; return 0;@@ -1979,7 +1956,7 @@ running configure, to aid debugging if configure makes a mistake. It was created by Haskell process package $as_me 1.0, which was-generated by GNU Autoconf 2.69. Invocation command line was+generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ @@ -2361,7 +2338,7 @@ IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2@@ -2401,7 +2378,7 @@ IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2@@ -2454,7 +2431,7 @@ IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2@@ -2495,7 +2472,7 @@ IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue@@ -2553,7 +2530,7 @@ IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2@@ -2597,7 +2574,7 @@ IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2@@ -3043,7 +3020,8 @@ /* end confdefs.h. */ #include <stdarg.h> #include <stdio.h>-struct stat;+#include <sys/types.h>+#include <sys/stat.h> /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int);@@ -3284,7 +3262,7 @@ for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"- as_fn_executable_p "$ac_path_GREP" || continue+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in@@ -3350,7 +3328,7 @@ for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"- as_fn_executable_p "$ac_path_EGREP" || continue+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in@@ -4214,16 +4192,16 @@ # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.- # In both cases, we have to default to `cp -pR'.+ # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||- as_ln_s='cp -pR'+ as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else- as_ln_s='cp -pR'+ as_ln_s='cp -p' fi else- as_ln_s='cp -pR'+ as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null@@ -4283,16 +4261,28 @@ as_mkdir_p=false fi --# as_fn_executable_p FILE-# ------------------------# Test if FILE is an executable regular file.-as_fn_executable_p ()-{- test -f "$1" && test -x "$1"-} # as_fn_executable_p-as_test_x='test -x'-as_executable_p=as_fn_executable_p+if test -x / >/dev/null 2>&1; then+ as_test_x='test -x'+else+ if ls -dL / >/dev/null 2>&1; then+ as_ls_L_option=L+ else+ as_ls_L_option=+ fi+ as_test_x='+ eval sh -c '\''+ if test -d "$1"; then+ test -d "$1/.";+ else+ case $1 in #(+ -*)set "./$1";;+ esac;+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((+ ???[sx]*):;;*)false;;esac;fi+ '\'' sh+ '+fi+as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"@@ -4314,7 +4304,7 @@ # values after options handling. ac_log=" This file was extended by Haskell process package $as_me 1.0, which was-generated by GNU Autoconf 2.69. Invocation command line was+generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS@@ -4367,10 +4357,10 @@ ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ Haskell process package config.status 1.0-configured by $0, generated by GNU Autoconf 2.69,+configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" -Copyright (C) 2012 Free Software Foundation, Inc.+Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -4450,7 +4440,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then- set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion+ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL'
process.cabal view
@@ -1,5 +1,5 @@ name: process-version: 1.2.2.0+version: 1.2.3.0 -- NOTE: Don't forget to update ./changelog.md license: BSD3 license-file: LICENSE@@ -61,10 +61,18 @@ build-depends: base >= 4.4 && < 4.9, directory >= 1.1 && < 1.3,- filepath >= 1.2 && < 1.4,+ filepath >= 1.2 && < 1.5, deepseq >= 1.1 && < 1.5 if os(windows) build-depends: Win32 >=2.2 && < 2.4 extra-libraries: kernel32 else build-depends: unix >= 2.5 && < 2.8++test-suite test+ default-language: Haskell2010+ hs-source-dirs: test+ main-is: main.hs+ type: exitcode-stdio-1.0+ build-depends: base+ , process
+ test/main.hs view
@@ -0,0 +1,14 @@+import Control.Exception+import System.IO.Error+import System.Process++main :: IO ()+main = do+ res <- handle (return . Left . isDoesNotExistError) $ do+ (_, _, _, ph) <- createProcess (proc "definitelydoesnotexist" [])+ { close_fds = True+ }+ fmap Right $ waitForProcess ph+ case res of+ Left True -> return ()+ _ -> error $ show res