1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "./SDL_internal.h"
22 
23 /* Simple error handling in SDL */
24 
25 #include "SDL_log.h"
26 #include "SDL_error.h"
27 #include "SDL_error_c.h"
28 
29 
30 /* Routine to get the thread-specific error variable */
31 #if SDL_THREADS_DISABLED
32 /* The default (non-thread-safe) global error variable */
33 static SDL_error SDL_global_error;
34 #define SDL_GetErrBuf() (&SDL_global_error)
35 #else
36 extern SDL_error *SDL_GetErrBuf(void);
37 #endif /* SDL_THREADS_DISABLED */
38 
39 #define SDL_ERRBUFIZE   1024
40 
41 /* Private functions */
42 
43 static const char *
SDL_LookupString(const char * key)44 SDL_LookupString(const char *key)
45 {
46     /* FIXME: Add code to lookup key in language string hash-table */
47     return key;
48 }
49 
50 /* Public functions */
51 
52 int
SDL_SetError(SDL_PRINTF_FORMAT_STRING const char * fmt,...)53 SDL_SetError(SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
54 {
55     va_list ap;
56     SDL_error *error;
57 
58     /* Ignore call if invalid format pointer was passed */
59     if (fmt == NULL) return -1;
60 
61     /* Copy in the key, mark error as valid */
62     error = SDL_GetErrBuf();
63     error->error = 1;
64     SDL_strlcpy((char *) error->key, fmt, sizeof(error->key));
65 
66     va_start(ap, fmt);
67     error->argc = 0;
68     while (*fmt) {
69         if (*fmt++ == '%') {
70             while (*fmt == '.' || (*fmt >= '0' && *fmt <= '9')) {
71                 ++fmt;
72             }
73             switch (*fmt++) {
74             case 0:            /* Malformed format string.. */
75                 --fmt;
76                 break;
77             case 'c':
78             case 'i':
79             case 'd':
80             case 'u':
81             case 'o':
82             case 'x':
83             case 'X':
84                 error->args[error->argc++].value_i = va_arg(ap, int);
85                 break;
86             case 'f':
87                 error->args[error->argc++].value_f = va_arg(ap, double);
88                 break;
89             case 'p':
90                 error->args[error->argc++].value_ptr = va_arg(ap, void *);
91                 break;
92             case 's':
93                 {
94                     int i = error->argc;
95                     const char *str = va_arg(ap, const char *);
96                     if (str == NULL)
97                         str = "(null)";
98                     SDL_strlcpy((char *) error->args[i].buf, str,
99                                 ERR_MAX_STRLEN);
100                     error->argc++;
101                 }
102                 break;
103             default:
104                 break;
105             }
106             if (error->argc >= ERR_MAX_ARGS) {
107                 break;
108             }
109         }
110     }
111     va_end(ap);
112 
113     /* If we are in debug mode, print out an error message */
114     SDL_LogDebug(SDL_LOG_CATEGORY_ERROR, "%s", SDL_GetError());
115 
116     return -1;
117 }
118 
119 #ifdef __GNUC__
120 #pragma GCC diagnostic push
121 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
122 #endif
123 /* This function has a bit more overhead than most error functions
124    so that it supports internationalization and thread-safe errors.
125 */
126 static char *
SDL_GetErrorMsg(char * errstr,int maxlen)127 SDL_GetErrorMsg(char *errstr, int maxlen)
128 {
129     SDL_error *error;
130 
131     /* Clear the error string */
132     *errstr = '\0';
133     --maxlen;
134 
135     /* Get the thread-safe error, and print it out */
136     error = SDL_GetErrBuf();
137     if (error->error) {
138         const char *fmt;
139         char *msg = errstr;
140         int len;
141         int argi;
142 
143         fmt = SDL_LookupString(error->key);
144         argi = 0;
145         while (*fmt && (maxlen > 0)) {
146             if (*fmt == '%') {
147                 char tmp[32], *spot = tmp;
148                 *spot++ = *fmt++;
149                 while ((*fmt == '.' || (*fmt >= '0' && *fmt <= '9'))
150                        && spot < (tmp + SDL_arraysize(tmp) - 2)) {
151                     *spot++ = *fmt++;
152                 }
153                 *spot++ = *fmt++;
154                 *spot++ = '\0';
155                 switch (spot[-2]) {
156                 case '%':
157                     *msg++ = '%';
158                     maxlen -= 1;
159                     break;
160                 case 'c':
161                 case 'i':
162                 case 'd':
163                 case 'u':
164                 case 'o':
165                 case 'x':
166                 case 'X':
167                     len =
168                         SDL_snprintf(msg, maxlen, tmp,
169                                      error->args[argi++].value_i);
170                     if (len > 0) {
171                         msg += len;
172                         maxlen -= len;
173                     }
174                     break;
175 
176                 case 'f':
177                     len =
178                         SDL_snprintf(msg, maxlen, tmp,
179                                      error->args[argi++].value_f);
180                     if (len > 0) {
181                         msg += len;
182                         maxlen -= len;
183                     }
184                     break;
185 
186                 case 'p':
187                     len =
188                         SDL_snprintf(msg, maxlen, tmp,
189                                      error->args[argi++].value_ptr);
190                     if (len > 0) {
191                         msg += len;
192                         maxlen -= len;
193                     }
194                     break;
195 
196                 case 's':
197                     len =
198                         SDL_snprintf(msg, maxlen, tmp,
199                                      SDL_LookupString(error->args[argi++].
200                                                       buf));
201                     if (len > 0) {
202                         msg += len;
203                         maxlen -= len;
204                     }
205                     break;
206 
207                 }
208             } else {
209                 *msg++ = *fmt++;
210                 maxlen -= 1;
211             }
212         }
213 
214         /* slide back if we've overshot the end of our buffer. */
215         if (maxlen < 0) {
216             msg -= (-maxlen) + 1;
217         }
218 
219         *msg = 0;               /* NULL terminate the string */
220     }
221     return (errstr);
222 }
223 #ifdef __GNUC__
224 #pragma GCC diagnostic pop
225 #endif
226 
227 /* Available for backwards compatibility */
228 const char *
SDL_GetError(void)229 SDL_GetError(void)
230 {
231     static char errmsg[SDL_ERRBUFIZE];
232 
233     return SDL_GetErrorMsg(errmsg, SDL_ERRBUFIZE);
234 }
235 
236 void
SDL_ClearError(void)237 SDL_ClearError(void)
238 {
239     SDL_error *error;
240 
241     error = SDL_GetErrBuf();
242     error->error = 0;
243 }
244 
245 /* Very common errors go here */
246 int
SDL_Error(SDL_errorcode code)247 SDL_Error(SDL_errorcode code)
248 {
249     switch (code) {
250     case SDL_ENOMEM:
251         return SDL_SetError("Out of memory");
252     case SDL_EFREAD:
253         return SDL_SetError("Error reading from datastream");
254     case SDL_EFWRITE:
255         return SDL_SetError("Error writing to datastream");
256     case SDL_EFSEEK:
257         return SDL_SetError("Error seeking in datastream");
258     case SDL_UNSUPPORTED:
259         return SDL_SetError("That operation is not supported");
260     default:
261         return SDL_SetError("Unknown SDL error");
262     }
263 }
264 
265 #ifdef TEST_ERROR
266 int
main(int argc,char * argv[])267 main(int argc, char *argv[])
268 {
269     char buffer[BUFSIZ + 1];
270 
271     SDL_SetError("Hi there!");
272     printf("Error 1: %s\n", SDL_GetError());
273     SDL_ClearError();
274     SDL_memset(buffer, '1', BUFSIZ);
275     buffer[BUFSIZ] = 0;
276     SDL_SetError("This is the error: %s (%f)", buffer, 1.0);
277     printf("Error 2: %s\n", SDL_GetError());
278     exit(0);
279 }
280 #endif
281 
282 /* vi: set ts=4 sw=4 expandtab: */
283