[mini] Fix test compiling when running !MOBILE
[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-2006 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 (Native.Errno errno);
59
60                 internal static readonly ErrorTranslator Translate;
61
62                 static ErrorMarshal ()
63                 {
64                         try {
65                                 Translate = new ErrorTranslator (strerror_r);
66                                 Translate (Native.Errno.ERANGE);
67                         }
68                         catch (EntryPointNotFoundException) {
69                                 Translate = new ErrorTranslator (strerror);
70                         }
71                 }
72
73                 private static string strerror (Native.Errno errno)
74                 {
75                         return Native.Stdlib.strerror (errno);
76                 }
77
78                 private static string strerror_r (Native.Errno errno)
79                 {
80                         StringBuilder buf = new StringBuilder (16);
81                         int r = 0;
82                         do {
83                                 buf.Capacity *= 2;
84                                 r = Native.Syscall.strerror_r (errno, buf);
85                         } while (r == -1 && Native.Stdlib.GetLastError() == Native.Errno.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                 [CLSCompliant (false)]
98                 public static string GetErrorDescription (Native.Errno errno)
99                 {
100                         return ErrorMarshal.Translate (errno);
101                 }
102
103                 public static IntPtr AllocHeap (long size)
104                 {
105                         if (size < 0)
106                                 throw new ArgumentOutOfRangeException ("size", "< 0");
107                         return Native.Stdlib.malloc ((ulong) size);
108                 }
109
110                 public static IntPtr ReAllocHeap (IntPtr ptr, long size)
111                 {
112                         if (size < 0)
113                                 throw new ArgumentOutOfRangeException ("size", "< 0");
114                         return Native.Stdlib.realloc (ptr, (ulong) size);
115                 }
116
117                 public static void FreeHeap (IntPtr ptr)
118                 {
119                         Native.Stdlib.free (ptr);
120                 }
121
122                 public static unsafe string PtrToStringUnix (IntPtr p)
123                 {
124                         if (p == IntPtr.Zero)
125                                 return null;
126
127                         int len = checked ((int) Native.Stdlib.strlen (p));
128                         return new string ((sbyte*) p, 0, len, UnixEncoding.Instance);
129                 }
130
131                 public static string PtrToString (IntPtr p)
132                 {
133                         if (p == IntPtr.Zero)
134                                 return null;
135                         return PtrToString (p, UnixEncoding.Instance);
136                 }
137
138                 public static unsafe string PtrToString (IntPtr p, Encoding encoding)
139                 {
140                         if (p == IntPtr.Zero)
141                                 return null;
142
143                         if (encoding == null)
144                                 throw new ArgumentNullException ("encoding");
145
146                         int len = GetStringByteLength (p, encoding);
147
148                         // Due to variable-length encoding schemes, GetStringByteLength() may
149                         // have returned multiple "null" characters.  (For example, when
150                         // encoding a string into UTF-8 there will be 4 terminating nulls.)
151                         // We don't want these null's to be in the returned string, so strip
152                         // them off.
153                         string s = new string ((sbyte*) p, 0, len, encoding);
154                         len = s.Length;
155                         while (len > 0 && s [len-1] == 0)
156                                 --len;
157                         if (len == s.Length) 
158                                 return s;
159                         return s.Substring (0, len);
160                 }
161
162                 private static int GetStringByteLength (IntPtr p, Encoding encoding)
163                 {
164                         Type encodingType = encoding.GetType ();
165
166                         int len = -1;
167
168                         // Encodings that will always end with a single null byte
169                         if (typeof(UTF8Encoding).IsAssignableFrom (encodingType) ||
170                                         typeof(UTF7Encoding).IsAssignableFrom (encodingType) ||
171                                         typeof(UnixEncoding).IsAssignableFrom (encodingType) ||
172                                         typeof(ASCIIEncoding).IsAssignableFrom (encodingType)) {
173                                 len = checked ((int) Native.Stdlib.strlen (p));
174                         }
175                         // Encodings that will always end with a 0x0000 16-bit word
176                         else if (typeof(UnicodeEncoding).IsAssignableFrom (encodingType)) {
177                                 len = GetInt16BufferLength (p);
178                         }
179                         // Encodings that will always end with a 0x00000000 32-bit word
180                         else if (typeof(UTF32Encoding).IsAssignableFrom (encodingType)) {
181                                 len = GetInt32BufferLength (p);
182                         }
183                         // Some non-public encoding, such as Latin1 or a DBCS charset.
184                         // Look for a sequence of encoding.GetMaxByteCount() bytes that are all
185                         // 0, which should be the terminating null.
186                         // This is "iffy", since it may fail for variable-width encodings; for
187                         // example, UTF8Encoding.GetMaxByteCount(1) = 4, so this would read 3
188                         // bytes past the end of the string, possibly into garbage memory
189                         // (which is why we special case UTF above).
190                         else {
191                                 len = GetRandomBufferLength (p, encoding.GetMaxByteCount(1));
192                         }
193
194                         if (len == -1)
195                                 throw new NotSupportedException ("Unable to determine native string buffer length");
196                         return len;
197                 }
198
199                 private static int GetInt16BufferLength (IntPtr p)
200                 {
201                         int len = 0;
202                         while (Marshal.ReadInt16 (p, len*2) != 0)
203                                 checked {++len;}
204                         return checked(len*2);
205                 }
206
207                 private static int GetInt32BufferLength (IntPtr p)
208                 {
209                         int len = 0;
210                         while (Marshal.ReadInt32 (p, len*4) != 0)
211                                 checked {++len;}
212                         return checked(len*4);
213                 }
214
215                 private static int GetRandomBufferLength (IntPtr p, int nullLength)
216                 {
217                         switch (nullLength) {
218                                 case 1: return checked ((int) Native.Stdlib.strlen (p));
219                                 case 2: return GetInt16BufferLength (p);
220                                 case 4: return GetInt32BufferLength (p);
221                         }
222
223                         int len = 0;
224                         int num_null_seen = 0;
225
226                         do {
227                                 byte b = Marshal.ReadByte (p, len++);
228                                 if (b == 0)
229                                         ++num_null_seen;
230                                 else
231                                         num_null_seen = 0;
232                         } while (num_null_seen != nullLength);
233
234                         return len;
235                 }
236
237                 /*
238                  * Marshal a C `char **'.  ANSI C `main' requirements are assumed:
239                  *
240                  *   stringArray is an array of pointers to C strings
241                  *   stringArray has a terminating NULL string.
242                  *
243                  * For example:
244                  *   stringArray[0] = "string 1";
245                  *   stringArray[1] = "string 2";
246                  *   stringArray[2] = NULL
247                  *
248                  * The terminating NULL is required so that we know when to stop looking
249                  * for strings.
250                  */
251                 public static string[] PtrToStringArray (IntPtr stringArray)
252                 {
253                         return PtrToStringArray (stringArray, UnixEncoding.Instance);
254                 }
255
256                 public static string[] PtrToStringArray (IntPtr stringArray, Encoding encoding)
257                 {
258                         if (stringArray == IntPtr.Zero)
259                                 return new string[]{};
260
261                         int argc = CountStrings (stringArray);
262                         return PtrToStringArray (argc, stringArray, encoding);
263                 }
264
265                 private static int CountStrings (IntPtr stringArray)
266                 {
267                         int count = 0;
268                         while (Marshal.ReadIntPtr (stringArray, count*IntPtr.Size) != IntPtr.Zero)
269                                 ++count;
270                         return count;
271                 }
272
273                 /*
274                  * Like PtrToStringArray(IntPtr), but it allows the user to specify how
275                  * many strings to look for in the array.  As such, the requirement for a
276                  * terminating NULL element is not required.
277                  *
278                  * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
279                  * stringArray[count] is NOT accessed (though ANSI C requires that 
280                  * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
281                  */
282                 public static string[] PtrToStringArray (int count, IntPtr stringArray)
283                 {
284                         return PtrToStringArray (count, stringArray, UnixEncoding.Instance);
285                 }
286
287                 public static string[] PtrToStringArray (int count, IntPtr stringArray, Encoding encoding)
288                 {
289                         if (count < 0)
290                                 throw new ArgumentOutOfRangeException ("count", "< 0");
291                         if (encoding == null)
292                                 throw new ArgumentNullException ("encoding");
293                         if (stringArray == IntPtr.Zero)
294                                 return new string[count];
295
296                         string[] members = new string[count];
297                         for (int i = 0; i < count; ++i) {
298                                 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
299                                 members[i] = PtrToString (s, encoding);
300                         }
301
302                         return members;
303                 }
304
305                 public static IntPtr StringToHeap (string s)
306                 {
307                         return StringToHeap (s, UnixEncoding.Instance);
308                 }
309
310                 public static IntPtr StringToHeap (string s, Encoding encoding)
311                 {
312                         return StringToHeap (s, 0, s.Length, encoding);
313                 }
314
315                 public static IntPtr StringToHeap (string s, int index, int count)
316                 {
317                         return StringToHeap (s, index, count, UnixEncoding.Instance);
318                 }
319
320                 public static IntPtr StringToHeap (string s, int index, int count, Encoding encoding)
321                 {
322                         if (s == null)
323                                 return IntPtr.Zero;
324
325                         if (encoding == null)
326                                 throw new ArgumentNullException ("encoding");
327
328                         if (index < 0 || count < 0)
329                                 throw new ArgumentOutOfRangeException ((index < 0 ? "index" : "count"),
330                                          "Non - negative number required.");
331
332                         if (s.Length - index < count)
333                                 throw new ArgumentOutOfRangeException ("s", "Index and count must refer to a location within the string.");
334
335                         int null_terminator_count = encoding.GetMaxByteCount (1);
336                         int length_without_null = encoding.GetByteCount (s);
337                         int marshalLength = checked (length_without_null + null_terminator_count);
338
339                         IntPtr mem = AllocHeap (marshalLength);
340                         if (mem == IntPtr.Zero)
341                                 throw new UnixIOException (Native.Errno.ENOMEM);
342
343                         unsafe {
344                                 fixed (char* p = s) {
345                                         byte* marshal = (byte*)mem;
346                                         int bytes_copied;
347
348                                         try {
349                                                 bytes_copied = encoding.GetBytes (p + index, count, marshal, marshalLength);
350                                         } catch {
351                                                 FreeHeap (mem);
352                                                 throw;
353                                         }
354
355                                         if (bytes_copied != length_without_null) {
356                                                 FreeHeap (mem);
357                                                 throw new NotSupportedException ("encoding.GetBytes() doesn't equal encoding.GetByteCount()!");
358                                         }
359
360                                         marshal += length_without_null;
361                                         for (int i = 0; i < null_terminator_count; ++i)
362                                                 marshal[i] = 0;
363                                 }
364                         }
365
366                         return mem;
367                 }
368
369                 public static bool ShouldRetrySyscall (int r)
370                 {
371                         if (r == -1 && Native.Stdlib.GetLastError () == Native.Errno.EINTR)
372                                 return true;
373                         return false;
374                 }
375
376                 [CLSCompliant (false)]
377                 public static bool ShouldRetrySyscall (int r, out Native.Errno errno)
378                 {
379                         errno = (Native.Errno) 0;
380                         if (r == -1 && (errno = Native.Stdlib.GetLastError ()) == Native.Errno.EINTR)
381                                 return true;
382                         return false;
383                 }
384
385                 // we can't permit any printf(3)-style formatting information, since that
386                 // would kill the stack.  However, replacing %% is silly, and some %* are
387                 // permitted (such as %m in syslog to print strerror(errno)).
388                 internal static string EscapeFormatString (string message, 
389                                 char [] permitted)
390                 {
391                         if (message == null)
392                                 return "";
393                         StringBuilder sb = new StringBuilder (message.Length);
394                         for (int i = 0; i < message.Length; ++i) {
395                                 char c = message [i];
396                                 sb.Append (c);
397                                 if (c == '%' && (i+1) < message.Length) {
398                                         char n = message [i+1];
399                                         if (n == '%' || IsCharPresent (permitted, n))
400                                                 sb.Append (n);
401                                         else
402                                                 sb.Append ('%').Append (n);
403                                         ++i;
404                                 }
405                                 // invalid format string: % at EOS.
406                                 else if (c == '%')
407                                         sb.Append ('%');
408                         }
409                         return sb.ToString ();
410                 }
411
412                 private static bool IsCharPresent (char[] array, char c)
413                 {
414                         if (array == null)
415                                 return false;
416                         for (int i = 0; i < array.Length; ++i)
417                                 if (array [i] == c)
418                                         return true;
419                         return false;
420                 }
421
422                 internal static Exception CreateExceptionForError (Native.Errno errno)
423                 {
424                         string message = GetErrorDescription (errno);
425                         UnixIOException p = new UnixIOException (errno);
426
427                         // Ordering: Order alphabetically by exception first (right column),
428                         // then order alphabetically by Errno value (left column) for the given
429                         // exception.
430                         switch (errno) {
431                                 case Native.Errno.EBADF:
432                                 case Native.Errno.EINVAL:        return new ArgumentException (message, p);
433
434                                 case Native.Errno.ERANGE:        return new ArgumentOutOfRangeException (message);
435                                 case Native.Errno.ENOTDIR:       return new DirectoryNotFoundException (message, p);
436                                 case Native.Errno.ENOENT:        return new FileNotFoundException (message, p);
437
438                                 case Native.Errno.EOPNOTSUPP:
439                                 case Native.Errno.EPERM:         return new InvalidOperationException (message, p);
440
441                                 case Native.Errno.ENOEXEC:       return new InvalidProgramException (message, p);
442
443                                 case Native.Errno.EIO:
444                                 case Native.Errno.ENOSPC:
445                                 case Native.Errno.ENOTEMPTY:
446                                 case Native.Errno.ENXIO:
447                                 case Native.Errno.EROFS:
448                                 case Native.Errno.ESPIPE:        return new IOException (message, p);
449
450                                 case Native.Errno.EFAULT:        return new NullReferenceException (message, p);
451                                 case Native.Errno.EOVERFLOW:     return new OverflowException (message, p);
452                                 case Native.Errno.ENAMETOOLONG:  return new PathTooLongException (message, p);
453
454                                 case Native.Errno.EACCES:
455                                 case Native.Errno.EISDIR:        return new UnauthorizedAccessException (message, p);
456
457                                 default: /* ignore */     break;
458                         }
459                         return p;
460                 }
461
462                 internal static Exception CreateExceptionForLastError ()
463                 {
464                         return CreateExceptionForError (Native.Stdlib.GetLastError());
465                 }
466
467                 [CLSCompliant (false)]
468                 public static void ThrowExceptionForError (Native.Errno errno)
469                 {
470                         throw CreateExceptionForError (errno);
471                 }
472
473                 public static void ThrowExceptionForLastError ()
474                 {
475                         throw CreateExceptionForLastError ();
476                 }
477
478                 [CLSCompliant (false)]
479                 public static void ThrowExceptionForErrorIf (int retval, Native.Errno errno)
480                 {
481                         if (retval == -1)
482                                 ThrowExceptionForError (errno);
483                 }
484
485                 public static void ThrowExceptionForLastErrorIf (int retval)
486                 {
487                         if (retval == -1)
488                                 ThrowExceptionForLastError ();
489                 }
490         }
491 }
492
493 // vim: noexpandtab