Merge pull request #485 from mtausig/master
[mono.git] / mcs / class / Microsoft.Build.Utilities / Mono.XBuild.Utilities / MSBuildUtils.cs
1 //
2 // Utilities.cs:
3 //
4 // Author:
5 //      Ankit Jain (jankit@novell.com)
6 //
7 // Copyright (c) 2009 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28 using System;
29 using System.Collections;
30 using System.Text;
31 using System.IO;
32 using System.Runtime.InteropServices;
33
34 namespace Mono.XBuild.Utilities {
35         internal static class MSBuildUtils {
36
37                 public readonly static bool RunningOnMac;
38                 public readonly static bool RunningOnWindows;
39                 static Hashtable charsToEscape;
40
41                 static MSBuildUtils ()
42                 {
43                         RunningOnWindows = Path.DirectorySeparatorChar == '\\';
44                         RunningOnMac = !RunningOnWindows && IsRunningOnMac ();
45
46                         charsToEscape = new Hashtable ();
47                         
48                         charsToEscape.Add ('$', null);
49                         charsToEscape.Add ('%', null);
50                         charsToEscape.Add ('\'', null);
51                         charsToEscape.Add ('(', null);
52                         charsToEscape.Add (')', null);
53                         charsToEscape.Add ('*', null);
54                         charsToEscape.Add (';', null);
55                         charsToEscape.Add ('?', null);
56                         charsToEscape.Add ('@', null);
57                 }
58         
59                 public static string Escape (string unescapedExpression)
60                 {
61                         StringBuilder sb = new StringBuilder ();
62                         
63                         foreach (char c in unescapedExpression) {
64                                 if (charsToEscape.Contains (c))
65                                         sb.AppendFormat ("%{0:x2}", (int) c);
66                                 else
67                                         sb.Append (c);
68                         }
69                         
70                         return sb.ToString ();
71                 }
72                 
73                 // FIXME: add tests for this
74                 internal static string Unescape (string escapedExpression)
75                 {
76                         StringBuilder sb = new StringBuilder ();
77                         
78                         int i = 0;
79                         while (i < escapedExpression.Length) {
80                                 sb.Append (Uri.HexUnescape (escapedExpression, ref i));
81                         }
82                         
83                         return sb.ToString ();
84                 }
85
86                 internal static string UnescapeFromXml (string text)
87                 {
88                         StringBuilder sb = new StringBuilder ();
89                         for (int i = 0; i < text.Length; i++) {
90                                 char c1 = text[i];
91                                 if (c1 == '&') {
92                                         int end = text.IndexOf (';', i);
93                                         if (end == -1)
94                                                 throw new FormatException ("Unterminated XML entity.");
95                                         string entity = text.Substring (i+1, end - i - 1);
96                                         switch (entity) {
97                                         case "lt":
98                                                 sb.Append ('<');
99                                                 break;
100                                         case "gt":
101                                                 sb.Append ('>');
102                                                 break;
103                                         case "amp":
104                                                 sb.Append ('&');
105                                                 break;
106                                         case "apos":
107                                                 sb.Append ('\'');
108                                                 break;
109                                         case "quot":
110                                                 sb.Append ('"');
111                                                 break;
112                                         default:
113                                                 throw new FormatException ("Unrecognized XML entity '&" + entity + ";'.");
114                                         }
115                                         i = end;
116                                 } else
117                                         sb.Append (c1);
118                         }
119                         return sb.ToString ();
120                 }
121
122                 [DllImport ("libc")]
123                 static extern int uname (IntPtr buf);
124
125                 //From Managed.Windows.Forms/XplatUI
126                 static bool IsRunningOnMac ()
127                 {
128                         IntPtr buf = IntPtr.Zero;
129                         try {
130                                 buf = System.Runtime.InteropServices.Marshal.AllocHGlobal (8192);
131                                 // This is a hacktastic way of getting sysname from uname ()
132                                 if (uname (buf) == 0) {
133                                         string os = System.Runtime.InteropServices.Marshal.PtrToStringAnsi (buf);
134                                         if (os == "Darwin")
135                                                 return true;
136                                 }
137                         } catch {
138                         } finally {
139                                 if (buf != IntPtr.Zero)
140                                         System.Runtime.InteropServices.Marshal.FreeHGlobal (buf);
141                         }
142                         return false;
143                 }
144
145                 internal static string FromMSBuildPath (string relPath)
146                 {
147                         string result = null;
148                         FromMSBuildPath (String.Empty, relPath, out result);
149                         return result;
150                 }
151
152                 internal static bool FromMSBuildPath (string basePath, string relPath, out string resultPath)
153                 {
154                         resultPath = relPath;
155                         
156                         if (string.IsNullOrEmpty (relPath))
157                                 return false;
158                         
159                         string path = relPath;
160                         if (!RunningOnWindows)
161                                 path = path.Replace ("\\", "/");
162                         
163                         path = Unescape (path);
164
165                         if (char.IsLetter (path [0]) && path.Length > 1 && path[1] == ':') {
166                                 if (RunningOnWindows) {
167                                         resultPath = path; // Return the escaped value
168                                         return true;
169                                 } else
170                                         return false;
171                         }
172                         
173                         if (basePath != null)
174                                 path = Path.Combine (basePath, path);
175                         
176                         if (System.IO.File.Exists (path) || System.IO.Directory.Exists (path)){
177                                 resultPath = Path.GetFullPath (path);
178                                 return true;
179                         }
180                                 
181                         if (Path.IsPathRooted (path) && !RunningOnWindows) {
182                                         
183                                 // Windows paths are case-insensitive. When mapping an absolute path
184                                 // we can try to find the correct case for the path.
185                                 
186                                 string[] names = path.Substring (1).Split ('/');
187                                 string part = "/";
188                                 
189                                 for (int n=0; n<names.Length; n++) {
190                                         string[] entries;
191
192                                         if (names [n] == ".."){
193                                                 if (part == "/")
194                                                         return false; // Can go further back. It's not an existing file
195                                                 part = Path.GetFullPath (part + "/..");
196                                                 continue;
197                                         }
198                                         
199                                         entries = Directory.GetFileSystemEntries (part);
200                                         
201                                         string fpath = null;
202                                         foreach (string e in entries) {
203                                                 if (string.Compare (Path.GetFileName (e), names[n], true) == 0) {
204                                                         fpath = e;
205                                                         break;
206                                                 }
207                                         }
208                                         if (fpath == null) {
209                                                 // Part of the path does not exist. Can't do any more checking.
210                                                 part = Path.GetFullPath (part);
211                                                 for (; n < names.Length; n++)
212                                                         part += "/" + names[n];
213                                                 resultPath = part;
214                                                 return true;
215                                         }
216
217                                         part = fpath;
218                                 }
219                                 resultPath = Path.GetFullPath (part);
220                         } else {
221                                 resultPath = Path.GetFullPath (path);
222                         }
223                         return true;
224                 }
225         }
226
227 }