+ Lots o' Renames, as the namespace changed.
[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         //  Problem:  But if strerror_r() isn't present, we're not thread safe!
55         // Solution:  Tough.  It's still better than nothing.  I think.  
56         //            Check UnixMarshal.IsErrorDescriptionThreadSafe if you need to
57         //            know which error translator is being used.
58         internal class ErrorMarshal
59         {
60                 public delegate string ErrorTranslator (Error errno);
61
62                 public static readonly ErrorTranslator Translate;
63                 public static readonly bool HaveStrerror_r;
64
65                 static ErrorMarshal ()
66                 {
67                         try {
68                                 Translate = new ErrorTranslator (strerror_r);
69                                 string ignore = Translate (Error.EPERM);
70                                 HaveStrerror_r = true;
71                         }
72                         catch (MissingMethodException e) {
73                                 Translate = new ErrorTranslator (strerror);
74                                 HaveStrerror_r = false;
75                         }
76                 }
77
78                 private static string strerror (Error errno)
79                 {
80                         return Syscall.strerror (errno);
81                 }
82
83                 private static string strerror_r (Error errno)
84                 {
85                         StringBuilder buf = new StringBuilder (16);
86                         int r = 0;
87                         do {
88                                 buf.Capacity *= 2;
89                                 r = Syscall.strerror_r (errno, buf);
90                         } while (r == -1 && Syscall.GetLastError() == Error.ERANGE);
91
92                         if (r == -1)
93                                 return "** Unknown error code: " + ((int) errno) + "**";
94                         return buf.ToString();
95                 }
96         }
97
98         public sealed /* static */ class UnixMarshal
99         {
100                 private UnixMarshal () {}
101
102                 public static string GetErrorDescription (Error errno)
103                 {
104                         return ErrorMarshal.Translate (errno);
105                 }
106
107                 public static bool IsErrorDescriptionThreadSafe {
108                         get {return ErrorMarshal.HaveStrerror_r;}
109                 }
110
111                 public static IntPtr Alloc (long size)
112                 {
113                         if (size < 0)
114                                 throw new ArgumentOutOfRangeException ("size", "< 0");
115                         return Stdlib.malloc ((ulong) size);
116                 }
117
118                 public static IntPtr ReAlloc (IntPtr ptr, long size)
119                 {
120                         if (size < 0)
121                                 throw new ArgumentOutOfRangeException ("size", "< 0");
122                         return Stdlib.realloc (ptr, (ulong) size);
123                 }
124
125                 public static void Free (IntPtr ptr)
126                 {
127                         Stdlib.free (ptr);
128                 }
129
130                 public static string PtrToString (IntPtr p)
131                 {
132                         // TODO: deal with character set issues.  Will PtrToStringAnsi always
133                         // "Do The Right Thing"?
134                         if (p == IntPtr.Zero)
135                                 return null;
136                         return Marshal.PtrToStringAnsi (p);
137                 }
138
139                 /*
140                  * Marshal a C `char **'.  ANSI C `main' requirements are assumed:
141                  *
142                  *   stringArray is an array of pointers to C strings
143                  *   stringArray has a terminating NULL string.
144                  *
145                  * For example:
146                  *   stringArray[0] = "string 1";
147                  *   stringArray[1] = "string 2";
148                  *   stringArray[2] = NULL
149                  *
150                  * The terminating NULL is required so that we know when to stop looking
151                  * for strings.
152                  */
153                 public static string[] PtrToStringArray (IntPtr stringArray)
154                 {
155                         if (stringArray == IntPtr.Zero)
156                                 return new string[]{};
157
158                         int argc = CountStrings (stringArray);
159                         return PtrToStringArray (argc, stringArray);
160                 }
161
162                 private static int CountStrings (IntPtr stringArray)
163                 {
164                         int count = -1;
165                         IntPtr item = Marshal.ReadIntPtr (stringArray, count * IntPtr.Size);
166                         do {
167                                 ++count;
168                         } while (Marshal.ReadIntPtr (stringArray, count * IntPtr.Size) != IntPtr.Zero);
169                         return count;
170                 }
171
172                 /*
173                  * Like PtrToStringArray(IntPtr), but it allows the user to specify how
174                  * many strings to look for in the array.  As such, the requirement for a
175                  * terminating NULL element is not required.
176                  *
177                  * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
178                  * stringArray[count] is NOT accessed (though ANSI C requires that 
179                  * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
180                  */
181                 public static string[] PtrToStringArray (int count, IntPtr stringArray)
182                 {
183                         if (count < 0)
184                                 throw new ArgumentOutOfRangeException ("count", "< 0");
185                         if (stringArray == IntPtr.Zero)
186                                 return new string[count];
187
188                         string[] members = new string[count];
189                         for (int i = 0; i < count; ++i) {
190                                 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
191                                 members[i] = PtrToString (s);
192                         }
193
194                         return members;
195                 }
196
197                 public static bool ShouldRetrySyscall (int r)
198                 {
199                         if (r == -1 && Syscall.GetLastError () == Error.EINTR)
200                                 return true;
201                         return false;
202                 }
203
204                 public static bool ShouldRetrySyscall (int r, out Error error)
205                 {
206                         error = (Error) 0;
207                         if (r == -1 && (error = Syscall.GetLastError ()) == Error.EINTR)
208                                 return true;
209                         return false;
210                 }
211
212                 private static Exception CreateExceptionForError (Error errno)
213                 {
214                         string message = GetErrorDescription (errno);
215                         UnixIOException p = new UnixIOException (errno);
216                         switch (errno) {
217                                 case Error.EFAULT:        return new NullReferenceException (message, p);
218                                 case Error.EINVAL:        return new ArgumentException (message, p);
219                                 case Error.EIO:
220                                   case Error.ENOSPC:
221                                   case Error.EROFS:
222                                   case Error.ESPIPE:
223                                         return new IOException (message, p);
224                                 case Error.ENAMETOOLONG:  return new PathTooLongException (message, p);
225                                 case Error.ENOENT:        return new FileNotFoundException (message, p);
226                                 case Error.ENOEXEC:       return new InvalidProgramException (message, p);
227                                 case Error.EOVERFLOW:     return new OverflowException (message, p);
228                                 case Error.ERANGE:        return new ArgumentOutOfRangeException (message);
229                                 default: /* ignore */     break;
230                         }
231                         return p;
232                 }
233
234                 public static void ThrowExceptionForError (Error errno)
235                 {
236                         throw CreateExceptionForError (errno);
237                 }
238
239                 public static void ThrowExceptionForLastError ()
240                 {
241                         throw CreateExceptionForError (Syscall.GetLastError());
242                 }
243
244                 public static void ThrowExceptionForErrorIf (int retval, Error errno)
245                 {
246                         if (retval == -1)
247                                 ThrowExceptionForError (errno);
248                 }
249
250                 public static void ThrowExceptionForLastErrorIf (int retval)
251                 {
252                         if (retval == -1)
253                                 ThrowExceptionForLastError ();
254                 }
255         }
256 }
257
258 // vim: noexpandtab