New test.
[mono.git] / support / errno.c
1 /*
2  * <errno.h> wrapper functions.
3  */
4
5 #include <errno.h>
6 #include <string.h>
7 #include "mph.h"
8 #include <stdio.h>
9
10 G_BEGIN_DECLS
11
12 void
13 Mono_Posix_Stdlib_SetLastError (int error_number)
14 {
15         errno = error_number;
16 }
17
18 #ifdef HAVE_STRERROR_R
19
20 /* 
21  * There are two versions of strerror_r: 
22  *  - the GNU version:  char *strerror_r (int errnum, char *buf, size_t n);
23  *  - the XPG version:    int strerror_r (int errnum, char *buf, size_t n);
24  *
25  * Ideally I could stick with the XPG version, but we need to support 
26  * Red Hat 9, which only supports the GNU version.
27  *
28  * Furthermore, I do NOT want to export the GNU version in Mono.Posix.dll, 
29  * as that's supposed to contain *standard* function definitions (give or 
30  * take a few GNU extensions).  Portability trumps all.
31  *
32  * Consequently, we export the functionality of the XPG version.  
33  * Internally, we se the GNU version if _GNU_SOURCE is defined, otherwise 
34  * we assume that the XPG version is present.
35  */
36
37 #ifdef _GNU_SOURCE
38 #define mph_min(x,y) ((x) <= (y) ? (x) : (y))
39
40 /* If you pass an invalid errno value to glibc 2.3.2's strerror_r, you get
41  * back the string "Unknown error" with the error value appended. */
42 static const char mph_unknown[] = "Unknown error ";
43
44 /*
45  * Translate the GNU semantics to the XPG semantics.
46  *
47  * From reading the (RH9-using) GLibc 2.3.2 sysdeps/generic/_strerror.c, 
48  * we can say the following:
49  *   - If errnum is a valid error number, a pointer to a constant string is
50  *     returned.  Thus, the prototype *lies* (it's not really a char*).
51  *     `buf' is unchanged (WTF?).
52  *   - If errnum is an *invalid* error number, an error message is copied
53  *     into `buf' and `buf' is returned.  The error message returned is
54  *     "Unknown error %i", where %i is the input errnum.
55  *
56  * Meanwhile, XPG always modifies `buf' if there's enough space, and either
57  * returns 0 (success) or -1 (error) with errno = EINVAL (bad errnum) or
58  * ERANGE (`buf' isn't big enough).  Also, GLibc 2.3.3 (which has the XPG
59  * version) first checks the validity of errnum first, then does the copy.
60  *
61  * Assuming that the GNU implementation doesn't change much (ha!), we can
62  * check for EINVAL by comparing the strerror_r return to `buf', OR by
63  * comparing the return value to "Uknown error".  (This assumes that 
64  * strerror_r will always only return the input buffer for errors.)
65  *
66  * Check for ERANGE by comparing the string length returned by strerror_r to
67  * `n'.
68  *
69  * Then pray that this actually works...
70  */
71 gint32
72 Mono_Posix_Syscall_strerror_r (int errnum, char *buf, mph_size_t n)
73 {
74         char *r;
75         char ebuf [sizeof(mph_unknown)];
76         size_t len;
77         size_t blen;
78
79         mph_return_if_size_t_overflow (n);
80
81         /* first, check for valid errnum */
82         r = strerror_r (errnum, ebuf, sizeof(ebuf));
83         len = strlen (r);
84
85         if (r == ebuf ||
86                         strncmp (r, mph_unknown, mph_min (len, sizeof(mph_unknown))) == 0) {
87                 errno = EINVAL;
88                 return -1;
89         }
90
91         /* valid errnum (we hope); is buffer big enough? */
92         blen = (size_t) n;
93         if ((len+1) > blen) {
94                 errno = ERANGE;
95                 return -1;
96         }
97
98         strncpy (buf, r, len);
99         buf[len] = '\0';
100
101         return 0;
102 }
103
104 #else /* !def _GNU_SOURCE */
105
106 gint32
107 Mono_Posix_Syscall_strerror_r (int errnum, char *buf, mph_size_t n)
108 {
109         mph_return_if_size_t_overflow (n);
110         return strerror_r (errnum, buf, (size_t) n);
111 }
112
113 #endif /* def _GNU_SOURCE */
114
115 #endif /* def HAVE_STRERROR_R */
116
117 G_END_DECLS
118
119 /*
120  * vim: noexpandtab
121  */