<pre style='margin:0'>
Rainer Müller (raimue) pushed a commit to branch vcs-fetch
in repository macports-base.
</pre>
<p><a href="https://github.com/macports/macports-base/commit/114227909bc1cba340d0c67624c533f1178db627">https://github.com/macports/macports-base/commit/114227909bc1cba340d0c67624c533f1178db627</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit 114227909bc1cba340d0c67624c533f1178db627
</span>Author: Rainer Müller <raimue@macports.org>
AuthorDate: Sun Apr 15 17:30:30 2018 +0200
<span style='display:block; white-space:pre;color:#404040;'> pextlib: Add system -o/-e to capture stdout/stderr
</span>---
src/pextlib1.0/system.c | 156 ++++++++++++++++++++++++++++++++++------
src/pextlib1.0/tests/system.tcl | 18 +++++
2 files changed, 152 insertions(+), 22 deletions(-)
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/pextlib1.0/system.c b/src/pextlib1.0/system.c
</span><span style='display:block; white-space:pre;color:#808080;'>index c043dd3..81ad99d 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/pextlib1.0/system.c
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/pextlib1.0/system.c
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -56,6 +56,7 @@
</span> #include <limits.h>
#include <errno.h>
#include <signal.h>
<span style='display:block; white-space:pre;background:#e0ffe0;'>+#include <poll.h>
</span>
#include "system.h"
#include "Pextlib.h"
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -109,7 +110,7 @@ static void handle_sigint(int s) {
</span> interrupted_by = s;
}
<span style='display:block; white-space:pre;background:#ffe0e0;'>-/* usage: system ?-notty? ?-nodup? ?-nice value? ?-W path? command */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+/* usage: system ?-o outvar? ?-e errvar? ?-notty? ?-nodup? ?-nice value? ?-W path? command */
</span> int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *args[7];
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -117,10 +118,11 @@ int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Ob
</span> int sandbox = 0;
char *sandbox_exec_path = NULL;
char *profilestr = NULL;
<span style='display:block; white-space:pre;background:#ffe0e0;'>- FILE *pdes;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- int fdset[2], nullfd;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int fdseto[2], fdsete[2], nullfd;
</span> int ret;
int osetsid = 0;
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_Obj *ograb = NULL; /* != NULL, grab output from stdout */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_Obj *egrab = NULL; /* != NULL, grab output from stderr */
</span> int odup = 1; /* redirect stdin/stdout/stderr by default */
int oniceval = INT_MAX; /* magic value indicating no change */
const char *path = NULL;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -140,7 +142,13 @@ int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Ob
</span>
for (i = 1; i < objc - 1; i++) {
char *arg = Tcl_GetString(objv[i]);
<span style='display:block; white-space:pre;background:#ffe0e0;'>- if (strcmp(arg, "-notty") == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (strcmp(arg, "-o") == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ i++;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ograb = objv[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else if (strcmp(arg, "-e") == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ i++;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ egrab = objv[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else if (strcmp(arg, "-notty") == 0) {
</span> osetsid = 1;
} else if (strcmp(arg, "-nodup") == 0) {
odup = 0;
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -171,6 +179,11 @@ int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Ob
</span> ui_debug(interp, "system: %s", cmdstring);
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((ograb || egrab) && !odup) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_SetResult(interp, "-o or -e cannot be used together with -nodup", TCL_STATIC);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> /* check if and how we should use sandbox-exec */
sandbox = check_sandboxing(interp, &sandbox_exec_path, &profilestr);
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -179,10 +192,16 @@ int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Ob
</span> * popen() itself is not used because stderr is also desired.
*/
if (odup) {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- if (pipe(fdset) != 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (pipe(fdseto) != 0) {
</span> Tcl_SetResult(interp, strerror(errno), TCL_STATIC);
return TCL_ERROR;
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (pipe(fdsete) != 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_SetResult(interp, strerror(errno), TCL_STATIC);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ return TCL_ERROR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> }
/*
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -212,13 +231,20 @@ int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Ob
</span> /*NOTREACHED*/
case 0: /* child */
if (odup) {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- close(fdset[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ close(fdseto[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ close(fdsete[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span>
if ((nullfd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
_exit(1);
dup2(nullfd, STDIN_FILENO);
<span style='display:block; white-space:pre;background:#ffe0e0;'>- dup2(fdset[1], STDOUT_FILENO);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- dup2(fdset[1], STDERR_FILENO);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dup2(fdseto[1], STDOUT_FILENO);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dup2(fdseto[1], STDERR_FILENO);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ dup2(fdsete[1], STDERR_FILENO);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> }
/* drop the controlling terminal if requested */
if (osetsid) {
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -282,29 +308,112 @@ int SystemCmd(ClientData clientData UNUSED, Tcl_Interp *interp, int objc, Tcl_Ob
</span> }
if (odup) {
<span style='display:block; white-space:pre;background:#ffe0e0;'>- close(fdset[1]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ close(fdseto[1]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ close(fdsete[1]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ FILE *fpout = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ FILE *fperr = NULL;
</span>
<span style='display:block; white-space:pre;background:#ffe0e0;'>- /* read from simulated popen() pipe */
</span> read_failed = 0;
<span style='display:block; white-space:pre;background:#ffe0e0;'>- pdes = fdopen(fdset[0], "r");
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- if (pdes) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fpout = fdopen(fdseto[0], "r");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!fpout) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ read_failed = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_SetResult(interp, strerror(errno), TCL_STATIC);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fperr = fdopen(fdsete[0], "r");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!fperr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ read_failed = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_SetResult(interp, strerror(errno), TCL_STATIC);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (!read_failed) {
</span> char *line = NULL;
size_t linesz = 0;
ssize_t linelen;
<span style='display:block; white-space:pre;background:#ffe0e0;'>- while ((linelen = getline(&line, &linesz, pdes)) > 0) {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- /* replace '\n' if it exists */
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- if (line[linelen - 1] == '\n') {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- line[linelen - 1] = '\0';
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ struct pollfd fds[2];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ FILE **fps[2];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ struct Tcl_Obj *tclobjs[2] = { NULL, NULL };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ int nfds = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fds[0].fd = fileno(fpout);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fds[0].events = POLLIN;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fps[0] = &fpout;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (ograb) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tclobjs[0] = Tcl_NewStringObj("", -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (fperr != NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fds[1].fd = fileno(fperr);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fds[1].events = POLLIN;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fps[1] = &fperr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ tclobjs[1] = Tcl_NewStringObj("", -1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ nfds++;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* read from stdout/stderr pipes */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ while (read_failed == 0 && (fpout != NULL || fperr != NULL)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (poll(fds, nfds, 0) < 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (errno == EINTR) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ read_failed = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_SetResult(interp, strerror(errno), TCL_STATIC);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ break;
</span> }
<span style='display:block; white-space:pre;background:#ffe0e0;'>- ui_info(interp, "%s", line);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ for (i = 0; i < nfds; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((fds[i].revents & ~(POLLIN|POLLHUP)) != 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ read_failed = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_Obj *result = Tcl_ObjPrintf("system: poll: unexpected revent %d", fds[i].revents);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_SetObjResult(interp, result);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if ((fds[i].revents & (POLLIN|POLLHUP)) != 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* if the event is POLLHUP, another read will also give us EOF */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ linelen = getline(&line, &linesz, *fps[i]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (linelen > 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (tclobjs[i] != NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_AppendToObj(tclobjs[i], line, linelen);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* replace '\n' if it exists */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (line[linelen - 1] == '\n') {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ line[linelen - 1] = '\0';
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ ui_info(interp, "%s", line);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* EOF */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fclose(*fps[i]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ *fps[i] = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fds[i].events &= ~POLLIN;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* cleanup */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (fpout != NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fclose(fpout);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (fperr != NULL) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ fclose(fperr);
</span> }
<span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* assign output variables */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (ograb) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_ObjSetVar2(interp, ograb, NULL, tclobjs[0], 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ Tcl_ObjSetVar2(interp, egrab, NULL, tclobjs[1], 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> free(line);
<span style='display:block; white-space:pre;background:#ffe0e0;'>- fclose(pdes);
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- } else {
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- read_failed = 1;
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>- Tcl_SetResult(interp, strerror(errno), TCL_STATIC);
</span> }
}
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -360,7 +469,10 @@ cleanup:
</span>
if (odup) {
/* Cleanup. */
<span style='display:block; white-space:pre;background:#ffe0e0;'>- close(fdset[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ close(fdseto[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if (egrab) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ close(fdsete[0]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span> }
return status;
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/src/pextlib1.0/tests/system.tcl b/src/pextlib1.0/tests/system.tcl
</span><span style='display:block; white-space:pre;color:#808080;'>index 7a3b2fc..b094a80 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/src/pextlib1.0/tests/system.tcl
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/src/pextlib1.0/tests/system.tcl
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -70,6 +70,24 @@ proc main {pextlibname} {
</span> check [string trim $output] "/usr"
}
<span style='display:block; white-space:pre;background:#e0ffe0;'>+ test_system -o out "echo stdout" {out} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ check [string trim $out] "stdout"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ test_system -o out "echo stdout; echo stderr >&2" {out} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ check [string trim $out] "stdout\nstderr"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ test_system -e err "echo stdout; echo stderr >&2" {err} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ check [string trim $output] "stdout"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ check [string trim $err] "stderr"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ test_system -o out -e err "echo stdout; echo stderr >&2" {out err} {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ check [string trim $out] "stdout"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ check [string trim $err] "stderr"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span> if {$failures > 0} {
exit 1
}
</pre><pre style='margin:0'>
</pre>