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