summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--stream.c79
-rw-r--r--txr.137
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 <kaz@kylheku.com>
+
+ * 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 <kaz@kylheku.com>
* 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 <system-command> [<mode-string>])
- (open-process <command> <mode-string> [<argument-strings>])
+ (open-process <program> <mode-string> [<argument-strings>])
.TP
Description:
@@ -12198,13 +12198,16 @@ The open-process function specifies a program to invoke via the <command>
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 <program>, 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 <mode-string> argument is compatible with the convention used by the POSIX
popen function.
The <argument-strings> argument is a list of strings which specifies additional
-optional arguments to be passed passed to the program. The <command> argument
+optional arguments to be passed passed to the program. The <program> argument
becomes the first argument, and <argument-strings> become the second and
subsequent arguments. If <argument-strings> is omitted, it defaults to empty.
@@ -12858,7 +12861,6 @@ into the filesystem at <path> 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 <path> 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 <system-command>)
+ (run <program> [<argument-list>])
+
+.TP
+Description:
+
+The sh function executes <system-command> using the system command interpreter.
+The run function spawns a <program>, 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