Skip to content
This repository was archived by the owner on Nov 1, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal partial class Interop
{
internal partial class Kernel32
{
[DllImport(Libraries.Kernel32, CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint ExpandEnvironmentStringsW(string lpSrc, ref char lpDst, uint nSize);
[DllImport(Libraries.Kernel32, EntryPoint = "ExpandEnvironmentStringsW", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint ExpandEnvironmentStrings(string lpSrc, ref char lpDst, uint nSize);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Runtime.InteropServices;

internal partial class Interop
{
internal partial class Kernel32
{
internal static unsafe int GetEnvironmentVariable(string lpName, Span<char> buffer)
{
fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer))
{
return GetEnvironmentVariable(lpName, bufferPtr, buffer.Length);
}
}

[DllImport(Libraries.Kernel32, EntryPoint = "GetEnvironmentVariableW", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern unsafe int GetEnvironmentVariable(string lpName, char* lpBuffer, int nSize);
}
}

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions src/System.Private.CoreLib/shared/System/Environment.NoRegistry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Collections;
using Microsoft.Win32;

namespace System
{
public static partial class Environment
{
// Systems without the Windows registry pretend that it's always empty.

private static string GetEnvironmentVariableFromRegistry(string variable, bool fromMachine) => null;

private static void SetEnvironmentVariableFromRegistry(string variable, string value, bool fromMachine) { }

private static IDictionary GetEnvironmentVariablesFromRegistry(bool fromMachine) => new Hashtable();
}
}
41 changes: 20 additions & 21 deletions src/System.Private.CoreLib/shared/System/Environment.Unix.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ namespace System
{
public static partial class Environment
{
internal static readonly bool IsMac = Interop.Sys.GetUnixName() == "OSX";
private static Func<string, object> s_directoryCreateDirectory;

private static string CurrentDirectoryCore
{
get { return Interop.Sys.GetCwd(); }
set { Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true); }
get => Interop.Sys.GetCwd();
set => Interop.CheckIo(Interop.Sys.ChDir(value), value, isDirectory: true);
}

public static int ExitCode { get { return EnvironmentAugments.ExitCode; } set { EnvironmentAugments.ExitCode = value; } }

private static string ExpandEnvironmentVariablesCore(string name)
{
Span<char> initialBuffer = stackalloc char[128];
Expand Down Expand Up @@ -100,14 +97,10 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder)
{
case SpecialFolder.CommonApplicationData: return "/usr/share";
case SpecialFolder.CommonTemplates: return "/usr/share/templates";
}
if (IsMac)
{
switch (folder)
{
case SpecialFolder.ProgramFiles: return "/Applications";
case SpecialFolder.System: return "/System";
}
#if PLATFORM_OSX
case SpecialFolder.ProgramFiles: return "/Applications";
case SpecialFolder.System: return "/System";
#endif
}

// All other paths are based on the XDG Base Directory Specification:
Expand Down Expand Up @@ -160,19 +153,25 @@ private static string GetFolderPathCoreWithoutValidation(SpecialFolder folder)
case SpecialFolder.MyVideos:
return ReadXdgDirectory(home, "XDG_VIDEOS_DIR", "Videos");

#if PLATFORM_OSX
case SpecialFolder.MyMusic:
return IsMac ? Path.Combine(home, "Music") : ReadXdgDirectory(home, "XDG_MUSIC_DIR", "Music");
return Path.Combine(home, "Music");
case SpecialFolder.MyPictures:
return IsMac ? Path.Combine(home, "Pictures") : ReadXdgDirectory(home, "XDG_PICTURES_DIR", "Pictures");
return Path.Combine(home, "Pictures");
case SpecialFolder.Fonts:
return IsMac ? Path.Combine(home, "Library", "Fonts") : Path.Combine(home, ".fonts");

return Path.Combine(home, "Library", "Fonts");
case SpecialFolder.Favorites:
if (IsMac) return Path.Combine(home, "Library", "Favorites");
break;
return Path.Combine(home, "Library", "Favorites");
case SpecialFolder.InternetCache:
if (IsMac) return Path.Combine(home, "Library", "Caches");
break;
return Path.Combine(home, "Library", "Caches");
#else
case SpecialFolder.MyMusic:
return ReadXdgDirectory(home, "XDG_MUSIC_DIR", "Music");
case SpecialFolder.MyPictures:
return ReadXdgDirectory(home, "XDG_PICTURES_DIR", "Pictures");
case SpecialFolder.Fonts:
return Path.Combine(home, ".fonts");
#endif
}

// No known path for the SpecialFolder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;

namespace System
{
public static partial class Environment
{
private static string GetEnvironmentVariableCore(string variable)
{
Span<char> buffer = stackalloc char[128]; // a somewhat reasonable default size
int requiredSize = Interop.Kernel32.GetEnvironmentVariable(variable, buffer);

if (requiredSize == 0 && Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND)
{
return null;
}

if (requiredSize <= buffer.Length)
{
return new string(buffer.Slice(0, requiredSize));
}

char[] chars = ArrayPool<char>.Shared.Rent(requiredSize);
try
{
buffer = chars;
requiredSize = Interop.Kernel32.GetEnvironmentVariable(variable, buffer);
if ((requiredSize == 0 && Marshal.GetLastWin32Error() == Interop.Errors.ERROR_ENVVAR_NOT_FOUND) ||
requiredSize > buffer.Length)
{
return null;
}

return new string(buffer.Slice(0, requiredSize));
}
finally
{
ArrayPool<char>.Shared.Return(chars);
}
}

private static void SetEnvironmentVariableCore(string variable, string value)
{
if (!Interop.Kernel32.SetEnvironmentVariable(variable, value))
{
int errorCode = Marshal.GetLastWin32Error();
switch (errorCode)
{
case Interop.Errors.ERROR_ENVVAR_NOT_FOUND:
// Allow user to try to clear a environment variable
return;

case Interop.Errors.ERROR_FILENAME_EXCED_RANGE:
// The error message from Win32 is "The filename or extension is too long",
// which is not accurate.
throw new ArgumentException(SR.Format(SR.Argument_LongEnvVarValue));

case Interop.Errors.ERROR_NOT_ENOUGH_MEMORY:
case Interop.Errors.ERROR_NO_SYSTEM_RESOURCES:
throw new OutOfMemoryException(Interop.Kernel32.GetMessage(errorCode));

default:
throw new ArgumentException(Interop.Kernel32.GetMessage(errorCode));
}
}
}

public static unsafe IDictionary GetEnvironmentVariables()
{
char* pStrings = Interop.Kernel32.GetEnvironmentStrings();
if (pStrings == null)
{
throw new OutOfMemoryException();
}

try
{
// Format for GetEnvironmentStrings is:
// [=HiddenVar=value\0]* [Variable=value\0]* \0
// See the description of Environment Blocks in MSDN's
// CreateProcess page (null-terminated array of null-terminated strings).

// Search for terminating \0\0 (two unicode \0's).
char* p = pStrings;
while (!(*p == '\0' && *(p + 1) == '\0'))
{
p++;
}
Span<char> block = new Span<char>(pStrings, (int)(p - pStrings + 1));

// Format for GetEnvironmentStrings is:
// (=HiddenVar=value\0 | Variable=value\0)* \0
// See the description of Environment Blocks in MSDN's
// CreateProcess page (null-terminated array of null-terminated strings).
// Note the =HiddenVar's aren't always at the beginning.

// Copy strings out, parsing into pairs and inserting into the table.
// The first few environment variable entries start with an '='.
// The current working directory of every drive (except for those drives
// you haven't cd'ed into in your DOS window) are stored in the
// environment block (as =C:=pwd) and the program's exit code is
// as well (=ExitCode=00000000).

var results = new Hashtable();
for (int i = 0; i < block.Length; i++)
{
int startKey = i;

// Skip to key. On some old OS, the environment block can be corrupted.
// Some will not have '=', so we need to check for '\0'.
while (block[i] != '=' && block[i] != '\0')
{
i++;
}

if (block[i] == '\0')
{
continue;
}

// Skip over environment variables starting with '='
if (i - startKey == 0)
{
while (block[i] != 0)
{
i++;
}

continue;
}

string key = new string(block.Slice(startKey, i - startKey));
i++; // skip over '='

int startValue = i;
while (block[i] != 0)
{
i++; // Read to end of this entry
}

string value = new string(block.Slice(startValue, i - startValue)); // skip over 0 handled by for loop's i++
try
{
results.Add(key, value);
}
catch (ArgumentException)
{
// Throw and catch intentionally to provide non-fatal notification about corrupted environment block
}
}
return results;
}
finally
{
bool success = Interop.Kernel32.FreeEnvironmentStrings(pStrings);
Debug.Assert(success);
}
}
}
}
Loading