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