Correct typo
[mono.git] / mcs / class / corlib / System / Environment.cs
1 //------------------------------------------------------------------------------
2 // 
3 // System.Environment.cs 
4 //
5 // Copyright (C) 2001 Moonlight Enterprises, All Rights Reserved
6 // 
7 // Author:         Jim Richardson, develop@wtfo-guru.com
8 //                 Dan Lewis (dihlewis@yahoo.co.uk)
9 // Created:        Saturday, August 11, 2001 
10 //
11 //------------------------------------------------------------------------------
12 //
13 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System.IO;
36 using System.Collections;
37 using System.Runtime.CompilerServices;
38 using System.Security;
39 using System.Security.Permissions;
40 using System.Text;
41
42 namespace System {
43
44 #if NET_2_0
45         public static class Environment {
46 #else
47         public sealed class Environment {
48
49                 private Environment ()
50                 {
51                 }
52 #endif
53                 /*
54                  * This is the version number of the corlib-runtime interface. When
55                  * making changes to this interface (by changing the layout
56                  * of classes the runtime knows about, changing icall signature or
57                  * semantics etc), increment this variable. Also increment the
58                  * pair of this variable in the runtime in metadata/appdomain.c.
59                  * Changes which are already detected at runtime, like the addition
60                  * of icalls, do not require an increment.
61                  */
62                 private const int mono_corlib_version = 53;
63                 
64                 [MonoTODO]
65                 public enum SpecialFolder
66                 {       // TODO: Determine if these windoze style folder identifiers 
67                         //       have unix/linux counterparts
68 #if NET_2_0
69                         MyDocuments = 0x05,
70 #endif
71 #if NET_1_1
72                         Desktop = 0x00,
73                         MyComputer = 0x11,
74 #endif
75                         Programs = 0x02,
76                         Personal = 0x05,
77                         Favorites = 0x06,
78                         Startup = 0x07,
79                         Recent = 0x08,
80                         SendTo = 0x09,
81                         StartMenu = 0x0b,
82                         MyMusic = 0x0d,
83                         DesktopDirectory = 0x10,
84                         Templates = 0x15,
85                         ApplicationData = 0x1a,
86                         LocalApplicationData = 0x1c,
87                         InternetCache = 0x20,
88                         Cookies = 0x21,
89                         History = 0x22,
90                         CommonApplicationData   = 0x23,
91                         System = 0x25,
92                         ProgramFiles = 0x26,
93                         MyPictures = 0x27,
94                         CommonProgramFiles = 0x2b,
95                 }
96
97                 /// <summary>
98                 /// Gets the command line for this process
99                 /// </summary>
100                 public static string CommandLine {
101                         // note: security demand inherited from calling GetCommandLineArgs
102                         get {
103                                 // FIXME: we may need to quote, but any sane person
104                                 // should use GetCommandLineArgs () instead.
105                                 return String.Join (" ", GetCommandLineArgs ());
106                         }
107                 }
108
109                 /// <summary>
110                 /// Gets or sets the current directory. Actually this is supposed to get
111                 /// and/or set the process start directory acording to the documentation
112                 /// but actually test revealed at beta2 it is just Getting/Setting the CurrentDirectory
113                 /// </summary>
114                 public static string CurrentDirectory
115                 {
116                         get {
117                                 return Directory.GetCurrentDirectory ();
118                         }
119                         set {
120                                 Directory.SetCurrentDirectory (value);
121                         }
122                 }
123
124                 /// <summary>
125                 /// Gets or sets the exit code of this process
126                 /// </summary>
127                 public extern static int ExitCode
128                 {       
129                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
130                         get;
131                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
132                         set;
133                 }
134
135 #if NET_1_1
136                 static
137 #endif
138                 public extern bool HasShutdownStarted
139                 {
140                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
141                         get;
142                 }
143                 
144
145                 /// <summary>
146                 /// Gets the name of the local computer
147                 /// </summary>
148                 public extern static string MachineName {
149                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
150                         [EnvironmentPermission (SecurityAction.Demand, Read="COMPUTERNAME")]
151                         [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
152                         get;
153                 }
154
155                 /// <summary>
156                 /// Gets the standard new line value
157                 /// </summary>
158                 public extern static string NewLine {
159                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
160                         get;
161                 }
162
163                 //
164                 // Support methods and fields for OSVersion property
165                 //
166                 static OperatingSystem os;
167
168                 internal static extern PlatformID Platform {
169                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
170                         get;
171                 }
172
173                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
174                 internal static extern string GetOSVersionString ();
175
176                 /// <summary>
177                 /// Gets the current OS version information
178                 /// </summary>
179                 public static OperatingSystem OSVersion {
180                         get {
181                                 if (os == null) {
182                                         Version v = Version.CreateFromString (GetOSVersionString ());
183                                         PlatformID p = Platform;
184 #if NET_2_0
185                                         if ((int) p == 128)
186                                                 p = PlatformID.Unix;
187 #endif
188                                         os = new OperatingSystem (p, v);
189                                 }
190                                 return os;
191                         }
192                 }
193
194                 /// <summary>
195                 /// Get StackTrace
196                 /// </summary>
197                 public static string StackTrace {
198                         [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
199                         get {
200                                 System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace (1, true);
201                                 return trace.ToString ();
202                         }
203                 }
204
205                 /// <summary>
206                 /// Get a fully qualified path to the system directory
207                 /// </summary>
208                 public static string SystemDirectory {
209                         get {
210                                 return GetFolderPath (SpecialFolder.System);
211                         }
212                 }
213
214                 /// <summary>
215                 /// Get the number of milliseconds that have elapsed since the system was booted
216                 /// </summary>
217                 public extern static int TickCount {
218                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
219                         get;
220                 }
221
222                 /// <summary>
223                 /// Get UserDomainName
224                 /// </summary>
225                 public static string UserDomainName {
226                         // FIXME: this variable doesn't exist (at least not on WinXP) - reported to MS as FDBK20562
227                         [EnvironmentPermission (SecurityAction.Demand, Read="USERDOMAINNAME")]
228                         get {
229                                 return MachineName;
230                         }
231                 }
232
233                 /// <summary>
234                 /// Gets a flag indicating whether the process is in interactive mode
235                 /// </summary>
236                 [MonoTODO]
237                 public static bool UserInteractive {
238                         get {
239                                 return false;
240                         }
241                 }
242
243                 /// <summary>
244                 /// Get the user name of current process is running under
245                 /// </summary>
246                 public extern static string UserName {
247                         [MethodImplAttribute (MethodImplOptions.InternalCall)]
248                         [EnvironmentPermission (SecurityAction.Demand, Read="USERNAME;USER")]
249                         get;
250                 }
251
252                 /// <summary>
253                 /// Get the version of the common language runtime 
254                 /// </summary>
255                 public static Version Version {
256                         get {
257                                 return new Version (Consts.FxFileVersion);
258                         }
259                 }
260
261                 /// <summary>
262                 /// Get the amount of physical memory mapped to process
263                 /// </summary>
264                 [MonoTODO]
265                 public static long WorkingSet {
266                         [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
267                         get { return 0; }
268                 }
269
270                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
271                 [SecurityPermission (SecurityAction.Demand, UnmanagedCode=true)]
272                 public extern static void Exit (int exitCode);
273
274                 /// <summary>
275                 /// Substitute environment variables in the argument "name"
276                 /// </summary>
277                 public static string ExpandEnvironmentVariables (string name)
278                 {
279                         if (name == null)
280                                 throw new ArgumentNullException ("name");
281
282                         int off1 = name.IndexOf ('%');
283                         if (off1 == -1)
284                                 return name;
285
286                         int len = name.Length;
287                         int off2 = 0;
288                         if (off1 == len - 1 || (off2 = name.IndexOf ('%', off1 + 1)) == -1)
289                                 return name;
290
291                         StringBuilder result = new StringBuilder ();
292                         result.Append (name, 0, off1);
293                         Hashtable tbl = null;
294                         do {
295                                 string var = name.Substring (off1 + 1, off2 - off1 - 1);
296                                 string value = GetEnvironmentVariable (var);
297                                 if (value == null && Environment.IsRunningOnWindows) {
298                                         // On windows, env. vars. are case insensitive
299                                         if (tbl == null)
300                                                 tbl = GetEnvironmentVariablesNoCase ();
301
302                                         value = tbl [var] as string;
303                                 }
304                                 
305                                 // If value not found, add %FOO to stream,
306                                 //  and use the closing % for the next iteration.
307                                 // If value found, expand it in place of %FOO%
308                                 if (value == null) {
309                                         result.Append ('%');
310                                         result.Append (var);
311                                         off2--;
312                                 } else {
313                                         result.Append (value);
314                                 }
315                                 int oldOff2 = off2;
316                                 off1 = name.IndexOf ('%', off2 + 1);
317                                 // If no % found for off1, don't look for one for off2
318                                 off2 = (off1 == -1 || off2 > len-1)? -1 :name.IndexOf ('%', off1 + 1);
319                                 // textLen is the length of text between the closing % of current iteration
320                                 //  and the starting % of the next iteration if any. This text is added to output
321                                 int textLen;
322                                 // If no new % found, use all the remaining text
323                                 if (off1 == -1 || off2 == -1)
324                                         textLen = len - oldOff2 - 1;
325                                 // If value found in current iteration, use text after current closing % and next %
326                                 else if(value != null)
327                                         textLen = off1 - oldOff2 - 1;
328                                 // If value not found in current iteration, but a % was found for next iteration,
329                                 //  use text from current closing % to the next %.
330                                 else
331                                         textLen = off1 - oldOff2;
332                                 if(off1 >= oldOff2 || off1 == -1)
333                                         result.Append (name, oldOff2+1, textLen);
334                         } while (off2 > -1 && off2 < len);
335                                 
336                         return result.ToString ();
337
338                 }
339
340                 /// <summary>
341                 /// Return an array of the command line arguments of the current process
342                 /// </summary>
343                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
344                 [EnvironmentPermissionAttribute (SecurityAction.Demand, Read = "PATH")]
345                 public extern static string[] GetCommandLineArgs ();
346
347                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
348                 internal extern static string internalGetEnvironmentVariable (string name);
349
350                 /// <summary>
351                 /// Return a string containing the value of the environment
352                 /// variable identifed by parameter "variable"
353                 /// </summary>
354                 public static string GetEnvironmentVariable (string name)
355                 {
356                         if (SecurityManager.SecurityEnabled) {
357                                 new EnvironmentPermission (EnvironmentPermissionAccess.Read, name).Demand ();
358                         }
359                         return internalGetEnvironmentVariable (name);
360                 }
361
362                 static Hashtable GetEnvironmentVariablesNoCase ()
363                 {
364                         Hashtable vars = new Hashtable (CaseInsensitiveHashCodeProvider.Default,
365                                                         CaseInsensitiveComparer.Default);
366
367                         foreach (string name in GetEnvironmentVariableNames ()) {
368                                 vars [name] = internalGetEnvironmentVariable (name);
369                         }
370
371                         return vars;
372                 }
373
374                 /// <summary>
375                 /// Return a set of all environment variables and their values
376                 /// </summary>
377 #if NET_2_0
378                 public static IDictionary GetEnvironmentVariables ()
379                 {
380                         StringBuilder sb = null;
381                         if (SecurityManager.SecurityEnabled) {
382                                 // we must have access to each variable to get the lot
383                                 sb = new StringBuilder ();
384                                 // but (performance-wise) we do not want a stack-walk
385                                 // for each of them so we concatenate them
386                         }
387
388                         Hashtable vars = new Hashtable ();
389                         foreach (string name in GetEnvironmentVariableNames ()) {
390                                 vars [name] = internalGetEnvironmentVariable (name);
391                                 if (sb != null) {
392                                         sb.Append (name);
393                                         sb.Append (";");
394                                 }
395                         }
396
397                         if (sb != null) {
398                                 new EnvironmentPermission (EnvironmentPermissionAccess.Read, sb.ToString ()).Demand ();
399                         }
400                         return vars;
401                 }
402 #else
403                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
404                 public static IDictionary GetEnvironmentVariables ()
405                 {
406                         Hashtable vars = new Hashtable ();
407                         foreach (string name in GetEnvironmentVariableNames ()) {
408                                 vars [name] = internalGetEnvironmentVariable (name);
409                         }
410                         return vars;
411                 }
412 #endif
413
414                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
415                 private extern static string GetWindowsFolderPath (int folder);
416
417                 /// <summary>
418                 /// Returns the fully qualified path of the
419                 /// folder specified by the "folder" parameter
420                 /// </summary>
421                 public static string GetFolderPath (SpecialFolder folder)
422                 {
423                         string dir = null;
424
425                         if (Environment.IsRunningOnWindows) {
426                                 dir = GetWindowsFolderPath ((int) folder);
427                         } else {
428                                 dir = InternalGetFolderPath (folder);
429                         }
430
431                         if ((dir != null) && (dir.Length > 0) && SecurityManager.SecurityEnabled) {
432                                 new FileIOPermission (FileIOPermissionAccess.PathDiscovery, dir).Demand ();
433                         }
434                         return dir;
435                 }
436
437                 // the security runtime (and maybe other parts of corlib) needs the
438                 // information to initialize themselves before permissions can be checked
439                 internal static string InternalGetFolderPath (SpecialFolder folder)
440                 {
441                         string home = internalGetHome ();
442
443                         // http://freedesktop.org/Standards/basedir-spec/basedir-spec-0.6.html
444
445                         // note: skip security check for environment variables
446                         string data = internalGetEnvironmentVariable ("XDG_DATA_HOME");
447                         if ((data == null) || (data == String.Empty)) {
448                                 data = Path.Combine (home, ".local");
449                                 data = Path.Combine (data, "share");
450                         }
451
452                         // note: skip security check for environment variables
453                         string config = internalGetEnvironmentVariable ("XDG_CONFIG_HOME");
454                         if ((config == null) || (config == String.Empty)) {
455                                 config = Path.Combine (home, ".config");
456                         }
457
458                         switch (folder) {
459 #if NET_1_1
460                         // MyComputer is a virtual directory
461                         case SpecialFolder.MyComputer:
462                                 return String.Empty;
463 #endif
464                         // personal == ~
465                         case SpecialFolder.Personal:
466                                 return home;
467                         // use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
468                         case SpecialFolder.ApplicationData:
469                                 return config;
470                         //use FDO's DATA_HOME. This is *NOT* synced
471                         case SpecialFolder.LocalApplicationData:
472                                 return data;
473 #if NET_1_1
474                         case SpecialFolder.Desktop:
475 #endif
476                         case SpecialFolder.DesktopDirectory:
477                                 return Path.Combine (home, "Desktop");
478                         
479                         // these simply dont exist on Linux
480                         // The spec says if a folder doesnt exist, we
481                         // should return ""
482                         case SpecialFolder.Favorites:
483                         case SpecialFolder.Programs:
484                         case SpecialFolder.SendTo:
485                         case SpecialFolder.StartMenu:
486                         case SpecialFolder.Startup:
487                         case SpecialFolder.MyMusic:
488                         case SpecialFolder.MyPictures:
489                         case SpecialFolder.Templates:
490                         case SpecialFolder.Cookies:
491                         case SpecialFolder.History:
492                         case SpecialFolder.InternetCache:
493                         case SpecialFolder.Recent:
494                         case SpecialFolder.CommonProgramFiles:
495                         case SpecialFolder.ProgramFiles:
496                         case SpecialFolder.System:
497                                 return String.Empty;
498                         // This is where data common to all users goes
499                         case SpecialFolder.CommonApplicationData:
500                                 return "/usr/share";
501                         default:
502                                 throw new ArgumentException ("Invalid SpecialFolder");
503                         }
504                 }
505
506                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
507                 public static string[] GetLogicalDrives ()
508                 {
509                         return GetLogicalDrivesInternal ();
510                 }
511
512                 // FIXME: Anyone using this anywhere ?
513                 static internal string GetResourceString (string s) { return ""; }
514
515                 
516 #if NET_2_0
517                 public static string GetEnvironmentVariable (string variable, EnvironmentVariableTarget target)
518                 {
519                         switch (target) {
520                         case EnvironmentVariableTarget.Process:
521                                 return GetEnvironmentVariable (variable);
522                         case EnvironmentVariableTarget.Machine:
523                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
524                                 if (!IsRunningOnWindows)
525                                         return null;
526                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
527                                         return env.GetValue (variable).ToString ();
528                                 }
529                         case EnvironmentVariableTarget.User:
530                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
531                                 if (!IsRunningOnWindows)
532                                         return null;
533                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", false)) {
534                                         return env.GetValue (variable).ToString ();
535                                 }
536                         default:
537                                 throw new ArgumentException ("target");
538                         }
539                 }
540
541                 public static IDictionary GetEnvironmentVariables (EnvironmentVariableTarget target)
542                 {
543                         IDictionary variables = (IDictionary)new Hashtable ();
544                         switch (target) {
545                         case EnvironmentVariableTarget.Process:
546                                 variables = GetEnvironmentVariables ();
547                                 break;
548                         case EnvironmentVariableTarget.Machine:
549                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
550                                 if (IsRunningOnWindows) {
551                                         using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment")) {
552                                                 string[] value_names = env.GetValueNames ();
553                                                 foreach (string value_name in value_names)
554                                                         variables.Add (value_name, env.GetValue (value_name));
555                                         }
556                                 }
557                                 break;
558                         case EnvironmentVariableTarget.User:
559                                 new EnvironmentPermission (PermissionState.Unrestricted).Demand ();
560                                 if (IsRunningOnWindows) {
561                                         using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment")) {
562                                                 string[] value_names = env.GetValueNames ();
563                                                 foreach (string value_name in value_names)
564                                                         variables.Add (value_name, env.GetValue (value_name));
565                                         }
566                                 }
567                                 break;
568                         default:
569                                 throw new ArgumentException ("target");
570                         }
571                         return variables;
572                 }
573
574                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
575                 public static void SetEnvironmentVariable (string variable, string value)
576                 {
577                         SetEnvironmentVariable (variable, value, EnvironmentVariableTarget.Process);
578                 }
579
580                 [EnvironmentPermission (SecurityAction.Demand, Unrestricted = true)]
581                 public static void SetEnvironmentVariable (string variable, string value, EnvironmentVariableTarget target)
582                 {
583                         if (variable == null)
584                                 throw new ArgumentNullException ("variable");
585                         if (variable == String.Empty)
586                                 throw new ArgumentException ("String cannot be of zero length.", "variable");
587                         if (variable.IndexOf ('=') != -1)
588                                 throw new ArgumentException ("Environment variable name cannot contain an equal character.", "variable");
589                         if (variable[0] == '\0')
590                                 throw new ArgumentException ("The first char in the string is the null character.", "variable");
591
592                         switch (target) {
593                         case EnvironmentVariableTarget.Process:
594                                 InternalSetEnvironmentVariable (variable, value);
595                                 break;
596                         case EnvironmentVariableTarget.Machine:
597                                 if (!IsRunningOnWindows)
598                                         return;
599                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.LocalMachine.OpenSubKey (@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true)) {
600                                         if (value == null || value.Length == 0)
601                                                 env.DeleteValue (variable, false);
602                                         else
603                                                 env.SetValue (variable, value);
604                                 }
605                                 break;
606                         case EnvironmentVariableTarget.User:
607                                 if (!IsRunningOnWindows)
608                                         return;
609                                 using (Microsoft.Win32.RegistryKey env = Microsoft.Win32.Registry.CurrentUser.OpenSubKey ("Environment", true)) {
610                                         if (value == null || value.Length == 0)
611                                                 env.DeleteValue (variable, false);
612                                         else
613                                                 env.SetValue (variable, value);
614                                 }
615                                 break;
616                         default:
617                                 throw new ArgumentException ("target");
618                         }
619                 }
620
621                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
622                 internal static extern void InternalSetEnvironmentVariable (string variable, string value);
623
624                 [MonoTODO]
625                 public static int ProcessorCount {
626                         [EnvironmentPermission (SecurityAction.Demand, Read="NUMBER_OF_PROCESSORS")]
627                         get {
628                                 // note: Changes to the NUMBER_OF_PROCESSORS environment variable
629                                 // under Windows doesn't affect the (good) value returned.
630                                 throw new NotImplementedException ();
631                         }
632                 }
633
634                 [MonoTODO ("not much documented")]
635                 [SecurityPermission (SecurityAction.LinkDemand, UnmanagedCode=true)]
636                 public static void FailFast (string message)
637                 {
638                         throw new NotImplementedException ();
639                 }
640 #endif
641
642                 // private methods
643
644                 internal static bool IsRunningOnWindows {
645                         get { return ((int) Platform != 128); }
646                 }
647
648                 private static string GacPath {
649                         get {
650                                 if (Environment.IsRunningOnWindows) {
651                                         /* On windows, we don't know the path where mscorlib.dll will be installed */
652                                         string corlibDir = new DirectoryInfo (Path.GetDirectoryName (typeof (int).Assembly.Location)).Parent.Parent.FullName;
653                                         return Path.Combine (Path.Combine (corlibDir, "mono"), "gac");
654                                 }
655
656                                 return Path.Combine (Path.Combine (internalGetGacPath (), "mono"), "gac");
657                         }
658                 }
659
660                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
661                 private extern static string [] GetLogicalDrivesInternal ();
662
663                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
664                 private extern static string [] GetEnvironmentVariableNames ();
665
666                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
667                 internal extern static string GetMachineConfigPath ();
668
669                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
670                 internal extern static string internalGetGacPath ();
671
672                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
673                 internal extern static string internalGetHome ();
674         }
675 }
676