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