1 // Copyright (c) 1999-2018 David Muse
2 // See the COPYING file for more information
3
4 #include <rudiments/private/config.h>
5 #include <rudiments/error.h>
6 #include <rudiments/charstring.h>
7
8 #include <stdio.h>
9
10 // to get the more portable XSI-compliant strerror_r rather than the GNU version
11 #undef __USE_GNU
12
13 // to avoid __THROW errors on some systems (fedora 2)
14 #undef __REDIRECT
15
16 // for strerror (or similar)
17 #ifdef RUDIMENTS_HAVE_STRINGS_H
18 #include <strings.h>
19 #endif
20 #ifdef RUDIMENTS_HAVE_STRING_H
21 #include <string.h>
22 #endif
23
24 #ifdef RUDIMENTS_HAVE_WINDOWS_H
25 #include <windows.h>
26 #endif
27
clearError()28 void error::clearError() {
29 errno=0;
30 }
31
setErrorNumber(int32_t err)32 void error::setErrorNumber(int32_t err) {
33 errno=err;
34 }
35
getErrorNumber()36 int32_t error::getErrorNumber() {
37 // This is here for an odd reason. Apparently, in some debugging
38 // environments, if you set a breakpoint here, gdb will segfault if you
39 // run "print errno". You can safely "print errornumber" though.
40 int32_t errornumber=errno;
41 return errornumber;
42 }
43
getErrorString()44 char *error::getErrorString() {
45 #if defined(RUDIMENTS_HAVE_STRERROR_S) || \
46 defined(RUDIMENTS_HAVE_XSI_STRERROR_R) || \
47 defined(RUDIMENTS_HAVE_GNU_STRERROR_R)
48
49 int32_t errornumber=getErrorNumber();
50
51 for (size_t size=256; size<=1024; size=size+256) {
52
53 char *buffer=new char[size];
54
55 #if defined(RUDIMENTS_HAVE_STRERROR_S)
56 errno_t result=strerror_s(buffer,size,errornumber);
57 if (!result) {
58 return buffer;
59 } else if (result!=ERANGE) {
60 break;
61 }
62 setErrorNumber(errornumber);
63 #elif defined(RUDIMENTS_HAVE_XSI_STRERROR_R)
64 int result=strerror_r(errornumber,buffer,size);
65 if (!result) {
66 return buffer;
67 } else if (result==EINVAL) {
68 // strerror_r on older freebsd, doesn't like it
69 // if errornumber is 0. It returns EINVAL,
70 // sets the error number to EINVAL, and
71 // doesn't fill the buffer. Emulate the
72 // behavior of strerror(0) by setting the
73 // buffer to "Unknown error: 0" and resetting
74 // the error number.
75 charstring::copy(buffer,"Unknown error: 0");
76 setErrorNumber(errornumber);
77 return buffer;
78 } else if (getErrorNumber()!=ERANGE) {
79 break;
80 }
81 setErrorNumber(errornumber);
82 #elif defined(RUDIMENTS_HAVE_GNU_STRERROR_R)
83 char *result=strerror_r(errornumber,buffer,size);
84 if (getErrorNumber()==ERANGE) {
85 setErrorNumber(errornumber);
86 } else {
87 return result;
88 }
89 #endif
90
91 delete[] buffer;
92 }
93
94 return NULL;
95 #elif defined(RUDIMENTS_HAVE_STRERROR)
96 return charstring::duplicate(strerror(errno));
97 #else
98 #error no strerror or anything like it
99 #endif
100 }
101
clearNativeError()102 void error::clearNativeError() {
103 #if defined(RUDIMENTS_HAVE_GETLASTERROR)
104 SetLastError(0);
105 #else
106 clearError();
107 #endif
108 }
109
setNativeErrorNumber(int32_t err)110 void error::setNativeErrorNumber(int32_t err) {
111 #if defined(RUDIMENTS_HAVE_GETLASTERROR)
112 SetLastError(err);
113 #else
114 clearError();
115 #endif
116 }
117
getNativeErrorNumber()118 int32_t error::getNativeErrorNumber() {
119 #if defined(RUDIMENTS_HAVE_GETLASTERROR)
120 return GetLastError();
121 #else
122 return getErrorNumber();
123 #endif
124 }
125
getNativeErrorString()126 char *error::getNativeErrorString() {
127 #if defined(RUDIMENTS_HAVE_GETLASTERROR)
128 char *nativeerrorstring=NULL;
129 LPVOID errorstring=NULL;
130 DWORD errornumber=GetLastError();
131
132 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|
133 FORMAT_MESSAGE_FROM_SYSTEM|
134 FORMAT_MESSAGE_IGNORE_INSERTS,
135 NULL,errornumber,
136 MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
137 (LPTSTR)&errorstring,0,NULL)) {
138 nativeerrorstring=charstring::duplicate(
139 (char *)errorstring);
140 }
141 LocalFree(errorstring);
142 return nativeerrorstring;
143 #else
144 return getErrorString();
145 #endif
146 }
147