src/SDL_log.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 05 Jul 2013 00:41:34 -0400
changeset 7344 3d9397262c10
parent 7343 faf0d8c7dbec
child 7350 302af2a46a66
permissions -rw-r--r--
Strip newlines from messages in SDL_Log*() before calling logging function.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 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_config.h"
    22 
    23 /* Simple log messages in SDL */
    24 
    25 #include "SDL_log.h"
    26 
    27 #if HAVE_STDIO_H
    28 #include <stdio.h>
    29 #endif
    30 
    31 #if defined(__WIN32__)
    32 #include "core/windows/SDL_windows.h"
    33 #elif defined(__ANDROID__)
    34 #include <android/log.h>
    35 #endif
    36 
    37 #define DEFAULT_PRIORITY                SDL_LOG_PRIORITY_CRITICAL
    38 #define DEFAULT_ASSERT_PRIORITY         SDL_LOG_PRIORITY_WARN
    39 #define DEFAULT_APPLICATION_PRIORITY    SDL_LOG_PRIORITY_INFO
    40 #define DEFAULT_TEST_PRIORITY           SDL_LOG_PRIORITY_VERBOSE
    41 
    42 /* Forward definition of error function */
    43 extern int SDL_SetError(const char *fmt, ...);
    44 
    45 typedef struct SDL_LogLevel
    46 {
    47     int category;
    48     SDL_LogPriority priority;
    49     struct SDL_LogLevel *next;
    50 } SDL_LogLevel;
    51 
    52 /* The default log output function */
    53 static void SDL_LogOutput(void *userdata,
    54                           int category, SDL_LogPriority priority,
    55                           const char *message);
    56 
    57 static SDL_LogLevel *SDL_loglevels;
    58 static SDL_LogPriority SDL_default_priority = DEFAULT_PRIORITY;
    59 static SDL_LogPriority SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
    60 static SDL_LogPriority SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
    61 static SDL_LogPriority SDL_test_priority = DEFAULT_TEST_PRIORITY;
    62 static SDL_LogOutputFunction SDL_log_function = SDL_LogOutput;
    63 static void *SDL_log_userdata = NULL;
    64 
    65 static const char *SDL_priority_prefixes[SDL_NUM_LOG_PRIORITIES] = {
    66     NULL,
    67     "VERBOSE",
    68     "DEBUG",
    69     "INFO",
    70     "WARN",
    71     "ERROR",
    72     "CRITICAL"
    73 };
    74 
    75 #ifdef __ANDROID__
    76 static const char *SDL_category_prefixes[SDL_LOG_CATEGORY_RESERVED1] = {
    77     "APP",
    78     "ERROR",
    79     "SYSTEM",
    80     "AUDIO",
    81     "VIDEO",
    82     "RENDER",
    83     "INPUT"
    84 };
    85 
    86 static int SDL_android_priority[SDL_NUM_LOG_PRIORITIES] = {
    87     ANDROID_LOG_VERBOSE,
    88     ANDROID_LOG_DEBUG,
    89     ANDROID_LOG_INFO,
    90     ANDROID_LOG_WARN,
    91     ANDROID_LOG_ERROR,
    92     ANDROID_LOG_FATAL
    93 };
    94 #endif /* __ANDROID__ */
    95 
    96 
    97 void
    98 SDL_LogSetAllPriority(SDL_LogPriority priority)
    99 {
   100     SDL_LogLevel *entry;
   101 
   102     for (entry = SDL_loglevels; entry; entry = entry->next) {
   103         entry->priority = priority;
   104     }
   105     SDL_default_priority = priority;
   106     SDL_assert_priority = priority;
   107     SDL_application_priority = priority;
   108 }
   109 
   110 void
   111 SDL_LogSetPriority(int category, SDL_LogPriority priority)
   112 {
   113     SDL_LogLevel *entry;
   114 
   115     for (entry = SDL_loglevels; entry; entry = entry->next) {
   116         if (entry->category == category) {
   117             entry->priority = priority;
   118             return;
   119         }
   120     }
   121 
   122     /* Create a new entry */
   123     entry = (SDL_LogLevel *)SDL_malloc(sizeof(*entry));
   124     if (entry) {
   125         entry->category = category;
   126         entry->priority = priority;
   127         entry->next = SDL_loglevels;
   128         SDL_loglevels = entry;
   129     }
   130 }
   131 
   132 SDL_LogPriority
   133 SDL_LogGetPriority(int category)
   134 {
   135     SDL_LogLevel *entry;
   136 
   137     for (entry = SDL_loglevels; entry; entry = entry->next) {
   138         if (entry->category == category) {
   139             return entry->priority;
   140         }
   141     }
   142 
   143     if (category == SDL_LOG_CATEGORY_TEST) {
   144         return SDL_test_priority;
   145     } else if (category == SDL_LOG_CATEGORY_APPLICATION) {
   146         return SDL_application_priority;
   147     } else if (category == SDL_LOG_CATEGORY_ASSERT) {
   148         return SDL_assert_priority;
   149     } else {
   150         return SDL_default_priority;
   151     }
   152 }
   153 
   154 void
   155 SDL_LogResetPriorities(void)
   156 {
   157     SDL_LogLevel *entry;
   158 
   159     while (SDL_loglevels) {
   160         entry = SDL_loglevels;
   161         SDL_loglevels = entry->next;
   162         SDL_free(entry);
   163     }
   164 
   165     SDL_default_priority = DEFAULT_PRIORITY;
   166     SDL_assert_priority = DEFAULT_ASSERT_PRIORITY;
   167     SDL_application_priority = DEFAULT_APPLICATION_PRIORITY;
   168     SDL_test_priority = DEFAULT_TEST_PRIORITY;
   169 }
   170 
   171 void
   172 SDL_Log(const char *fmt, ...)
   173 {
   174     va_list ap;
   175 
   176     va_start(ap, fmt);
   177     SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, fmt, ap);
   178     va_end(ap);
   179 }
   180 
   181 void
   182 SDL_LogVerbose(int category, const char *fmt, ...)
   183 {
   184     va_list ap;
   185 
   186     va_start(ap, fmt);
   187     SDL_LogMessageV(category, SDL_LOG_PRIORITY_VERBOSE, fmt, ap);
   188     va_end(ap);
   189 }
   190 
   191 void
   192 SDL_LogDebug(int category, const char *fmt, ...)
   193 {
   194     va_list ap;
   195 
   196     va_start(ap, fmt);
   197     SDL_LogMessageV(category, SDL_LOG_PRIORITY_DEBUG, fmt, ap);
   198     va_end(ap);
   199 }
   200 
   201 void
   202 SDL_LogInfo(int category, const char *fmt, ...)
   203 {
   204     va_list ap;
   205 
   206     va_start(ap, fmt);
   207     SDL_LogMessageV(category, SDL_LOG_PRIORITY_INFO, fmt, ap);
   208     va_end(ap);
   209 }
   210 
   211 void
   212 SDL_LogWarn(int category, const char *fmt, ...)
   213 {
   214     va_list ap;
   215 
   216     va_start(ap, fmt);
   217     SDL_LogMessageV(category, SDL_LOG_PRIORITY_WARN, fmt, ap);
   218     va_end(ap);
   219 }
   220 
   221 void
   222 SDL_LogError(int category, const char *fmt, ...)
   223 {
   224     va_list ap;
   225 
   226     va_start(ap, fmt);
   227     SDL_LogMessageV(category, SDL_LOG_PRIORITY_ERROR, fmt, ap);
   228     va_end(ap);
   229 }
   230 
   231 void
   232 SDL_LogCritical(int category, const char *fmt, ...)
   233 {
   234     va_list ap;
   235 
   236     va_start(ap, fmt);
   237     SDL_LogMessageV(category, SDL_LOG_PRIORITY_CRITICAL, fmt, ap);
   238     va_end(ap);
   239 }
   240 
   241 void
   242 SDL_LogMessage(int category, SDL_LogPriority priority, const char *fmt, ...)
   243 {
   244     va_list ap;
   245 
   246     va_start(ap, fmt);
   247     SDL_LogMessageV(category, priority, fmt, ap);
   248     va_end(ap);
   249 }
   250 
   251 #ifdef __ANDROID__
   252 static const char *
   253 GetCategoryPrefix(int category)
   254 {
   255     if (category < SDL_LOG_CATEGORY_RESERVED1) {
   256         return SDL_category_prefixes[category];
   257     }
   258     if (category < SDL_LOG_CATEGORY_CUSTOM) {
   259         return "RESERVED";
   260     }
   261     return "CUSTOM";
   262 }
   263 #endif /* __ANDROID__ */
   264 
   265 void
   266 SDL_LogMessageV(int category, SDL_LogPriority priority, const char *fmt, va_list ap)
   267 {
   268     char *message;
   269     size_t len;
   270 
   271     /* Nothing to do if we don't have an output function */
   272     if (!SDL_log_function) {
   273         return;
   274     }
   275 
   276     /* Make sure we don't exceed array bounds */
   277     if ((int)priority < 0 || priority >= SDL_NUM_LOG_PRIORITIES) {
   278         return;
   279     }
   280 
   281     /* See if we want to do anything with this message */
   282     if (priority < SDL_LogGetPriority(category)) {
   283         return;
   284     }
   285 
   286     message = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   287     if (!message) {
   288         return;
   289     }
   290 
   291     SDL_vsnprintf(message, SDL_MAX_LOG_MESSAGE, fmt, ap);
   292 
   293     /* Chop off final endline. */
   294     len = SDL_strlen(message);
   295     if ((len > 0) && (message[len-1] == '\n')) {
   296         message[--len] = '\0';
   297         if ((len > 0) && (message[len-1] == '\r')) {  /* catch "\r\n", too. */
   298             message[--len] = '\0';
   299         }
   300     }
   301 
   302     SDL_log_function(SDL_log_userdata, category, priority, message);
   303     SDL_stack_free(message);
   304 }
   305 
   306 #if defined(__WIN32__)
   307 /* Flag tracking the attachment of the console: 0=unattached, 1=attached, -1=error */
   308 static int consoleAttached = 0;
   309 
   310 /* Handle to stderr output of console. */
   311 static HANDLE stderrHandle = NULL;
   312 #endif
   313 
   314 static void
   315 SDL_LogOutput(void *userdata, int category, SDL_LogPriority priority,
   316               const char *message)
   317 {
   318 #if defined(__WIN32__)
   319     /* Way too many allocations here, urgh */
   320     {
   321         char *output;
   322         size_t length;
   323         LPTSTR tstr;
   324         BOOL attachResult;
   325         DWORD attachError;
   326         unsigned long charsWritten; 
   327 
   328         /* Maybe attach console and get stderr handle */
   329         if (consoleAttached == 0) {
   330             attachResult = AttachConsole(ATTACH_PARENT_PROCESS);
   331             if (!attachResult) {
   332                     attachError = GetLastError();
   333                     if (attachError == ERROR_INVALID_HANDLE) {
   334                         SDL_SetError("Parent process has no console");
   335                         consoleAttached = -1;
   336                     } else if (attachError == ERROR_GEN_FAILURE) {
   337                          SDL_SetError("Could not attach to console of parent process");
   338                          consoleAttached = -1;
   339                     } else if (attachError == ERROR_ACCESS_DENIED) {  
   340                          /* Already attached */
   341                         consoleAttached = 1;
   342                     } else {
   343                         SDL_SetError("Error %d attaching console", attachError);
   344                         consoleAttached = -1;
   345                     }
   346                 } else {
   347                     /* Newly attached */
   348                     consoleAttached = 1;
   349                 }
   350 			
   351                 if (consoleAttached == 1) {
   352                         stderrHandle = GetStdHandle(STD_ERROR_HANDLE);
   353                 }
   354         }
   355 
   356         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 1 + 1;
   357         output = SDL_stack_alloc(char, length);
   358         SDL_snprintf(output, length, "%s: %s\n", SDL_priority_prefixes[priority], message);
   359         tstr = WIN_UTF8ToString(output);
   360         
   361         /* Output to debugger */
   362         OutputDebugString(tstr);
   363        
   364         /* Screen output to stderr, if console was attached. */
   365         if (consoleAttached == 1) {
   366                 if (!WriteConsole(stderrHandle, tstr, lstrlen(tstr), &charsWritten, NULL)) {
   367                     SDL_SetError("Error %d calling WriteConsole", GetLastError());
   368                 }
   369                 if (charsWritten == ERROR_NOT_ENOUGH_MEMORY) {
   370                     SDL_SetError("Insufficient heap memory to write message of size %d", length);
   371                 }
   372         }
   373 
   374         SDL_free(tstr);
   375         SDL_stack_free(output);
   376     }
   377 #elif defined(__ANDROID__)
   378     {
   379         char tag[32];
   380 
   381         SDL_snprintf(tag, SDL_arraysize(tag), "SDL/%s", GetCategoryPrefix(category));
   382         __android_log_write(SDL_android_priority[priority], tag, message);
   383     }
   384 #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
   385     /* Technically we don't need SDL_VIDEO_DRIVER_COCOA, but that's where this function is defined for now.
   386     */
   387     extern void SDL_NSLog(const char *text);
   388     {
   389         char *text;
   390 
   391         text = SDL_stack_alloc(char, SDL_MAX_LOG_MESSAGE);
   392         if (text) {
   393             SDL_snprintf(text, SDL_MAX_LOG_MESSAGE, "%s: %s", SDL_priority_prefixes[priority], message);
   394             SDL_NSLog(text);
   395             SDL_stack_free(text);
   396             return;
   397         }
   398     }
   399 #elif defined(__PSP__)
   400     {
   401         unsigned int length;
   402         char*        output;
   403         FILE*        pFile;
   404         /* !!! FIXME: is there any reason we didn't just use fprintf() here? */
   405         length = SDL_strlen(SDL_priority_prefixes[priority]) + 2 + SDL_strlen(message) + 2;
   406         output = SDL_stack_alloc(char, length);
   407         SDL_snprintf(output, length, "%s: %s\n", SDL_priority_prefixes[priority], message);
   408         pFile = fopen ("SDL_Log.txt", "a");
   409         fwrite (output, strlen (output), 1, pFile);
   410         SDL_stack_free(output);
   411         fclose (pFile);
   412     }
   413 #endif
   414 #if HAVE_STDIO_H
   415     fprintf(stderr, "%s: %s\n", SDL_priority_prefixes[priority], message);
   416 #endif
   417 }
   418 
   419 void
   420 SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata)
   421 {
   422     if (callback) {
   423         *callback = SDL_log_function;
   424     }
   425     if (userdata) {
   426         *userdata = SDL_log_userdata;
   427     }
   428 }
   429 
   430 void
   431 SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata)
   432 {
   433     SDL_log_function = callback;
   434     SDL_log_userdata = userdata;
   435 }
   436 
   437 /* vi: set ts=4 sw=4 expandtab: */