* Stdlib.cs: On miguel's suggestion, rename Sighandler_t to SignalHandler.
[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                                 ignore = ignore;
71                                 HaveStrerror_r = true;
72                         }
73                         catch (EntryPointNotFoundException e) {
74                                 Translate = new ErrorTranslator (strerror);
75                                 HaveStrerror_r = false;
76                         }
77                 }
78
79                 private static string strerror (Error errno)
80                 {
81                         return Syscall.strerror (errno);
82                 }
83
84                 private static string strerror_r (Error errno)
85                 {
86                         StringBuilder buf = new StringBuilder (16);
87                         int r = 0;
88                         do {
89                                 buf.Capacity *= 2;
90                                 r = Syscall.strerror_r (errno, buf);
91                         } while (r == -1 && Syscall.GetLastError() == Error.ERANGE);
92
93                         if (r == -1)
94                                 return "** Unknown error code: " + ((int) errno) + "**";
95                         return buf.ToString();
96                 }
97         }
98
99         public sealed /* static */ class UnixMarshal
100         {
101                 private UnixMarshal () {}
102
103                 public static string GetErrorDescription (Error errno)
104                 {
105                         return ErrorMarshal.Translate (errno);
106                 }
107
108                 public static bool IsErrorDescriptionThreadSafe {
109                         get {return ErrorMarshal.HaveStrerror_r;}
110                 }
111
112                 public static IntPtr Alloc (long size)
113                 {
114                         if (size < 0)
115                                 throw new ArgumentOutOfRangeException ("size", "< 0");
116                         return Stdlib.malloc ((ulong) size);
117                 }
118
119                 public static IntPtr ReAlloc (IntPtr ptr, long size)
120                 {
121                         if (size < 0)
122                                 throw new ArgumentOutOfRangeException ("size", "< 0");
123                         return Stdlib.realloc (ptr, (ulong) size);
124                 }
125
126                 public static void Free (IntPtr ptr)
127                 {
128                         Stdlib.free (ptr);
129                 }
130
131                 public static string PtrToString (IntPtr p)
132                 {
133                         // TODO: deal with character set issues.  Will PtrToStringAnsi always
134                         // "Do The Right Thing"?
135                         if (p == IntPtr.Zero)
136                                 return null;
137                         return Marshal.PtrToStringAnsi (p);
138                 }
139
140                 /*
141                  * Marshal a C `char **'.  ANSI C `main' requirements are assumed:
142                  *
143                  *   stringArray is an array of pointers to C strings
144                  *   stringArray has a terminating NULL string.
145                  *
146                  * For example:
147                  *   stringArray[0] = "string 1";
148                  *   stringArray[1] = "string 2";
149                  *   stringArray[2] = NULL
150                  *
151                  * The terminating NULL is required so that we know when to stop looking
152                  * for strings.
153                  */
154                 public static string[] PtrToStringArray (IntPtr stringArray)
155                 {
156                         if (stringArray == IntPtr.Zero)
157                                 return new string[]{};
158
159                         int argc = CountStrings (stringArray);
160                         return PtrToStringArray (argc, stringArray);
161                 }
162
163                 private static int CountStrings (IntPtr stringArray)
164                 {
165                         int count = 0;
166                         while (Marshal.ReadIntPtr (stringArray, count*IntPtr.Size) != IntPtr.Zero)
167                                 ++count;
168                         return count;
169                 }
170
171                 /*
172                  * Like PtrToStringArray(IntPtr), but it allows the user to specify how
173                  * many strings to look for in the array.  As such, the requirement for a
174                  * terminating NULL element is not required.
175                  *
176                  * Usage is similar to ANSI C `main': count is argc, stringArray is argv.
177                  * stringArray[count] is NOT accessed (though ANSI C requires that 
178                  * argv[argc] = NULL, which PtrToStringArray(IntPtr) requires).
179                  */
180                 public static string[] PtrToStringArray (int count, IntPtr stringArray)
181                 {
182                         if (count < 0)
183                                 throw new ArgumentOutOfRangeException ("count", "< 0");
184                         if (stringArray == IntPtr.Zero)
185                                 return new string[count];
186
187                         string[] members = new string[count];
188                         for (int i = 0; i < count; ++i) {
189                                 IntPtr s = Marshal.ReadIntPtr (stringArray, i * IntPtr.Size);
190                                 members[i] = PtrToString (s);
191                         }
192
193                         return members;
194                 }
195
196                 public static bool ShouldRetrySyscall (int r)
197                 {
198                         if (r == -1 && Syscall.GetLastError () == Error.EINTR)
199                                 return true;
200                         return false;
201                 }
202
203                 public static bool ShouldRetrySyscall (int r, out Error error)
204                 {
205                         error = (Error) 0;
206                         if (r == -1 && (error = Syscall.GetLastError ()) == Error.EINTR)
207                                 return true;
208                         return false;
209                 }
210
211                 // we can't permit any printf(3)-style formatting information, since that
212                 // would kill the stack.  However, replacing %% is silly, and some %* are
213                 // permitted (such as %m in syslog to print strerror(errno)).
214                 internal static string EscapeFormatString (string message, 
215                                 char [] permitted)
216                 {
217                         if (message == null)
218                                 return "";
219                         StringBuilder sb = new StringBuilder (message.Length);
220                         for (int i = 0; i < message.Length; ++i) {
221                                 char c = message [i];
222                                 sb.Append (c);
223                                 if (c == '%' && (i+1) < message.Length) {
224                                         char n = message [i+1];
225                                         if (n == '%' || IsCharPresent (permitted, n))
226                                                 sb.Append (n);
227                                         else
228                                                 sb.Append ('%').Append (n);
229                                         ++i;
230                                 }
231                                 // invalid format string: % at EOS.
232                                 else if (c == '%')
233                                         sb.Append ('%');
234                         }
235                         return sb.ToString ();
236                 }
237
238                 private static bool IsCharPresent (char[] array, char c)
239                 {
240                         if (array == null)
241                                 return false;
242                         for (int i = 0; i < array.Length; ++i)
243                                 if (array [i] == c)
244                                         return true;
245                         return false;
246                 }
247
248                 internal static Exception CreateExceptionForError (Error errno)
249                 {
250                         string message = GetErrorDescription (errno);
251                         UnixIOException p = new UnixIOException (errno);
252                         switch (errno) {
253                                 case Error.EFAULT:        return new NullReferenceException (message, p);
254                                 case Error.EINVAL:        return new ArgumentException (message, p);
255                                 case Error.EIO:
256                                   case Error.ENOSPC:
257                                   case Error.EROFS:
258                                   case Error.ESPIPE:
259                                         return new IOException (message, p);
260                                 case Error.ENAMETOOLONG:  return new PathTooLongException (message, p);
261                                 case Error.ENOENT:        return new FileNotFoundException (message, p);
262                                 case Error.ENOEXEC:       return new InvalidProgramException (message, p);
263                                 case Error.EOVERFLOW:     return new OverflowException (message, p);
264                                 case Error.ERANGE:        return new ArgumentOutOfRangeException (message);
265                                 default: /* ignore */     break;
266                         }
267                         return p;
268                 }
269
270                 public static void ThrowExceptionForError (Error errno)
271                 {
272                         throw CreateExceptionForError (errno);
273                 }
274
275                 public static void ThrowExceptionForLastError ()
276                 {
277                         throw CreateExceptionForError (Syscall.GetLastError());
278                 }
279
280                 public static void ThrowExceptionForErrorIf (int retval, Error errno)
281                 {
282                         if (retval == -1)
283                                 ThrowExceptionForError (errno);
284                 }
285
286                 public static void ThrowExceptionForLastErrorIf (int retval)
287                 {
288                         if (retval == -1)
289                                 ThrowExceptionForLastError ();
290                 }
291         }
292 }
293
294 // vim: noexpandtab