New tests.
[mono.git] / mcs / class / corlib / System / Environment.cs
index c21030f8b35b9faacf6bcd15c7e6e999b4914494..91e02d48e58781e6b323b30f1a0728e30f38fe79 100644 (file)
@@ -10,7 +10,7 @@
 //
 //------------------------------------------------------------------------------
 //
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
 using System.IO;
-//using System.Diagnostics;
 using System.Collections;
+using System.Runtime.CompilerServices;
 using System.Security;
 using System.Security.Permissions;
-using System.Runtime.CompilerServices;
 using System.Text;
+using System.Runtime.InteropServices;
+
+namespace System {
+
+       [ComVisible (true)]
+       public static class Environment {
 
-namespace System
-{
-       public sealed class Environment
-       {
                /*
                 * This is the version number of the corlib-runtime interface. When
                 * making changes to this interface (by changing the layout
-                * of classes the runtime knows about, changing icall semantics etc),
-                * increment this variable. Also increment the
+                * of classes the runtime knows about, changing icall signature or
+                * semantics etc), increment this variable. Also increment the
                 * pair of this variable in the runtime in metadata/appdomain.c.
                 * Changes which are already detected at runtime, like the addition
                 * of icalls, do not require an increment.
                 */
-               private const int mono_corlib_version = 22;
-
-               private Environment ()
-               {
-               }
+#pragma warning disable 169
+               private const int mono_corlib_version = 90;
+#pragma warning restore 169
 
-               [MonoTODO]
+               [ComVisible (true)]
                public enum SpecialFolder
-               {       // TODO: Determine if these windoze style folder identifiers 
-                       //       have unix/linux counterparts
-
-#if NET_1_1
+               {       
+                       MyDocuments = 0x05,
                        Desktop = 0x00,
                        MyComputer = 0x11,
-#endif
                        Programs = 0x02,
                        Personal = 0x05,
                        Favorites = 0x06,
@@ -89,18 +84,51 @@ namespace System
                        ProgramFiles = 0x26,
                        MyPictures = 0x27,
                        CommonProgramFiles = 0x2b,
+#if NET_4_0 || MOONLIGHT
+                       MyVideos = 0x0e,
+#endif
+#if NET_4_0
+                       NetworkShortcuts = 0x13,
+                       Fonts = 0x14,
+                       CommonStartMenu = 0x16,
+                       CommonPrograms = 0x17,
+                       CommonStartup = 0x18,
+                       CommonDesktopDirectory = 0x19,
+                       PrinterShortcuts = 0x1b,
+                       Windows = 0x24,
+                       UserProfile = 0x28,
+                       SystemX86 = 0x29,
+                       ProgramFilesX86 = 0x2a,
+                       CommonProgramFilesX86 = 0x2c,
+                       CommonTemplates = 0x2d,
+                       CommonDocuments = 0x2e,
+                       CommonAdminTools = 0x2f,
+                       AdminTools = 0x30,
+                       CommonMusic = 0x35,
+                       CommonPictures = 0x36,
+                       CommonVideos = 0x37,
+                       Resources = 0x38,
+                       LocalizedResources = 0x39,
+                       CommonOemLinks = 0x3a,
+                       CDBurning = 0x3b,
+#endif
+               }
+
+#if NET_4_0
+               public
+#endif
+               enum SpecialFolderOption {
+                       None = 0,
+                       DoNotVerify = 0x4000,
+                       Create = 0x8000
                }
 
-               // TODO: Make sure the security attributes do what I expect
-                       
                /// <summary>
                /// Gets the command line for this process
                /// </summary>
-               public static string CommandLine
-               {       // TODO: Coordinate with implementor of EnvironmentPermissionAttribute
-                       // [EnvironmentPermissionAttribute(SecurityAction.Demand, Read = "COMMANDLINE")]
-                       get
-                       {
+               public static string CommandLine {
+                       // note: security demand inherited from calling GetCommandLineArgs
+                       get {
                                // FIXME: we may need to quote, but any sane person
                                // should use GetCommandLineArgs () instead.
                                return String.Join (" ", GetCommandLineArgs ());
@@ -114,9 +142,6 @@ namespace System
                /// </summary>
                public static string CurrentDirectory
                {
-                       // originally it was my thought that the external call would be made in
-                       // the directory class however that class has additional security requirements
-                       // so the Directory class will call this class for its get/set current directory
                        get {
                                return Directory.GetCurrentDirectory ();
                        }
@@ -136,10 +161,7 @@ namespace System
                        set;
                }
 
-#if NET_1_1
-               static
-#endif
-               public extern bool HasShutdownStarted
+               static public extern bool HasShutdownStarted
                {
                        [MethodImplAttribute (MethodImplOptions.InternalCall)]
                        get;
@@ -151,6 +173,8 @@ namespace System
                /// </summary>
                public extern static string MachineName {
                        [MethodImplAttribute (MethodImplOptions.InternalCall)]
+                       [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
+                       [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
                        get;
                }
 
@@ -182,7 +206,8 @@ namespace System
                        get {
                                if (os == null) {
                                        Version v = Version.CreateFromString (GetOSVersionString ());
-                                       os = new OperatingSystem (Platform, v);
+                                       PlatformID p = Platform;
+                                       os = new OperatingSystem (p, v);
                                }
                                return os;
                        }
@@ -192,12 +217,13 @@ namespace System
                /// Get StackTrace
                /// </summary>
                public static string StackTrace {
+                       [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
                        get {
-                               System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (1);
+                               System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (0, true);
                                return trace.ToString ();
                        }
                }
-
+#if !NET_2_1
                /// <summary>
                /// Get a fully qualified path to the system directory
                /// </summary>
@@ -206,7 +232,7 @@ namespace System
                                return GetFolderPath (SpecialFolder.System);
                        }
                }
-
+#endif
                /// <summary>
                /// Get the number of milliseconds that have elapsed since the system was booted
                /// </summary>
@@ -219,6 +245,8 @@ namespace System
                /// Get UserDomainName
                /// </summary>
                public static string UserDomainName {
+                       // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
+                       [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
                        get {
                                return MachineName;
                        }
@@ -227,7 +255,7 @@ namespace System
                /// <summary>
                /// Gets a flag indicating whether the process is in interactive mode
                /// </summary>
-               [MonoTODO]
+               [MonoTODO ("Currently always returns false, regardless of interactive state")]
                public static bool UserInteractive {
                        get {
                                return false;
@@ -237,9 +265,9 @@ namespace System
                /// <summary>
                /// Get the user name of current process is running under
                /// </summary>
-               public extern static string UserName
-               {
+               public extern static string UserName {
                        [MethodImplAttribute (MethodImplOptions.InternalCall)]
+                       [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
                        get;
                }
 
@@ -248,26 +276,21 @@ namespace System
                /// </summary>
                public static Version Version {
                        get {
-#if NET_1_1                                
-                               return new Version (1, 1, 4322, 573);
-#else
-                               return new Version (1, 0, 3705, 288);
-#endif
+                               return new Version (Consts.FxFileVersion);
                        }
                }
 
                /// <summary>
                /// Get the amount of physical memory mapped to process
                /// </summary>
-               [MonoTODO]
-               public static long WorkingSet
-               {
-                       get {
-                               return 0;
-                       }
+               [MonoTODO ("Currently always returns zero")]
+               public static long WorkingSet {
+                       [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
+                       get { return 0; }
                }
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
                public extern static void Exit (int exitCode);
 
                /// <summary>
@@ -287,14 +310,13 @@ namespace System
                        if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
                                return name;
 
-                       PlatformID platform = Platform;
                        StringBuilder result = new StringBuilder ();
                        result.Append (name, 0, off1);
                        Hashtable tbl = null;
                        do {
                                string var = name.Substring (off1 + 1, off2 - off1 - 1);
                                string value = GetEnvironmentVariable (var);
-                               if (value == null && (int) platform != 128) {
+                               if (value == null && Environment.IsRunningOnWindows) {
                                        // On windows, env. vars. are case insensitive
                                        if (tbl == null)
                                                tbl = GetEnvironmentVariablesNoCase ();
@@ -302,42 +324,64 @@ namespace System
                                        value = tbl [var] as string;
                                }
                                
+                               // If value not found, add %FOO to stream,
+                               //  and use the closing % for the next iteration.
+                               // If value found, expand it in place of %FOO%
                                if (value == null) {
                                        result.Append ('%');
                                        result.Append (var);
-                                       result.Append ('%');
+                                       off2--;
                                } else {
                                        result.Append (value);
                                }
-
-                               if (off2 + 1 == len) {
-                                       off1 = off2;
-                                       off2 = -1;
-                               } else {
-                                       off1 = off2 + 1;
-                                       off2 = (off1 + 1 == len) ? -1 : name.IndexOf ('%', off1 + 1);
-                               }
-
-                       } while (off2 != -1);
-
-                       if (off1 + 1 < len)
-                               result.Append (name.Substring (off1));
-
+                               int oldOff2 = off2;
+                               off1 = name.IndexOf ('%', off2 + 1);
+                               // If no % found for off1, don't look for one for off2
+                               off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
+                               // textLen is the length of text between the closing % of current iteration
+                               //  and the starting % of the next iteration if any. This text is added to output
+                               int textLen;
+                               // If no new % found, use all the remaining text
+                               if (off1 == -1 || off2 == -1)
+                                       textLen = len - oldOff2 - 1;
+                               // If value found in current iteration, use text after current closing % and next %
+                               else if(value != null)
+                                       textLen = off1 - oldOff2 - 1;
+                               // If value not found in current iteration, but a % was found for next iteration,
+                               //  use text from current closing % to the next %.
+                               else
+                                       textLen = off1 - oldOff2;
+                               if(off1 >= oldOff2 || off1 == -1)
+                                       result.Append (name, oldOff2+1, textLen);
+                       } while (off2 > -1 && off2 < len);
+                               
                        return result.ToString ();
+
                }
 
                /// <summary>
                /// Return an array of the command line arguments of the current process
                /// </summary>
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               public extern static string[] GetCommandLineArgs();
+               [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
+               public extern static string[] GetCommandLineArgs ();
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static string internalGetEnvironmentVariable (string variable);
 
                /// <summary>
                /// Return a string containing the value of the environment
                /// variable identifed by parameter "variable"
                /// </summary>
-               [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               public extern static string GetEnvironmentVariable (string name);
+               public static string GetEnvironmentVariable (string variable)
+               {
+#if !NET_2_1
+                       if (SecurityManager.SecurityEnabled) {
+                               new EnvironmentPermission (EnvironmentPermissionAccess.Read, variable).Demand ();
+                       }
+#endif
+                       return internalGetEnvironmentVariable (variable);
+               }
 
                static Hashtable GetEnvironmentVariablesNoCase ()
                {
@@ -345,7 +389,7 @@ namespace System
                                                        CaseInsensitiveComparer.Default);
 
                        foreach (string name in GetEnvironmentVariableNames ()) {
-                               vars [name] = GetEnvironmentVariable (name);
+                               vars [name] = internalGetEnvironmentVariable (name);
                        }
 
                        return vars;
@@ -354,17 +398,42 @@ namespace System
                /// <summary>
                /// Return a set of all environment variables and their values
                /// </summary>
-          
-               public static IDictionary GetEnvironmentVariables()
+#if !NET_2_1
+               public static IDictionary GetEnvironmentVariables ()
                {
+                       StringBuilder sb = null;
+                       if (SecurityManager.SecurityEnabled) {
+                               // we must have access to each variable to get the lot
+                               sb = new StringBuilder ();
+                               // but (performance-wise) we do not want a stack-walk
+                               // for each of them so we concatenate them
+                       }
+
                        Hashtable vars = new Hashtable ();
                        foreach (string name in GetEnvironmentVariableNames ()) {
-                               vars [name] = GetEnvironmentVariable (name);
+                               vars [name] = internalGetEnvironmentVariable (name);
+                               if (sb != null) {
+                                       sb.Append (name);
+                                       sb.Append (";");
+                               }
                        }
 
+                       if (sb != null) {
+                               new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
+                       }
                        return vars;
                }
-
+#else
+               [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
+               public static IDictionary GetEnvironmentVariables ()
+               {
+                       Hashtable vars = new Hashtable ();
+                       foreach (string name in GetEnvironmentVariableNames ()) {
+                               vars [name] = internalGetEnvironmentVariable (name);
+                       }
+                       return vars;
+               }
+#endif
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern static string GetWindowsFolderPath (int folder);
@@ -375,44 +444,122 @@ namespace System
                /// </summary>
                public static string GetFolderPath (SpecialFolder folder)
                {
-                       if ((int) Platform != 128)
-                               return GetWindowsFolderPath ((int) folder);
+                       return GetFolderPath (folder, SpecialFolderOption.None);
+               }
+#if NET_4_0
+               [MonoTODO ("Figure out the folder path for all the new values in SpecialFolder. Use the 'option' argument.")]
+               public
+#endif
+               static string GetFolderPath(SpecialFolder folder, SpecialFolderOption option)
+               {
+                       SecurityManager.EnsureElevatedPermissions (); // this is a no-op outside moonlight
+
+                       string dir = null;
+
+                       if (Environment.IsRunningOnWindows) {
+                               dir = GetWindowsFolderPath ((int) folder);
+                       } else {
+                               dir = InternalGetFolderPath (folder);
+                       }
+#if !NET_2_1
+                       if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
+                               new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
+                       }
+#endif
+                       return dir;
+               }
+
+               private static string ReadXdgUserDir (string config_dir, string home_dir, 
+                       string key, string fallback)
+               {
+                       string env_path = internalGetEnvironmentVariable (key);
+                       if (env_path != null && env_path != String.Empty) {
+                               return env_path;
+                       }
+
+                       string user_dirs_path = Path.Combine (config_dir, "user-dirs.dirs");
+
+                       if (!File.Exists (user_dirs_path)) {
+                               return Path.Combine (home_dir, fallback);
+                       }
+
+                       try {
+                               using(StreamReader reader = new StreamReader (user_dirs_path)) {
+                                       string line;
+                                       while ((line = reader.ReadLine ()) != null) {
+                                               line = line.Trim ();
+                                               int delim_index = line.IndexOf ('=');
+                        if(delim_index > 8 && line.Substring (0, delim_index) == key) {
+                            string path = line.Substring (delim_index + 1).Trim ('"');
+                            bool relative = false;
+
+                            if (path.StartsWith ("$HOME/")) {
+                                relative = true;
+                                path = path.Substring (6);
+                            } else if (!path.StartsWith ("/")) {
+                                relative = true;
+                            }
+
+                            return relative ? Path.Combine (home_dir, path) : path;
+                        }
+                                       }
+                               }
+                       } catch (FileNotFoundException) {
+                       }
+
+                       return Path.Combine (home_dir, fallback);
+               }
+
 
+               // the security runtime (and maybe other parts of corlib) needs the
+               // information to initialize themselves before permissions can be checked
+               internal static string InternalGetFolderPath (SpecialFolder folder)
+               {
                        string home = internalGetHome ();
 
                        // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
-                       string data = GetEnvironmentVariable ("XDG_DATA_HOME");
+
+                       // note: skip security check for environment variables
+                       string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
                        if ((data == null) || (data == String.Empty)) {
                                data = Path.Combine (home, ".local");
                                data = Path.Combine (data, "share");
                        }
 
-                       string config = GetEnvironmentVariable ("XDG_CONFIG_HOME");
+                       // note: skip security check for environment variables
+                       string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
                        if ((config == null) || (config == String.Empty)) {
                                config = Path.Combine (home, ".config");
                        }
 
                        switch (folder) {
-#if NET_1_1
                        // MyComputer is a virtual directory
                        case SpecialFolder.MyComputer:
-                               return "";
-#endif
+                               return String.Empty;
+
                        // personal == ~
                        case SpecialFolder.Personal:
+#if MONOTOUCH
+                               return Path.Combine (home, "Documents");
+#else
                                return home;
+#endif
                        // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
                        case SpecialFolder.ApplicationData:
                                return config;
                        //use FDO's DATA_HOME. This is *NOT* synced
                        case SpecialFolder.LocalApplicationData:
                                return data;
-#if NET_1_1
                        case SpecialFolder.Desktop:
-#endif
                        case SpecialFolder.DesktopDirectory:
-                               return Path.Combine (home, "Desktop");
-                       
+                               return ReadXdgUserDir (config, home, "XDG_DESKTOP_DIR", "Desktop");
+
+                       case SpecialFolder.MyMusic:
+                               return ReadXdgUserDir (config, home, "XDG_MUSIC_DIR", "Music");
+
+                       case SpecialFolder.MyPictures:
+                               return ReadXdgUserDir (config, home, "XDG_PICTURES_DIR", "Pictures");
+                               
                        // these simply dont exist on Linux
                        // The spec says if a folder doesnt exist, we
                        // should return ""
@@ -421,8 +568,6 @@ namespace System
                        case SpecialFolder.SendTo:
                        case SpecialFolder.StartMenu:
                        case SpecialFolder.Startup:
-                       case SpecialFolder.MyMusic:
-                       case SpecialFolder.MyPictures:
                        case SpecialFolder.Templates:
                        case SpecialFolder.Cookies:
                        case SpecialFolder.History:
@@ -431,7 +576,7 @@ namespace System
                        case SpecialFolder.CommonProgramFiles:
                        case SpecialFolder.ProgramFiles:
                        case SpecialFolder.System:
-                               return "";
+                               return String.Empty;
                        // This is where data common to all users goes
                        case SpecialFolder.CommonApplicationData:
                                return "/usr/share";
@@ -440,27 +585,186 @@ namespace System
                         }
                 }
 
+               [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
                public static string[] GetLogicalDrives ()
                {
                        return GetLogicalDrivesInternal ();
                }
 
-               static internal string GetResourceString (string s) { return ""; }
+#if !NET_2_1
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private static extern void internalBroadcastSettingChange ();
+
+               public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
+               {
+                       switch (target) {
+                       case EnvironmentVariableTarget.Process:
+                               return GetEnvironmentVariable (variable);
+                       case EnvironmentVariableTarget.Machine:
+                               new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
+                               if (!IsRunningOnWindows)
+                                       return null;
+                               using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
+                                       object regvalue = env.GetValue (variable);
+                                       return (regvalue == null) ? null : regvalue.ToString ();
+                               }
+                       case EnvironmentVariableTarget.User:
+                               new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
+                               if (!IsRunningOnWindows)
+                                       return null;
+                               using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
+                                       object regvalue = env.GetValue (variable);
+                                       return (regvalue == null) ? null : regvalue.ToString ();
+                               }
+                       default:
+                               throw new ArgumentException ("target");
+                       }
+               }
+
+               public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
+               {
+                       IDictionary variables = (IDictionary)new Hashtable ();
+                       switch (target) {
+                       case EnvironmentVariableTarget.Process:
+                               variables = GetEnvironmentVariables ();
+                               break;
+                       case EnvironmentVariableTarget.Machine:
+                               new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
+                               if (IsRunningOnWindows) {
+                                       using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
+                                               string[] value_names = env.GetValueNames ();
+                                               foreach (string value_name in value_names)
+                                                       variables.Add (value_name, env.GetValue (value_name));
+                                       }
+                               }
+                               break;
+                       case EnvironmentVariableTarget.User:
+                               new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
+                               if (IsRunningOnWindows) {
+                                       using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
+                                               string[] value_names = env.GetValueNames ();
+                                               foreach (string value_name in value_names)
+                                                       variables.Add (value_name, env.GetValue (value_name));
+                                       }
+                               }
+                               break;
+                       default:
+                               throw new ArgumentException ("target");
+                       }
+                       return variables;
+               }
+
+               [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
+               public static void SetEnvironmentVariable (string variable, string value)
+               {
+                       SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
+               }
+
+               [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
+               public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
+               {
+                       if (variable == null)
+                               throw new ArgumentNullException ("variable");
+                       if (variable == String.Empty)
+                               throw new ArgumentException ("String cannot be of zero length.", "variable");
+                       if (variable.IndexOf ('=') != -1)
+                               throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
+                       if (variable[0] == '\0')
+                               throw new ArgumentException ("The first char in the string is the null character.", "variable");
+
+                       switch (target) {
+                       case EnvironmentVariableTarget.Process:
+                               InternalSetEnvironmentVariable (variable, value);
+                               break;
+                       case EnvironmentVariableTarget.Machine:
+                               if (!IsRunningOnWindows)
+                                       return;
+                               using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
+                                       if (String.IsNullOrEmpty (value))
+                                               env.DeleteValue (variable, false);
+                                       else
+                                               env.SetValue (variable, value);
+                                       internalBroadcastSettingChange ();
+                               }
+                               break;
+                       case EnvironmentVariableTarget.User:
+                               if (!IsRunningOnWindows)
+                                       return;
+                               using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
+                                       if (String.IsNullOrEmpty (value))
+                                               env.DeleteValue (variable, false);
+                                       else
+                                               env.SetValue (variable, value);
+                                       internalBroadcastSettingChange ();
+                               }
+                               break;
+                       default:
+                               throw new ArgumentException ("target");
+                       }
+               }
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal static extern void InternalSetEnvironmentVariable (string variable, string value);
+#endif
+               [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
+               public static void FailFast (string message)
+               {
+                       throw new NotImplementedException ();
+               }
+
+#if NET_4_0 || MOONLIGHT
+               [SecurityCritical]
+               public static void FailFast (string message, Exception exception)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+
+#if NET_4_0
+               public static bool Is64BitOperatingSystem {
+                       get { return IntPtr.Size == 8; } // FIXME: is this good enough?
+               }
+
+               public static bool Is64BitProcess {
+                       get { return Is64BitOperatingSystem; }
+               }
+
+               public static int SystemPageSize {
+                       get { return GetPageSize (); }
+               }
+#endif
+
+               public static extern int ProcessorCount {
+                       [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
+                       [MethodImplAttribute (MethodImplOptions.InternalCall)]
+                       get;                    
+               }
 
                // private methods
 
+               internal static bool IsRunningOnWindows {
+                       get { return ((int) Platform < 4); }
+               }
+#if !NET_2_1
+               //
+               // Used by gacutil.exe
+               //
+#pragma warning disable 169            
                private static string GacPath {
                        get {
-                               if ((int) Platform != 128) {
+                               if (Environment.IsRunningOnWindows) {
                                        /* On windows, we don't know the path where mscorlib.dll will be installed */
-                                       string corlibDir = Path.GetDirectoryName (typeof (int).Assembly.Location);
+                                       string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
                                        return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
                                }
 
                                return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
                        }
                }
-
+#pragma warning restore 169
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static string internalGetGacPath ();
+#endif
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
                private extern static string [] GetLogicalDrivesInternal ();
 
@@ -471,10 +775,18 @@ namespace System
                internal extern static string GetMachineConfigPath ();
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               internal extern static string internalGetGacPath ();
+               internal extern static string internalGetHome ();
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               internal extern static string internalGetHome ();
+               internal extern static int GetPageSize ();
+
+               static internal bool IsUnix {
+                       get {
+                               int platform = (int) Environment.Platform;
+
+                               return (platform == 4 || platform == 128 || platform == 6);
+                       }
+               }
        }
 }