summaryrefslogtreecommitdiff
path: root/src/libs/log
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/log')
-rw-r--r--src/libs/log/Makeinfo15
-rw-r--r--src/libs/log/loginternal.h24
-rw-r--r--src/libs/log/msgbox.h23
-rw-r--r--src/libs/log/msgbox_macosx.m42
-rw-r--r--src/libs/log/msgbox_stub.c34
-rw-r--r--src/libs/log/msgbox_win.c67
-rw-r--r--src/libs/log/uqmlog.c331
-rw-r--r--src/libs/log/uqmlog.h59
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__ */