mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 22:50:38 +03:00
Merge pull request #7727 from CyberShadow/process
std.process touchups merged-on-behalf-of: Nicholas Wilson <thewilsonator@users.noreply.github.com>
This commit is contained in:
commit
970317c461
1 changed files with 89 additions and 82 deletions
171
std/process.d
171
std/process.d
|
@ -108,6 +108,7 @@ version (Windows)
|
||||||
import std.internal.cstring;
|
import std.internal.cstring;
|
||||||
import std.range.primitives;
|
import std.range.primitives;
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
import std.traits : isSomeChar;
|
||||||
|
|
||||||
version (OSX)
|
version (OSX)
|
||||||
version = Darwin;
|
version = Darwin;
|
||||||
|
@ -213,9 +214,7 @@ static:
|
||||||
string opIndex(scope const(char)[] name) @safe
|
string opIndex(scope const(char)[] name) @safe
|
||||||
{
|
{
|
||||||
import std.exception : enforce;
|
import std.exception : enforce;
|
||||||
string value;
|
return get(name, null).enforce("Environment variable not found: "~name);
|
||||||
enforce(getImpl(name, value), "Environment variable not found: "~name);
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -253,8 +252,8 @@ static:
|
||||||
string get(scope const(char)[] name, string defaultValue = null) @safe
|
string get(scope const(char)[] name, string defaultValue = null) @safe
|
||||||
{
|
{
|
||||||
string value;
|
string value;
|
||||||
auto found = getImpl(name, value);
|
getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; });
|
||||||
return found ? value : defaultValue;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,7 +321,7 @@ static:
|
||||||
multi-threaded programs. See e.g.
|
multi-threaded programs. See e.g.
|
||||||
$(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
|
$(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).
|
||||||
*/
|
*/
|
||||||
void remove(scope const(char)[] name) @trusted nothrow @nogc // TODO: @safe
|
void remove(scope const(char)[] name) @trusted nothrow @nogc
|
||||||
{
|
{
|
||||||
version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
|
version (Windows) SetEnvironmentVariableW(name.tempCStringW(), null);
|
||||||
else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
|
else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());
|
||||||
|
@ -446,8 +445,13 @@ static:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Retrieves the environment variable, returns false on failure.
|
version (Windows) alias OSChar = WCHAR;
|
||||||
bool getImpl(scope const(char)[] name, out string value) @trusted
|
else version (Posix) alias OSChar = char;
|
||||||
|
|
||||||
|
// Retrieves the environment variable. Calls `sink` with a
|
||||||
|
// temporary buffer of OS characters, or `null` if the variable
|
||||||
|
// doesn't exist.
|
||||||
|
void getImpl(scope const(char)[] name, scope void delegate(const(OSChar)[]) @safe sink) @trusted
|
||||||
{
|
{
|
||||||
version (Windows)
|
version (Windows)
|
||||||
{
|
{
|
||||||
|
@ -468,15 +472,12 @@ private:
|
||||||
{
|
{
|
||||||
immutable err = GetLastError();
|
immutable err = GetLastError();
|
||||||
if (err == ERROR_ENVVAR_NOT_FOUND)
|
if (err == ERROR_ENVVAR_NOT_FOUND)
|
||||||
return false;
|
return sink(null);
|
||||||
if (err != NO_ERROR) // Some other Windows error, throw.
|
if (err != NO_ERROR) // Some other Windows error, throw.
|
||||||
throw new WindowsException(err);
|
throw new WindowsException(err);
|
||||||
}
|
}
|
||||||
if (len <= 1)
|
if (len <= 1)
|
||||||
{
|
return sink("");
|
||||||
value = "";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
buf.length = len;
|
buf.length = len;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -488,21 +489,15 @@ private:
|
||||||
{
|
{
|
||||||
immutable err = GetLastError();
|
immutable err = GetLastError();
|
||||||
if (err == NO_ERROR) // sucessfully read a 0-length variable
|
if (err == NO_ERROR) // sucessfully read a 0-length variable
|
||||||
{
|
return sink("");
|
||||||
value = "";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist
|
if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist
|
||||||
return false;
|
return sink(null);
|
||||||
// some other windows error
|
// some other windows error
|
||||||
throw new WindowsException(err);
|
throw new WindowsException(err);
|
||||||
}
|
}
|
||||||
assert(lenRead != buf.length, "impossible according to msft docs");
|
assert(lenRead != buf.length, "impossible according to msft docs");
|
||||||
if (lenRead < buf.length) // the buffer was long enough
|
if (lenRead < buf.length) // the buffer was long enough
|
||||||
{
|
return sink(buf[0 .. lenRead]);
|
||||||
value = toUTF8(buf[0 .. lenRead]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// resize and go around again, because the environment variable grew
|
// resize and go around again, because the environment variable grew
|
||||||
buf.length = lenRead;
|
buf.length = lenRead;
|
||||||
}
|
}
|
||||||
|
@ -512,26 +507,31 @@ private:
|
||||||
import core.stdc.string : strlen;
|
import core.stdc.string : strlen;
|
||||||
|
|
||||||
const vz = core.sys.posix.stdlib.getenv(name.tempCString());
|
const vz = core.sys.posix.stdlib.getenv(name.tempCString());
|
||||||
if (vz == null) return false;
|
if (vz == null) return sink(null);
|
||||||
auto v = vz[0 .. strlen(vz)];
|
return sink(vz[0 .. strlen(vz)]);
|
||||||
|
}
|
||||||
// Cache the last call's result.
|
|
||||||
static string lastResult;
|
|
||||||
if (v.empty)
|
|
||||||
{
|
|
||||||
// Return non-null array for blank result to distinguish from
|
|
||||||
// not-present result.
|
|
||||||
lastResult = "";
|
|
||||||
}
|
|
||||||
else if (v != lastResult)
|
|
||||||
{
|
|
||||||
lastResult = v.idup;
|
|
||||||
}
|
|
||||||
value = lastResult;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else static assert(0);
|
else static assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string cachedToString(C)(scope const(C)[] v) @safe
|
||||||
|
{
|
||||||
|
import std.algorithm.comparison : equal;
|
||||||
|
|
||||||
|
// Cache the last call's result.
|
||||||
|
static string lastResult;
|
||||||
|
if (v.empty)
|
||||||
|
{
|
||||||
|
// Return non-null array for blank result to distinguish from
|
||||||
|
// not-present result.
|
||||||
|
lastResult = "";
|
||||||
|
}
|
||||||
|
else if (!v.equal(lastResult))
|
||||||
|
{
|
||||||
|
import std.conv : to;
|
||||||
|
lastResult = v.to!string;
|
||||||
|
}
|
||||||
|
return lastResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@safe unittest
|
@safe unittest
|
||||||
|
@ -809,17 +809,17 @@ Pid spawnProcess(scope const(char[])[] args,
|
||||||
const string[string] env = null,
|
const string[string] env = null,
|
||||||
Config config = Config.none,
|
Config config = Config.none,
|
||||||
scope const char[] workDir = null)
|
scope const char[] workDir = null)
|
||||||
@trusted // TODO: Should be @safe
|
@safe
|
||||||
{
|
{
|
||||||
version (Windows)
|
version (Windows)
|
||||||
{
|
{
|
||||||
const commandLine = escapeShellArguments(args);
|
const commandLine = escapeShellArguments(args);
|
||||||
const program = args.length ? args[0] : null;
|
const program = args.length ? args[0] : null;
|
||||||
return spawnProcessImpl(commandLine, program, stdin, stdout, stderr, env, config, workDir);
|
return spawnProcessWin(commandLine, program, stdin, stdout, stderr, env, config, workDir);
|
||||||
}
|
}
|
||||||
else version (Posix)
|
else version (Posix)
|
||||||
{
|
{
|
||||||
return spawnProcessImpl(args, stdin, stdout, stderr, env, config, workDir);
|
return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
static assert(0);
|
static assert(0);
|
||||||
|
@ -882,13 +882,13 @@ envz should be a zero-terminated array of zero-terminated strings
|
||||||
on the form "var=value".
|
on the form "var=value".
|
||||||
*/
|
*/
|
||||||
version (Posix)
|
version (Posix)
|
||||||
private Pid spawnProcessImpl(scope const(char[])[] args,
|
private Pid spawnProcessPosix(scope const(char[])[] args,
|
||||||
File stdin,
|
File stdin,
|
||||||
File stdout,
|
File stdout,
|
||||||
File stderr,
|
File stderr,
|
||||||
scope const string[string] env,
|
scope const string[string] env,
|
||||||
Config config,
|
Config config,
|
||||||
scope const(char)[] workDir)
|
scope const(char)[] workDir)
|
||||||
@trusted // TODO: Should be @safe
|
@trusted // TODO: Should be @safe
|
||||||
{
|
{
|
||||||
import core.exception : RangeError;
|
import core.exception : RangeError;
|
||||||
|
@ -1217,14 +1217,14 @@ envz must be a pointer to a block of UTF-16 characters on the form
|
||||||
"var1=value1\0var2=value2\0...varN=valueN\0\0".
|
"var1=value1\0var2=value2\0...varN=valueN\0\0".
|
||||||
*/
|
*/
|
||||||
version (Windows)
|
version (Windows)
|
||||||
private Pid spawnProcessImpl(scope const(char)[] commandLine,
|
private Pid spawnProcessWin(scope const(char)[] commandLine,
|
||||||
scope const(char)[] program,
|
scope const(char)[] program,
|
||||||
File stdin,
|
File stdin,
|
||||||
File stdout,
|
File stdout,
|
||||||
File stderr,
|
File stderr,
|
||||||
const string[string] env,
|
scope const string[string] env,
|
||||||
Config config,
|
Config config,
|
||||||
scope const(char)[] workDir)
|
scope const(char)[] workDir)
|
||||||
@trusted
|
@trusted
|
||||||
{
|
{
|
||||||
import core.exception : RangeError;
|
import core.exception : RangeError;
|
||||||
|
@ -1291,12 +1291,7 @@ private Pid spawnProcessImpl(scope const(char)[] commandLine,
|
||||||
if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr,
|
if (!CreateProcessW(null, commandLine.tempCStringW().buffPtr,
|
||||||
null, null, true, dwCreationFlags,
|
null, null, true, dwCreationFlags,
|
||||||
envz, workDir.length ? pworkDir : null, &startinfo, &pi))
|
envz, workDir.length ? pworkDir : null, &startinfo, &pi))
|
||||||
{
|
throw ProcessException.newFromLastError("Failed to spawn process \"" ~ cast(string) program ~ '"');
|
||||||
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
|
||||||
throw new ProcessException(text("Executable file not found: ", program));
|
|
||||||
else
|
|
||||||
throw ProcessException.newFromLastError("Failed to spawn new process");
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out if we should close any of the streams
|
// figure out if we should close any of the streams
|
||||||
if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO
|
if (!(config & Config.retainStdin ) && stdinFD > STDERR_FILENO
|
||||||
|
@ -1434,28 +1429,39 @@ version (Windows) @system unittest
|
||||||
// (checking that it is in fact executable).
|
// (checking that it is in fact executable).
|
||||||
version (Posix)
|
version (Posix)
|
||||||
private string searchPathFor(scope const(char)[] executable)
|
private string searchPathFor(scope const(char)[] executable)
|
||||||
@trusted //TODO: @safe nothrow
|
@safe
|
||||||
{
|
{
|
||||||
import std.algorithm.iteration : splitter;
|
import std.algorithm.iteration : splitter;
|
||||||
import std.conv : to;
|
import std.conv : text;
|
||||||
import std.path : buildPath;
|
import std.path : chainPath;
|
||||||
|
|
||||||
auto pathz = core.stdc.stdlib.getenv("PATH");
|
string result;
|
||||||
if (pathz == null) return null;
|
|
||||||
|
|
||||||
foreach (dir; splitter(to!string(pathz), ':'))
|
environment.getImpl("PATH",
|
||||||
{
|
(scope const(char)[] path)
|
||||||
auto execPath = buildPath(dir, executable);
|
{
|
||||||
if (isExecutable(execPath)) return execPath;
|
if (!path)
|
||||||
}
|
return;
|
||||||
|
|
||||||
return null;
|
foreach (dir; splitter(path, ":"))
|
||||||
|
{
|
||||||
|
auto execPath = chainPath(dir, executable);
|
||||||
|
if (isExecutable(execPath))
|
||||||
|
{
|
||||||
|
result = text(execPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether the file exists and can be executed by the
|
// Checks whether the file exists and can be executed by the
|
||||||
// current user.
|
// current user.
|
||||||
version (Posix)
|
version (Posix)
|
||||||
private bool isExecutable(scope const(char)[] path) @trusted nothrow @nogc //TODO: @safe
|
private bool isExecutable(R)(R path) @trusted nothrow @nogc
|
||||||
|
if (isInputRange!R && isSomeChar!(ElementEncodingType!R))
|
||||||
{
|
{
|
||||||
return (access(path.tempCString(), X_OK) == 0);
|
return (access(path.tempCString(), X_OK) == 0);
|
||||||
}
|
}
|
||||||
|
@ -1865,7 +1871,7 @@ Pid spawnShell(scope const(char)[] command,
|
||||||
Config config = Config.none,
|
Config config = Config.none,
|
||||||
scope const(char)[] workDir = null,
|
scope const(char)[] workDir = null,
|
||||||
scope string shellPath = nativeShell)
|
scope string shellPath = nativeShell)
|
||||||
@trusted // TODO: Should be @safe
|
@safe
|
||||||
{
|
{
|
||||||
version (Windows)
|
version (Windows)
|
||||||
{
|
{
|
||||||
|
@ -1875,7 +1881,7 @@ Pid spawnShell(scope const(char)[] command,
|
||||||
// See CMD.EXE /? for details.
|
// See CMD.EXE /? for details.
|
||||||
const commandLine = escapeShellFileName(shellPath)
|
const commandLine = escapeShellFileName(shellPath)
|
||||||
~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
|
~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`;
|
||||||
return spawnProcessImpl(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir);
|
return spawnProcessWin(commandLine, shellPath, stdin, stdout, stderr, env, config, workDir);
|
||||||
}
|
}
|
||||||
else version (Posix)
|
else version (Posix)
|
||||||
{
|
{
|
||||||
|
@ -1883,7 +1889,7 @@ Pid spawnShell(scope const(char)[] command,
|
||||||
args[0] = shellPath;
|
args[0] = shellPath;
|
||||||
args[1] = shellSwitch;
|
args[1] = shellSwitch;
|
||||||
args[2] = command;
|
args[2] = command;
|
||||||
return spawnProcessImpl(args, stdin, stdout, stderr, env, config, workDir);
|
return spawnProcessPosix(args, stdin, stdout, stderr, env, config, workDir);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
static assert(0);
|
static assert(0);
|
||||||
|
@ -3046,7 +3052,7 @@ auto execute(scope const(char[])[] args,
|
||||||
Config config = Config.none,
|
Config config = Config.none,
|
||||||
size_t maxOutput = size_t.max,
|
size_t maxOutput = size_t.max,
|
||||||
scope const(char)[] workDir = null)
|
scope const(char)[] workDir = null)
|
||||||
@trusted //TODO: @safe
|
@safe
|
||||||
{
|
{
|
||||||
return executeImpl!pipeProcess(args, env, config, maxOutput, workDir);
|
return executeImpl!pipeProcess(args, env, config, maxOutput, workDir);
|
||||||
}
|
}
|
||||||
|
@ -3057,7 +3063,7 @@ auto execute(scope const(char)[] program,
|
||||||
Config config = Config.none,
|
Config config = Config.none,
|
||||||
size_t maxOutput = size_t.max,
|
size_t maxOutput = size_t.max,
|
||||||
scope const(char)[] workDir = null)
|
scope const(char)[] workDir = null)
|
||||||
@trusted //TODO: @safe
|
@safe
|
||||||
{
|
{
|
||||||
return executeImpl!pipeProcess(program, env, config, maxOutput, workDir);
|
return executeImpl!pipeProcess(program, env, config, maxOutput, workDir);
|
||||||
}
|
}
|
||||||
|
@ -3069,7 +3075,7 @@ auto executeShell(scope const(char)[] command,
|
||||||
size_t maxOutput = size_t.max,
|
size_t maxOutput = size_t.max,
|
||||||
scope const(char)[] workDir = null,
|
scope const(char)[] workDir = null,
|
||||||
string shellPath = nativeShell)
|
string shellPath = nativeShell)
|
||||||
@trusted //TODO: @safe
|
@safe
|
||||||
{
|
{
|
||||||
return executeImpl!pipeShell(command,
|
return executeImpl!pipeShell(command,
|
||||||
env,
|
env,
|
||||||
|
@ -3087,6 +3093,7 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)(
|
||||||
size_t maxOutput = size_t.max,
|
size_t maxOutput = size_t.max,
|
||||||
scope const(char)[] workDir = null,
|
scope const(char)[] workDir = null,
|
||||||
ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init)
|
ExtraPipeFuncArgs extraArgs = ExtraPipeFuncArgs.init)
|
||||||
|
@trusted //TODO: @safe
|
||||||
{
|
{
|
||||||
import std.algorithm.comparison : min;
|
import std.algorithm.comparison : min;
|
||||||
import std.array : appender;
|
import std.array : appender;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue