Merge pull request #1317 from nealef/master
[mono.git] / mcs / class / Mono.Posix / Mono.Unix / UnixMarshal.cs
index 225849067efeae3d2672c81a64f7f4562b5e8c64..9282356acf54dce7a9cb0bf9e2d0549efb51e1df 100644 (file)
@@ -4,7 +4,7 @@
 // Authors:
 //   Jonathan Pryor (jonpryor@vt.edu)
 //
-// (C) 2004 Jonathan Pryor
+// (C) 2004-2006 Jonathan Pryor
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -55,7 +55,7 @@ namespace Mono.Unix {
        //            be thread-safe between managed & unmanaged code.
        internal class ErrorMarshal
        {
-               internal delegate string ErrorTranslator (Error errno);
+               internal delegate string ErrorTranslator (Native.Errno errno);
 
                internal static readonly ErrorTranslator Translate;
 
@@ -63,26 +63,26 @@ namespace Mono.Unix {
                {
                        try {
                                Translate = new ErrorTranslator (strerror_r);
-                               Translate (Error.ERANGE);
+                               Translate (Native.Errno.ERANGE);
                        }
-                       catch (EntryPointNotFoundException e) {
+                       catch (EntryPointNotFoundException) {
                                Translate = new ErrorTranslator (strerror);
                        }
                }
 
-               private static string strerror (Error errno)
+               private static string strerror (Native.Errno errno)
                {
-                       return Stdlib.strerror (errno);
+                       return Native.Stdlib.strerror (errno);
                }
 
-               private static string strerror_r (Error errno)
+               private static string strerror_r (Native.Errno errno)
                {
                        StringBuilder buf = new StringBuilder (16);
                        int r = 0;
                        do {
                                buf.Capacity *= 2;
-                               r = Syscall.strerror_r (errno, buf);
-                       } while (r == -1 && Stdlib.GetLastError() == Error.ERANGE);
+                               r = Native.Syscall.strerror_r (errno, buf);
+                       } while (r == -1 && Native.Stdlib.GetLastError() == Native.Errno.ERANGE);
 
                        if (r == -1)
                                return "** Unknown error code: " + ((int) errno) + "**";
@@ -94,43 +94,140 @@ namespace Mono.Unix {
        {
                private UnixMarshal () {}
 
-               [Obsolete ("Use GetErrorDescription (Mono.Unix.Native.Errno)")]
-               public static string GetErrorDescription (Error errno)
+               [CLSCompliant (false)]
+               public static string GetErrorDescription (Native.Errno errno)
                {
                        return ErrorMarshal.Translate (errno);
                }
 
-               public static string GetErrorDescription (Native.Errno errno)
+               public static IntPtr AllocHeap (long size)
                {
-                       return ErrorMarshal.Translate ((Error) (int) errno);
+                       if (size < 0)
+                               throw new ArgumentOutOfRangeException ("size", "< 0");
+                       return Native.Stdlib.malloc ((ulong) size);
                }
 
-               public static IntPtr Alloc (long size)
+               public static IntPtr ReAllocHeap (IntPtr ptr, long size)
                {
                        if (size < 0)
                                throw new ArgumentOutOfRangeException ("size", "< 0");
-                       return Stdlib.malloc ((ulong) size);
+                       return Native.Stdlib.realloc (ptr, (ulong) size);
                }
 
-               public static IntPtr ReAlloc (IntPtr ptr, long size)
+               public static void FreeHeap (IntPtr ptr)
                {
-                       if (size < 0)
-                               throw new ArgumentOutOfRangeException ("size", "< 0");
-                       return Stdlib.realloc (ptr, (ulong) size);
+                       Native.Stdlib.free (ptr);
                }
 
-               public static void Free (IntPtr ptr)
+               public static unsafe string PtrToStringUnix (IntPtr p)
                {
-                       Stdlib.free (ptr);
+                       if (p == IntPtr.Zero)
+                               return null;
+
+                       int len = checked ((int) Native.Stdlib.strlen (p));
+                       return new string ((sbyte*) p, 0, len, UnixEncoding.Instance);
                }
 
                public static string PtrToString (IntPtr p)
                {
-                       // TODO: deal with character set issues.  Will PtrToStringAnsi always
-                       // "Do The Right Thing"?
                        if (p == IntPtr.Zero)
                                return null;
-                       return Marshal.PtrToStringAnsi (p);
+                       return PtrToString (p, UnixEncoding.Instance);
+               }
+
+               public static unsafe string PtrToString (IntPtr p, Encoding encoding)
+               {
+                       if (p == IntPtr.Zero)
+                               return null;
+
+                       if (encoding == null)
+                               throw new ArgumentNullException ("encoding");
+
+                       int len = GetStringByteLength (p, encoding);
+
+                       // Due to variable-length encoding schemes, GetStringByteLength() may
+                       // have returned multiple "null" characters.  (For example, when
+                       // encoding a string into UTF-8 there will be 4 terminating nulls.)
+                       // We don't want these null's to be in the returned string, so strip
+                       // them off.
+                       string s = new string ((sbyte*) p, 0, len, encoding);
+                       len = s.Length;
+                       while (len > 0 && s [len-1] == 0)
+                               --len;
+                       if (len == s.Length) 
+                               return s;
+                       return s.Substring (0, len);
+               }
+
+               private static int GetStringByteLength (IntPtr p, Encoding encoding)
+               {
+                       Type encodingType = encoding.GetType ();
+
+                       int len = -1;
+
+                       // Encodings that will always end with a single null byte
+                       if (typeof(UTF8Encoding).IsAssignableFrom (encodingType) ||
+                                       typeof(UTF7Encoding).IsAssignableFrom (encodingType) ||
+                                       typeof(UnixEncoding).IsAssignableFrom (encodingType) ||
+                                       typeof(ASCIIEncoding).IsAssignableFrom (encodingType)) {
+                               len = checked ((int) Native.Stdlib.strlen (p));
+                       }
+                       // Encodings that will always end with a 0x0000 16-bit word
+                       else if (typeof(UnicodeEncoding).IsAssignableFrom (encodingType)) {
+                               len = GetInt16BufferLength (p);
+                       }
+                       // Some non-public encoding, such as Latin1 or a DBCS charset.
+                       // Look for a sequence of encoding.GetMaxByteCount() bytes that are all
+                       // 0, which should be the terminating null.
+                       // This is "iffy", since it may fail for variable-width encodings; for
+                       // example, UTF8Encoding.GetMaxByteCount(1) = 4, so this would read 3
+                       // bytes past the end of the string, possibly into garbage memory
+                       // (which is why we special case UTF above).
+                       else {
+                               len = GetRandomBufferLength (p, encoding.GetMaxByteCount(1));
+                       }
+
+                       if (len == -1)
+                               throw new NotSupportedException ("Unable to determine native string buffer length");
+                       return len;
+               }
+
+               private static int GetInt16BufferLength (IntPtr p)
+               {
+                       int len = 0;
+                       while (Marshal.ReadInt16 (p, len*2) != 0)
+                               checked {++len;}
+                       return checked(len*2);
+               }
+
+               private static int GetInt32BufferLength (IntPtr p)
+               {
+                       int len = 0;
+                       while (Marshal.ReadInt32 (p, len*4) != 0)
+                               checked {++len;}
+                       return checked(len*4);
+               }
+
+               private static int GetRandomBufferLength (IntPtr p, int nullLength)
+               {
+                       switch (nullLength) {
+                               case 1: return checked ((int) Native.Stdlib.strlen (p));
+                               case 2: return GetInt16BufferLength (p);
+                               case 4: return GetInt32BufferLength (p);
+                       }
+
+                       int len = 0;
+                       int num_null_seen = 0;
+
+                       do {
+                               byte b = Marshal.ReadByte (p, len++);
+                               if (b == 0)
+                                       ++num_null_seen;
+                               else
+                                       num_null_seen = 0;
+                       } while (num_null_seen != nullLength);
+
+                       return len;
                }
 
                /*
@@ -148,12 +245,17 @@ namespace Mono.Unix {
                 * for strings.
                 */
                public static string[] PtrToStringArray (IntPtr stringArray)
+               {
+                       return PtrToStringArray (stringArray, UnixEncoding.Instance);
+               }
+
+               public static string[] PtrToStringArray (IntPtr stringArray, Encoding encoding)
                {
                        if (stringArray == IntPtr.Zero)
                                return new string[]{};
 
                        int argc = CountStrings (stringArray);
-                       return PtrToStringArray (argc, stringArray);
+                       return PtrToStringArray (argc, stringArray, encoding);
                }
 
                private static int CountStrings (IntPtr stringArray)
@@ -174,35 +276,64 @@ namespace Mono.Unix {
                 * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
                 */
                public static string[] PtrToStringArray (int count, IntPtr stringArray)
+               {
+                       return PtrToStringArray (count, stringArray, UnixEncoding.Instance);
+               }
+
+               public static string[] PtrToStringArray (int count, IntPtr stringArray, Encoding encoding)
                {
                        if (count < 0)
                                throw new ArgumentOutOfRangeException ("count", "< 0");
+                       if (encoding == null)
+                               throw new ArgumentNullException ("encoding");
                        if (stringArray == IntPtr.Zero)
                                return new string[count];
 
                        string[] members = new string[count];
                        for (int i = 0; i < count; ++i) {
                                IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
-                               members[i] = PtrToString (s);
+                               members[i] = PtrToString (s, encoding);
                        }
 
                        return members;
                }
 
-               public static IntPtr StringToAlloc (string s)
+               public static IntPtr StringToHeap (string s)
+               {
+                       return StringToHeap (s, UnixEncoding.Instance);
+               }
+
+               public static IntPtr StringToHeap (string s, Encoding encoding)
                {
-                       return StringToAlloc (s, Encoding.UTF8);
+                       return StringToHeap (s, 0, s.Length, encoding);
                }
 
-               public static IntPtr StringToAlloc (string s, Encoding e)
+               public static IntPtr StringToHeap (string s, int index, int count)
                {
-                       byte[] marshal = new byte [e.GetByteCount (s) + 1];
-                       if (e.GetBytes (s, 0, s.Length, marshal, 0) != (marshal.Length-1))
-                               throw new NotSupportedException ("e.GetBytes() doesn't equal e.GetByteCount()!");
-                       marshal [marshal.Length-1] = 0;
-                       IntPtr mem = Alloc (marshal.Length);
+                       return StringToHeap (s, index, count, UnixEncoding.Instance);
+               }
+
+               public static IntPtr StringToHeap (string s, int index, int count, Encoding encoding)
+               {
+                       if (s == null)
+                               return IntPtr.Zero;
+
+                       if (encoding == null)
+                               throw new ArgumentNullException ("encoding");
+
+                       int min_byte_count = encoding.GetMaxByteCount(1);
+                       char[] copy = s.ToCharArray (index, count);
+                       byte[] marshal = new byte [encoding.GetByteCount (copy) + min_byte_count];
+
+                       int bytes_copied = encoding.GetBytes (copy, 0, copy.Length, marshal, 0);
+
+                       if (bytes_copied != (marshal.Length-min_byte_count))
+                               throw new NotSupportedException ("encoding.GetBytes() doesn't equal encoding.GetByteCount()!");
+
+                       IntPtr mem = AllocHeap (marshal.Length);
                        if (mem == IntPtr.Zero)
-                               throw new OutOfMemoryException ();
+                               throw new UnixIOException (Native.Errno.ENOMEM);
+
                        bool copied = false;
                        try {
                                Marshal.Copy (marshal, 0, mem, marshal.Length);
@@ -210,31 +341,24 @@ namespace Mono.Unix {
                        }
                        finally {
                                if (!copied)
-                                       Free (mem);
+                                       FreeHeap (mem);
                        }
+
                        return mem;
                }
 
                public static bool ShouldRetrySyscall (int r)
                {
-                       if (r == -1 && Stdlib.GetLastError () == Error.EINTR)
-                               return true;
-                       return false;
-               }
-
-               [Obsolete ("Use ShouldRetrySyscall (int, out Mono.Unix.Native.Errno")]
-               public static bool ShouldRetrySyscall (int r, out Error error)
-               {
-                       error = (Error) 0;
-                       if (r == -1 && (error = Stdlib.GetLastError ()) == Error.EINTR)
+                       if (r == -1 && Native.Stdlib.GetLastError () == Native.Errno.EINTR)
                                return true;
                        return false;
                }
 
-               public static bool ShouldRetrySyscall (int r, out Native.Errno error)
+               [CLSCompliant (false)]
+               public static bool ShouldRetrySyscall (int r, out Native.Errno errno)
                {
-                       error = (Native.Errno) 0;
-                       if (r == -1 && (error = Native.Stdlib.GetLastError ()) == Native.Errno.EINTR)
+                       errno = (Native.Errno) 0;
+                       if (r == -1 && (errno = Native.Stdlib.GetLastError ()) == Native.Errno.EINTR)
                                return true;
                        return false;
                }
@@ -276,46 +400,41 @@ namespace Mono.Unix {
                        return false;
                }
 
-               [Obsolete ("Use CreateExceptionForError (Mono.Unix.Native.Errno)")]
-               internal static Exception CreateExceptionForError (Error errno)
-               {
-                       string message = GetErrorDescription (errno);
-                       UnixIOException p = new UnixIOException (errno);
-                       switch (errno) {
-                               case Error.EFAULT:        return new NullReferenceException (message, p);
-                               case Error.EINVAL:        return new ArgumentException (message, p);
-                               case Error.EIO:
-                                 case Error.ENOSPC:
-                                 case Error.EROFS:
-                                 case Error.ESPIPE:
-                                       return new IOException (message, p);
-                               case Error.ENAMETOOLONG:  return new PathTooLongException (message, p);
-                               case Error.ENOENT:        return new FileNotFoundException (message, p);
-                               case Error.ENOEXEC:       return new InvalidProgramException (message, p);
-                               case Error.EOVERFLOW:     return new OverflowException (message, p);
-                               case Error.ERANGE:        return new ArgumentOutOfRangeException (message);
-                               default: /* ignore */     break;
-                       }
-                       return p;
-               }
-
                internal static Exception CreateExceptionForError (Native.Errno errno)
                {
                        string message = GetErrorDescription (errno);
                        UnixIOException p = new UnixIOException (errno);
+
+                       // Ordering: Order alphabetically by exception first (right column),
+                       // then order alphabetically by Errno value (left column) for the given
+                       // exception.
                        switch (errno) {
-                               case Native.Errno.EFAULT:        return new NullReferenceException (message, p);
+                               case Native.Errno.EBADF:
                                case Native.Errno.EINVAL:        return new ArgumentException (message, p);
-                               case Native.Errno.EIO:
-                                 case Native.Errno.ENOSPC:
-                                 case Native.Errno.EROFS:
-                                 case Native.Errno.ESPIPE:
-                                       return new IOException (message, p);
-                               case Native.Errno.ENAMETOOLONG:  return new PathTooLongException (message, p);
+
+                               case Native.Errno.ERANGE:        return new ArgumentOutOfRangeException (message);
+                               case Native.Errno.ENOTDIR:       return new DirectoryNotFoundException (message, p);
                                case Native.Errno.ENOENT:        return new FileNotFoundException (message, p);
+
+                               case Native.Errno.EOPNOTSUPP:
+                               case Native.Errno.EPERM:         return new InvalidOperationException (message, p);
+
                                case Native.Errno.ENOEXEC:       return new InvalidProgramException (message, p);
+
+                               case Native.Errno.EIO:
+                               case Native.Errno.ENOSPC:
+                               case Native.Errno.ENOTEMPTY:
+                               case Native.Errno.ENXIO:
+                               case Native.Errno.EROFS:
+                               case Native.Errno.ESPIPE:        return new IOException (message, p);
+
+                               case Native.Errno.EFAULT:        return new NullReferenceException (message, p);
                                case Native.Errno.EOVERFLOW:     return new OverflowException (message, p);
-                               case Native.Errno.ERANGE:        return new ArgumentOutOfRangeException (message);
+                               case Native.Errno.ENAMETOOLONG:  return new PathTooLongException (message, p);
+
+                               case Native.Errno.EACCES:
+                               case Native.Errno.EISDIR:        return new UnauthorizedAccessException (message, p);
+
                                default: /* ignore */     break;
                        }
                        return p;
@@ -323,15 +442,10 @@ namespace Mono.Unix {
 
                internal static Exception CreateExceptionForLastError ()
                {
-                       return CreateExceptionForError (Stdlib.GetLastError());
-               }
-
-               [Obsolete ("Use ThrowExceptionForError (Mono.Unix.Native.Errno)")]
-               public static void ThrowExceptionForError (Error errno)
-               {
-                       throw CreateExceptionForError (errno);
+                       return CreateExceptionForError (Native.Stdlib.GetLastError());
                }
 
+               [CLSCompliant (false)]
                public static void ThrowExceptionForError (Native.Errno errno)
                {
                        throw CreateExceptionForError (errno);
@@ -342,13 +456,7 @@ namespace Mono.Unix {
                        throw CreateExceptionForLastError ();
                }
 
-               [Obsolete ("Use ThrowExceptionForErrorIf (int, Mono.Unix.Native.Errno)")]
-               public static void ThrowExceptionForErrorIf (int retval, Error errno)
-               {
-                       if (retval == -1)
-                               ThrowExceptionForError (errno);
-               }
-
+               [CLSCompliant (false)]
                public static void ThrowExceptionForErrorIf (int retval, Native.Errno errno)
                {
                        if (retval == -1)