src/SDL_error.c
author Ryan C. Gordon <icculus@icculus.org>
Tue, 24 Mar 2015 03:12:35 -0400
changeset 9430 92622f92bb8c
parent 8820 0e935d5b193a
child 9619 b94b6d0bff0f
permissions -rw-r--r--
Make SDL error string formatting deal with nasty corner cases.

We continued looping while maxlen > 0, but maxlen was unsigned, so an overflow
would make it a large number instead of negative. Fixed.

Some snprintf() implementations might return a negative value if there isn't
enough space, and we now check for that.

Don't overrun the SDL error message buffer, if snprintf() returned the number
of chars it wanted to write instead of the number it did.

snprintf is a portability mess, we should just never use the C runtime for it.

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