aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog25
-rw-r--r--configure.in2
-rw-r--r--intern.h15
-rw-r--r--io.c312
-rw-r--r--process.c410
-rw-r--r--win32/win32.c170
-rw-r--r--win32/win32.h8
7 files changed, 549 insertions, 393 deletions
diff --git a/ChangeLog b/ChangeLog
index a59ded30c7..a4d12fc85e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+Mon Feb 16 15:45:22 2004 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * configure.in: check functions, fork spawnv.
+
+ * io.c (rb_io_s_popen): accept argv not only single command line.
+
+ * process.c (rb_proc_exec_n): export.
+
+ * process.c (rb_check_argv): check if arguments are safe to invoke.
+
+ * process.c (rb_fork): retry to fork.
+
+ * process.c (rb_spawn): spawn child process asynchronously.
+
+ * process.c (rb_f_system): raise an exception if the command could not
+ execute.
+
+ * win32/win32.c (rb_w32_argv_size): count necessary size for joined
+ arguments.
+
+ * win32/win32.c (rb_w32_join_argv): join arguments with quoting.
+
+ * win32/win32.c (rb_w32_pipe_exec, rb_w32_spawn, rb_w32_aspawn):
+ accept program name adding to command line.
+
Mon Feb 16 15:18:33 2004 Minero Aoki <aamine@loveruby.net>
* lib/racc/parser.rb: add note for Racc full package.
diff --git a/configure.in b/configure.in
index 012cb86acb..9ea1bb1efb 100644
--- a/configure.in
+++ b/configure.in
@@ -392,7 +392,7 @@ AC_CHECK_FUNCS(ftello)
AC_REPLACE_FUNCS(dup2 memmove mkdir strcasecmp strncasecmp strerror strftime\
strchr strstr strtoul crypt flock vsnprintf\
isnan finite isinf hypot acosh erf)
-AC_CHECK_FUNCS(fmod killpg wait4 waitpid syscall chroot fsync getcwd\
+AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall chroot fsync getcwd\
truncate chsize times utimes fcntl lockf lstat link symlink readlink\
setitimer setruid seteuid setreuid setresuid setproctitle\
setrgid setegid setregid setresgid issetugid pause lchown lchmod\
diff --git a/intern.h b/intern.h
index 5a22b8caea..9bfa69b28d 100644
--- a/intern.h
+++ b/intern.h
@@ -12,6 +12,9 @@
**********************************************************************/
+#ifndef RUBY_INTERN_H
+#define RUBY_INTERN_H 1
+
/*
* Functions and variables that are used by more than one source file of
* the kernel.
@@ -335,10 +338,20 @@ VALUE rb_lastline_get _((void));
void rb_lastline_set _((VALUE));
VALUE rb_sym_all_symbols _((void));
/* process.c */
+struct rb_exec_arg {
+ int argc;
+ VALUE *argv;
+ const char *prog;
+};
+int rb_proc_exec_n _((int, VALUE*, const char*));
int rb_proc_exec _((const char*));
+VALUE rb_check_argv _((int, VALUE*));
+int rb_exec _((const struct rb_exec_arg*));
+int rb_fork _((int*, int (*)_((void*)), void*));
VALUE rb_f_exec _((int,VALUE*));
int rb_waitpid _((int,int*,int));
void rb_syswait _((int));
+int rb_spawn _((int, VALUE*));
VALUE rb_proc_times _((VALUE));
VALUE rb_detach_process _((int));
/* range.c */
@@ -486,3 +499,5 @@ VALUE rb_mod_remove_cvar _((VALUE, VALUE));
/* version.c */
void ruby_show_version _((void));
void ruby_show_copyright _((void));
+
+#endif /* RUBY_INTERN_H */
diff --git a/io.c b/io.c
index aeba25ad46..8e1a58bef6 100644
--- a/io.c
+++ b/io.c
@@ -2448,7 +2448,7 @@ rb_file_sysopen(fname, flags, mode)
return rb_file_sysopen_internal(io_alloc(rb_cFile), fname, flags, mode);
}
-#if defined (_WIN32) || defined(DJGPP) || defined(__CYGWIN__) || defined(__human68k__) || defined(__VMS)
+#if defined(__CYGWIN__) || !defined(HAVE_FORK)
static struct pipe_list {
OpenFile *fptr;
struct pipe_list *next;
@@ -2510,7 +2510,7 @@ pipe_finalize(fptr, noraise)
OpenFile *fptr;
int noraise;
{
-#if !defined (__CYGWIN__) && !defined(_WIN32)
+#if !defined(HAVE_FORK) && !defined(_WIN32)
extern VALUE rb_last_status;
int status;
if (fptr->f) {
@@ -2545,152 +2545,178 @@ rb_io_unbuffered(fptr)
rb_io_synchronized(fptr);
}
-static VALUE
-pipe_open(pname, mode)
- char *pname, *mode;
-{
- int modef = rb_io_mode_flags(mode);
- OpenFile *fptr;
-
-#if defined(DJGPP) || defined(__human68k__) || defined(__VMS)
- FILE *f = popen(pname, mode);
+struct popen_arg {
+ struct rb_exec_arg exec;
+ int pr[2], pw[2];
+};
- if (!f) rb_sys_fail(pname);
- else {
- VALUE port = io_alloc(rb_cIO);
-
- MakeOpenFile(port, fptr);
- fptr->finalize = pipe_finalize;
- fptr->mode = modef;
-
- pipe_add_fptr(fptr);
- if (modef & FMODE_READABLE) fptr->f = f;
- if (modef & FMODE_WRITABLE) {
- if (fptr->f) fptr->f2 = f;
- else fptr->f = f;
- rb_io_synchronized(fptr);
+static void
+popen_redirect(p)
+ struct popen_arg *p;
+{
+ if (p->pr[1] != -1) {
+ close(p->pr[0]);
+ if (p->pr[1] != 1) {
+ dup2(p->pr[1], 1);
+ close(p->pr[1]);
}
- return (VALUE)port;
}
-#else
-#ifdef _WIN32
- int pid;
- FILE *fpr, *fpw;
-
-retry:
- pid = pipe_exec(pname, rb_io_mode_modenum(mode), &fpr, &fpw);
- if (pid == -1) { /* exec failed */
- if (errno == EAGAIN) {
- rb_thread_sleep(1);
- goto retry;
+ if (p->pw[0] != -1) {
+ close(p->pw[1]);
+ if (p->pw[0] != 0) {
+ dup2(p->pw[0], 0);
+ close(p->pw[0]);
}
- rb_sys_fail(pname);
}
- else {
- VALUE port = io_alloc(rb_cIO);
+}
- MakeOpenFile(port, fptr);
- fptr->mode = modef;
- fptr->mode |= FMODE_SYNC;
- fptr->pid = pid;
+#ifdef HAVE_FORK
+static int
+popen_exec(p)
+ struct popen_arg *p;
+{
+ int fd;
- if (modef & FMODE_READABLE) {
- fptr->f = fpr;
- }
- if (modef & FMODE_WRITABLE) {
- if (fptr->f) fptr->f2 = fpw;
- else fptr->f = fpw;
- }
- fptr->finalize = pipe_finalize;
- pipe_add_fptr(fptr);
- return (VALUE)port;
- }
+ popen_redirect(p);
+ for (fd = 3; fd < NOFILE; fd++) {
+#ifdef FD_CLOEXEC
+ fcntl(fd, F_SETFL, FD_CLOEXEC);
#else
- int pid, pr[2], pw[2];
+ close(fd);
+#endif
+ }
+ return rb_exec(&p->exec);
+}
+#endif
+
+static VALUE
+pipe_open(argc, argv, pname, mode)
+ int argc;
+ VALUE *argv;
+ char *pname, *mode;
+{
+ int modef = rb_io_mode_flags(mode);
+ int pid = 0;
+ OpenFile *fptr;
+ FILE *fpr, *fpw;
+ VALUE port, arg0;
+#if defined(HAVE_FORK)
+ int status;
+ struct popen_arg arg;
volatile int doexec;
+#elif defined(_WIN32)
+ int openmode = rb_io_mode_modenum(mode);
+ char *cmd = pname, *prog = NULL;
+#endif
- if (((modef & FMODE_READABLE) && pipe(pr) == -1) ||
- ((modef & FMODE_WRITABLE) && pipe(pw) == -1))
- rb_sys_fail(pname);
+ if (!pname) {
+ arg0 = rb_check_argv(argc, argv);
+ if (arg0) pname = StringValuePtr(arg0);
+ }
- doexec = (strcmp("-", pname) != 0);
+#if defined(HAVE_FORK)
+ doexec = (argc > 0) || (strcmp("-", pname) != 0);
if (!doexec) {
fflush(stdin); /* is it really needed? */
fflush(stdout);
fflush(stderr);
}
-
- retry:
- switch ((pid = fork())) {
- case 0: /* child */
+ arg.pr[0] = arg.pr[1] = arg.pw[0] = arg.pw[1] = -1;
+ if ((modef & FMODE_READABLE) && pipe(arg.pr) == -1) {
+ rb_sys_fail(pname);
+ }
+ if ((modef & FMODE_WRITABLE) && pipe(arg.pw) == -1) {
if (modef & FMODE_READABLE) {
- close(pr[0]);
- if (pr[1] != 1) {
- dup2(pr[1], 1);
- close(pr[1]);
- }
+ int e = errno;
+ close(arg.pr[0]); close(arg.pr[1]);
+ errno = e;
}
- if (modef & FMODE_WRITABLE) {
- close(pw[1]);
- if (pw[0] != 0) {
- dup2(pw[0], 0);
- close(pw[0]);
- }
+ rb_sys_fail(pname);
+ }
+
+ if (doexec) {
+ arg.exec.argc = argc;
+ arg.exec.argv = argv;
+ arg.exec.prog = pname;
+ pid = rb_fork(&status, popen_exec, &arg);
+ }
+ else {
+ pid = rb_fork(&status, 0, 0);
+ if (pid == 0) { /* child */
+ popen_redirect(&arg);
+ rb_io_synchronized(RFILE(orig_stdout)->fptr);
+ rb_io_synchronized(RFILE(orig_stderr)->fptr);
+ return Qnil;
}
+ }
- if (doexec) {
- int fd;
+ /* parent */
+ if (modef & FMODE_READABLE) close(arg.pr[1]);
+ if (modef & FMODE_WRITABLE) close(arg.pw[0]);
+ if (pid == -1) {
+ if (modef & FMODE_READABLE) close(arg.pr[0]);
+ if (modef & FMODE_WRITABLE) close(arg.pw[1]);
+ rb_sys_fail(pname);
+ }
+#define PIPE_FDOPEN(i) (rb_fdopen((i?arg.pw:arg.pr)[i], i?"w":"r"))
+#elif defined(_WIN32)
+ if (argc) {
+ char **args = ALLOC_N(char *, argc+1);
+ int i;
- for (fd = 3; fd < NOFILE; fd++)
- close(fd);
- rb_proc_exec(pname);
- fprintf(stderr, "%s:%d: command not found: %s\n",
- ruby_sourcefile, ruby_sourceline, pname);
- _exit(127);
+ for (i = 0; i < argc; ++i) {
+ args[i] = RSTRING(argv[i])->ptr;
}
- rb_io_synchronized(RFILE(orig_stdout)->fptr);
- rb_io_synchronized(RFILE(orig_stderr)->fptr);
- return Qnil;
-
- case -1: /* fork failed */
- if (errno == EAGAIN) {
+ args[i] = NULL;
+ cmd = ALLOCA_N(char, rb_w32_argv_size(args));
+ rb_w32_join_argv(cmd, args);
+ free(args);
+ prog = pname;
+ }
+ while ((pid = rb_w32_pipe_exec(cmd, prog, openmode, &fpr, &fpw)) == -1) {
+ /* exec failed */
+ switch (errno) {
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
rb_thread_sleep(1);
- goto retry;
+ break;
+ defined:
+ rb_sys_fail(pname);
+ break;
}
- close(pr[0]); close(pw[1]);
- rb_sys_fail(pname);
- break;
-
- default: /* parent */
- if (pid < 0) rb_sys_fail(pname);
- else {
- VALUE port = io_alloc(rb_cIO);
+ }
+#define PIPE_FDOPEN(i) (i?fpw:fpr)
+#else
+ if (argc > 0) {
+ prog = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
+ pname = StringValuePtr(prog);
+ }
+ fpr = popen(pname, mode);
- MakeOpenFile(port, fptr);
- fptr->mode = modef;
- fptr->mode |= FMODE_SYNC;
- fptr->pid = pid;
+ if (!fpr) rb_sys_fail(pname);
+#define PIPE_FDOPEN(i) (fpr)
+#endif
- if (modef & FMODE_READABLE) {
- close(pr[1]);
- fptr->f = rb_fdopen(pr[0], "r");
- }
- if (modef & FMODE_WRITABLE) {
- FILE *f = rb_fdopen(pw[1], "w");
+ port = io_alloc(rb_cIO);
+ MakeOpenFile(port, fptr);
+ fptr->mode = modef | FMODE_SYNC;
+ fptr->pid = pid;
- close(pw[0]);
- if (fptr->f) fptr->f2 = f;
- else fptr->f = f;
- }
-#if defined (__CYGWIN__)
- fptr->finalize = pipe_finalize;
- pipe_add_fptr(fptr);
-#endif
- return port;
- }
+ if (modef & FMODE_READABLE) {
+ fptr->f = PIPE_FDOPEN(0);
}
+ if (modef & FMODE_WRITABLE) {
+ fpw = PIPE_FDOPEN(1);
+ if (fptr->f) fptr->f2 = fpw;
+ else fptr->f = fpw;
+ }
+#if defined (__CYGWIN__) || !defined(HAVE_FORK)
+ fptr->finalize = pipe_finalize;
+ pipe_add_fptr(fptr);
#endif
-#endif
+ return port;
}
static VALUE
@@ -2714,7 +2740,7 @@ rb_io_popen(str, argc, argv, klass)
mode = StringValuePtr(pmode);
}
SafeStringValue(pname);
- port = pipe_open(str, mode);
+ port = pipe_open(0, 0, str, mode);
if (NIL_P(port)) {
/* child */
if (rb_block_given_p()) {
@@ -2779,12 +2805,46 @@ rb_io_s_popen(argc, argv, klass)
VALUE *argv;
VALUE klass;
{
- char *str = 0;
+ char *mode;
+ VALUE pname, pmode, port, tmp;
+ char mbuf[4];
- if (argc >= 1) {
- str = StringValuePtr(argv[0]);
+ if (rb_scan_args(argc, argv, "11", &pname, &pmode) == 1) {
+ mode = "r";
+ }
+ else if (FIXNUM_P(pmode)) {
+ mode = rb_io_modenum_mode(FIX2INT(pmode), mbuf);
}
- return rb_io_popen(str, argc, argv, klass);
+ else {
+ mode = StringValuePtr(pmode);
+ }
+ tmp = rb_check_array_type(pname);
+ if (!NIL_P(tmp)) {
+ long argc = RARRAY(tmp)->len;
+ VALUE *argv = ALLOCA_N(VALUE, argc);
+
+ MEMCPY(argv, RARRAY(tmp)->ptr, VALUE, argc);
+ port = pipe_open(argc, argv, 0, mode);
+ }
+ else {
+ SafeStringValue(pname);
+ port = pipe_open(0, 0, RSTRING(pname)->ptr, mode);
+ if (NIL_P(port)) {
+ /* child */
+ if (rb_block_given_p()) {
+ rb_yield(Qnil);
+ fflush(stdout);
+ fflush(stderr);
+ _exit(0);
+ }
+ return Qnil;
+ }
+ }
+ RBASIC(port)->klass = klass;
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, port, io_close, port);
+ }
+ return port;
}
static VALUE
@@ -2985,7 +3045,7 @@ rb_io_open(fname, mode)
char *fname, *mode;
{
if (fname[0] == '|') {
- return pipe_open(fname+1, mode);
+ return pipe_open(0, 0, fname+1, mode);
}
else {
return rb_file_open(fname, mode);
@@ -4179,7 +4239,7 @@ rb_f_backquote(obj, str)
OpenFile *fptr;
SafeStringValue(str);
- port = pipe_open(RSTRING(str)->ptr, "r");
+ port = pipe_open(0, 0, RSTRING(str)->ptr, "r");
if (NIL_P(port)) return rb_str_new(0,0);
GetOpenFile(port, fptr);
diff --git a/process.c b/process.c
index 697b7c3a84..18c46c2659 100644
--- a/process.c
+++ b/process.c
@@ -23,6 +23,9 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
#ifdef __DJGPP__
#include <process.h>
#endif
@@ -912,12 +915,16 @@ proc_exec_v(argv, prog)
char **argv;
char *prog;
{
+ int err;
+
if (!prog)
prog = argv[0];
security(prog);
prog = dln_find_exe(prog, 0);
- if (!prog)
+ if (!prog) {
+ errno = ENOENT;
return -1;
+ }
#if (defined(MSDOS) && !defined(DJGPP)) || defined(__human68k__) || defined(__EMX__) || defined(OS2)
{
@@ -958,26 +965,23 @@ proc_exec_v(argv, prog)
#endif /* MSDOS or __human68k__ or __EMX__ */
before_exec();
execv(prog, argv);
+ err = errno;
after_exec();
+ errno = err;
return -1;
}
-static int
-proc_exec_n(argc, argv, progv)
+int
+rb_proc_exec_n(argc, argv, prog)
int argc;
VALUE *argv;
- VALUE progv;
+ const char *prog;
{
- char *prog = 0;
char **args;
int i;
- if (progv) {
- prog = RSTRING(progv)->ptr;
- }
args = ALLOCA_N(char*, argc+1);
for (i=0; i<argc; i++) {
- SafeStringValue(argv[i]);
args[i] = RSTRING(argv[i])->ptr;
}
args[i] = 0;
@@ -1000,22 +1004,21 @@ rb_proc_exec(str)
#ifdef _WIN32
before_exec();
- do_spawn(P_OVERLAY, (char *)str);
+ rb_w32_spawn(P_OVERLAY, (char *)str, 0);
after_exec();
#else
for (s=str; *s; s++) {
if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
-#if defined(MSDOS)
int status;
+#if defined(MSDOS)
before_exec();
status = system(str);
after_exec();
if (status != -1)
exit(status);
-#else
-#if defined(__human68k__) || defined(__CYGWIN32__) || defined(__EMX__)
+#elif defined(__human68k__) || defined(__CYGWIN32__) || defined(__EMX__)
char *shell = dln_find_exe("sh", 0);
- int status = -1;
+ status = -1;
before_exec();
if (shell)
execl(shell, "sh", "-c", str, (char *) NULL);
@@ -1027,8 +1030,9 @@ rb_proc_exec(str)
#else
before_exec();
execl("/bin/sh", "sh", "-c", str, (char *)NULL);
+ status = errno;
after_exec();
-#endif
+ errno = status;
#endif
return -1;
}
@@ -1050,12 +1054,20 @@ rb_proc_exec(str)
return -1;
}
-#if defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32)
+#if defined(_WIN32)
+#define HAVE_SPAWNV 1
+#endif
+
+#if !defined(HAVE_FORK) && defined(HAVE_SPAWNV)
static int
proc_spawn_v(argv, prog)
char **argv;
char *prog;
{
+#if defined(_WIN32)
+ char *cmd = ALLOCA_N(char, rb_w32_argv_size(argv));
+ return rb_w32_spawn(P_NOWAIT, rb_w32_join_argv(cmd, argv), prog);
+#else
char *extension;
int status;
@@ -1091,13 +1103,10 @@ proc_spawn_v(argv, prog)
}
#endif
before_exec();
-#if defined(_WIN32)
- status = do_aspawn(P_WAIT, prog, argv);
-#else
status = spawnv(P_WAIT, prog, argv);
-#endif
after_exec();
return status;
+#endif
}
static int
@@ -1111,29 +1120,25 @@ proc_spawn_n(argc, argv, prog)
args = ALLOCA_N(char*, argc + 1);
for (i = 0; i < argc; i++) {
- SafeStringValue(argv[i]);
args[i] = RSTRING(argv[i])->ptr;
}
- if (prog)
- SafeStringValue(prog);
args[i] = (char*) 0;
if (args[0])
return proc_spawn_v(args, prog ? RSTRING(prog)->ptr : 0);
return -1;
}
-#if !defined(_WIN32)
+#if defined(_WIN32)
+#define proc_spawn(str) rb_w32_spawn(P_NOWAIT, str, 0)
+#else
static int
-proc_spawn(sv)
- VALUE sv;
-{
+proc_spawn(str)
char *str;
+{
char *s, *t;
char **argv, **a;
int status;
- SafeStringValue(sv);
- str = s = RSTRING(sv)->ptr;
for (s = str; *s; s++) {
if (*s != ' ' && !ISALPHA(*s) && strchr("*?{}[]<>()~&|\\$;'`\"\n",*s)) {
char *shell = dln_find_exe("sh", 0);
@@ -1156,6 +1161,35 @@ proc_spawn(sv)
#endif
#endif
+VALUE
+rb_check_argv(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE tmp, prog;
+ int i;
+
+ if (argc == 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+
+ prog = 0;
+ tmp = rb_check_array_type(argv[0]);
+ if (!NIL_P(tmp)) {
+ if (RARRAY(tmp)->len != 2) {
+ rb_raise(rb_eArgError, "wrong first argument");
+ }
+ prog = RARRAY(tmp)->ptr[0];
+ SafeStringValue(prog);
+ argv[0] = RARRAY(tmp)->ptr[1];
+ }
+ for (i = 0; i < argc; i++) {
+ SafeStringValue(argv[i]);
+ }
+ security(RSTRING(prog ? prog : argv[0])->ptr);
+ return prog;
+}
+
/*
* call-seq:
* exec(command [, arg, ...])
@@ -1186,35 +1220,116 @@ rb_f_exec(argc, argv)
int argc;
VALUE *argv;
{
- VALUE prog = 0;
- VALUE tmp;
+ struct rb_exec_arg e;
+ VALUE prog;
- if (argc == 0) {
- rb_raise(rb_eArgError, "wrong number of arguments");
+ prog = rb_check_argv(argc, argv);
+ if (!prog && argc == 1) {
+ --argc;
+ prog = *argv++;
}
+ e.argc = argc;
+ e.argv = argv;
+ e.prog = prog ? RSTRING(prog)->ptr : 0;
+ rb_exec(&e);
+ rb_sys_fail(RSTRING(argv[0])->ptr);
+ return Qnil; /* dummy */
+}
- tmp = rb_check_array_type(argv[0]);
- if (!NIL_P(tmp)) {
- if (RARRAY(tmp)->len != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- prog = RARRAY(tmp)->ptr[0];
- SafeStringValue(prog);
- argv[0] = RARRAY(tmp)->ptr[1];
- }
- if (argc == 1 && prog == 0) {
- VALUE cmd = argv[0];
+int
+rb_exec(e)
+ const struct rb_exec_arg *e;
+{
+ int argc = e->argc;
+ VALUE *argv = e->argv;
+ const char *prog = e->prog;
- SafeStringValue(cmd);
- rb_proc_exec(RSTRING(cmd)->ptr);
+ if (argc == 0) {
+ rb_proc_exec(prog);
}
else {
- proc_exec_n(argc, argv, prog);
+ rb_proc_exec_n(argc, argv, prog);
}
- rb_sys_fail(RSTRING(argv[0])->ptr);
- return Qnil; /* dummy */
+ return errno;
}
+#ifdef HAVE_FORK
+int
+rb_fork(status, chfunc, charg)
+ int *status;
+ int (*chfunc) _((void *));
+ void *charg;
+{
+ int pid, err, state = 0, ep[2];
+
+#ifndef __VMS
+ fflush(stdout);
+ fflush(stderr);
+#endif
+
+#ifdef FD_CLOEXEC
+ if (chfunc) {
+ if (pipe(ep)) return -1;
+ if (fcntl(ep[0], F_SETFD, FD_CLOEXEC) ||
+ fcntl(ep[1], F_SETFD, FD_CLOEXEC)) {
+ err = errno;
+ close(ep[0]);
+ close(ep[1]);
+ errno = err;
+ return -1;
+ }
+ }
+#endif
+ while ((pid = fork()) < 0) {
+ switch (errno) {
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ if (!status && !chfunc) {
+ rb_thread_sleep(1);
+ continue;
+ }
+ else {
+ rb_protect((VALUE (*)())rb_thread_sleep, 1, &state);
+ if (status) *status = state;
+ if (!state) continue;
+ }
+ default:
+#ifdef FD_CLOEXEC
+ if (chfunc) {
+ err = errno;
+ close(ep[0]);
+ close(ep[1]);
+ errno = err;
+ }
+#endif
+ if (state && !status) rb_jump_tag(state);
+ return -1;
+ }
+ }
+ if (!pid) {
+ if (chfunc) {
+ err = (*chfunc)(charg);
+ write(ep[1], &err, sizeof(err));
+ _exit(127);
+ }
+ }
+ else if (chfunc) {
+ close(ep[1]);
+ if ((state = read(ep[0], &err, sizeof(err))) < 0) {
+ err = errno;
+ }
+ close(ep[0]);
+ if (state) {
+ rb_syswait(pid);
+ errno = err;
+ return -1;
+ }
+ }
+ return pid;
+}
+#endif
/*
* call-seq:
@@ -1227,11 +1342,12 @@ static VALUE
rb_f_fork(obj)
VALUE obj;
{
-#if !defined(__human68k__) && !defined(_WIN32) && !defined(__MACOS__) && !defined(__EMX__) && !defined(__VMS)
+#ifdef HAVE_FORK
int pid;
rb_secure(2);
- switch (pid = fork()) {
+
+ switch (pid = rb_fork(0, 0, 0)) {
case 0:
#ifdef linux
after_exec();
@@ -1337,6 +1453,48 @@ rb_syswait(pid)
}
}
+int
+rb_spawn(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int status;
+ VALUE prog;
+#if defined HAVE_FORK
+ int pid;
+ struct rb_exec_arg earg;
+#endif
+
+ prog = rb_check_argv(argc, argv);
+
+ if (!prog && argc == 1) {
+ --argc;
+ prog = *argv++;
+ }
+#if defined HAVE_FORK
+ earg.argc = argc;
+ earg.argv = argv;
+ earg.prog = prog ? RSTRING(prog)->ptr : 0;
+ status = rb_fork(&status, (int (*)_((void*)))rb_exec, &earg);
+#elif defined HAVE_SPAWNV
+ if (!argc) {
+ status = proc_spawn(RSTRING(prog)->ptr);
+ }
+ else {
+ status = proc_spawn_n(argc, argv, prog);
+ }
+#else
+ prog = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
+ status = system(StringValuePtr(prog));
+# if defined(__human68k__) || defined(__DJGPP__)
+ last_status_set(status == -1 ? 127 : status, 0);
+# else
+ last_status_set((status & 0xff) << 8, 0);
+# endif
+#endif
+ return status;
+}
+
/*
* call-seq:
* system(cmd [, arg, ...]) => true or false
@@ -1362,135 +1520,39 @@ rb_f_system(argc, argv)
VALUE *argv;
{
int status;
-#if defined(__EMX__)
- VALUE cmd;
-
- fflush(stdout);
- fflush(stderr);
- if (argc == 0) {
- rb_last_status = Qnil;
- rb_raise(rb_eArgError, "wrong number of arguments");
- }
-
- if (TYPE(argv[0]) == T_ARRAY) {
- if (RARRAY(argv[0])->len != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- argv[0] = RARRAY(argv[0])->ptr[0];
- }
- cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
-
- SafeStringValue(cmd);
- status = do_spawn(RSTRING(cmd)->ptr);
- last_status_set(status, 0);
-#elif defined(__human68k__) || defined(__DJGPP__) || defined(_WIN32)
- volatile VALUE prog = 0;
-
- fflush(stdout);
- fflush(stderr);
- if (argc == 0) {
- rb_last_status = Qnil;
- rb_raise(rb_eArgError, "wrong number of arguments");
- }
- if (TYPE(argv[0]) == T_ARRAY) {
- if (RARRAY(argv[0])->len != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- prog = RARRAY(argv[0])->ptr[0];
- argv[0] = RARRAY(argv[0])->ptr[1];
- }
-
- if (argc == 1 && prog == 0) {
-#if defined(_WIN32)
- SafeStringValue(argv[0]);
- status = do_spawn(P_WAIT, RSTRING(argv[0])->ptr);
-#else
- status = proc_spawn(argv[0]);
-#endif
- }
- else {
- status = proc_spawn_n(argc, argv, prog);
- }
-#if defined(_WIN32)
- last_status_set(status, 0);
-#else
- last_status_set(status == -1 ? 127 : status, 0);
+ status = rb_spawn(argc, argv);
+ if (status == -1) rb_sys_fail(RSTRING(argv[0])->ptr);
+#if defined(HAVE_FORK) || defined(HAVE_SPAWNV)
+ rb_syswait(status);
+ status = NUM2INT(rb_last_status);
#endif
-#elif defined(__VMS)
- VALUE cmd;
-
- if (argc == 0) {
- rb_last_status = Qnil;
- rb_raise(rb_eArgError, "wrong number of arguments");
- }
+ if (status == EXIT_SUCCESS) return Qtrue;
+ return Qfalse;
+}
- if (TYPE(argv[0]) == T_ARRAY) {
- if (RARRAY(argv[0])->len != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- argv[0] = RARRAY(argv[0])->ptr[0];
- }
- cmd = rb_ary_join(rb_ary_new4(argc, argv), rb_str_new2(" "));
+/*
+ * call-seq:
+ * spawn(cmd [, arg, ...]) => pid
+ *
+ * Similar to <code>Kernel::system</code> except for not waiting for
+ * end of _cmd_, but returns its <i>pid</i>.
+ */
- SafeStringValue(cmd);
- status = system(RSTRING(cmd)->ptr);
- last_status_set((status & 0xff) << 8, 0);
-#else
- volatile VALUE prog = 0;
+static VALUE
+rb_f_spawn(argc, argv)
+ int argc;
+ VALUE *argv;
+{
int pid;
- int i;
- fflush(stdout);
- fflush(stderr);
- if (argc == 0) {
- rb_last_status = Qnil;
- rb_raise(rb_eArgError, "wrong number of arguments");
- }
-
- if (TYPE(argv[0]) == T_ARRAY) {
- if (RARRAY(argv[0])->len != 2) {
- rb_raise(rb_eArgError, "wrong first argument");
- }
- prog = RARRAY(argv[0])->ptr[0];
- argv[0] = RARRAY(argv[0])->ptr[1];
- }
-
- if (prog) {
- SafeStringValue(prog);
- }
- for (i = 0; i < argc; i++) {
- SafeStringValue(argv[i]);
- }
- retry:
- switch (pid = fork()) {
- case 0:
- if (argc == 1 && prog == 0) {
- rb_proc_exec(RSTRING(argv[0])->ptr);
- }
- else {
- proc_exec_n(argc, argv, prog);
- }
- _exit(127);
- break; /* not reached */
-
- case -1:
- if (errno == EAGAIN) {
- rb_thread_sleep(1);
- goto retry;
- }
- rb_sys_fail(0);
- break;
-
- default:
- rb_syswait(pid);
- }
-
- status = NUM2INT(rb_last_status);
+ pid = rb_spawn(argc, argv);
+ if (pid == -1) rb_sys_fail(RSTRING(argv[0])->ptr);
+#if defined(HAVE_FORK) || defined(HAVE_SPAWNV)
+ return INT2NUM(pid);
+#else
+ return Qnil;
#endif
-
- if (status == EXIT_SUCCESS) return Qtrue;
- return Qfalse;
}
/*
@@ -3314,6 +3376,7 @@ Init_process()
rb_define_global_function("fork", rb_f_fork, 0);
rb_define_global_function("exit!", rb_f_exit_bang, -1);
rb_define_global_function("system", rb_f_system, -1);
+ rb_define_global_function("spawn", rb_f_spawn, -1);
rb_define_global_function("sleep", rb_f_sleep, -1);
rb_mProcess = rb_define_module("Process");
@@ -3332,6 +3395,7 @@ Init_process()
#endif
rb_define_singleton_method(rb_mProcess, "fork", rb_f_fork, 0);
+ rb_define_singleton_method(rb_mProcess, "spawn", rb_f_spawn, -1);
rb_define_singleton_method(rb_mProcess, "exit!", rb_f_exit_bang, -1);
rb_define_singleton_method(rb_mProcess, "exit", rb_f_exit, -1); /* in eval.c */
rb_define_singleton_method(rb_mProcess, "abort", rb_f_abort, -1); /* in eval.c */
diff --git a/win32/win32.c b/win32/win32.c
index 4436464697..7eea4b76b0 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -589,8 +589,79 @@ rb_w32_get_osfhandle(int fh)
return _get_osfhandle(fh);
}
+int
+rb_w32_argv_size(argv)
+ char **argv;
+{
+ char *p, **t;
+ int len, n, bs, quote;
+
+ for (t = argv, len = 0; *t; t++) {
+ for (p = *t, n = quote = bs = 0; *p; ++p) {
+ switch (*p) {
+ case '\\':
+ ++bs;
+ break;
+ case '"':
+ n += bs + 1; bs = 0;
+ quote = 1;
+ break;
+ case ' ': case '\t':
+ quote = 1;
+ default:
+ bs = 0;
+ p = CharNext(p) - 1;
+ break;
+ }
+ }
+ len += p - *t + n + 1;
+ if (quote) len += 2;
+ }
+ return len;
+}
+
+char *
+rb_w32_join_argv(cmd, argv)
+ char *cmd;
+ char **argv;
+{
+ char *p, *q, *s, **t;
+ int n, bs, quote;
+
+ for (t = argv, q = cmd; p = *t; t++) {
+ quote = 0;
+ s = p;
+ if (!*p || strpbrk(p, " \t\"")) {
+ quote = 1;
+ *q++ = '"';
+ }
+ for (bs = 0; *p; ++p) {
+ switch (*p) {
+ case '\\':
+ ++bs;
+ break;
+ case '"':
+ memcpy(q, s, n = p - s); q += n; s = p;
+ memset(q, '\\', ++bs); q += bs; bs = 0;
+ break;
+ default:
+ bs = 0;
+ p = CharNext(p) - 1;
+ break;
+ }
+ }
+ memcpy(q, s, n = p - s);
+ q += n;
+ if (quote) *q++ = '"';
+ *q++ = ' ';
+ }
+ if (q > cmd) --q;
+ *q = '\0';
+ return cmd;
+}
+
pid_t
-pipe_exec(char *cmd, int mode, FILE **fpr, FILE **fpw)
+rb_w32_pipe_exec(char *cmd, char *prog, int mode, FILE **fpr, FILE **fpw)
{
struct ChildRecord* child;
HANDLE hReadIn, hReadOut;
@@ -667,7 +738,7 @@ pipe_exec(char *cmd, int mode, FILE **fpr, FILE **fpw)
CloseHandle(hCurProc);
/* create child process */
- child = CreateChild(cmd, NULL, &sa, hWriteIn, hReadOut, NULL);
+ child = CreateChild(cmd, prog, &sa, hWriteIn, hReadOut, NULL);
if (!child) {
if (reading) {
CloseHandle(hReadOut);
@@ -729,9 +800,10 @@ pipe_exec(char *cmd, int mode, FILE **fpr, FILE **fpw)
extern VALUE rb_last_status;
int
-do_spawn(mode, cmd)
+rb_w32_spawn(mode, cmd, prog)
int mode;
char *cmd;
+char *prog;
{
struct ChildRecord *child;
DWORD exitcode;
@@ -746,7 +818,7 @@ char *cmd;
return -1;
}
- child = CreateChild(cmd, NULL, NULL, NULL, NULL, NULL);
+ child = CreateChild(cmd, prog, NULL, NULL, NULL, NULL);
if (!child) {
return -1;
}
@@ -768,97 +840,15 @@ char *cmd;
}
int
-do_aspawn(mode, prog, argv)
+rb_w32_aspawn(mode, prog, argv)
int mode;
char *prog;
char **argv;
{
- char *cmd, *p, *q, *s, **t;
- int len, n, bs, quote;
- struct ChildRecord *child;
- DWORD exitcode;
-
- switch (mode) {
- case P_WAIT:
- case P_NOWAIT:
- case P_OVERLAY:
- break;
- default:
- errno = EINVAL;
- return -1;
- }
+ int len = rb_w32_argv_size(argv);
+ char *cmd = ALLOCA_N(char, len);
- for (t = argv, len = 0; *t; t++) {
- for (p = *t, n = quote = bs = 0; *p; ++p) {
- switch (*p) {
- case '\\':
- ++bs;
- break;
- case '"':
- n += bs + 1; bs = 0;
- quote = 1;
- break;
- case ' ': case '\t':
- quote = 1;
- default:
- bs = 0;
- p = CharNext(p) - 1;
- break;
- }
- }
- len += p - *t + n + 1;
- if (quote) len += 2;
- }
- cmd = ALLOCA_N(char, len);
- for (t = argv, q = cmd; p = *t; t++) {
- quote = 0;
- s = p;
- if (!*p || strpbrk(p, " \t\"")) {
- quote = 1;
- *q++ = '"';
- }
- for (bs = 0; *p; ++p) {
- switch (*p) {
- case '\\':
- ++bs;
- break;
- case '"':
- memcpy(q, s, n = p - s); q += n; s = p;
- memset(q, '\\', ++bs); q += bs; bs = 0;
- break;
- default:
- bs = 0;
- p = CharNext(p) - 1;
- break;
- }
- }
- memcpy(q, s, n = p - s);
- q += n;
- if (quote) *q++ = '"';
- *q++ = ' ';
- }
- if (q > cmd) --q;
- *q = '\0';
-
- child = CreateChild(cmd, prog, NULL, NULL, NULL, NULL);
- if (!child) {
- return -1;
- }
-
- switch (mode) {
- case P_WAIT:
- rb_syswait(child->pid);
- return NUM2INT(rb_last_status);
- case P_NOWAIT:
- return child->pid;
- case P_OVERLAY:
- WaitForSingleObject(child->hProcess, INFINITE);
- GetExitCodeProcess(child->hProcess, &exitcode);
- CloseChildHandle(child);
- _exit(exitcode);
- default:
- return -1; /* not reached */
- }
+ return rb_w32_spawn(mode, rb_w32_join_argv(cmd, argv), prog);
}
static struct ChildRecord *
diff --git a/win32/win32.h b/win32/win32.h
index 82bc90ed77..ed930ae6b9 100644
--- a/win32/win32.h
+++ b/win32/win32.h
@@ -120,7 +120,7 @@ extern "C++" {
#undef stat
#define stat(path,st) rb_w32_stat(path,st)
#undef execv
-#define execv(path,argv) do_aspawn(P_OVERLAY,path,argv)
+#define execv(path,argv) rb_w32_aspawn(P_OVERLAY,path,argv)
#ifdef __MINGW32__
struct timezone {
@@ -175,8 +175,10 @@ extern int chown(const char *, int, int);
extern int link(const char *, const char *);
extern int gettimeofday(struct timeval *, struct timezone *);
extern pid_t waitpid (pid_t, int *, int);
-extern int do_spawn(int, char *);
-extern int do_aspawn(int, char *, char **);
+extern int rb_w32_argv_size(char **);
+extern char *rb_w32_join_argv(char *, char **);
+extern int rb_w32_spawn(int, char *, char*);
+extern int rb_w32_aspawn(int, char *, char **);
extern int kill(int, int);
extern pid_t rb_w32_getpid(void);