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