copying the latest Sys.Web.Services from trunk.
[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                                 Translate (Error.ERANGE);
70                                 HaveStrerror_r = true;
71                         }
72                         catch (EntryPointNotFoundException e) {
73                                 Translate = new ErrorTranslator (strerror);
74                                 HaveStrerror_r = false;
75                         }
76                 }
77
78                 private static string strerror (Error errno)
79                 {
80                         return Stdlib.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 && Stdlib.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 = 0;
165                         while (Marshal.ReadIntPtr (stringArray, count*IntPtr.Size) != IntPtr.Zero)
166                                 ++count;
167                         return count;
168                 }
169
170                 /*
171                  * Like PtrToStringArray(IntPtr), but it allows the user to specify how
172                  * many strings to look for in the array.  As such, the requirement for a
173                  * terminating NULL element is not required.
174                  *
175                  * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
176                  * stringArray[count] is NOT accessed (though ANSI C requires that 
177                  * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
178                  */
179                 public static string[] PtrToStringArray (int count, IntPtr stringArray)
180                 {
181                         if (count < 0)
182                                 throw new ArgumentOutOfRangeException ("count", "< 0");
183                         if (stringArray == IntPtr.Zero)
184                                 return new string[count];
185
186                         string[] members = new string[count];
187                         for (int i = 0; i < count; ++i) {
188                                 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
189                                 members[i] = PtrToString (s);
190                         }
191
192                         return members;
193                 }
194
195                 public static bool ShouldRetrySyscall (int r)
196                 {
197                         if (r == -1 && Stdlib.GetLastError () == Error.EINTR)
198                                 return true;
199                         return false;
200                 }
201
202                 public static bool ShouldRetrySyscall (int r, out Error error)
203                 {
204                         error = (Error) 0;
205                         if (r == -1 && (error = Stdlib.GetLastError ()) == Error.EINTR)
206                                 return true;
207                         return false;
208                 }
209
210                 // we can't permit any printf(3)-style formatting information, since that
211                 // would kill the stack.  However, replacing %% is silly, and some %* are
212                 // permitted (such as %m in syslog to print strerror(errno)).
213                 internal static string EscapeFormatString (string message, 
214                                 char [] permitted)
215                 {
216                         if (message == null)
217                                 return "";
218                         StringBuilder sb = new StringBuilder (message.Length);
219                         for (int i = 0; i < message.Length; ++i) {
220                                 char c = message [i];
221                                 sb.Append (c);
222                                 if (c == '%' && (i+1) < message.Length) {
223                                         char n = message [i+1];
224                                         if (n == '%' || IsCharPresent (permitted, n))
225                                                 sb.Append (n);
226                                         else
227                                                 sb.Append ('%').Append (n);
228                                         ++i;
229                                 }
230                                 // invalid format string: % at EOS.
231                                 else if (c == '%')
232                                         sb.Append ('%');
233                         }
234                         return sb.ToString ();
235                 }
236
237                 private static bool IsCharPresent (char[] array, char c)
238                 {
239                         if (array == null)
240                                 return false;
241                         for (int i = 0; i < array.Length; ++i)
242                                 if (array [i] == c)
243                                         return true;
244                         return false;
245                 }
246
247                 internal static Exception CreateExceptionForError (Error errno)
248                 {
249                         string message = GetErrorDescription (errno);
250                         UnixIOException p = new UnixIOException (errno);
251                         switch (errno) {
252                                 case Error.EFAULT:        return new NullReferenceException (message, p);
253                                 case Error.EINVAL:        return new ArgumentException (message, p);
254                                 case Error.EIO:
255                                   case Error.ENOSPC:
256                                   case Error.EROFS:
257                                   case Error.ESPIPE:
258                                         return new IOException (message, p);
259                                 case Error.ENAMETOOLONG:  return new PathTooLongException (message, p);
260                                 case Error.ENOENT:        return new FileNotFoundException (message, p);
261                                 case Error.ENOEXEC:       return new InvalidProgramException (message, p);
262                                 case Error.EOVERFLOW:     return new OverflowException (message, p);
263                                 case Error.ERANGE:        return new ArgumentOutOfRangeException (message);
264                                 default: /* ignore */     break;
265                         }
266                         return p;
267                 }
268
269                 internal static Exception CreateExceptionForLastError ()
270                 {
271                         return CreateExceptionForError (Stdlib.GetLastError());
272                 }
273
274                 public static void ThrowExceptionForError (Error errno)
275                 {
276                         throw CreateExceptionForError (errno);
277                 }
278
279                 public static void ThrowExceptionForLastError ()
280                 {
281                         throw CreateExceptionForLastError ();
282                 }
283
284                 public static void ThrowExceptionForErrorIf (int retval, Error errno)
285                 {
286                         if (retval == -1)
287                                 ThrowExceptionForError (errno);
288                 }
289
290                 public static void ThrowExceptionForLastErrorIf (int retval)
291                 {
292                         if (retval == -1)
293                                 ThrowExceptionForLastError ();
294                 }
295         }
296 }
297
298 // vim: noexpandtab