<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>