From 5251ac1ca49b5abc0d68f77291eb463f2040eb31 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Thu, 13 Mar 2014 01:07:49 -0700 Subject: * stream.c (open_process): If execvp fails, use errno as the exit status. (sh, run): New static functions. (stream_init): sh and run registered as intrinsics. --- ChangeLog | 7 ++++++ stream.c | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- txr.1 | 37 ++++++++++++++++++++++++++---- 3 files changed, 118 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index e8ddb285..5ec29e8d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2014-03-13 Kaz Kylheku + + * stream.c (open_process): If execvp fails, use errno as the + exit status. + (sh, run): New static functions. + (stream_init): sh and run registered as intrinsics. + 2014-03-12 Kaz Kylheku * lib.c (iffi): Bugfix: was still using incorrect, outdated optional diff --git a/stream.c b/stream.c index 8e049212..1744c28c 100644 --- a/stream.c +++ b/stream.c @@ -2176,7 +2176,7 @@ val open_process(val name, val mode_str, val args) } execvp(utf8name, argv); - _exit(1); + _exit(errno); } else { int whichfd; char *utf8mode = utf8_dup_to(c_str(mode_str)); @@ -2271,6 +2271,81 @@ val open_process(val name, val mode_str, val args) } #endif +static val sh(val command) +{ + char *cmd = utf8_dup_to(c_str(command)); + int status = system(cmd); + if (status < 0) + return nil; +#if HAVE_SYS_WAIT + if (WIFEXITED(status)) { + int exitstatus = WEXITSTATUS(status); + return num(exitstatus); + } +#endif + return status == 0 ? zero : nil; +} + +#if HAVE_FORK_STUFF +static val run(val name, val args) +{ + pid_t pid; + char **argv = 0, *utf8name = 0; + val iter; + int i, nargs; + + args = default_bool_arg(args); + nargs = c_num(length(args)) + 1; + + argv = (char **) chk_malloc((nargs + 2) * sizeof *argv); + + for (i = 0, iter = cons(name, args); iter; i++, iter = cdr(iter)) { + val arg = car(iter); + argv[i] = utf8_dup_to(c_str(arg)); + } + argv[i] = 0; + + utf8name = utf8_dup_to(c_str(name)); + + pid = fork(); + + if (pid == -1) { + for (i = 0; i < nargs; i++) + free(argv[i]); + free(argv); + uw_throwf(file_error_s, lit("opening process ~a, fork syscall failed: ~a/~s"), + name, num(errno), string_utf8(strerror(errno)), nao); + } + + if (pid == 0) { + execvp(utf8name, argv); + _exit(errno); + } else { + int status; + for (i = 0; i < nargs; i++) + free(argv[i]); + free(argv); + while (waitpid(pid, &status, 0) == -1 && errno == EINTR) + ; + if (status < 0) + return nil; +#if HAVE_SYS_WAIT + if (WIFEXITED(status)) { + int exitstatus = WEXITSTATUS(status); + return num(exitstatus); + } +#endif + return status == 0 ? zero : nil; + } +} +#else +static val run(val command, val args) +{ + val win_cmdline = win_make_cmdline(cons(name, args)); + return sh(win_cmdline); +} +#endif + static void cat_stream_print(val stream, val out) { val streams = (val) stream->co.handle; @@ -2748,6 +2823,8 @@ void stream_init(void) reg_fun(intern(lit("open-command"), user_package), func_n2o(open_command, 1)); reg_fun(intern(lit("open-pipe"), user_package), func_n2(open_command)); reg_fun(intern(lit("open-process"), user_package), func_n3o(open_process, 2)); + reg_fun(intern(lit("sh"), user_package), func_n1(sh)); + reg_fun(intern(lit("run"), user_package), func_n2o(run, 1)); reg_fun(intern(lit("remove-path"), user_package), func_n1(remove_path)); reg_fun(intern(lit("rename-path"), user_package), func_n2(rename_path)); reg_fun(intern(lit("open-files"), user_package), func_n2o(open_files, 1)); diff --git a/txr.1 b/txr.1 index b3908121..0d278fef 100644 --- a/txr.1 +++ b/txr.1 @@ -12177,7 +12177,7 @@ Description: Syntax: (open-command []) - (open-process []) + (open-process []) .TP Description: @@ -12198,13 +12198,16 @@ The open-process function specifies a program to invoke via the argument. This is subject the the operating system's search strategy. On POSIX systems, if it is an absolute or relative path, it is treated as such, but if it is a simple base name, then it is subject to searching -via the components of the PATH environment variable. +via the components of the PATH environment variable. If open-process +is not able to find , or is otherwise unable to execute +the proram, the child process will exit, using the value of errno +as its exit status. This value can be retrieved via close-stream. The argument is compatible with the convention used by the POSIX popen function. The argument is a list of strings which specifies additional -optional arguments to be passed passed to the program. The argument +optional arguments to be passed passed to the program. The argument becomes the first argument, and become the second and subsequent arguments. If is omitted, it defaults to empty. @@ -12858,7 +12861,6 @@ into the filesystem at also. If these functions succeed, they return t. Otherwise they throw an exception of type file-error. - .SS Function readlink .TP @@ -12873,6 +12875,33 @@ If names a filesystem object which is a symbolic link, the readlink function reads the contents of that symbolic link and returns it as a string. Otherwise, it fails by throwing an exception of type file-error. +.SS Functions sh and run + +.TP +Syntax: + + (sh ) + (run []) + +.TP +Description: + +The sh function executes using the system command interpreter. +The run function spawns a , searching for it using the +system PATH. Using either method, the executed process receives environment +variables from the parent. + +TXR blocks until the process finishes executing. If the program terminates +normally, then its integer exit status is returned. The value zero indicates +successful termination. + +The return value nil indicates an abnormal termination, or the inability +to run the process at all. + +In the case of the run function, if the child process is created successfully +but the proram cannot be executed, then the exit status will be an errno value +from the failed exec attempt. + .SH UNIX SIGNAL HANDLING On platforms where certain advanced features of POSIX signal handling are -- cgit v1.2.3