aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Bennett <steveb@workware.net.au>2016-09-14 14:15:47 +1000
committerSteve Bennett <steveb@workware.net.au>2017-10-17 07:39:39 +1000
commit1fd4fb6a645fa107d644f1ca0a0a8d7aa702e1d5 (patch)
tree78c2baa61687fa296dc418cd1b978b3e046813f6
parent4be55f10c4d2071f493b9a1b60197c5e20f3203d (diff)
downloadjimtcl-1fd4fb6a645fa107d644f1ca0a0a8d7aa702e1d5.zip
jimtcl-1fd4fb6a645fa107d644f1ca0a0a8d7aa702e1d5.tar.gz
jimtcl-1fd4fb6a645fa107d644f1ca0a0a8d7aa702e1d5.tar.bz2
signal, exec, wait, pid: improvements, especially to exec
- fix popen [open "|pipeline ..."] to return meaningful status in close (but note that stderr is not captured) - popen pipelines can now be used as the target of exec redirection - overally improvements to exec on windows. Now crt file descriptors are used throughout - add support for [pid], [wait] and popen on windows - os.wait is now wait, and integrates with [exec ... &] to be able to wait for running background tasks - [socket pipe] is now also [pipe] and is supported on windows - [file tempfile] is supported on windows - move duplicated code between jim-aio.c and jim-exec.c to jimiocompat.c - Fix [exec] on windows to match unix semantics wrt sharing the parent stream unless redirected rather than using /dev/null - On windows redirect to or from /dev/null is automatically converted to NUL: - If signal support is disabled, implement a minimal Jim_SignalId() for exec and wait - aio now supports getfd, to return the underlying file descriptor. This is used by exec to support redirection, and allows popen channels to support exec redirection. Signed-off-by: Steve Bennett <steveb@workware.net.au>
-rw-r--r--Makefile.in2
-rw-r--r--auto.def7
-rw-r--r--jim-aio.c170
-rw-r--r--jim-exec.c839
-rw-r--r--jim-file.c2
-rw-r--r--jim-nosignal.c28
-rw-r--r--jim-posix.c96
-rw-r--r--jim-signal.c10
-rw-r--r--jim-signal.h8
-rw-r--r--jim-win32compat.h5
-rw-r--r--jim.h2
-rw-r--r--jim_tcl.txt61
-rw-r--r--jimiocompat.c214
-rw-r--r--jimiocompat.h80
-rwxr-xr-xmake-bootstrap-jim4
-rw-r--r--tclcompat.tcl21
-rw-r--r--tests/exec.test32
-rw-r--r--tests/exec2.test26
-rw-r--r--tests/pid.test6
19 files changed, 845 insertions, 768 deletions
diff --git a/Makefile.in b/Makefile.in
index dfbdc4d..be663ab 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -51,7 +51,7 @@ JIMSH_CC := $(CXX) $(CXXFLAGS)
JIMSH_CC := $(CC) $(CFLAGS)
@endif
-OBJS := _load-static-exts.o jim-subcmd.o jim-interactive.o jim-format.o jim.o utf8.o jimregexp.o \
+OBJS := _load-static-exts.o jim-subcmd.o jim-interactive.o jim-format.o jim.o utf8.o jimregexp.o jimiocompat.o \
@EXTRA_OBJS@ @C_EXT_OBJS@ @TCL_EXT_OBJS@
JIMSH := jimsh@EXEEXT@
diff --git a/auto.def b/auto.def
index 4b454be..d1b26f9 100644
--- a/auto.def
+++ b/auto.def
@@ -354,7 +354,7 @@ dict set extdb info {
sdl { pkg-config SDL_gfx check {[cc-check-function-in-lib SDL_SetVideoMode SDL] && [cc-check-function-in-lib rectangleRGBA SDL_gfx]}
libdep {lib_SDL_SetVideoMode lib_rectangleRGBA}
}
- signal { check {[have-feature sigaction] && [have-feature vfork]} }
+ signal { check {[have-feature sigaction]} }
sqlite3 { pkg-config sqlite3 check {[cc-check-function-in-lib sqlite3_prepare_v2 sqlite3]} libdep lib_sqlite3_prepare_v2 }
zlib { pkg-config zlib check {[cc-check-function-in-lib deflate z]} libdep lib_deflate }
syslog { check {[have-feature syslog]} }
@@ -434,6 +434,11 @@ if {$jimregexp || [opt-bool jim-regexp]} {
}
}
+# poor-man's signals
+if {"signal" ni $extinfo(static-c)} {
+ lappend extra_objs jim-nosignal.o
+}
+
if {[ext-get-status load] eq "n"} {
# If we don't have load, no need to support shared objects
define SH_LINKFLAGS ""
diff --git a/jim-aio.c b/jim-aio.c
index d0124c6..e72cce5 100644
--- a/jim-aio.c
+++ b/jim-aio.c
@@ -52,6 +52,7 @@
#endif
#include "jim.h"
+#include "jimiocompat.h"
#if defined(HAVE_SYS_SOCKET_H) && defined(HAVE_SELECT) && defined(HAVE_NETINET_IN_H) && defined(HAVE_NETDB_H) && defined(HAVE_ARPA_INET_H)
#include <sys/socket.h>
@@ -62,6 +63,9 @@
#ifdef HAVE_SYS_UN_H
#include <sys/un.h>
#endif
+#define HAVE_SOCKETS
+#elif defined (__MINGW32__)
+/* currently mingw32 doesn't support sockets, but has pipe, fdopen */
#else
#define JIM_ANSIC
#endif
@@ -99,9 +103,15 @@
#endif
#endif
+#ifdef JIM_ANSIC
+/* no fdopen() with ANSIC, so can't support these */
+#undef HAVE_PIPE
+#undef HAVE_SOCKETPAIR
+#endif
+
#define JimCheckStreamError(interp, af) af->fops->error(af)
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
+#if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
union sockaddr_any {
struct sockaddr sa;
struct sockaddr_in sin;
@@ -284,7 +294,7 @@ static int JimAioSubCmdProc(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *filename,
const char *hdlfmt, int family, const char *mode);
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
+#if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
static int JimParseIPv6Address(Jim_Interp *interp, const char *hostport, union sockaddr_any *sa, int *salen)
{
#if IPV6
@@ -591,6 +601,16 @@ FILE *Jim_AioFilehandle(Jim_Interp *interp, Jim_Obj *command)
return af->fp;
}
+static int aio_cmd_getfd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ AioFile *af = Jim_CmdPrivData(interp);
+
+ fflush(af->fp);
+ Jim_SetResultInt(interp, fileno(af->fp));
+
+ return JIM_OK;
+}
+
static int aio_cmd_copy(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
@@ -726,7 +746,7 @@ static int aio_cmd_isatty(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return JIM_OK;
}
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
+#if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
static int aio_cmd_recvfrom(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
AioFile *af = Jim_CmdPrivData(interp);
@@ -853,7 +873,7 @@ static int aio_cmd_eof(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
static int aio_cmd_close(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc == 3) {
-#if !defined(JIM_ANSIC) && defined(HAVE_SHUTDOWN)
+#if defined(HAVE_SOCKETS) && defined(HAVE_SHUTDOWN)
static const char * const options[] = { "r", "w", NULL };
enum { OPT_R, OPT_W, };
int option;
@@ -944,7 +964,7 @@ static int aio_cmd_ndelay(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
}
#endif
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
+#if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
#define SOCKOPT_BOOL 0
#define SOCKOPT_INT 1
#define SOCKOPT_TIMEVAL 2 /* not currently supported */
@@ -1043,7 +1063,7 @@ static int aio_cmd_sockopt(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
Jim_SetResultFormatted(interp, "Unknown sockopt %#s", argv[0]);
return JIM_ERR;
}
-#endif
+#endif /* JIM_BOOTSTRAP */
#ifdef HAVE_FSYNC
static int aio_cmd_sync(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
@@ -1374,6 +1394,13 @@ static const jim_subcmd_type aio_command_table[] = {
2,
/* Description: Copy up to 'size' bytes to the given filehandle, or to eof if no size. */
},
+ { "getfd",
+ NULL,
+ aio_cmd_getfd,
+ 0,
+ 0,
+ /* Description: Internal command to return the underlying file descriptor. */
+ },
{ "gets",
"?var?",
aio_cmd_gets,
@@ -1395,7 +1422,7 @@ static const jim_subcmd_type aio_command_table[] = {
0,
/* Description: Is the file descriptor a tty? */
},
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
+#if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
{ "recvfrom",
"len ?addrvar?",
aio_cmd_recvfrom,
@@ -1424,6 +1451,13 @@ static const jim_subcmd_type aio_command_table[] = {
1,
/* Description: Set the listen backlog for server socket */
},
+ { "sockopt",
+ "?opt 0|1?",
+ aio_cmd_sockopt,
+ 0,
+ 2,
+ /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
+ },
#endif /* JIM_BOOTSTRAP */
{ "flush",
NULL,
@@ -1477,15 +1511,6 @@ static const jim_subcmd_type aio_command_table[] = {
/* Description: Set O_NDELAY (if arg). Returns current/new setting. */
},
#endif
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
- { "sockopt",
- "?opt 0|1?",
- aio_cmd_sockopt,
- 0,
- 2,
- /* Description: Return a dictionary of sockopts, or set the value of a sockopt */
- },
-#endif
#ifdef HAVE_FSYNC
{ "sync",
NULL,
@@ -1525,7 +1550,8 @@ static const jim_subcmd_type aio_command_table[] = {
/* Description: Returns script, or invoke exception-script when oob data, {} to remove */
},
#endif
-#if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
+#if !defined(JIM_BOOTSTRAP)
+#if defined(JIM_SSL)
{ "ssl",
"?-server cert priv?",
aio_cmd_ssl,
@@ -1541,8 +1567,8 @@ static const jim_subcmd_type aio_command_table[] = {
0,
/* Description: Verifies the certificate of a SSL/TLS channel */
},
-#endif /* JIM_BOOTSTRAP */
-#if defined(HAVE_STRUCT_FLOCK) && !defined(JIM_BOOTSTRAP)
+#endif
+#if defined(HAVE_STRUCT_FLOCK)
{ "lock",
NULL,
aio_cmd_lock,
@@ -1557,8 +1583,8 @@ static const jim_subcmd_type aio_command_table[] = {
0,
/* Description: Relase a lock. */
},
-#endif /* JIM_BOOTSTRAP */
-#if defined(HAVE_TERMIOS_H) && !defined(JIM_BOOTSTRAP)
+#endif
+#if defined(HAVE_TERMIOS_H)
{ "tty",
"?baud rate? ?data bits? ?stop bits? ?parity even|odd|none? ?handshake xonxoff|rtscts|none? ?input raw|cooked? ?output raw|cooked? ?vmin n? ?vtime n?",
aio_cmd_tty,
@@ -1566,6 +1592,7 @@ static const jim_subcmd_type aio_command_table[] = {
-1,
/* Description: Get or set tty settings - valid only on a tty */
},
+#endif
#endif /* JIM_BOOTSTRAP */
{ NULL }
};
@@ -1665,17 +1692,17 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *fi
Jim_IncrRefCount(filename);
if (fh == NULL) {
-#if !defined(JIM_ANSIC)
if (fd >= 0) {
+#ifndef JIM_ANSIC
fh = fdopen(fd, mode);
+#endif
}
else
-#endif
fh = fopen(Jim_String(filename), mode);
if (fh == NULL) {
JimAioSetError(interp, filename);
-#if !defined(JIM_ANSIC)
+#ifndef JIM_ANSIC
if (fd >= 0) {
close(fd);
}
@@ -1689,7 +1716,9 @@ static AioFile *JimMakeChannel(Jim_Interp *interp, FILE *fh, int fd, Jim_Obj *fi
af = Jim_Alloc(sizeof(*af));
memset(af, 0, sizeof(*af));
af->fp = fh;
+#ifndef JIM_ANSIC
af->fd = fileno(fh);
+#endif
af->filename = filename;
#ifdef FD_CLOEXEC
if ((openFlags & AIO_KEEPOPEN) == 0) {
@@ -1721,7 +1750,6 @@ static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
if (JimMakeChannel(interp, NULL, p[0], filename, hdlfmt, family, mode[0])) {
Jim_Obj *objPtr = Jim_NewListObj(interp, NULL, 0);
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
-
if (JimMakeChannel(interp, NULL, p[1], filename, hdlfmt, family, mode[1])) {
Jim_ListAppendElement(interp, objPtr, Jim_GetResult(interp));
Jim_SetResult(interp, objPtr);
@@ -1737,7 +1765,27 @@ static int JimMakeChannelPair(Jim_Interp *interp, int p[2], Jim_Obj *filename,
}
#endif
-#if !defined(JIM_ANSIC) && !defined(JIM_BOOTSTRAP)
+#ifdef HAVE_PIPE
+static int JimAioPipeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ int p[2];
+ static const char *mode[2] = { "r", "w" };
+
+ if (argc != 1) {
+ Jim_WrongNumArgs(interp, 1, argv, "");
+ return JIM_ERR;
+ }
+
+ if (pipe(p) != 0) {
+ JimAioSetError(interp, NULL);
+ return JIM_ERR;
+ }
+
+ return JimMakeChannelPair(interp, p, argv[0], "aio.pipe%ld", 0, mode);
+}
+#endif
+
+#if defined(HAVE_SOCKETS) && !defined(JIM_BOOTSTRAP)
static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
@@ -1979,22 +2027,10 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
#if defined(HAVE_PIPE)
case SOCK_STREAM_PIPE:
- {
- int p[2];
- static const char *mode[2] = { "r", "w" };
-
- if (argc != 2 || ipv6) {
- goto wrongargs;
- }
-
- if (pipe(p) < 0) {
- JimAioSetError(interp, NULL);
- return JIM_ERR;
- }
-
- return JimMakeChannelPair(interp, p, argv[1], "aio.pipe%ld", 0, mode);
+ if (argc != 2 || ipv6) {
+ goto wrongargs;
}
- break;
+ return JimAioPipeCommand(interp, 1, &argv[1]);
#endif
default:
@@ -2006,53 +2042,6 @@ static int JimAioSockCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
}
#endif /* JIM_BOOTSTRAP */
-/**
- * Returns the file descriptor of a writable, newly created temp file
- * or -1 on error.
- *
- * On success, leaves the filename in the interpreter result, otherwise
- * leaves an error message.
- */
-int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template)
-{
-#ifdef HAVE_MKSTEMP
- int fd;
- mode_t mask;
- Jim_Obj *filenameObj;
-
- if (filename_template == NULL) {
- const char *tmpdir = getenv("TMPDIR");
- if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
- tmpdir = "/tmp/";
- }
- filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
- if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
- Jim_AppendString(interp, filenameObj, "/", 1);
- }
- Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
- }
- else {
- filenameObj = Jim_NewStringObj(interp, filename_template, -1);
- }
-
- /* Update the template name directly with the filename */
- mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
- fd = mkstemp(filenameObj->bytes);
- umask(mask);
- if (fd < 0) {
- Jim_IncrRefCount(filenameObj);
- Jim_SetResultFormatted(interp, "%#s: %s", filenameObj, strerror(errno));
- return -1;
- }
-
- Jim_SetResult(interp, filenameObj);
- return fd;
-#else
- Jim_SetResultString(interp, "platform has no tempfile support", -1);
- return -1;
-#endif
-}
-
#if defined(JIM_SSL) && !defined(JIM_BOOTSTRAP)
static int JimAioLoadSSLCertsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
@@ -2085,9 +2074,12 @@ int Jim_aioInit(Jim_Interp *interp)
#endif
Jim_CreateCommand(interp, "open", JimAioOpenCommand, NULL, NULL);
-#ifndef JIM_ANSIC
+#ifdef HAVE_SOCKETS
Jim_CreateCommand(interp, "socket", JimAioSockCommand, NULL, NULL);
#endif
+#ifdef HAVE_PIPE
+ Jim_CreateCommand(interp, "pipe", JimAioPipeCommand, NULL, NULL);
+#endif
/* Create filehandles for stdin, stdout and stderr */
JimMakeChannel(interp, stdin, -1, NULL, "stdin", 0, "r");
diff --git a/jim-exec.c b/jim-exec.c
index 125f41a..0d248f4 100644
--- a/jim-exec.c
+++ b/jim-exec.c
@@ -93,84 +93,24 @@ int Jim_execInit(Jim_Interp *interp)
#include <errno.h>
#include <signal.h>
+#include "jim-signal.h"
+#include "jimiocompat.h"
+#include <sys/stat.h>
-#if defined(__MINGW32__)
- /* XXX: Should we use this implementation for cygwin too? msvc? */
- #ifndef STRICT
- #define STRICT
- #endif
- #define WIN32_LEAN_AND_MEAN
- #include <windows.h>
- #include <fcntl.h>
-
- typedef HANDLE fdtype;
- typedef HANDLE pidtype;
- #define JIM_BAD_FD INVALID_HANDLE_VALUE
- #define JIM_BAD_PID INVALID_HANDLE_VALUE
- #define JimCloseFd CloseHandle
-
- #define WIFEXITED(STATUS) 1
- #define WEXITSTATUS(STATUS) (STATUS)
- #define WIFSIGNALED(STATUS) 0
- #define WTERMSIG(STATUS) 0
- #define WNOHANG 1
-
- static fdtype JimFileno(FILE *fh);
- static pidtype JimWaitPid(pidtype pid, int *status, int nohang);
- static fdtype JimDupFd(fdtype infd);
- static fdtype JimOpenForRead(const char *filename);
- static FILE *JimFdOpenForRead(fdtype fd);
- static int JimPipe(fdtype pipefd[2]);
- static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env,
- fdtype inputId, fdtype outputId, fdtype errorId);
- static int JimErrno(void);
-#else
- #include "jim-signal.h"
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/wait.h>
- #include <sys/stat.h>
-
- typedef int fdtype;
- typedef int pidtype;
- #define JimPipe pipe
- #define JimErrno() errno
- #define JIM_BAD_FD -1
- #define JIM_BAD_PID -1
- #define JimFileno fileno
- #define JimReadFd read
- #define JimCloseFd close
- #define JimWaitPid waitpid
- #define JimDupFd dup
- #define JimFdOpenForRead(FD) fdopen((FD), "r")
- #define JimOpenForRead(NAME) open((NAME), O_RDONLY, 0)
-
- #ifndef HAVE_EXECVPE
- #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
- #endif
-#endif
+struct WaitInfoTable;
-static const char *JimStrError(void);
static char **JimOriginalEnviron(void);
static char **JimSaveEnv(char **env);
static void JimRestoreEnv(char **env);
static int JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv,
- pidtype **pidArrayPtr, fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr);
-static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr);
+ pidtype **pidArrayPtr, int *inPipePtr, int *outPipePtr, int *errFilePtr);
+static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr);
static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr, Jim_Obj *errStrObj);
-static fdtype JimCreateTemp(Jim_Interp *interp, const char *contents, int len);
-static fdtype JimOpenForWrite(const char *filename, int append);
-static int JimRewindFd(fdtype fd);
+static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv);
-static void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
-{
- Jim_SetResultFormatted(interp, "%s: %s", msg, JimStrError());
-}
-
-static const char *JimStrError(void)
-{
- return strerror(JimErrno());
-}
+#if defined(__MINGW32__)
+static pidtype JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId);
+#endif
/*
* If the last character of 'objPtr' is a newline, then remove
@@ -191,10 +131,10 @@ static void Jim_RemoveTrailingNewline(Jim_Obj *objPtr)
* Read from 'fd', append the data to strObj and close 'fd'.
* Returns 1 if data was added, 0 if not, or -1 on error.
*/
-static int JimAppendStreamToString(Jim_Interp *interp, fdtype fd, Jim_Obj *strObj)
+static int JimAppendStreamToString(Jim_Interp *interp, int fd, Jim_Obj *strObj)
{
char buf[256];
- FILE *fh = JimFdOpenForRead(fd);
+ FILE *fh = fdopen(fd, "r");
int ret = 0;
if (fh == NULL) {
@@ -292,40 +232,16 @@ static void JimFreeEnv(char **env, char **original_environ)
}
}
-#ifndef jim_ext_signal
-/* Implement trivial Jim_SignalId() and Jim_SignalName(), just good enough for JimCheckWaitStatus() */
-const char *Jim_SignalId(int sig)
-{
- static char buf[10];
- snprintf(buf, sizeof(buf), "%d", sig);
- return buf;
-}
-
-const char *Jim_SignalName(int sig)
-{
- return Jim_SignalId(sig);
-}
-#endif
-
-/*
- * Create and store an appropriate value for the global variable $::errorCode
- * Based on pid and waitStatus.
- *
- * Returns JIM_OK for a normal exit with code 0, otherwise returns JIM_ERR.
- *
- * Note that $::errorCode is left unchanged for a normal exit.
- * Details of any abnormal exit is appended to the errStrObj, unless it is NULL.
- */
-static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
+static Jim_Obj *JimMakeErrorCode(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
{
- Jim_Obj *errorCode;
+ Jim_Obj *errorCode = Jim_NewListObj(interp, NULL, 0);
- if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
- return JIM_OK;
+ if (pid == JIM_BAD_PID || pid == JIM_NO_PID) {
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "NONE", -1));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, -1));
}
- errorCode = Jim_NewListObj(interp, NULL, 0);
-
- if (WIFEXITED(waitStatus)) {
+ else if (WIFEXITED(waitStatus)) {
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, "CHILDSTATUS", -1));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, WEXITSTATUS(waitStatus)));
@@ -333,14 +249,17 @@ static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, J
else {
const char *type;
const char *action;
+ const char *signame;
if (WIFSIGNALED(waitStatus)) {
type = "CHILDKILLED";
action = "killed";
+ signame = Jim_SignalId(WTERMSIG(waitStatus));
}
else {
type = "CHILDSUSP";
action = "suspended";
+ signame = "none";
}
Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, type, -1));
@@ -353,17 +272,33 @@ static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, J
}
Jim_ListAppendElement(interp, errorCode, Jim_NewIntObj(interp, (long)pid));
- Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalId(WTERMSIG(waitStatus)), -1));
- Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, Jim_SignalName(WTERMSIG(waitStatus)), -1));
+ Jim_ListAppendElement(interp, errorCode, Jim_NewStringObj(interp, signame, -1));
+ }
+ return errorCode;
+}
+
+/*
+ * Create and store an appropriate value for the global variable $::errorCode
+ * Based on pid and waitStatus.
+ *
+ * Returns JIM_OK for a normal exit with code 0, otherwise returns JIM_ERR.
+ *
+ * Note that $::errorCode is left unchanged for a normal exit.
+ * Details of any abnormal exit is appended to the errStrObj, unless it is NULL.
+ */
+static int JimCheckWaitStatus(Jim_Interp *interp, pidtype pid, int waitStatus, Jim_Obj *errStrObj)
+{
+ if (WIFEXITED(waitStatus) && WEXITSTATUS(waitStatus) == 0) {
+ return JIM_OK;
}
- Jim_SetGlobalVariableStr(interp, "errorCode", errorCode);
+ Jim_SetGlobalVariableStr(interp, "errorCode", JimMakeErrorCode(interp, pid, waitStatus, errStrObj));
return JIM_ERR;
}
/*
- * Data structures of the following type are used by JimFork and
- * JimWaitPids to keep track of child processes.
+ * Data structures of the following type are used by exec and
+ * wait to keep track of child processes.
*/
struct WaitInfo
@@ -373,10 +308,12 @@ struct WaitInfo
int flags; /* Various flag bits; see below for definitions. */
};
+/* This table is shared by exec and os.wait */
struct WaitInfoTable {
struct WaitInfo *info; /* Table of outstanding processes */
int size; /* Size of the allocated table */
int used; /* Number of entries in use */
+ int refcount; /* Free the table once the refcount drops to 0 */
};
/*
@@ -395,8 +332,10 @@ static void JimFreeWaitInfoTable(struct Jim_Interp *interp, void *privData)
{
struct WaitInfoTable *table = privData;
- Jim_Free(table->info);
- Jim_Free(table);
+ if (--table->refcount == 0) {
+ Jim_Free(table->info);
+ Jim_Free(table);
+ }
}
static struct WaitInfoTable *JimAllocWaitInfoTable(void)
@@ -404,22 +343,46 @@ static struct WaitInfoTable *JimAllocWaitInfoTable(void)
struct WaitInfoTable *table = Jim_Alloc(sizeof(*table));
table->info = NULL;
table->size = table->used = 0;
+ table->refcount = 1;
return table;
}
+/**
+ * Removes the given pid from the wait table.
+ *
+ * Returns 0 if OK or -1 if not found.
+ */
+static int JimWaitRemove(struct WaitInfoTable *table, pidtype pid)
+{
+ int i;
+
+ /* Find it in the table */
+ for (i = 0; i < table->used; i++) {
+ if (pid == table->info[i].pid) {
+ if (i != table->used - 1) {
+ table->info[i] = table->info[table->used - 1];
+ }
+ table->used--;
+ return 0;
+ }
+ }
+ return -1;
+}
+
/*
* The main [exec] command
*/
static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- fdtype outputId; /* File id for output pipe. -1 means command overrode. */
- fdtype errorId; /* File id for temporary file containing error output. */
+ int outputId; /* File id for output pipe. -1 means command overrode. */
+ int errorId; /* File id for temporary file containing error output. */
pidtype *pidPtr;
int numPids, result;
int child_siginfo = 1;
Jim_Obj *childErrObj;
Jim_Obj *errStrObj;
+ struct WaitInfoTable *table = Jim_CmdPrivData(interp);
/*
* See if the command is to be run in the background; if so, create
@@ -440,7 +403,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, (long)pidPtr[i]));
}
Jim_SetResult(interp, listObj);
- JimDetachPids(interp, numPids, pidPtr);
+ JimDetachPids(table, numPids, pidPtr);
Jim_Free(pidPtr);
return JIM_OK;
}
@@ -460,7 +423,7 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
errStrObj = Jim_NewStringObj(interp, "", 0);
/* Read from the output pipe until EOF */
- if (outputId != JIM_BAD_FD) {
+ if (outputId != -1) {
if (JimAppendStreamToString(interp, outputId, errStrObj) < 0) {
result = JIM_ERR;
Jim_SetResultErrno(interp, "error reading from output pipe");
@@ -481,9 +444,9 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
* Note that unlike Tcl, the presence of stderr output does not cause
* exec to return an error.
*/
- if (errorId != JIM_BAD_FD) {
+ if (errorId != -1) {
int ret;
- JimRewindFd(errorId);
+ lseek(errorId, 0, SEEK_SET);
ret = JimAppendStreamToString(interp, errorId, errStrObj);
if (ret < 0) {
Jim_SetResultErrno(interp, "error reading from error pipe");
@@ -510,6 +473,65 @@ static int Jim_ExecCmd(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
return result;
}
+/**
+ * Does waitpid() on the given pid, and then removes the
+ * entry from the wait table.
+ *
+ * Returns the pid if OK and updates *statusPtr with the status,
+ * or JIM_BAD_PID if the pid was not in the table.
+ */
+static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
+{
+ if (JimWaitRemove(table, pid) == 0) {
+ /* wait for it */
+ waitpid(pid, statusPtr, 0);
+ return pid;
+ }
+
+ /* Not found */
+ return JIM_BAD_PID;
+}
+
+/**
+ * Indicates that one or more child processes have been placed in
+ * background and are no longer cared about.
+ * These children can be cleaned up with JimReapDetachedPids().
+ */
+static void JimDetachPids(struct WaitInfoTable *table, int numPids, const pidtype *pidPtr)
+{
+ int j;
+
+ for (j = 0; j < numPids; j++) {
+ /* Find it in the table */
+ int i;
+ for (i = 0; i < table->used; i++) {
+ if (pidPtr[j] == table->info[i].pid) {
+ table->info[i].flags |= WI_DETACHED;
+ break;
+ }
+ }
+ }
+}
+
+/* Use 'name getfd' to get the file descriptor associated with channel 'name'
+ * Returns the file descriptor or -1 on error
+ */
+static int JimGetChannelFd(Jim_Interp *interp, const char *name)
+{
+ Jim_Obj *objv[2];
+
+ objv[0] = Jim_NewStringObj(interp, name, -1);
+ objv[1] = Jim_NewStringObj(interp, "getfd", -1);
+
+ if (Jim_EvalObjVector(interp, 2, objv) == JIM_OK) {
+ jim_wide fd;
+ if (Jim_GetWide(interp, Jim_GetResult(interp), &fd) == JIM_OK) {
+ return fd;
+ }
+ }
+ return -1;
+}
+
static void JimReapDetachedPids(struct WaitInfoTable *table)
{
struct WaitInfo *waitPtr;
@@ -525,7 +547,7 @@ static void JimReapDetachedPids(struct WaitInfoTable *table)
for (count = table->used; count > 0; waitPtr++, count--) {
if (waitPtr->flags & WI_DETACHED) {
int status;
- pidtype pid = JimWaitPid(waitPtr->pid, &status, WNOHANG);
+ pidtype pid = waitpid(waitPtr->pid, &status, WNOHANG);
if (pid == waitPtr->pid) {
/* Process has exited, so remove it from the table */
table->used--;
@@ -539,69 +561,78 @@ static void JimReapDetachedPids(struct WaitInfoTable *table)
}
}
-/**
- * Does waitpid() on the given pid, and then removes the
- * entry from the wait table.
+/*
+ * wait ?-nohang? ?pid?
*
- * Returns the pid if OK and updates *statusPtr with the status,
- * or JIM_BAD_PID if the pid was not in the table.
+ * An interface to waitpid(2)
+ *
+ * Returns a 3 element list.
+ *
+ * If the process has not exited or doesn't exist, returns:
+ *
+ * {NONE x x}
+ *
+ * If the process exited normally, returns:
+ *
+ * {CHILDSTATUS <pid> <exit-status>}
+ *
+ * If the process terminated on a signal, returns:
+ *
+ * {CHILDKILLED <pid> <signal>}
+ *
+ * Otherwise (core dump, stopped, continued, ...), returns:
+ *
+ * {CHILDSUSP <pid> none}
+ *
+ * With no arguments, reaps any finished background processes started by exec ... &
*/
-static pidtype JimWaitForProcess(struct WaitInfoTable *table, pidtype pid, int *statusPtr)
+static int Jim_WaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- int i;
+ struct WaitInfoTable *table = Jim_CmdPrivData(interp);
+ int nohang = 0;
+ pidtype pid;
+ long pidarg;
+ int status;
+ Jim_Obj *errCodeObj;
- /* Find it in the table */
- for (i = 0; i < table->used; i++) {
- if (pid == table->info[i].pid) {
- /* wait for it */
- JimWaitPid(pid, statusPtr, 0);
+ /* With no arguments, reap detached children */
+ if (argc == 1) {
+ JimReapDetachedPids(table);
+ return JIM_OK;
+ }
- /* Remove it from the table */
- if (i != table->used - 1) {
- table->info[i] = table->info[table->used - 1];
- }
- table->used--;
- return pid;
- }
+ if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
+ nohang = 1;
+ }
+ if (argc != nohang + 2) {
+ Jim_WrongNumArgs(interp, 1, argv, "?-nohang? ?pid?");
+ return JIM_ERR;
+ }
+ if (Jim_GetLong(interp, argv[nohang + 1], &pidarg) != JIM_OK) {
+ return JIM_ERR;
}
- /* Not found */
- return JIM_BAD_PID;
-}
+ pid = waitpid((pidtype)pidarg, &status, nohang ? WNOHANG : 0);
-/**
- * Indicates that one or more child processes have been placed in
- * background and are no longer cared about.
- * These children can be cleaned up with JimReapDetachedPids().
- */
-static void JimDetachPids(Jim_Interp *interp, int numPids, const pidtype *pidPtr)
-{
- int j;
- struct WaitInfoTable *table = Jim_CmdPrivData(interp);
+ errCodeObj = JimMakeErrorCode(interp, pid, status, NULL);
- for (j = 0; j < numPids; j++) {
- /* Find it in the table */
- int i;
- for (i = 0; i < table->used; i++) {
- if (pidPtr[j] == table->info[i].pid) {
- table->info[i].flags |= WI_DETACHED;
- break;
- }
- }
+ if (pid != JIM_BAD_PID && (WIFEXITED(status) || WIFSIGNALED(status))) {
+ /* The process has finished. Remove it from the wait table if it exists there */
+ JimWaitRemove(table, pid);
}
+ Jim_SetResult(interp, errCodeObj);
+ return JIM_OK;
}
-static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
+static int Jim_PidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- FILE *fh;
- Jim_Obj *fhObj;
-
- fhObj = Jim_NewStringObj(interp, name, -1);
- Jim_IncrRefCount(fhObj);
- fh = Jim_AioFilehandle(interp, fhObj);
- Jim_DecrRefCount(interp, fhObj);
+ if (argc != 1) {
+ Jim_WrongNumArgs(interp, 1, argv, "");
+ return JIM_ERR;
+ }
- return fh;
+ Jim_SetResultInt(interp, (jim_wide)getpid());
+ return JIM_OK;
}
/*
@@ -634,7 +665,7 @@ static FILE *JimGetAioFilehandle(Jim_Interp *interp, const char *name)
*/
static int
JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **pidArrayPtr,
- fdtype *inPipePtr, fdtype *outPipePtr, fdtype *errFilePtr)
+ int *inPipePtr, int *outPipePtr, int *errFilePtr)
{
pidtype *pidPtr = NULL; /* Points to malloc-ed array holding all
* the pids of child processes. */
@@ -671,23 +702,23 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **
* or NULL if output goes to stdout/pipe. */
const char *error = NULL; /* Holds name of stderr file to pipe to,
* or NULL if stderr goes to stderr/pipe. */
- fdtype inputId = JIM_BAD_FD;
+ int inputId = -1;
/* Readable file id input to current command in
- * pipeline (could be file or pipe). JIM_BAD_FD
+ * pipeline (could be file or pipe). -1
* means use stdin. */
- fdtype outputId = JIM_BAD_FD;
+ int outputId = -1;
/* Writable file id for output from current
* command in pipeline (could be file or pipe).
- * JIM_BAD_FD means use stdout. */
- fdtype errorId = JIM_BAD_FD;
+ * -1 means use stdout. */
+ int errorId = -1;
/* Writable file id for all standard error
- * output from all commands in pipeline. JIM_BAD_FD
+ * output from all commands in pipeline. -1
* means use stderr. */
- fdtype lastOutputId = JIM_BAD_FD;
+ int lastOutputId = -1;
/* Write file id for output from last command
* in pipeline (could be file or pipe).
* -1 means use stdout. */
- fdtype pipeIds[2]; /* File ids for pipe that's being created. */
+ int pipeIds[2]; /* File ids for pipe that's being created. */
int firstArg, lastArg; /* Indexes of first and last arguments in
* current command. */
int lastBar;
@@ -700,18 +731,16 @@ JimCreatePipeline(Jim_Interp *interp, int argc, Jim_Obj *const *argv, pidtype **
char **arg_array = Jim_Alloc(sizeof(*arg_array) * (argc + 1));
int arg_count = 0;
- JimReapDetachedPids(table);
-
if (inPipePtr != NULL) {
- *inPipePtr = JIM_BAD_FD;
+ *inPipePtr = -1;
}
if (outPipePtr != NULL) {
- *outPipePtr = JIM_BAD_FD;
+ *outPipePtr = -1;
}
if (errFilePtr != NULL) {
- *errFilePtr = JIM_BAD_FD;
+ *errFilePtr = -1;
}
- pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
+ pipeIds[0] = pipeIds[1] = -1;
/*
* First, scan through all the arguments to figure out the structure
@@ -825,39 +854,44 @@ badargs:
* Immediate data in command. Create temporary file and
* put data into file.
*/
- inputId = JimCreateTemp(interp, input, input_len);
- if (inputId == JIM_BAD_FD) {
+ inputId = Jim_MakeTempFile(interp, NULL, 1);
+ if (inputId == -1) {
goto error;
}
+ if (write(inputId, input, input_len) != input_len) {
+ Jim_SetResultErrno(interp, "couldn't write temp file");
+ close(inputId);
+ goto error;
+ }
+ lseek(inputId, 0L, SEEK_SET);
}
else if (inputFile == FILE_HANDLE) {
- /* Should be a file descriptor */
- FILE *fh = JimGetAioFilehandle(interp, input);
+ int fd = JimGetChannelFd(interp, input);
- if (fh == NULL) {
+ if (fd < 0) {
goto error;
}
- inputId = JimDupFd(JimFileno(fh));
+ inputId = dup(fd);
}
else {
/*
* File redirection. Just open the file.
*/
- inputId = JimOpenForRead(input);
- if (inputId == JIM_BAD_FD) {
- Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, JimStrError());
+ inputId = Jim_OpenForRead(input);
+ if (inputId == -1) {
+ Jim_SetResultFormatted(interp, "couldn't read file \"%s\": %s", input, strerror(Jim_Errno()));
goto error;
}
}
}
else if (inPipePtr != NULL) {
- if (JimPipe(pipeIds) != 0) {
+ if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create input pipe for command");
goto error;
}
inputId = pipeIds[0];
*inPipePtr = pipeIds[1];
- pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
+ pipeIds[0] = pipeIds[1] = -1;
}
/*
@@ -866,20 +900,19 @@ badargs:
*/
if (output != NULL) {
if (outputFile == FILE_HANDLE) {
- FILE *fh = JimGetAioFilehandle(interp, output);
- if (fh == NULL) {
+ int fd = JimGetChannelFd(interp, output);
+ if (fd < 0) {
goto error;
}
- fflush(fh);
- lastOutputId = JimDupFd(JimFileno(fh));
+ lastOutputId = dup(fd);
}
else {
/*
* Output is to go to a file.
*/
- lastOutputId = JimOpenForWrite(output, outputFile == FILE_APPEND);
- if (lastOutputId == JIM_BAD_FD) {
- Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, JimStrError());
+ lastOutputId = Jim_OpenForWrite(output, outputFile == FILE_APPEND);
+ if (lastOutputId == -1) {
+ Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", output, strerror(Jim_Errno()));
goto error;
}
}
@@ -888,43 +921,42 @@ badargs:
/*
* Output is to go to a pipe.
*/
- if (JimPipe(pipeIds) != 0) {
+ if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create output pipe");
goto error;
}
lastOutputId = pipeIds[1];
*outPipePtr = pipeIds[0];
- pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
+ pipeIds[0] = pipeIds[1] = -1;
}
/* If we are redirecting stderr with 2>filename or 2>@fileId, then we ignore errFilePtr */
if (error != NULL) {
if (errorFile == FILE_HANDLE) {
if (strcmp(error, "1") == 0) {
/* Special 2>@1 */
- if (lastOutputId != JIM_BAD_FD) {
- errorId = JimDupFd(lastOutputId);
+ if (lastOutputId != -1) {
+ errorId = dup(lastOutputId);
}
else {
/* No redirection of stdout, so just use 2>@stdout */
error = "stdout";
}
}
- if (errorId == JIM_BAD_FD) {
- FILE *fh = JimGetAioFilehandle(interp, error);
- if (fh == NULL) {
+ if (errorId == -1) {
+ int fd = JimGetChannelFd(interp, error);
+ if (fd < 0) {
goto error;
}
- fflush(fh);
- errorId = JimDupFd(JimFileno(fh));
+ errorId = dup(fd);
}
}
else {
/*
* Output is to go to a file.
*/
- errorId = JimOpenForWrite(error, errorFile == FILE_APPEND);
- if (errorId == JIM_BAD_FD) {
- Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, JimStrError());
+ errorId = Jim_OpenForWrite(error, errorFile == FILE_APPEND);
+ if (errorId == -1) {
+ Jim_SetResultFormatted(interp, "couldn't write file \"%s\": %s", error, strerror(Jim_Errno()));
goto error;
}
}
@@ -938,11 +970,11 @@ badargs:
* to complete before reading stderr, and processes couldn't complete
* because stderr was backed up.
*/
- errorId = JimCreateTemp(interp, NULL, 0);
- if (errorId == JIM_BAD_FD) {
+ errorId = Jim_MakeTempFile(interp, NULL, 1);
+ if (errorId == -1) {
goto error;
}
- *errFilePtr = JimDupFd(errorId);
+ *errFilePtr = dup(errorId);
}
/*
@@ -956,7 +988,7 @@ badargs:
}
for (firstArg = 0; firstArg < arg_count; numPids++, firstArg = lastArg + 1) {
int pipe_dup_err = 0;
- fdtype origErrorId = errorId;
+ int origErrorId = errorId;
for (lastArg = firstArg; lastArg < arg_count; lastArg++) {
if (strcmp(arg_array[lastArg], "|") == 0) {
@@ -979,14 +1011,14 @@ badargs:
outputId = lastOutputId;
}
else {
- if (JimPipe(pipeIds) != 0) {
+ if (pipe(pipeIds) != 0) {
Jim_SetResultErrno(interp, "couldn't create pipe");
goto error;
}
outputId = pipeIds[1];
}
- /* Need to do this befor vfork() */
+ /* Need to do this before vfork() */
if (pipe_dup_err) {
errorId = outputId;
}
@@ -1011,10 +1043,9 @@ badargs:
}
if (pid == 0) {
/* Child */
-
- if (inputId != -1) dup2(inputId, 0);
- if (outputId != -1) dup2(outputId, 1);
- if (errorId != -1) dup2(errorId, 2);
+ if (inputId != -1) dup2(inputId, fileno(stdin));
+ if (outputId != -1) dup2(outputId, fileno(stdout));
+ if (errorId != -1) dup2(errorId, fileno(stderr));
for (i = 3; (i <= outputId) || (i <= inputId) || (i <= errorId); i++) {
close(i);
@@ -1025,7 +1056,6 @@ badargs:
execvpe(arg_array[firstArg], &arg_array[firstArg], Jim_GetEnviron());
- /* Need to prep an error message before vfork(), just in case */
fprintf(stderr, "couldn't exec \"%s\"\n", arg_array[firstArg]);
#ifdef JIM_MAINTAINER
{
@@ -1063,15 +1093,15 @@ badargs:
* this child, then set up the input for the next child.
*/
- if (inputId != JIM_BAD_FD) {
- JimCloseFd(inputId);
+ if (inputId != -1) {
+ close(inputId);
}
- if (outputId != JIM_BAD_FD) {
- JimCloseFd(outputId);
- outputId = JIM_BAD_FD;
+ if (outputId != -1) {
+ close(outputId);
+ outputId = -1;
}
inputId = pipeIds[0];
- pipeIds[0] = pipeIds[1] = JIM_BAD_FD;
+ pipeIds[0] = pipeIds[1] = -1;
}
*pidArrayPtr = pidPtr;
@@ -1080,14 +1110,14 @@ badargs:
*/
cleanup:
- if (inputId != JIM_BAD_FD) {
- JimCloseFd(inputId);
+ if (inputId != -1) {
+ close(inputId);
}
- if (lastOutputId != JIM_BAD_FD) {
- JimCloseFd(lastOutputId);
+ if (lastOutputId != -1) {
+ close(lastOutputId);
}
- if (errorId != JIM_BAD_FD) {
- JimCloseFd(errorId);
+ if (errorId != -1) {
+ close(errorId);
}
Jim_Free(arg_array);
@@ -1102,28 +1132,28 @@ badargs:
*/
error:
- if ((inPipePtr != NULL) && (*inPipePtr != JIM_BAD_FD)) {
- JimCloseFd(*inPipePtr);
- *inPipePtr = JIM_BAD_FD;
+ if ((inPipePtr != NULL) && (*inPipePtr != -1)) {
+ close(*inPipePtr);
+ *inPipePtr = -1;
}
- if ((outPipePtr != NULL) && (*outPipePtr != JIM_BAD_FD)) {
- JimCloseFd(*outPipePtr);
- *outPipePtr = JIM_BAD_FD;
+ if ((outPipePtr != NULL) && (*outPipePtr != -1)) {
+ close(*outPipePtr);
+ *outPipePtr = -1;
}
- if ((errFilePtr != NULL) && (*errFilePtr != JIM_BAD_FD)) {
- JimCloseFd(*errFilePtr);
- *errFilePtr = JIM_BAD_FD;
+ if ((errFilePtr != NULL) && (*errFilePtr != -1)) {
+ close(*errFilePtr);
+ *errFilePtr = -1;
}
- if (pipeIds[0] != JIM_BAD_FD) {
- JimCloseFd(pipeIds[0]);
+ if (pipeIds[0] != -1) {
+ close(pipeIds[0]);
}
- if (pipeIds[1] != JIM_BAD_FD) {
- JimCloseFd(pipeIds[1]);
+ if (pipeIds[1] != -1) {
+ close(pipeIds[1]);
}
if (pidPtr != NULL) {
for (i = 0; i < numPids; i++) {
if (pidPtr[i] != JIM_BAD_PID) {
- JimDetachPids(interp, 1, &pidPtr[i]);
+ JimDetachPids(table, 1, &pidPtr[i]);
}
}
Jim_Free(pidPtr);
@@ -1174,6 +1204,7 @@ static int JimCleanupChildren(Jim_Interp *interp, int numPids, pidtype *pidPtr,
int Jim_execInit(Jim_Interp *interp)
{
+ struct WaitInfoTable *waitinfo;
if (Jim_PackageProvide(interp, "exec", "1.0", JIM_ERRMSG))
return JIM_ERR;
@@ -1191,227 +1222,18 @@ int Jim_execInit(Jim_Interp *interp)
(void)signal(SIGPIPE, SIG_IGN);
#endif
- Jim_CreateCommand(interp, "exec", Jim_ExecCmd, JimAllocWaitInfoTable(), JimFreeWaitInfoTable);
+ waitinfo = JimAllocWaitInfoTable();
+ Jim_CreateCommand(interp, "exec", Jim_ExecCmd, waitinfo, JimFreeWaitInfoTable);
+ waitinfo->refcount++;
+ Jim_CreateCommand(interp, "wait", Jim_WaitCommand, waitinfo, JimFreeWaitInfoTable);
+ Jim_CreateCommand(interp, "pid", Jim_PidCommand, 0, 0);
+
return JIM_OK;
}
#if defined(__MINGW32__)
/* Windows-specific (mingw) implementation */
-static SECURITY_ATTRIBUTES *JimStdSecAttrs(void)
-{
- static SECURITY_ATTRIBUTES secAtts;
-
- secAtts.nLength = sizeof(SECURITY_ATTRIBUTES);
- secAtts.lpSecurityDescriptor = NULL;
- secAtts.bInheritHandle = TRUE;
- return &secAtts;
-}
-
-static int JimErrno(void)
-{
- switch (GetLastError()) {
- case ERROR_FILE_NOT_FOUND: return ENOENT;
- case ERROR_PATH_NOT_FOUND: return ENOENT;
- case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
- case ERROR_ACCESS_DENIED: return EACCES;
- case ERROR_INVALID_HANDLE: return EBADF;
- case ERROR_BAD_ENVIRONMENT: return E2BIG;
- case ERROR_BAD_FORMAT: return ENOEXEC;
- case ERROR_INVALID_ACCESS: return EACCES;
- case ERROR_INVALID_DRIVE: return ENOENT;
- case ERROR_CURRENT_DIRECTORY: return EACCES;
- case ERROR_NOT_SAME_DEVICE: return EXDEV;
- case ERROR_NO_MORE_FILES: return ENOENT;
- case ERROR_WRITE_PROTECT: return EROFS;
- case ERROR_BAD_UNIT: return ENXIO;
- case ERROR_NOT_READY: return EBUSY;
- case ERROR_BAD_COMMAND: return EIO;
- case ERROR_CRC: return EIO;
- case ERROR_BAD_LENGTH: return EIO;
- case ERROR_SEEK: return EIO;
- case ERROR_WRITE_FAULT: return EIO;
- case ERROR_READ_FAULT: return EIO;
- case ERROR_GEN_FAILURE: return EIO;
- case ERROR_SHARING_VIOLATION: return EACCES;
- case ERROR_LOCK_VIOLATION: return EACCES;
- case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
- case ERROR_HANDLE_DISK_FULL: return ENOSPC;
- case ERROR_NOT_SUPPORTED: return ENODEV;
- case ERROR_REM_NOT_LIST: return EBUSY;
- case ERROR_DUP_NAME: return EEXIST;
- case ERROR_BAD_NETPATH: return ENOENT;
- case ERROR_NETWORK_BUSY: return EBUSY;
- case ERROR_DEV_NOT_EXIST: return ENODEV;
- case ERROR_TOO_MANY_CMDS: return EAGAIN;
- case ERROR_ADAP_HDW_ERR: return EIO;
- case ERROR_BAD_NET_RESP: return EIO;
- case ERROR_UNEXP_NET_ERR: return EIO;
- case ERROR_NETNAME_DELETED: return ENOENT;
- case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
- case ERROR_BAD_DEV_TYPE: return ENODEV;
- case ERROR_BAD_NET_NAME: return ENOENT;
- case ERROR_TOO_MANY_NAMES: return ENFILE;
- case ERROR_TOO_MANY_SESS: return EIO;
- case ERROR_SHARING_PAUSED: return EAGAIN;
- case ERROR_REDIR_PAUSED: return EAGAIN;
- case ERROR_FILE_EXISTS: return EEXIST;
- case ERROR_CANNOT_MAKE: return ENOSPC;
- case ERROR_OUT_OF_STRUCTURES: return ENFILE;
- case ERROR_ALREADY_ASSIGNED: return EEXIST;
- case ERROR_INVALID_PASSWORD: return EPERM;
- case ERROR_NET_WRITE_FAULT: return EIO;
- case ERROR_NO_PROC_SLOTS: return EAGAIN;
- case ERROR_DISK_CHANGE: return EXDEV;
- case ERROR_BROKEN_PIPE: return EPIPE;
- case ERROR_OPEN_FAILED: return ENOENT;
- case ERROR_DISK_FULL: return ENOSPC;
- case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
- case ERROR_INVALID_TARGET_HANDLE: return EBADF;
- case ERROR_INVALID_NAME: return ENOENT;
- case ERROR_PROC_NOT_FOUND: return ESRCH;
- case ERROR_WAIT_NO_CHILDREN: return ECHILD;
- case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
- case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
- case ERROR_SEEK_ON_DEVICE: return ESPIPE;
- case ERROR_BUSY_DRIVE: return EAGAIN;
- case ERROR_DIR_NOT_EMPTY: return EEXIST;
- case ERROR_NOT_LOCKED: return EACCES;
- case ERROR_BAD_PATHNAME: return ENOENT;
- case ERROR_LOCK_FAILED: return EACCES;
- case ERROR_ALREADY_EXISTS: return EEXIST;
- case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
- case ERROR_BAD_PIPE: return EPIPE;
- case ERROR_PIPE_BUSY: return EAGAIN;
- case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
- case ERROR_DIRECTORY: return ENOTDIR;
- }
- return EINVAL;
-}
-
-static int JimPipe(fdtype pipefd[2])
-{
- if (CreatePipe(&pipefd[0], &pipefd[1], NULL, 0)) {
- return 0;
- }
- return -1;
-}
-
-static fdtype JimDupFd(fdtype infd)
-{
- fdtype dupfd;
- pidtype pid = GetCurrentProcess();
-
- if (DuplicateHandle(pid, infd, pid, &dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
- return dupfd;
- }
- return JIM_BAD_FD;
-}
-
-static int JimRewindFd(fdtype fd)
-{
- return SetFilePointer(fd, 0, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER ? -1 : 0;
-}
-
-#if 0
-static int JimReadFd(fdtype fd, char *buffer, size_t len)
-{
- DWORD num;
-
- if (ReadFile(fd, buffer, len, &num, NULL)) {
- return num;
- }
- if (GetLastError() == ERROR_HANDLE_EOF || GetLastError() == ERROR_BROKEN_PIPE) {
- return 0;
- }
- return -1;
-}
-#endif
-
-static FILE *JimFdOpenForRead(fdtype fd)
-{
- return _fdopen(_open_osfhandle((int)fd, _O_RDONLY | _O_TEXT), "r");
-}
-
-static fdtype JimFileno(FILE *fh)
-{
- return (fdtype)_get_osfhandle(_fileno(fh));
-}
-
-static fdtype JimOpenForRead(const char *filename)
-{
- return CreateFile(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
- JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-}
-
-static fdtype JimOpenForWrite(const char *filename, int append)
-{
- fdtype fd = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
- JimStdSecAttrs(), append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
- if (append && fd != JIM_BAD_FD) {
- SetFilePointer(fd, 0, NULL, FILE_END);
- }
- return fd;
-}
-
-static FILE *JimFdOpenForWrite(fdtype fd)
-{
- return _fdopen(_open_osfhandle((int)fd, _O_TEXT), "w");
-}
-
-static pidtype JimWaitPid(pidtype pid, int *status, int nohang)
-{
- DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
- if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
- /* WAIT_TIMEOUT can only happend with WNOHANG */
- return JIM_BAD_PID;
- }
- GetExitCodeProcess(pid, &ret);
- *status = ret;
- CloseHandle(pid);
- return pid;
-}
-
-static HANDLE JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
-{
- char name[MAX_PATH];
- HANDLE handle;
-
- if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, "JIM", 0, name)) {
- return JIM_BAD_FD;
- }
-
- handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, JimStdSecAttrs(),
- CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
- NULL);
-
- if (handle == INVALID_HANDLE_VALUE) {
- goto error;
- }
-
- if (contents != NULL) {
- /* Use fdopen() to get automatic text-mode translation */
- FILE *fh = JimFdOpenForWrite(JimDupFd(handle));
- if (fh == NULL) {
- goto error;
- }
-
- if (fwrite(contents, len, 1, fh) != 1) {
- fclose(fh);
- goto error;
- }
- fseek(fh, 0, SEEK_SET);
- fclose(fh);
- }
- return handle;
-
- error:
- Jim_SetResultErrno(interp, "failed to create temp file");
- CloseHandle(handle);
- DeleteFile(name);
- return JIM_BAD_FD;
-}
-
static int
JimWinFindExecutable(const char *originalName, char fullPath[MAX_PATH])
{
@@ -1524,12 +1346,15 @@ JimWinBuildCommandLine(Jim_Interp *interp, char **argv)
return strObj;
}
+/**
+ * Note that inputId, etc. are osf_handles.
+ */
static pidtype
-JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId, fdtype outputId, fdtype errorId)
+JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, int inputId, int outputId, int errorId)
{
STARTUPINFO startInfo;
PROCESS_INFORMATION procInfo;
- HANDLE hProcess, h;
+ HANDLE hProcess;
char execPath[MAX_PATH];
pidtype pid = JIM_BAD_PID;
Jim_Obj *cmdLineObj;
@@ -1561,42 +1386,37 @@ JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId,
* and stderr of the child process. The duplicate handles are set to
* be inheritable, so the child process can use them.
*/
- if (inputId == JIM_BAD_FD) {
- if (CreatePipe(&startInfo.hStdInput, &h, JimStdSecAttrs(), 0) != FALSE) {
- CloseHandle(h);
- }
- } else {
- DuplicateHandle(hProcess, inputId, hProcess, &startInfo.hStdInput,
- 0, TRUE, DUPLICATE_SAME_ACCESS);
+ /*
+ * If stdin was not redirected, input should come from the parent's stdin
+ */
+ if (inputId == -1) {
+ inputId = _fileno(stdin);
}
- if (startInfo.hStdInput == JIM_BAD_FD) {
+ DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(inputId), hProcess, &startInfo.hStdInput,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ if (startInfo.hStdInput == INVALID_HANDLE_VALUE) {
goto end;
}
- if (outputId == JIM_BAD_FD) {
- startInfo.hStdOutput = CreateFile("NUL:", GENERIC_WRITE, 0,
- JimStdSecAttrs(), OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- } else {
- DuplicateHandle(hProcess, outputId, hProcess, &startInfo.hStdOutput,
- 0, TRUE, DUPLICATE_SAME_ACCESS);
+ /*
+ * If stdout was not redirected, output should go to the parent's stdout
+ */
+ if (outputId == -1) {
+ outputId = _fileno(stdout);
}
- if (startInfo.hStdOutput == JIM_BAD_FD) {
+ DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(outputId), hProcess, &startInfo.hStdOutput,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ if (startInfo.hStdOutput == INVALID_HANDLE_VALUE) {
goto end;
}
- if (errorId == JIM_BAD_FD) {
- /*
- * If handle was not set, errors should be sent to an infinitely
- * deep sink.
- */
-
- startInfo.hStdError = CreateFile("NUL:", GENERIC_WRITE, 0,
- JimStdSecAttrs(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- } else {
- DuplicateHandle(hProcess, errorId, hProcess, &startInfo.hStdError,
- 0, TRUE, DUPLICATE_SAME_ACCESS);
+ /* Ditto stderr */
+ if (errorId == -1) {
+ errorId = _fileno(stderr);
}
- if (startInfo.hStdError == JIM_BAD_FD) {
+ DuplicateHandle(hProcess, (HANDLE)_get_osfhandle(errorId), hProcess, &startInfo.hStdError,
+ 0, TRUE, DUPLICATE_SAME_ACCESS);
+ if (startInfo.hStdError == INVALID_HANDLE_VALUE) {
goto end;
}
@@ -1636,46 +1456,19 @@ JimStartWinProcess(Jim_Interp *interp, char **argv, char **env, fdtype inputId,
end:
Jim_FreeNewObj(interp, cmdLineObj);
- if (startInfo.hStdInput != JIM_BAD_FD) {
+ if (startInfo.hStdInput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdInput);
}
- if (startInfo.hStdOutput != JIM_BAD_FD) {
+ if (startInfo.hStdOutput != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdOutput);
}
- if (startInfo.hStdError != JIM_BAD_FD) {
+ if (startInfo.hStdError != INVALID_HANDLE_VALUE) {
CloseHandle(startInfo.hStdError);
}
return pid;
}
-#else
-/* Unix-specific implementation */
-static int JimOpenForWrite(const char *filename, int append)
-{
- return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
-}
-
-static int JimRewindFd(int fd)
-{
- return lseek(fd, 0L, SEEK_SET);
-}
-static int JimCreateTemp(Jim_Interp *interp, const char *contents, int len)
-{
- int fd = Jim_MakeTempFile(interp, NULL);
-
- if (fd != JIM_BAD_FD) {
- unlink(Jim_String(Jim_GetResult(interp)));
- if (contents) {
- if (write(fd, contents, len) != len) {
- Jim_SetResultErrno(interp, "couldn't write temp file");
- close(fd);
- return -1;
- }
- lseek(fd, 0L, SEEK_SET);
- }
- }
- return fd;
-}
+#else
static char **JimOriginalEnviron(void)
{
diff --git a/jim-file.c b/jim-file.c
index 07fe699..66522e3 100644
--- a/jim-file.c
+++ b/jim-file.c
@@ -492,7 +492,7 @@ static int file_cmd_mkdir(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
static int file_cmd_tempfile(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
- int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL);
+ int fd = Jim_MakeTempFile(interp, (argc >= 1) ? Jim_String(argv[0]) : NULL, 0);
if (fd < 0) {
return JIM_ERR;
diff --git a/jim-nosignal.c b/jim-nosignal.c
new file mode 100644
index 0000000..bc39712
--- /dev/null
+++ b/jim-nosignal.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <signal.h>
+
+#include <jim-signal.h>
+#include <jim.h>
+
+/* Implement trivial Jim_SignalId() just good enough for JimMakeErrorCode() in [exec] */
+
+
+/* This works for mingw, but is not really portable */
+#ifndef SIGPIPE
+#define SIGPIPE 13
+#endif
+#ifndef SIGINT
+#define SIGINT 2
+#endif
+
+const char *Jim_SignalId(int sig)
+{
+ static char buf[10];
+ switch (sig) {
+ case SIGINT: return "SIGINT";
+ case SIGPIPE: return "SIGPIPE";
+
+ }
+ snprintf(buf, sizeof(buf), "%d", sig);
+ return buf;
+}
diff --git a/jim-posix.c b/jim-posix.c
index af8c0f1..a4ba61e 100644
--- a/jim-posix.c
+++ b/jim-posix.c
@@ -37,7 +37,6 @@
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>
-#include <signal.h>
#include <errno.h>
#include "jimautoconf.h"
@@ -72,88 +71,6 @@ static int Jim_PosixForkCommand(Jim_Interp *interp, int argc, Jim_Obj *const *ar
}
#endif
-/*
- * os.wait ?-nohang? pid
- *
- * An interface to waitpid(2)
- *
- * Returns a 3 element list.
- *
- * If -nohang is specified, and the process is still alive, returns
- *
- * {0 none 0}
- *
- * If the process does not exist or has already been waited for, returns:
- *
- * {-1 error <error-description>}
- *
- * If the process exited normally, returns:
- *
- * {<pid> exit <exit-status>}
- *
- * If the process terminated on a signal, returns:
- *
- * {<pid> signal <signal-number>}
- *
- * Otherwise (core dump, stopped, continued, ...), returns:
- *
- * {<pid> other 0}
- */
-static int Jim_PosixWaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
-{
- int nohang = 0;
- long pid;
- int status;
- Jim_Obj *listObj;
- const char *type;
- int value;
-
- if (argc > 1 && Jim_CompareStringImmediate(interp, argv[1], "-nohang")) {
- nohang = 1;
- }
- if (argc != nohang + 2) {
- Jim_WrongNumArgs(interp, 1, argv, "?-nohang? pid");
- return JIM_ERR;
- }
- if (Jim_GetLong(interp, argv[nohang + 1], &pid) != JIM_OK) {
- return JIM_ERR;
- }
-
- pid = waitpid(pid, &status, nohang ? WNOHANG : 0);
- listObj = Jim_NewListObj(interp, NULL, 0);
- Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, pid));
- if (pid < 0) {
- type = "error";
- value = errno;
- }
- else if (pid == 0) {
- type = "none";
- value = 0;
- }
- else if (WIFEXITED(status)) {
- type = "exit";
- value = WEXITSTATUS(status);
- }
- else if (WIFSIGNALED(status)) {
- type = "signal";
- value = WTERMSIG(status);
- }
- else {
- type = "other";
- value = 0;
- }
-
- Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, type, -1));
- if (pid < 0) {
- Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, strerror(value), -1));
- }
- else {
- Jim_ListAppendElement(interp, listObj, Jim_NewIntObj(interp, value));
- }
- Jim_SetResult(interp, listObj);
- return JIM_OK;
-}
-
static int Jim_PosixGetidsCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
Jim_Obj *objv[8];
@@ -218,17 +135,6 @@ static int Jim_PosixUptimeCommand(Jim_Interp *interp, int argc, Jim_Obj *const *
return JIM_OK;
}
-static int Jim_PosixPidCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
-{
- if (argc != 1) {
- Jim_WrongNumArgs(interp, 1, argv, "");
- return JIM_ERR;
- }
-
- Jim_SetResultInt(interp, getpid());
- return JIM_OK;
-}
-
int Jim_posixInit(Jim_Interp *interp)
{
if (Jim_PackageProvide(interp, "posix", "1.0", JIM_ERRMSG))
@@ -237,10 +143,8 @@ int Jim_posixInit(Jim_Interp *interp)
#ifdef HAVE_FORK
Jim_CreateCommand(interp, "os.fork", Jim_PosixForkCommand, NULL, NULL);
#endif
- Jim_CreateCommand(interp, "os.wait", Jim_PosixWaitCommand, NULL, NULL);
Jim_CreateCommand(interp, "os.getids", Jim_PosixGetidsCommand, NULL, NULL);
Jim_CreateCommand(interp, "os.gethostname", Jim_PosixGethostnameCommand, NULL, NULL);
Jim_CreateCommand(interp, "os.uptime", Jim_PosixUptimeCommand, NULL, NULL);
- Jim_CreateCommand(interp, "pid", Jim_PosixPidCommand, NULL, NULL);
return JIM_OK;
}
diff --git a/jim-signal.c b/jim-signal.c
index 483d021..5fb9c9f 100644
--- a/jim-signal.c
+++ b/jim-signal.c
@@ -128,16 +128,6 @@ const char *Jim_SignalId(int sig)
return "unknown signal";
}
-const char *Jim_SignalName(int sig)
-{
-#ifdef HAVE_SYS_SIGLIST
- if (sig >= 0 && sig < NSIG) {
- return sys_siglist[sig];
- }
-#endif
- return Jim_SignalId(sig);
-}
-
/**
* Given the name of a signal, returns the signal value if found,
* or returns -1 (and sets an error) if not found.
diff --git a/jim-signal.h b/jim-signal.h
index 84dcd8b..09f688a 100644
--- a/jim-signal.h
+++ b/jim-signal.h
@@ -11,14 +11,6 @@ extern "C" {
*/
const char *Jim_SignalId(int sig);
-/**
- * If available, returns a short description of the given signal.
- * e.g. "Terminated", "Interrupted"
- *
- * Otherwise returns the same as Jim_SignalId()
- */
-const char *Jim_SignalName(int sig);
-
#ifdef __cplusplus
}
#endif
diff --git a/jim-win32compat.h b/jim-win32compat.h
index 325d1dd..16133b5 100644
--- a/jim-win32compat.h
+++ b/jim-win32compat.h
@@ -67,11 +67,6 @@ DIR *opendir(const char *name);
int closedir(DIR *dir);
struct dirent *readdir(DIR *dir);
-#elif defined(__MINGW32__)
-
-#include <stdlib.h>
-#define strtod __strtod
-
#endif
#endif /* WIN32 */
diff --git a/jim.h b/jim.h
index 65bf7cc..1dd0f0c 100644
--- a/jim.h
+++ b/jim.h
@@ -609,7 +609,7 @@ JIM_EXPORT char *Jim_StrDupLen(const char *s, int l);
/* environment */
JIM_EXPORT char **Jim_GetEnviron(void);
JIM_EXPORT void Jim_SetEnviron(char **env);
-JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template);
+JIM_EXPORT int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file);
/* evaluation */
JIM_EXPORT int Jim_Eval(Jim_Interp *interp, const char *script);
diff --git a/jim_tcl.txt b/jim_tcl.txt
index 6027873..d1eaffb 100644
--- a/jim_tcl.txt
+++ b/jim_tcl.txt
@@ -62,6 +62,10 @@ Changes between 0.77 and 0.78
6. Add scriptable autocompletion support with `history completion`
7. Add support for `tree delete`
8. Add support for `defer` and '$jim::defer'
+9. Renamed `os.wait` to `wait`, now more Tcl-compatible and compatible with `exec ... &`
+10. `pipe` is now a synonym for `socket pipe`
+11. Closing a pipe open with `open |...` now returns Tcl-like status
+12. It is now possible to used `exec` redirection with a pipe opened with `open |...`
Changes between 0.76 and 0.77
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3403,6 +3407,10 @@ switch.
Output to files is buffered internally by Tcl; the `flush`
command may be used to force buffered characters to be output.
+pipe
+~~~~
+Creates a pair of `aio` channels and returns the handles as a list: +{read write}+
+
pwd
~~~
+*pwd*+
@@ -4459,6 +4467,35 @@ Although 'add2' could have been implemented using `uplevel`
instead of `upvar`, `upvar` makes it simpler for 'add2'
to access the variable in the caller's procedure frame.
+wait
+~~~~
++*wait*+
+
++*wait -nohang* 'pid'+
+
+With no arguments, cleans up any processes started by `exec ... &` that have completed
+(reaps zombie processes).
+
+With one or two arguments, waits for a process by id, either returned by `exec ... &`
+or by `os.fork` (if supported).
+
+Waits for the process to complete, unless +-nohang+ is specified, in which case returns
+immediately if the process is still running.
+
+Returns a list of 3 elements.
+
++{NONE x x}+ if the process does not exist or has already been waited for, or
+if -nohang is specified, and the process is still alive.
+
++{CHILDSTATUS <pid> <exit-status>}+ if the process exited normally.
+
++{CHILDKILLED <pid> <signal>}+ if the process terminated on a signal.
+
++{CHILDSUSP <pid> none}+ if the process terminated for some other reason.
+
+Note that on platforms supporting waitpid(2), +pid+ can also be given special values such
+as 0 or -1. See waitpid(2) for more detail.
+
while
~~~~~
+*while* 'test body'+
@@ -4484,26 +4521,13 @@ OPTIONAL-EXTENSIONS
The following extensions may or may not be available depending upon
what options were selected when Jim Tcl was built.
+
[[cmd_1]]
-posix: os.fork, os.wait, os.gethostname, os.getids, os.uptime
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+posix: os.fork, os.gethostname, os.getids, os.uptime
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+*os.fork*+::
Invokes 'fork(2)' and returns the result.
-+*os.wait -nohang* 'pid'+::
- Invokes waitpid(2), with WNOHANG if +-nohang+ is specified.
- Returns a list of 3 elements.
-
- {0 none 0} if -nohang is specified, and the process is still alive.
-
- {-1 error <error-description>} if the process does not exist or has already been waited for.
-
- {<pid> exit <exit-status>} if the process exited normally.
-
- {<pid> signal <signal-number>} if the process terminated on a signal.
-
- {<pid> other 0} otherwise (core dump, stopped, continued, etc.)
-
+*os.gethostname*+::
Invokes 'gethostname(3)' and returns the result.
@@ -4759,11 +4783,10 @@ Various socket types may be created.
A UDP socket server.
+*socket pipe*+::
- A pipe. Note that unlike all other socket types, this command returns
- a list of two channels: {read write}
+ A synonym for `pipe`
+*socket pair*+::
- A socketpair (see socketpair(2)). Like `socket pipe`, this command returns
+ A socketpair (see socketpair(2)). Like `pipe`, this command returns
a list of two channels: {s1 s2}. These channels are both readable and writable.
This command creates a socket connected (client) or bound (server) to the given
diff --git a/jimiocompat.c b/jimiocompat.c
new file mode 100644
index 0000000..c8c3f86
--- /dev/null
+++ b/jimiocompat.c
@@ -0,0 +1,214 @@
+#include <string.h>
+#include "jimiocompat.h"
+
+void Jim_SetResultErrno(Jim_Interp *interp, const char *msg)
+{
+ Jim_SetResultFormatted(interp, "%s: %s", msg, strerror(Jim_Errno()));
+}
+
+#if defined(__MINGW32__)
+#include <sys/stat.h>
+
+int Jim_Errno(void)
+{
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND: return ENOENT;
+ case ERROR_PATH_NOT_FOUND: return ENOENT;
+ case ERROR_TOO_MANY_OPEN_FILES: return EMFILE;
+ case ERROR_ACCESS_DENIED: return EACCES;
+ case ERROR_INVALID_HANDLE: return EBADF;
+ case ERROR_BAD_ENVIRONMENT: return E2BIG;
+ case ERROR_BAD_FORMAT: return ENOEXEC;
+ case ERROR_INVALID_ACCESS: return EACCES;
+ case ERROR_INVALID_DRIVE: return ENOENT;
+ case ERROR_CURRENT_DIRECTORY: return EACCES;
+ case ERROR_NOT_SAME_DEVICE: return EXDEV;
+ case ERROR_NO_MORE_FILES: return ENOENT;
+ case ERROR_WRITE_PROTECT: return EROFS;
+ case ERROR_BAD_UNIT: return ENXIO;
+ case ERROR_NOT_READY: return EBUSY;
+ case ERROR_BAD_COMMAND: return EIO;
+ case ERROR_CRC: return EIO;
+ case ERROR_BAD_LENGTH: return EIO;
+ case ERROR_SEEK: return EIO;
+ case ERROR_WRITE_FAULT: return EIO;
+ case ERROR_READ_FAULT: return EIO;
+ case ERROR_GEN_FAILURE: return EIO;
+ case ERROR_SHARING_VIOLATION: return EACCES;
+ case ERROR_LOCK_VIOLATION: return EACCES;
+ case ERROR_SHARING_BUFFER_EXCEEDED: return ENFILE;
+ case ERROR_HANDLE_DISK_FULL: return ENOSPC;
+ case ERROR_NOT_SUPPORTED: return ENODEV;
+ case ERROR_REM_NOT_LIST: return EBUSY;
+ case ERROR_DUP_NAME: return EEXIST;
+ case ERROR_BAD_NETPATH: return ENOENT;
+ case ERROR_NETWORK_BUSY: return EBUSY;
+ case ERROR_DEV_NOT_EXIST: return ENODEV;
+ case ERROR_TOO_MANY_CMDS: return EAGAIN;
+ case ERROR_ADAP_HDW_ERR: return EIO;
+ case ERROR_BAD_NET_RESP: return EIO;
+ case ERROR_UNEXP_NET_ERR: return EIO;
+ case ERROR_NETNAME_DELETED: return ENOENT;
+ case ERROR_NETWORK_ACCESS_DENIED: return EACCES;
+ case ERROR_BAD_DEV_TYPE: return ENODEV;
+ case ERROR_BAD_NET_NAME: return ENOENT;
+ case ERROR_TOO_MANY_NAMES: return ENFILE;
+ case ERROR_TOO_MANY_SESS: return EIO;
+ case ERROR_SHARING_PAUSED: return EAGAIN;
+ case ERROR_REDIR_PAUSED: return EAGAIN;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_CANNOT_MAKE: return ENOSPC;
+ case ERROR_OUT_OF_STRUCTURES: return ENFILE;
+ case ERROR_ALREADY_ASSIGNED: return EEXIST;
+ case ERROR_INVALID_PASSWORD: return EPERM;
+ case ERROR_NET_WRITE_FAULT: return EIO;
+ case ERROR_NO_PROC_SLOTS: return EAGAIN;
+ case ERROR_DISK_CHANGE: return EXDEV;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ case ERROR_OPEN_FAILED: return ENOENT;
+ case ERROR_DISK_FULL: return ENOSPC;
+ case ERROR_NO_MORE_SEARCH_HANDLES: return EMFILE;
+ case ERROR_INVALID_TARGET_HANDLE: return EBADF;
+ case ERROR_INVALID_NAME: return ENOENT;
+ case ERROR_PROC_NOT_FOUND: return ESRCH;
+ case ERROR_WAIT_NO_CHILDREN: return ECHILD;
+ case ERROR_CHILD_NOT_COMPLETE: return ECHILD;
+ case ERROR_DIRECT_ACCESS_HANDLE: return EBADF;
+ case ERROR_SEEK_ON_DEVICE: return ESPIPE;
+ case ERROR_BUSY_DRIVE: return EAGAIN;
+ case ERROR_DIR_NOT_EMPTY: return EEXIST;
+ case ERROR_NOT_LOCKED: return EACCES;
+ case ERROR_BAD_PATHNAME: return ENOENT;
+ case ERROR_LOCK_FAILED: return EACCES;
+ case ERROR_ALREADY_EXISTS: return EEXIST;
+ case ERROR_FILENAME_EXCED_RANGE: return ENAMETOOLONG;
+ case ERROR_BAD_PIPE: return EPIPE;
+ case ERROR_PIPE_BUSY: return EAGAIN;
+ case ERROR_PIPE_NOT_CONNECTED: return EPIPE;
+ case ERROR_DIRECTORY: return ENOTDIR;
+ }
+ return EINVAL;
+}
+
+pidtype waitpid(pidtype pid, int *status, int nohang)
+{
+ DWORD ret = WaitForSingleObject(pid, nohang ? 0 : INFINITE);
+ if (ret == WAIT_TIMEOUT || ret == WAIT_FAILED) {
+ /* WAIT_TIMEOUT can only happend with WNOHANG */
+ return JIM_BAD_PID;
+ }
+ GetExitCodeProcess(pid, &ret);
+ *status = ret;
+ CloseHandle(pid);
+ return pid;
+}
+
+int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
+{
+ char name[MAX_PATH];
+ HANDLE handle;
+
+ if (!GetTempPath(MAX_PATH, name) || !GetTempFileName(name, filename_template ? filename_template : "JIM", 0, name)) {
+ return -1;
+ }
+
+ handle = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | (unlink_file ? FILE_FLAG_DELETE_ON_CLOSE : 0),
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ goto error;
+ }
+
+ Jim_SetResultString(interp, name, -1);
+ return _open_osfhandle((int)handle, _O_RDWR | _O_TEXT);
+
+ error:
+ Jim_SetResultErrno(interp, name);
+ DeleteFile(name);
+ return -1;
+}
+
+int Jim_OpenForWrite(const char *filename, int append)
+{
+ if (strcmp(filename, "/dev/null") == 0) {
+ filename = "nul:";
+ }
+ int fd = _open(filename, _O_WRONLY | _O_CREAT | _O_TEXT | (append ? _O_APPEND : _O_TRUNC), _S_IREAD | _S_IWRITE);
+ if (fd >= 0 && append) {
+ /* Note that _O_APPEND doesn't actually work. need to do it manually */
+ _lseek(fd, 0L, SEEK_END);
+ }
+ return fd;
+}
+
+int Jim_OpenForRead(const char *filename)
+{
+ if (strcmp(filename, "/dev/null") == 0) {
+ filename = "nul:";
+ }
+ return _open(filename, _O_RDONLY | _O_TEXT, 0);
+}
+
+#elif defined(HAVE_UNISTD_H)
+
+/* Unix-specific implementation */
+
+int Jim_MakeTempFile(Jim_Interp *interp, const char *filename_template, int unlink_file)
+{
+ int fd;
+ mode_t mask;
+ Jim_Obj *filenameObj;
+
+ if (filename_template == NULL) {
+ const char *tmpdir = getenv("TMPDIR");
+ if (tmpdir == NULL || *tmpdir == '\0' || access(tmpdir, W_OK) != 0) {
+ tmpdir = "/tmp/";
+ }
+ filenameObj = Jim_NewStringObj(interp, tmpdir, -1);
+ if (tmpdir[0] && tmpdir[strlen(tmpdir) - 1] != '/') {
+ Jim_AppendString(interp, filenameObj, "/", 1);
+ }
+ Jim_AppendString(interp, filenameObj, "tcl.tmp.XXXXXX", -1);
+ }
+ else {
+ filenameObj = Jim_NewStringObj(interp, filename_template, -1);
+ }
+
+ /* Update the template name directly with the filename */
+ mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
+#ifdef HAVE_MKSTEMP
+ fd = mkstemp(filenameObj->bytes);
+#else
+ if (mktemp(filenameObj->bytes) == NULL) {
+ fd = -1;
+ }
+ else {
+ fd = open(filenameObj->bytes, O_RDWR | O_CREAT | O_TRUNC);
+ }
+#endif
+ umask(mask);
+ if (fd < 0) {
+ Jim_SetResultErrno(interp, Jim_String(filenameObj));
+ Jim_FreeNewObj(interp, filenameObj);
+ return -1;
+ }
+ if (unlink_file) {
+ remove(Jim_String(filenameObj));
+ }
+
+ Jim_SetResult(interp, filenameObj);
+ return fd;
+}
+
+int Jim_OpenForWrite(const char *filename, int append)
+{
+ return open(filename, O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), 0666);
+}
+
+int Jim_OpenForRead(const char *filename)
+{
+ return open(filename, O_RDONLY, 0);
+}
+
+#endif
diff --git a/jimiocompat.h b/jimiocompat.h
new file mode 100644
index 0000000..2cbb578
--- /dev/null
+++ b/jimiocompat.h
@@ -0,0 +1,80 @@
+#ifndef JIMIOCOMPAT_H
+#define JIMIOCOMPAT_H
+
+/*
+ * Cross-platform compatibility functions and types for I/O.
+ * Currently used by jim-aio.c and jim-exec.c
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include "jimautoconf.h"
+#include <jim.h>
+#include <jim-win32compat.h>
+
+/**
+ * Set an error result based on errno and the given message.
+ */
+void Jim_SetResultErrno(Jim_Interp *interp, const char *msg);
+
+/**
+ * Opens the file for writing (and appending if append is true).
+ * Returns the file descriptor, or -1 on failure.
+ */
+int Jim_OpenForWrite(const char *filename, int append);
+
+/**
+ * Opens the file for reading.
+ * Returns the file descriptor, or -1 on failure.
+ */
+int Jim_OpenForRead(const char *filename);
+
+#if defined(__MINGW32__)
+ #ifndef STRICT
+ #define STRICT
+ #endif
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #include <fcntl.h>
+ #include <io.h>
+ #include <process.h>
+
+ typedef HANDLE pidtype;
+ #define JIM_BAD_PID INVALID_HANDLE_VALUE
+ /* Note that this isn't a separate value on Windows since we don't have os.fork */
+ #define JIM_NO_PID INVALID_HANDLE_VALUE
+
+ /* These seem to accord with the conventions used by msys/mingw32 */
+ #define WIFEXITED(STATUS) (((STATUS) & 0xff00) == 0)
+ #define WEXITSTATUS(STATUS) ((STATUS) & 0x00ff)
+ #define WIFSIGNALED(STATUS) (((STATUS) & 0xff00) != 0)
+ #define WTERMSIG(STATUS) (((STATUS) >> 8) & 0xff)
+ #define WNOHANG 1
+
+ /**
+ * Unix-compatible errno
+ */
+ int Jim_Errno(void);
+ pidtype waitpid(pidtype pid, int *status, int nohang);
+
+ #define HAVE_PIPE
+ #define pipe(P) _pipe((P), 0, O_NOINHERIT)
+
+#elif defined(HAVE_UNISTD_H)
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/wait.h>
+ #include <sys/stat.h>
+
+ typedef int pidtype;
+ #define Jim_Errno() errno
+ #define JIM_BAD_PID -1
+ #define JIM_NO_PID 0
+
+ #ifndef HAVE_EXECVPE
+ #define execvpe(ARG0, ARGV, ENV) execvp(ARG0, ARGV)
+ #endif
+#endif
+
+#endif
diff --git a/make-bootstrap-jim b/make-bootstrap-jim
index 23f852a..7484b11 100755
--- a/make-bootstrap-jim
+++ b/make-bootstrap-jim
@@ -115,7 +115,7 @@ outputsource()
}
# Now output header files, removing references to jim header files
-for i in jim-win32compat.h utf8.h jim.h jim-subcmd.h jimregexp.h ; do
+for i in jim-win32compat.h utf8.h jim.h jim-subcmd.h jimregexp.h jim-signal.h jimiocompat.h; do
outputsource $i
done
@@ -129,7 +129,7 @@ done
makeloadexts $allexts
# And finally the core source code
-for i in jim.c jim-subcmd.c utf8.c jim-format.c jimregexp.c jim-win32compat.c; do
+for i in jim.c jim-subcmd.c utf8.c jim-format.c jimregexp.c jimiocompat.c jim-win32compat.c jim-nosignal.c; do
outputsource $i
done
echo "#ifndef JIM_BOOTSTRAP_LIB_ONLY"
diff --git a/tclcompat.tcl b/tclcompat.tcl
index e2ae56e..b3452d8 100644
--- a/tclcompat.tcl
+++ b/tclcompat.tcl
@@ -120,7 +120,7 @@ proc {file copy} {{force {}} source target} {
# 'open "|..." ?mode?" will invoke this wrapper around exec/pipe
# Note that we return a lambda which also provides the 'pid' command
proc popen {cmd {mode r}} {
- lassign [socket pipe] r w
+ lassign [pipe] r w
try {
if {[string match "w*" $mode]} {
lappend cmd <@$r &
@@ -137,11 +137,26 @@ proc popen {cmd {mode r}} {
if {$cmd eq "pid"} {
return $pids
}
+ if {$cmd eq "getfd"} {
+ $f getfd
+ }
if {$cmd eq "close"} {
$f close
# And wait for the child processes to complete
- foreach p $pids { os.wait $p }
- return
+ set retopts {}
+ foreach p $pids {
+ lassign [wait $p] status - rc
+ if {$status eq "CHILDSTATUS"} {
+ if {$rc == 0} {
+ continue
+ }
+ set msg "child process exited abnormally"
+ } else {
+ set msg "child killed: received signal"
+ }
+ set retopts [list -code error -errorcode [list $status $p $rc] $msg]
+ }
+ return {*}$retopts
}
tailcall $f $cmd {*}$args
}
diff --git a/tests/exec.test b/tests/exec.test
index 76a1b2d..0eb218a 100644
--- a/tests/exec.test
+++ b/tests/exec.test
@@ -17,26 +17,23 @@ source [file dirname [info script]]/testing.tcl
needs cmd exec
needs cmd flush
-needs cmd after eventloop
testConstraint unix [expr {$tcl_platform(platform) eq {unix}}]
# Sleep which supports fractions of a second
if {[info commands sleep] eq {}} {
proc sleep {n} {
- after [expr {int($n * 1000)}]
+ exec {*}$::sleepx $n
}
}
set f [open sleepx w]
-puts $f "#![info nameofexecutable]"
puts $f {
- set seconds [lindex $argv 0]
- after [expr {int($seconds * 1000)}]
+ sleep "$@"
}
close $f
#catch {exec chmod +x sleepx}
-set sleepx [list [info nameofexecutable] sleepx]
+set sleepx [list sh sleepx]
# Basic operations.
@@ -416,6 +413,29 @@ test exec-16.1 {flush output before exec} -body {
Second line
Third line}
+test exec-17.1 {redirecting from command pipeline} -setup {
+ makeFile "abc\nghi\njkl" gorp.file
+} -body {
+ set f [open "|cat gorp.file | wc -l" r]
+ set result [lindex [exec cat <@$f] 0]
+ close $f
+ set result
+} -cleanup {
+ file delete gorp.file
+} -result {3}
+
+test exec-17.2 {redirecting to command pipeline} -setup {
+ makeFile "abc\nghi\njkl" gorp.file
+} -body {
+ set f [open "|wc -l >gorp2.file" w]
+ exec cat gorp.file >@$f
+ flush $f
+ close $f
+ lindex [exec cat gorp2.file] 0
+} -cleanup {
+ file delete gorp.file gorp2.file
+} -result {3}
+
file delete sleepx
testreport
diff --git a/tests/exec2.test b/tests/exec2.test
index e43bba0..91108da 100644
--- a/tests/exec2.test
+++ b/tests/exec2.test
@@ -44,4 +44,30 @@ test exec2-2.4 "Remove all env var" {
array set env [array get saveenv]
+test exec2-3.1 "close pipeline return value" {
+ set f [open |false]
+ set rc [catch {close $f} msg opts]
+ lassign [dict get $opts -errorcode] status pid exitcode
+ list $rc $msg $status $exitcode
+} {1 {child process exited abnormally} CHILDSTATUS 1}
+
+test exec2-3.2 "close pipeline return value" -body {
+ set f [open "|echo abc | grep def | wc" ]
+ set rc [catch {close $f} msg opts]
+ lassign [dict get $opts -errorcode] status pid exitcode
+ list $rc $msg $status $exitcode
+} -match glob -result {1 {child killed*} CHILDKILLED SIGPIPE}
+
+
+test exec2-3.4 "wait for background task" {
+ set pid [exec sleep 0.1 &]
+ lassign [wait $pid] status newpid exitcode
+ if {$pid != $newpid} {
+ error "Got wrong pid from wait"
+ } else {
+ list $status $exitcode
+ }
+} {CHILDSTATUS 0}
+
+
testreport
diff --git a/tests/pid.test b/tests/pid.test
index 6a534a5..56ffcf8 100644
--- a/tests/pid.test
+++ b/tests/pid.test
@@ -19,7 +19,7 @@ needs cmd pid posix
needs cmd exec
catch {package require regexp}
testConstraint regexp [expr {[info commands regexp] ne {}}]
-testConstraint socket [expr {[info commands socket] ne {}}]
+testConstraint pipe [expr {[info commands pipe] ne {}}]
testConstraint getpid [expr {[catch pid] == 0}]
# This is a proxy for tcl || tclcompat
testConstraint pidchan [expr {[info commands fconfigure] ne {}}]
@@ -29,7 +29,7 @@ file delete test1
test pid-1.1 {pid command} {regexp getpid} {
regexp {(^[0-9]+$)|(^0x[0-9a-fA-F]+$)} [pid]
} 1
-test pid-1.2 {pid command} {regexp socket pidchan} {
+test pid-1.2 {pid command} {regexp pipe pidchan} {
set f [open {| echo foo | cat >test1} w]
set pids [pid $f]
close $f
@@ -38,7 +38,7 @@ test pid-1.2 {pid command} {regexp socket pidchan} {
[regexp {^[0-9]+$} [lindex $pids 1]] \
[expr {[lindex $pids 0] == [lindex $pids 1]}]
} {2 1 1 0}
-test pid-1.3 {pid command} {socket pidchan} {
+test pid-1.3 {pid command} {pipe pidchan} {
set f [open test1 w]
set pids [pid $f]
close $f