2 // Mono.Unix/UnixMarshal.cs
5 // Jonathan Pryor (jonpryor@vt.edu)
7 // (C) 2004 Jonathan Pryor
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:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
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.
31 using System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Serialization;
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
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
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
58 internal delegate string ErrorTranslator (Error errno);
60 internal static readonly ErrorTranslator Translate;
62 static ErrorMarshal ()
65 Translate = new ErrorTranslator (strerror_r);
66 Translate (Error.ERANGE);
68 catch (EntryPointNotFoundException e) {
69 Translate = new ErrorTranslator (strerror);
73 private static string strerror (Error errno)
75 return Stdlib.strerror (errno);
78 private static string strerror_r (Error errno)
80 StringBuilder buf = new StringBuilder (16);
84 r = Syscall.strerror_r (errno, buf);
85 } while (r == -1 && Stdlib.GetLastError() == Error.ERANGE);
88 return "** Unknown error code: " + ((int) errno) + "**";
89 return buf.ToString();
93 public sealed /* static */ class UnixMarshal
95 private UnixMarshal () {}
97 public static string GetErrorDescription (Error errno)
99 return ErrorMarshal.Translate (errno);
102 public static IntPtr Alloc (long size)
105 throw new ArgumentOutOfRangeException ("size", "< 0");
106 return Stdlib.malloc ((ulong) size);
109 public static IntPtr ReAlloc (IntPtr ptr, long size)
112 throw new ArgumentOutOfRangeException ("size", "< 0");
113 return Stdlib.realloc (ptr, (ulong) size);
116 public static void Free (IntPtr ptr)
121 public static string PtrToString (IntPtr p)
123 // TODO: deal with character set issues. Will PtrToStringAnsi always
124 // "Do The Right Thing"?
125 if (p == IntPtr.Zero)
127 return Marshal.PtrToStringAnsi (p);
131 * Marshal a C `char **'. ANSI C `main' requirements are assumed:
133 * stringArray is an array of pointers to C strings
134 * stringArray has a terminating NULL string.
137 * stringArray[0] = "string 1";
138 * stringArray[1] = "string 2";
139 * stringArray[2] = NULL
141 * The terminating NULL is required so that we know when to stop looking
144 public static string[] PtrToStringArray (IntPtr stringArray)
146 if (stringArray == IntPtr.Zero)
147 return new string[]{};
149 int argc = CountStrings (stringArray);
150 return PtrToStringArray (argc, stringArray);
153 private static int CountStrings (IntPtr stringArray)
156 while (Marshal.ReadIntPtr (stringArray, count*IntPtr.Size) != IntPtr.Zero)
162 * Like PtrToStringArray(IntPtr), but it allows the user to specify how
163 * many strings to look for in the array. As such, the requirement for a
164 * terminating NULL element is not required.
166 * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
167 * stringArray[count] is NOT accessed (though ANSI C requires that
168 * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
170 public static string[] PtrToStringArray (int count, IntPtr stringArray)
173 throw new ArgumentOutOfRangeException ("count", "< 0");
174 if (stringArray == IntPtr.Zero)
175 return new string[count];
177 string[] members = new string[count];
178 for (int i = 0; i < count; ++i) {
179 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
180 members[i] = PtrToString (s);
186 public static bool ShouldRetrySyscall (int r)
188 if (r == -1 && Stdlib.GetLastError () == Error.EINTR)
193 public static bool ShouldRetrySyscall (int r, out Error error)
196 if (r == -1 && (error = Stdlib.GetLastError ()) == Error.EINTR)
201 // we can't permit any printf(3)-style formatting information, since that
202 // would kill the stack. However, replacing %% is silly, and some %* are
203 // permitted (such as %m in syslog to print strerror(errno)).
204 internal static string EscapeFormatString (string message,
209 StringBuilder sb = new StringBuilder (message.Length);
210 for (int i = 0; i < message.Length; ++i) {
211 char c = message [i];
213 if (c == '%' && (i+1) < message.Length) {
214 char n = message [i+1];
215 if (n == '%' || IsCharPresent (permitted, n))
218 sb.Append ('%').Append (n);
221 // invalid format string: % at EOS.
225 return sb.ToString ();
228 private static bool IsCharPresent (char[] array, char c)
232 for (int i = 0; i < array.Length; ++i)
238 internal static Exception CreateExceptionForError (Error errno)
240 string message = GetErrorDescription (errno);
241 UnixIOException p = new UnixIOException (errno);
243 case Error.EFAULT: return new NullReferenceException (message, p);
244 case Error.EINVAL: return new ArgumentException (message, p);
249 return new IOException (message, p);
250 case Error.ENAMETOOLONG: return new PathTooLongException (message, p);
251 case Error.ENOENT: return new FileNotFoundException (message, p);
252 case Error.ENOEXEC: return new InvalidProgramException (message, p);
253 case Error.EOVERFLOW: return new OverflowException (message, p);
254 case Error.ERANGE: return new ArgumentOutOfRangeException (message);
255 default: /* ignore */ break;
260 internal static Exception CreateExceptionForLastError ()
262 return CreateExceptionForError (Stdlib.GetLastError());
265 public static void ThrowExceptionForError (Error errno)
267 throw CreateExceptionForError (errno);
270 public static void ThrowExceptionForLastError ()
272 throw CreateExceptionForLastError ();
275 public static void ThrowExceptionForErrorIf (int retval, Error errno)
278 ThrowExceptionForError (errno);
281 public static void ThrowExceptionForLastErrorIf (int retval)
284 ThrowExceptionForLastError ();