Wolfgang Thallergcc16 merge934c3885 months agoHistory
4626 lines · 153.5 KB · D
RawDownload
// Written in the D programming language. /**Functions for starting and interacting with other processes, and forworking with the current process' execution environment. Process_handling:$(UL $(LI    $(LREF spawnProcess) spawns a new process, optionally assigning it an    arbitrary set of standard input, output, and error streams.    The function returns immediately, leaving the child process to execute    in parallel with its parent.  All other functions in this module that    spawn processes are built around `spawnProcess`.)$(LI    $(LREF wait) makes the parent process wait for a child process to    terminate.  In general one should always do this, to avoid    child processes becoming "zombies" when the parent process exits.    Scope guards are perfect for this – see the $(LREF spawnProcess)    documentation for examples.  $(LREF tryWait) is similar to `wait`,    but does not block if the process has not yet terminated.)$(LI    $(LREF pipeProcess) also spawns a child process which runs    in parallel with its parent.  However, instead of taking    arbitrary streams, it automatically creates a set of    pipes that allow the parent to communicate with the child    through the child's standard input, output, and/or error streams.    This function corresponds roughly to C's `popen` function.)$(LI    $(LREF execute) starts a new process and waits for it    to complete before returning.  Additionally, it captures    the process' standard output and error streams and returns    the output of these as a string.)$(LI    $(LREF spawnShell), $(LREF pipeShell) and $(LREF executeShell) work like    `spawnProcess`, `pipeProcess` and `execute`, respectively,    except that they take a single command string and run it through    the current user's default command interpreter.    `executeShell` corresponds roughly to C's `system` function.)$(LI    $(LREF kill) attempts to terminate a running process.)) The following table compactly summarises the different process creationfunctions and how they relate to each other:$(BOOKTABLE,    $(TR $(TH )         $(TH Runs program directly)         $(TH Runs shell command))    $(TR $(TD Low-level process creation)         $(TD $(LREF spawnProcess))         $(TD $(LREF spawnShell)))    $(TR $(TD Automatic input/output redirection using pipes)         $(TD $(LREF pipeProcess))         $(TD $(LREF pipeShell)))    $(TR $(TD Execute and wait for completion, collect output)         $(TD $(LREF execute))         $(TD $(LREF executeShell)))) Other_functionality:$(UL$(LI    $(LREF pipe) is used to create unidirectional pipes.)$(LI    $(LREF environment) is an interface through which the current process'    environment variables can be read and manipulated.)$(LI    $(LREF escapeShellCommand) and $(LREF escapeShellFileName) are useful    for constructing shell command lines in a portable way.)) Authors:    $(LINK2 https://github.com/kyllingstad, Lars Tandle Kyllingstad),    $(LINK2 https://github.com/schveiguy, Steven Schveighoffer),    $(HTTP thecybershadow.net, Vladimir Panteleev)Copyright:    Copyright (c) 2013, the authors. All rights reserved.License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).Source:    $(PHOBOSSRC std/process.d)Macros:    OBJECTREF=$(REF1 $0, object) Note:Most of the functionality in this module is not available on iOS, tvOSand watchOS. The only functions available on those platforms are:$(LREF environment), $(LREF thisProcessID) and $(LREF thisThreadID).*/module std.process; import core.thread : ThreadID; version (Posix){    import core.sys.posix.sys.wait;    import core.sys.posix.unistd;}version (Windows){    import core.stdc.stdio;    import core.sys.windows.winbase;    import core.sys.windows.winnt;    import std.utf;    import std.windows.syserror;} import std.internal.cstring;import std.range;import std.stdio; version (OSX)    version = Darwin;else version (iOS){    version = Darwin;    version = iOSDerived;}else version (TVOS){    version = Darwin;    version = iOSDerived;}else version (WatchOS){    version = Darwin;    version = iOSDerived;}  // Some of the following should be moved to druntime.private{    // Microsoft Visual C Runtime (MSVCRT) declarations.    version (CRuntime_Microsoft)    {        import core.stdc.stdint;        enum        {            STDIN_FILENO  = 0,            STDOUT_FILENO = 1,            STDERR_FILENO = 2,        }    }     // POSIX API declarations.    version (Posix)    {        import core.sys.posix.unistd : getEnvironPtr = environ;         @system unittest        {            import core.thread : Thread;            new Thread({assert(getEnvironPtr !is null);}).start();        }    }} // private // =============================================================================// Environment variable manipulation.// ============================================================================= /**Manipulates _environment variables using an associative-array-likeinterface. This class contains only static methods, and cannot be instantiated.See below for examples of use.*/abstract final class environment{    static import core.sys.posix.stdlib;    import core.stdc.errno : errno, EINVAL; static:    /**    Retrieves the value of the environment variable with the given `name`.    ---    auto path = environment["PATH"];    ---     Throws:    $(OBJECTREF Exception) if the environment variable does not exist,    or $(REF UTFException, std,utf) if the variable contains invalid UTF-16    characters (Windows only).     See_also:    $(LREF environment.get), which doesn't throw on failure.    */    string opIndex(scope const(char)[] name) @safe    {        import std.exception : enforce;        return get(name, null).enforce("Environment variable not found: "~name);    }     /**    Retrieves the value of the environment variable with the given `name`,    or a default value if the variable doesn't exist.     Unlike $(LREF environment.opIndex), this function never throws on Posix.    ---    auto sh = environment.get("SHELL", "/bin/sh");    ---    This function is also useful in checking for the existence of an    environment variable.    ---    auto myVar = environment.get("MYVAR");    if (myVar is null)    {        // Environment variable doesn't exist.        // Note that we have to use 'is' for the comparison, since        // myVar == null is also true if the variable exists but is        // empty.    }    ---    Params:        name = name of the environment variable to retrieve        defaultValue = default value to return if the environment variable doesn't exist.     Returns:        the value of the environment variable if found, otherwise        `null` if the environment doesn't exist.     Throws:    $(REF UTFException, std,utf) if the variable contains invalid UTF-16    characters (Windows only).    */    string get(scope const(char)[] name, string defaultValue = null) @safe    {        string value;        getImpl(name, (result) { value = result ? cachedToString(result) : defaultValue; });        return value;    }     /**    Assigns the given `value` to the environment variable with the given    `name`.    If `value` is null the variable is removed from environment.     If the variable does not exist, it will be created. If it already exists,    it will be overwritten.    ---    environment["foo"] = "bar";    ---     Throws:    $(OBJECTREF Exception) if the environment variable could not be added        (e.g. if the name is invalid).     Note:    On some platforms, modifying environment variables may not be allowed in    multi-threaded programs. See e.g.    $(LINK2 https://www.gnu.org/software/libc/manual/html_node/Environment-Access.html#Environment-Access, glibc).    */    inout(char)[] opIndexAssign(return scope inout char[] value, scope const(char)[] name) @trusted    {        version (Posix)        {            import std.exception : enforce, errnoEnforce;            if (value is null)            {                remove(name);                return value;            }            if (core.sys.posix.stdlib.setenv(name.tempCString(), value.tempCString(), 1) != -1)            {                return value;            }            // The default errno error message is very uninformative            // in the most common case, so we handle it manually.            enforce(errno != EINVAL,                "Invalid environment variable name: '"~name~"'");            errnoEnforce(false,                "Failed to add environment variable");            assert(0);        }        else version (Windows)        {            import std.windows.syserror : wenforce;            wenforce(                SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW()),            );            return value;        }        else static assert(0);    }     /**    Removes the environment variable with the given `name`.     If the variable isn't in the environment, this function returns    successfully without doing anything.     Note:    On some platforms, modifying environment variables may not be allowed in    multi-threaded programs. See e.g.    $(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    {        version (Windows)    SetEnvironmentVariableW(name.tempCStringW(), null);        else version (Posix) core.sys.posix.stdlib.unsetenv(name.tempCString());        else static assert(0);    }     /**    Identify whether a variable is defined in the environment.     Because it doesn't return the value, this function is cheaper than `get`.    However, if you do need the value as well, you should just check the    return of `get` for `null` instead of using this function first.     Example:    -------------    // good usage    if ("MY_ENV_FLAG" in environment)        doSomething();     // bad usage    if ("MY_ENV_VAR" in environment)        doSomething(environment["MY_ENV_VAR"]);     // do this instead    if (auto var = environment.get("MY_ENV_VAR"))        doSomething(var);    -------------    */    bool opBinaryRight(string op : "in")(scope const(char)[] name) @trusted    {        if (name is null)            return false;        version (Posix)            return core.sys.posix.stdlib.getenv(name.tempCString()) !is null;        else version (Windows)        {            SetLastError(NO_ERROR);            if (GetEnvironmentVariableW(name.tempCStringW, null, 0) > 0)                return true;            immutable err = GetLastError();            if (err == NO_ERROR)                return true; // zero-length environment variable on Wine / XP            if (err == ERROR_ENVVAR_NOT_FOUND)                return false;            // Some other Windows error, throw.            throw new WindowsException(err);        }        else static assert(0);    }     /**    Copies all environment variables into an associative array.     Windows_specific:    While Windows environment variable names are case insensitive, D's    built-in associative arrays are not.  This function will store all    variable names in uppercase (e.g. `PATH`).     Throws:    $(OBJECTREF Exception) if the environment variables could not        be retrieved (Windows only).    */    string[string] toAA() @trusted    {        import std.conv : to;        string[string] aa;        version (Posix)        {            auto environ = getEnvironPtr;            for (int i=0; environ[i] != null; ++i)            {                import std.string : indexOf;                 immutable varDef = to!string(environ[i]);                immutable eq = indexOf(varDef, '=');                assert(eq >= 0);                 immutable name = varDef[0 .. eq];                immutable value = varDef[eq+1 .. $];                 // In POSIX, environment variables may be defined more                // than once.  This is a security issue, which we avoid                // by checking whether the key already exists in the array.                // For more info:                // http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/environment-variables.html                if (name !in aa)  aa[name] = value;            }        }        else version (Windows)        {            import std.exception : enforce;            import std.uni : toUpper;            auto envBlock = GetEnvironmentStringsW();            enforce(envBlock, "Failed to retrieve environment variables.");            scope(exit) FreeEnvironmentStringsW(envBlock);             for (int i=0; envBlock[i] != '\0'; ++i)            {                auto start = i;                while (envBlock[i] != '=') ++i;                immutable name = toUTF8(toUpper(envBlock[start .. i]));                 start = i+1;                while (envBlock[i] != '\0') ++i;                 // Ignore variables with empty names. These are used internally                // by Windows to keep track of each drive's individual current                // directory.                if (!name.length)                    continue;                 // Just like in POSIX systems, environment variables may be                // defined more than once in an environment block on Windows,                // and it is just as much of a security issue there.  Moreso,                // in fact, due to the case insensensitivity of variable names,                // which is not handled correctly by all programs.                auto val = toUTF8(envBlock[start .. i]);                if (name !in aa) aa[name] = val is null ? "" : val;            }        }        else static assert(0);        return aa;    } private:    version (Windows) alias OSChar = WCHAR;    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    {        // fix issue https://issues.dlang.org/show_bug.cgi?id=24549        if (name is null)            return sink(null);         version (Windows)        {            // first we ask windows how long the environment variable is,            // then we try to read it in to a buffer of that length. Lots            // of error conditions because the windows API is nasty.             import std.conv : to;            const namezTmp = name.tempCStringW();            WCHAR[] buf;             // clear error because GetEnvironmentVariable only says it sets it            // if the environment variable is missing, not on other errors.            SetLastError(NO_ERROR);            // len includes terminating null            immutable len = GetEnvironmentVariableW(namezTmp, null, 0);            if (len == 0)            {                immutable err = GetLastError();                if (err == ERROR_ENVVAR_NOT_FOUND)                    return sink(null);                if (err != NO_ERROR) // Some other Windows error, throw.                    throw new WindowsException(err);            }            if (len <= 1)                return sink("");            buf.length = len;             while (true)            {                // lenRead is either the number of bytes read w/o null - if buf was long enough - or                // the number of bytes necessary *including* null if buf wasn't long enough                immutable lenRead = GetEnvironmentVariableW(namezTmp, buf.ptr, to!DWORD(buf.length));                if (lenRead == 0)                {                    immutable err = GetLastError();                    if (err == NO_ERROR) // sucessfully read a 0-length variable                        return sink("");                    if (err == ERROR_ENVVAR_NOT_FOUND) // variable didn't exist                        return sink(null);                    // some other windows error                    throw new WindowsException(err);                }                assert(lenRead != buf.length, "impossible according to msft docs");                if (lenRead < buf.length) // the buffer was long enough                    return sink(buf[0 .. lenRead]);                // resize and go around again, because the environment variable grew                buf.length = lenRead;            }        }        else version (Posix)        {            import core.stdc.string : strlen;             const vz = core.sys.posix.stdlib.getenv(name.tempCString());            if (vz == null) return sink(null);            return sink(vz[0 .. strlen(vz)]);       }        else static assert(0);    }     string cachedToString(C)(scope const(C)[] v) @safe    {        import std.algorithm.comparison : equal; 

Lines 1–500 of 4626.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t