// 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. Load more
// Written in the D programming language. /** Functions for starting and interacting with other processes, and for working 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 creation functions 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, tvOS and 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-like interface. 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;