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 + + * 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 * 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); } +struct popen_arg { + struct rb_exec_arg exec; + int pr[2], pw[2]; +}; + +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]); + } + } + if (p->pw[0] != -1) { + close(p->pw[1]); + if (p->pw[0] != 0) { + dup2(p->pw[0], 0); + close(p->pw[0]); + } + } +} + +#ifdef HAVE_FORK +static int +popen_exec(p) + struct popen_arg *p; +{ + int fd; + + popen_redirect(p); + for (fd = 3; fd < NOFILE; fd++) { +#ifdef FD_CLOEXEC + fcntl(fd, F_SETFL, FD_CLOEXEC); +#else + close(fd); +#endif + } + return rb_exec(&p->exec); +} +#endif + static VALUE -pipe_open(pname, mode) +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; - -#if defined(DJGPP) || defined(__human68k__) || defined(__VMS) - FILE *f = popen(pname, mode); - - 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); - } - 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; - } - rb_sys_fail(pname); - } - else { - VALUE port = io_alloc(rb_cIO); - - MakeOpenFile(port, fptr); - fptr->mode = modef; - fptr->mode |= FMODE_SYNC; - fptr->pid = pid; - - 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; - } -#else - int pid, pr[2], pw[2]; + 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 */ - if (modef & FMODE_READABLE) { - close(pr[0]); - if (pr[1] != 1) { - dup2(pr[1], 1); - close(pr[1]); - } - } - if (modef & FMODE_WRITABLE) { - close(pw[1]); - if (pw[0] != 0) { - dup2(pw[0], 0); - close(pw[0]); - } - } - - if (doexec) { - int fd; - - 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); - } - rb_io_synchronized(RFILE(orig_stdout)->fptr); - rb_io_synchronized(RFILE(orig_stderr)->fptr); - return Qnil; - - case -1: /* fork failed */ - if (errno == EAGAIN) { - rb_thread_sleep(1); - goto retry; - } - close(pr[0]); close(pw[1]); + 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); - break; + } + if ((modef & FMODE_WRITABLE) && pipe(arg.pw) == -1) { + if (modef & FMODE_READABLE) { + int e = errno; + close(arg.pr[0]); close(arg.pr[1]); + errno = e; + } + rb_sys_fail(pname); + } - default: /* parent */ - if (pid < 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; - - 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"); - - 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 (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; } } + + /* 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 (i = 0; i < argc; ++i) { + args[i] = RSTRING(argv[i])->ptr; + } + 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); + break; + defined: + rb_sys_fail(pname); + break; + } + } +#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); + + if (!fpr) rb_sys_fail(pname); +#define PIPE_FDOPEN(i) (fpr) #endif + + port = io_alloc(rb_cIO); + MakeOpenFile(port, fptr); + fptr->mode = modef | FMODE_SYNC; + fptr->pid = pid; + + 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 + 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"; } - return rb_io_popen(str, argc, argv, klass); + else if (FIXNUM_P(pmode)) { + mode = rb_io_modenum_mode(FIX2INT(pmode), mbuf); + } + 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 #endif +#ifdef HAVE_FCNTL_H +#include +#endif #ifdef __DJGPP__ #include #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; iptr; } 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"); - } - - 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]; - - SafeStringValue(cmd); - rb_proc_exec(RSTRING(cmd)->ptr); - } - else { - proc_exec_n(argc, argv, prog); + 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 */ } +int +rb_exec(e) + const struct rb_exec_arg *e; +{ + int argc = e->argc; + VALUE *argv = e->argv; + const char *prog = e->prog; + + if (argc == 0) { + rb_proc_exec(prog); + } + else { + rb_proc_exec_n(argc, argv, prog); + } + 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,137 +1520,41 @@ 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); -#endif -#elif defined(__VMS) - VALUE cmd; - - 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 = system(RSTRING(cmd)->ptr); - last_status_set((status & 0xff) << 8, 0); -#else - volatile VALUE prog = 0; - 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 = 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 - if (status == EXIT_SUCCESS) return Qtrue; return Qfalse; } +/* + * call-seq: + * spawn(cmd [, arg, ...]) => pid + * + * Similar to Kernel::system except for not waiting for + * end of _cmd_, but returns its pid. + */ + +static VALUE +rb_f_spawn(argc, argv) + int argc; + VALUE *argv; +{ + int pid; + + 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 +} + /* * call-seq: * sleep(duration=0) => fixnum @@ -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; + int len = rb_w32_argv_size(argv); + char *cmd = ALLOCA_N(char, len); - switch (mode) { - case P_WAIT: - case P_NOWAIT: - case P_OVERLAY: - break; - default: - errno = EINVAL; - return -1; - } - - 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);