diff options
Diffstat (limited to 'src/libs/log')
-rw-r--r-- | src/libs/log/Makeinfo | 15 | ||||
-rw-r--r-- | src/libs/log/loginternal.h | 24 | ||||
-rw-r--r-- | src/libs/log/msgbox.h | 23 | ||||
-rw-r--r-- | src/libs/log/msgbox_macosx.m | 42 | ||||
-rw-r--r-- | src/libs/log/msgbox_stub.c | 34 | ||||
-rw-r--r-- | src/libs/log/msgbox_win.c | 67 | ||||
-rw-r--r-- | src/libs/log/uqmlog.c | 331 | ||||
-rw-r--r-- | src/libs/log/uqmlog.h | 59 |
8 files changed, 595 insertions, 0 deletions
diff --git a/src/libs/log/Makeinfo b/src/libs/log/Makeinfo new file mode 100644 index 0000000..124260f --- /dev/null +++ b/src/libs/log/Makeinfo @@ -0,0 +1,15 @@ +uqm_CFILES="uqmlog.c" +uqm_HFILES="loginternal.h msgbox.h uqmlog.h" + +case "$HOST_SYSTEM" in + Darwin) + uqm_MFILES="msgbox_macosx.m" + ;; + MINGW32*|CYGWIN*) + uqm_CFILES="$uqm_CFILES msgbox_win.c" + ;; + *) + uqm_CFILES="$uqm_CFILES msgbox_stub.c" + ;; +esac + diff --git a/src/libs/log/loginternal.h b/src/libs/log/loginternal.h new file mode 100644 index 0000000..4457155 --- /dev/null +++ b/src/libs/log/loginternal.h @@ -0,0 +1,24 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef UQM_LOGINTERNAL_H_INCL__ +#define UQM_LOGINTERNAL_H_INCL__ + +#include <stdio.h> + +extern FILE *streamOut; + +#endif /* UQM_LOGINTERNAL_H_INCL__ */ diff --git a/src/libs/log/msgbox.h b/src/libs/log/msgbox.h new file mode 100644 index 0000000..72934a5 --- /dev/null +++ b/src/libs/log/msgbox.h @@ -0,0 +1,23 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef UQM_MSGBOX_H_INCL__ +#define UQM_MSGBOX_H_INCL__ + +extern void log_displayBox (const /*UTF-8*/char *title, int isError, + const /*UTF-8*/char *msg); + +#endif /* UQM_MSGBOX_H_INCL__ */ diff --git a/src/libs/log/msgbox_macosx.m b/src/libs/log/msgbox_macosx.m new file mode 100644 index 0000000..eca32be --- /dev/null +++ b/src/libs/log/msgbox_macosx.m @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#import <Cocoa/Cocoa.h> +#import "msgbox.h" + +void +log_displayBox (const /*UTF-8*/char *title, int isError, + const /*UTF-8*/char *msg) +{ + @autoreleasepool { + NSAlert *alert = [[NSAlert alloc] init]; + NSString *titleStr = [NSString stringWithUTF8String:title]; + NSString *msgStr = [NSString stringWithUTF8String:msg]; + + if (alert && titleStr && msgStr) { + alert.alertStyle = isError ? NSAlertStyleCritical : NSAlertStyleInformational; + alert.messageText = titleStr; + alert.informativeText = msgStr; + + [alert runModal]; + } + + [msgStr release]; + [titleStr release]; + [alert release]; + } +} + diff --git a/src/libs/log/msgbox_stub.c b/src/libs/log/msgbox_stub.c new file mode 100644 index 0000000..8e0b6b6 --- /dev/null +++ b/src/libs/log/msgbox_stub.c @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "msgbox.h" +#include "loginternal.h" + +void +log_displayBox (const /*UTF-8*/char *title, int isError, + const /*UTF-8*/char *msg) +{ + // We do not know how to display a box. Perhaps it's done with a + // hefty dose of pixie dust, or perhaps with a hammer and nails. + // So just inform the user of our predicament + fprintf (streamOut, "Do not know how to display %s box\n", + isError ? "an error" : "a"); + + // Suppress the compiler warnings in any case. + (void)title; + (void)msg; +} + diff --git a/src/libs/log/msgbox_win.c b/src/libs/log/msgbox_win.c new file mode 100644 index 0000000..c4e0021 --- /dev/null +++ b/src/libs/log/msgbox_win.c @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "msgbox.h" +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <stdlib.h> +#include <malloc.h> + + +// Converts a UTF-8 string to Windows WideChar. +// Caller is responsible for free()ing the returned string. +static LPWSTR +toWideChar (const /*UTF-8*/char *str) +{ + int cch; + LPWSTR wstr; + + cch = MultiByteToWideChar (CP_UTF8, 0, str, -1, NULL, 0); + if (cch == 0) + return NULL; // failed, probably no UTF8 converter + + wstr = malloc (cch * sizeof (WCHAR)); + if (!wstr) + return NULL; // out of memory + + cch = MultiByteToWideChar (CP_UTF8, 0, str, -1, wstr, cch); + if (cch == 0) + { // Failed. It should not fail here if it succeeded just above, + // but it did. Not much can be done about it. + free (wstr); + return NULL; + } + + return wstr; +} + +void +log_displayBox (const /*UTF-8*/char *title, int isError, + const /*UTF-8*/char *msg) +{ + LPWSTR swTitle = toWideChar (title); + LPWSTR swMsg = toWideChar (msg); + UINT uType = isError ? MB_ICONWARNING : MB_ICONINFORMATION; + + if (swTitle && swMsg) + MessageBoxW (NULL, swMsg, swTitle, uType); + else // Could not convert; let's try ASCII, though it may look ugly + MessageBoxA (NULL, msg, title, uType); + + free (swTitle); + free (swMsg); +} + diff --git a/src/libs/log/uqmlog.c b/src/libs/log/uqmlog.c new file mode 100644 index 0000000..e054edb --- /dev/null +++ b/src/libs/log/uqmlog.c @@ -0,0 +1,331 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "uqmlog.h" +#include "loginternal.h" +#include "msgbox.h" +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include "libs/threadlib.h" + +#ifndef MAX_LOG_ENTRY_SIZE +# define MAX_LOG_ENTRY_SIZE 256 +#endif + +#ifndef MAX_LOG_ENTRIES +# define MAX_LOG_ENTRIES 128 +#endif + +typedef char log_Entry[MAX_LOG_ENTRY_SIZE]; + +// static buffers in case we run out of memory +static log_Entry queue[MAX_LOG_ENTRIES]; +static log_Entry msgNoThread; +static char msgBuf[16384]; + +static int maxLevel = log_Error; +static int maxStreamLevel = log_Debug; +static int maxDisp = 10; +static int qtotal = 0; +static int qhead = 0; +static int qtail = 0; +static volatile bool noThreadReady = false; +static bool showBox = true; +static bool errorBox = true; + +FILE *streamOut; + +static volatile int qlock = 0; +static Mutex qmutex; + +static void exitCallback (void); +static void displayLog (bool isError); + +static void +lockQueue (void) +{ + if (!qlock) + return; + + LockMutex (qmutex); +} + +static void +unlockQueue (void) +{ + if (!qlock) + return; + + UnlockMutex (qmutex); +} + +static void +removeExcess (int room) +{ + room = maxDisp - room; + if (room < 0) + room = 0; + + for ( ; qtotal > room; --qtotal, ++qtail) + ; + qtail %= MAX_LOG_ENTRIES; +} + +static int +acquireSlot (void) +{ + int slot; + + lockQueue (); + + removeExcess (1); + slot = qhead; + qhead = (qhead + 1) % MAX_LOG_ENTRIES; + ++qtotal; + + unlockQueue (); + + return slot; +} + +// queues the non-threaded message when present +static void +queueNonThreaded (void) +{ + int slot; + + // This is not perfect. A race condition still exists + // between buffering the no-thread message and setting + // the noThreadReady flag. Neither does this prevent + // the fully or partially overwritten message (by + // another competing thread). But it is 'good enough' + if (!noThreadReady) + return; + noThreadReady = false; + + slot = acquireSlot (); + memcpy (queue[slot], msgNoThread, sizeof (msgNoThread)); +} + +void +log_init (int max_lines) +{ + int i; + + maxDisp = max_lines; + streamOut = stderr; + + // pre-term queue strings + for (i = 0; i < MAX_LOG_ENTRIES; ++i) + queue[i][MAX_LOG_ENTRY_SIZE - 1] = '\0'; + + msgBuf[sizeof (msgBuf) - 1] = '\0'; + msgNoThread[sizeof (msgNoThread) - 1] = '\0'; + + // install exit handlers + atexit (exitCallback); +} + +void +log_initThreads (void) +{ + qmutex = CreateMutex ("Logging Lock", SYNC_CLASS_RESOURCE); + qlock = 1; +} + +int +log_exit (int code) +{ + showBox = false; + + if (qlock) + { + qlock = 0; + DestroyMutex (qmutex); + qmutex = 0; + } + + return code; +} + +void +log_setLevel (int level) +{ + maxLevel = level; + //maxStreamLevel = level; +} + +FILE * +log_setOutput (FILE *out) +{ + FILE *old = streamOut; + streamOut = out; + + return old; +} + +void +log_addV (log_Level level, const char *fmt, va_list list) +{ + log_Entry full_msg; + vsnprintf (full_msg, sizeof (full_msg) - 1, fmt, list); + full_msg[sizeof (full_msg) - 1] = '\0'; + + if ((int)level <= maxStreamLevel) + { + fprintf (streamOut, "%s\n", full_msg); + } + + if ((int)level <= maxLevel) + { + int slot; + + queueNonThreaded (); + + slot = acquireSlot (); + memcpy (queue[slot], full_msg, sizeof (queue[0])); + } +} + +void +log_add (log_Level level, const char *fmt, ...) +{ + va_list list; + + va_start (list, fmt); + log_addV (level, fmt, list); + va_end (list); +} + +// non-threaded version of 'add' +// uses single-instance static storage with entry into the +// queue delayed until the next threaded 'add' or 'exit' +void +log_add_nothreadV (log_Level level, const char *fmt, va_list list) +{ + log_Entry full_msg; + vsnprintf (full_msg, sizeof (full_msg) - 1, fmt, list); + full_msg[sizeof (full_msg) - 1] = '\0'; + + if ((int)level <= maxStreamLevel) + { + fprintf (streamOut, "%s\n", full_msg); + } + + if ((int)level <= maxLevel) + { + memcpy (msgNoThread, full_msg, sizeof (msgNoThread)); + noThreadReady = true; + } +} + +void +log_add_nothread (log_Level level, const char *fmt, ...) +{ + va_list list; + + va_start (list, fmt); + log_add_nothreadV (level, fmt, list); + va_end (list); +} + +void +log_showBox (bool show, bool err) +{ + showBox = show; + errorBox = err; +} + +// sets the maximum log lines captured for the final +// display to the user on failure exit +void +log_captureLines (int num) +{ + if (num > MAX_LOG_ENTRIES) + num = MAX_LOG_ENTRIES; + if (num < 1) + num = 1; + maxDisp = num; + + // remove any extra lines already on queue + lockQueue (); + removeExcess (0); + unlockQueue (); +} + +static void +exitCallback (void) +{ + if (showBox) + displayLog (errorBox); + + log_exit (0); +} + +static void +displayLog (bool isError) +{ + char *p = msgBuf; + int left = sizeof (msgBuf) - 1; + int len; + int ptr; + + if (isError) + { + strcpy (p, "The Ur-Quan Masters encountered a fatal error.\n" + "Part of the log follows:\n\n"); + len = strlen (p); + p += len; + left -= len; + } + + // Glue the log entries together + // Locking is not a good idea at this point and we do not + // really need it -- the worst that can happen is we get + // an extra or an incomplete message + for (ptr = qtail; ptr != qhead && left > 0; + ptr = (ptr + 1) % MAX_LOG_ENTRIES) + { + len = strlen (queue[ptr]) + 1; + if (len > left) + len = left; + memcpy (p, queue[ptr], len); + p[len - 1] = '\n'; + p += len; + left -= len; + } + + // Glue the non-threaded message if present + if (noThreadReady) + { + noThreadReady = false; + len = strlen (msgNoThread); + if (len > left) + len = left; + memcpy (p, msgNoThread, len); + p += len; + left -= len; + } + + *p = '\0'; + + log_displayBox ("The Ur-Quan Masters", isError, msgBuf); +} + diff --git a/src/libs/log/uqmlog.h b/src/libs/log/uqmlog.h new file mode 100644 index 0000000..38db089 --- /dev/null +++ b/src/libs/log/uqmlog.h @@ -0,0 +1,59 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef UQMLOG_H_INCL__ +#define UQMLOG_H_INCL__ + +#include "port.h" +#include "types.h" +#include <stdio.h> +#include <stdarg.h> + +extern void log_init (int max_lines); +extern void log_initThreads (void); +extern int log_exit (int code); + +extern FILE * log_setOutput (FILE *out); + // sets the new output stream and returns the previous one +extern void log_setLevel (int level); +extern void log_showBox (bool show, bool err); +extern void log_captureLines (int num); +#define LOG_CAPTURE_ALL 1000000 // unreasonably big number + +typedef enum +{ + log_Nothing = 0, + log_User, + log_Fatal = log_User, + log_Error, + log_Warning, + log_Info, + log_Debug, + log_All, + +} log_Level; + +extern void log_add (log_Level, const char *fmt, ...) + PRINTF_FUNCTION(2, 3); +extern void log_addV (log_Level, const char *fmt, va_list) + VPRINTF_FUNCTION(2); +extern void log_add_nothread (log_Level, const char *fmt, ...) + PRINTF_FUNCTION(2, 3); +extern void log_add_nothreadV (log_Level, const char *fmt, va_list) + VPRINTF_FUNCTION(2); + + +#endif /* UQMLOG_H_INCL__ */ |