svn path=/branches/mono-1-1-9/mcs/; revision=51216
[mono.git] / mcs / class / Mono.Posix / Mono.Unix / UnixMarshal.cs
1 //
2 // Mono.Unix/UnixMarshal.cs
3 //
4 // Authors:
5 //   Jonathan Pryor (jonpryor@vt.edu)
6 //
7 // (C) 2004 Jonathan Pryor
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
29 using System;
30 using System.IO;
31 using System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization;
34 using System.Text;
35 using Mono.Unix;
36
37 namespace Mono.Unix {
38
39         // Scenario:  We want to be able to translate an Error to a string.
40         //  Problem:  Thread-safety.  Strerror(3) isn't thread safe (unless
41         //            thread-local-variables are used, which is probably only 
42         //            true on Windows).
43         // Solution:  Use strerror_r().
44         //  Problem:  strerror_r() isn't portable. 
45         //            (Apparently Solaris doesn't provide it.)
46         // Solution:  Cry.  Then introduce an intermediary, ErrorMarshal.
47         //            ErrorMarshal exposes a single public delegate, Translator,
48         //            which will convert an Error to a string.  It's static
49         //            constructor first tries using strerror_r().  If it works,
50         //            great; use it in the future.  If it doesn't work, fallback to
51         //            using strerror(3).
52         //            This should be thread safe, since the check is done within the
53         //            class constructor lock.
54         //            Strerror(3) will be thread-safe from managed code, but won't
55         //            be thread-safe between managed & unmanaged code.
56         internal class ErrorMarshal
57         {
58                 internal delegate string ErrorTranslator (Error errno);
59
60                 internal static readonly ErrorTranslator Translate;
61
62                 static ErrorMarshal ()
63                 {
64                         try {
65                                 Translate = new ErrorTranslator (strerror_r);
66                                 Translate (Error.ERANGE);
67                         }
68                         catch (EntryPointNotFoundException e) {
69                                 Translate = new ErrorTranslator (strerror);
70                         }
71                 }
72
73                 private static string strerror (Error errno)
74                 {
75                         return Stdlib.strerror (errno);
76                 }
77
78                 private static string strerror_r (Error errno)
79                 {
80                         StringBuilder buf = new StringBuilder (16);
81                         int r = 0;
82                         do {
83                                 buf.Capacity *= 2;
84                                 r = Syscall.strerror_r (errno, buf);
85                         } while (r == -1 && Stdlib.GetLastError() == Error.ERANGE);
86
87                         if (r == -1)
88                                 return "** Unknown error code: " + ((int) errno) + "**";
89                         return buf.ToString();
90                 }
91         }
92
93         public sealed /* static */ class UnixMarshal
94         {
95                 private UnixMarshal () {}
96
97                 public static string GetErrorDescription (Error errno)
98                 {
99                         return ErrorMarshal.Translate (errno);
100                 }
101
102                 public static IntPtr Alloc (long size)
103                 {
104                         if (size < 0)
105                                 throw new ArgumentOutOfRangeException ("size", "< 0");
106                         return Stdlib.malloc ((ulong) size);
107                 }
108
109                 public static IntPtr ReAlloc (IntPtr ptr, long size)
110                 {
111                         if (size < 0)
112                                 throw new ArgumentOutOfRangeException ("size", "< 0");
113                         return Stdlib.realloc (ptr, (ulong) size);
114                 }
115
116                 public static void Free (IntPtr ptr)
117                 {
118                         Stdlib.free (ptr);
119                 }
120
121                 public static string PtrToString (IntPtr p)
122                 {
123                         // TODO: deal with character set issues.  Will PtrToStringAnsi always
124                         // "Do The Right Thing"?
125                         if (p == IntPtr.Zero)
126                                 return null;
127                         return Marshal.PtrToStringAnsi (p);
128                 }
129
130                 /*
131                  * Marshal a C `char **'.  ANSI C `main' requirements are assumed:
132                  *
133                  *   stringArray is an array of pointers to C strings
134                  *   stringArray has a terminating NULL string.
135                  *
136                  * For example:
137                  *   stringArray[0] = "string 1";
138                  *   stringArray[1] = "string 2";
139                  *   stringArray[2] = NULL
140                  *
141                  * The terminating NULL is required so that we know when to stop looking
142                  * for strings.
143                  */
144                 public static string[] PtrToStringArray (IntPtr stringArray)
145                 {
146                         if (stringArray == IntPtr.Zero)
147                                 return new string[]{};
148
149                         int argc = CountStrings (stringArray);
150                         return PtrToStringArray (argc, stringArray);
151                 }
152
153                 private static int CountStrings (IntPtr stringArray)
154                 {
155                         int count = 0;
156                         while (Marshal.ReadIntPtr (stringArray, count*IntPtr.Size) != IntPtr.Zero)
157                                 ++count;
158                         return count;
159                 }
160
161                 /*
162                  * Like PtrToStringArray(IntPtr), but it allows the user to specify how
163                  * many strings to look for in the array.  As such, the requirement for a
164                  * terminating NULL element is not required.
165                  *
166                  * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
167                  * stringArray[count] is NOT accessed (though ANSI C requires that 
168                  * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
169                  */
170                 public static string[] PtrToStringArray (int count, IntPtr stringArray)
171                 {
172                         if (count < 0)
173                                 throw new ArgumentOutOfRangeException ("count", "< 0");
174                         if (stringArray == IntPtr.Zero)
175                                 return new string[count];
176
177                         string[] members = new string[count];
178                         for (int i = 0; i < count; ++i) {
179                                 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
180                                 members[i] = PtrToString (s);
181                         }
182
183                         return members;
184                 }
185
186                 public static bool ShouldRetrySyscall (int r)
187                 {
188                         if (r == -1 && Stdlib.GetLastError () == Error.EINTR)
189                                 return true;
190                         return false;
191                 }
192
193                 public static bool ShouldRetrySyscall (int r, out Error error)
194                 {
195                         error = (Error) 0;
196                         if (r == -1 && (error = Stdlib.GetLastError ()) == Error.EINTR)
197                                 return true;
198                         return false;
199                 }
200
201                 // we can't permit any printf(3)-style formatting information, since that
202                 // would kill the stack.  However, replacing %% is silly, and some %* are
203                 // permitted (such as %m in syslog to print strerror(errno)).
204                 internal static string EscapeFormatString (string message, 
205                                 char [] permitted)
206                 {
207                         if (message == null)
208                                 return "";
209                         StringBuilder sb = new StringBuilder (message.Length);
210                         for (int i = 0; i < message.Length; ++i) {
211                                 char c = message [i];
212                                 sb.Append (c);
213                                 if (c == '%' && (i+1) < message.Length) {
214                                         char n = message [i+1];
215                                         if (n == '%' || IsCharPresent (permitted, n))
216                                                 sb.Append (n);
217                                         else
218                                                 sb.Append ('%').Append (n);
219                                         ++i;
220                                 }
221                                 // invalid format string: % at EOS.
222                                 else if (c == '%')
223                                         sb.Append ('%');
224                         }
225                         return sb.ToString ();
226                 }
227
228                 private static bool IsCharPresent (char[] array, char c)
229                 {
230                         if (array == null)
231                                 return false;
232                         for (int i = 0; i < array.Length; ++i)
233                                 if (array [i] == c)
234                                         return true;
235                         return false;
236                 }
237
238                 internal static Exception CreateExceptionForError (Error errno)
239                 {
240                         string message = GetErrorDescription (errno);
241                         UnixIOException p = new UnixIOException (errno);
242                         switch (errno) {
243                                 case Error.EFAULT:        return new NullReferenceException (message, p);
244                                 case Error.EINVAL:        return new ArgumentException (message, p);
245                                 case Error.EIO:
246                                   case Error.ENOSPC:
247                                   case Error.EROFS:
248                                   case Error.ESPIPE:
249                                         return new IOException (message, p);
250                                 case Error.ENAMETOOLONG:  return new PathTooLongException (message, p);
251                                 case Error.ENOENT:        return new FileNotFoundException (message, p);
252                                 case Error.ENOEXEC:       return new InvalidProgramException (message, p);
253                                 case Error.EOVERFLOW:     return new OverflowException (message, p);
254                                 case Error.ERANGE:        return new ArgumentOutOfRangeException (message);
255                                 default: /* ignore */     break;
256                         }
257                         return p;
258                 }
259
260                 internal static Exception CreateExceptionForLastError ()
261                 {
262                         return CreateExceptionForError (Stdlib.GetLastError());
263                 }
264
265                 public static void ThrowExceptionForError (Error errno)
266                 {
267                         throw CreateExceptionForError (errno);
268                 }
269
270                 public static void ThrowExceptionForLastError ()
271                 {
272                         throw CreateExceptionForLastError ();
273                 }
274
275                 public static void ThrowExceptionForErrorIf (int retval, Error errno)
276                 {
277                         if (retval == -1)
278                                 ThrowExceptionForError (errno);
279                 }
280
281                 public static void ThrowExceptionForLastErrorIf (int retval)
282                 {
283                         if (retval == -1)
284                                 ThrowExceptionForLastError ();
285                 }
286         }
287 }
288
289 // vim: noexpandtab