Mark Evenson evenson@panix.com writes:
On Mar 26, 2012, at 6:12 PM, Pascal J. Bourguignon wrote:
(lisp-implementation-version) --> "1.0.1"
The documentation of extensions:run-program is misleading:
:environment An alist of STRINGs (name . value) describing the new environment. The default is to copy the environment of the current process.
The alist doesn't describe the NEW environment, it is only MERGED into the current environment.
[…]
Which behavior would you prefer?
I agree that for usual programs, merging is useful. On the other hand, if you mess with the environment it's often for security considerations, and there it's better to start with a blank slate.
I certainly expected that :environment '() provide an empty environment, just like execle/execvpe:
char* args[]={"env",0}; char* envv[]={0}; execpe("/bin/env",args,envv);
We certainly have imprecise documentation here. The merged environment behavior what is easiest with the underlying JVM API which is based on the notion of UNIX exec(). In practice, I would venture that that most people would expect the merged behavior, as it is the common pattern for such wrappers around UNIX execv() and friends which have to copy the environment anyways at an operating system level before it replaces it with the new code. (Ok, since every contemporary OS probably has "copy-on-write" semantics here, I'll stop making OS-dependent statements of doubtful utility).
I like the following API, which is implemented in clisp.
(getenv) --> the current environment as an a-list. (("LC_CTYPE" . "C") ("TERM" . "/bin/bash") …)
(getenv "TERM") --> the string value, or NIL if the variable is not present in the environment. "/bin/bash"
(setf (getenv "LC_CTYPE") "en_US.UTF-8")
changes the environment for the current process and its future children.
(setf (getenv "LC_CTYPE") nil)
removes the environment variable if it exists in the environment of the current process and its future children.
Then merging behavior for run-program would be obtained with:
(run-program … :environment (acons "TERM" "linux" (getenv)))
Replacing behavior:
(run-program … :environment '(("TERM" . "linux") ("SHELL" . "/bin/bash")))
and an empty environment can be provided:
(run-program … :environment '())
run-program wouldn't change the environment of the current process.
(let ((env (getenv))) (run-program … :environment other-env) (set-equal env (getenv) :test 'equal)) ;; (there's no guarantee on the order of the environment).
I would advocate tightening the documentation. If Pascal has a further need here, I'll take a stab at a version which would somehow "wipe" the inherited environment.
Since the semantics of our EXT:RUN-PROGRAM implementation were cribbed from SBCL's under the need to implement just as much as ASDF2 required, we should analyze what SBCL does in this situation as well.
Also, in a shell environment, an empty variable is not the same as an inexistant variable, so I cannot just loop over all the existing variables to set them to an empty string. I see no SYSTEM::%PROCESS-BUILDER-ENV-REM function…
This sounds like you really want the ability to constructs a clean NEW environment. If we were able to implement that, would your need to iterate through existing variables disappear?
For the specific purpose of calling run-program, yes. However, I think it's useful to be able to iterate thru existing variables.