Subject: Re: [armedbear-devel] run-program :environment nil
From: Rudi Schlatte <[email-masked]>
To: Ville Voutilainen <[email-masked]>
Date: Thu, 29 Mar 2012 15:41:39 +0200

Ville Voutilainen <[email-masked]> writes:

> Here's a tentative patch. I opted for a new keyword argument, since
> the new function approach would've led to duplication or to a macro
> that has the new argument anyway.  Untested so far, so this is for
> general review. Also lacking the docstring changes.

Here's my version, using your getenv and process-builder changes.  I
get by with only the :environment key, see the docstring for an example
on how to overwrite PATH and keep the rest.  Lightly tested.

Rudi

Changes in HEAD
	Modified   src/org/armedbear/lisp/Extensions.java
diff --git a/src/org/armedbear/lisp/Extensions.java b/src/org/armedbear/lisp/Extensions.java
index 5ff3bb1..5d3e2b5 100644
--- a/src/org/armedbear/lisp/Extensions.java
+++ b/src/org/armedbear/lisp/Extensions.java
@@ -37,6 +37,7 @@ import static org.armedbear.lisp.Lisp.*;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.*;
 
 public final class Extensions
 {
@@ -317,4 +318,29 @@ public final class Extensions
         return NIL;
     }
   }
+
+
+  // ### getenv-all => alist
+  private static final Primitive GETENV_ALL = new getenv_all();
+  private static class getenv_all extends Primitive
+  {
+    getenv_all()
+    {
+      super("getenv-all", PACKAGE_EXT, true, "variable",
+             "Returns all environment variables as an alist containing (name . value)");
+    }
+    @Override
+    public LispObject execute()
+    {
+      LispObject result = NIL;
+      Map<String, String> env = System.getenv();
+      for (Map.Entry<String, String> entry : env.entrySet()) {
+          Cons entryPair = new Cons(new SimpleString(entry.getKey()),
+                                    new SimpleString(entry.getValue()));
+          result = new Cons(entryPair, result);
+      }
+      return result;
+    }
+  }
+
 }
	Modified   src/org/armedbear/lisp/run-program.lisp
diff --git a/src/org/armedbear/lisp/run-program.lisp b/src/org/armedbear/lisp/run-program.lisp
index 042d2a5..36da70e 100644
--- a/src/org/armedbear/lisp/run-program.lisp
+++ b/src/org/armedbear/lisp/run-program.lisp
@@ -40,12 +40,14 @@
 ;;; This implementation uses the JVM facilities for running external
 ;;; processes.
 ;;; <http://download.oracle.com/javase/6/docs/api/java/lang/ProcessBuilder.html>.
-(defun run-program (program args &key environment (wait t))
+(defun run-program (program args &key (environment nil environment-p) (wait t))
   ;;For documentation, see below.
   (let ((pb (%make-process-builder program args)))
-    (when environment
+    (when environment-p
       (let ((env-map (%process-builder-environment pb)))
-        (dolist (entry environment)
+        (%process-builder-env-clear env-map)
+        ;; entries at the front have priority, similar to keyword processing
+        (dolist (entry (reverse environment))
           (%process-builder-env-put env-map
                                     (princ-to-string (car entry))
                                     (princ-to-string (cdr entry))))))
@@ -55,7 +57,7 @@
 
 (setf (documentation 'run-program 'function)
       "Creates a new process running the the PROGRAM.
-ARGS are a list of strings to be passed to the program as arguments. 
+ARGS is a list of strings to be passed to the program as arguments. 
 
 For no arguments, use nil which means that just the name of the
 program is passed as arg 0.
@@ -80,9 +82,16 @@ Notes about Unix environments (as in the :environment):
 The &key arguments have the following meanings:
 
 :environment 
+
     An alist of STRINGs (name . value) describing the new
-    environment. The default is to copy the environment of the current
-    process.
+    environment. The default is to use the environment of the current
+    process. When an explicit :environment argument (including NIL) is
+    passed, no merging with the current environment takes place.  If the
+    argument contains multiple entries with the same name, the first one
+    takes precedence.  E.g., to overwrite the PATH entry, use
+
+    (run-program \"ls\" nil
+      :environment (acons \"PATH\" \"/bin:/usr/bin\" (ext:getenv-all)))
 
 :wait 
     If non-NIL, which is the default, wait until the created process
@@ -131,6 +140,9 @@ The &key arguments have the following meanings:
 (defun %process-builder-env-put (env-map key value)
   (java:jcall "put" env-map key value))
 
+(defun %process-builder-env-clear (env-map)
+  (java:jcall "clear" env-map))
+
 (defun %process-builder-start (pb)
   (java:jcall "start" pb))