📄 gmessages.c
字号:
/* GLIB - Library of useful routines for C programming * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. *//* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. *//* * MT safe */#ifdef HAVE_CONFIG_H#include "glibconfig.h"#endif#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#include "glib.h"#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include <signal.h>#include <locale.h>#include <errno.h>#include "gdebug.h"#ifdef G_OS_WIN32typedef FILE* GFileDescriptor;#elsetypedef gint GFileDescriptor;#endif/* --- structures --- */typedef struct _GLogDomain GLogDomain;typedef struct _GLogHandler GLogHandler;struct _GLogDomain{ gchar *log_domain; GLogLevelFlags fatal_mask; GLogHandler *handlers; GLogDomain *next;};struct _GLogHandler{ guint id; GLogLevelFlags log_level; GLogFunc log_func; gpointer data; GLogHandler *next;};/* --- prototypes --- */#ifndef HAVE_C99_VSNPRINTFstatic gsize printf_string_upper_bound (const gchar *format, gboolean may_warn, va_list args);#endif /* !HAVE_C99_VSNPRINTF *//* --- variables --- */static GMutex *g_messages_lock = NULL;static GLogDomain *g_log_domains = NULL;static GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;static GPrintFunc glib_print_func = NULL;static GPrintFunc glib_printerr_func = NULL;static GPrivate *g_log_depth = NULL;static GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG;/* --- functions --- */#ifdef G_OS_WIN32# define STRICT# include <windows.h># undef STRICT# include <process.h> /* For _getpid() */static gboolean alloc_console_called = FALSE;static gboolean win32_keep_fatal_message = FALSE;/* This default message will usually be overwritten. *//* Yes, a fixed size buffer is bad. So sue me. But g_error is never * with huge strings, is it? */static gchar fatal_msg_buf[1000] = "Unspecified fatal error encountered, aborting.";static gchar *fatal_msg_ptr = fatal_msg_buf;/* Just use stdio. If we're out of memory, we're hosed anyway. */#undef writestatic inline intdowrite (GFileDescriptor fd, const void *buf, unsigned int len){ if (win32_keep_fatal_message) { memcpy (fatal_msg_ptr, buf, len); fatal_msg_ptr += len; *fatal_msg_ptr = 0; return len; } fwrite (buf, len, 1, fd); fflush (fd); return len;}#define write(fd, buf, len) dowrite(fd, buf, len)static voidensure_stdout_valid (void){ HANDLE handle; if (win32_keep_fatal_message) return; if (!alloc_console_called) { handle = GetStdHandle (STD_OUTPUT_HANDLE); if (handle == INVALID_HANDLE_VALUE) { AllocConsole (); alloc_console_called = TRUE; freopen ("CONOUT$", "w", stdout); } }}#else#define ensure_stdout_valid() /* Define as empty */#endifstatic voidwrite_string (GFileDescriptor fd, const gchar *string){ write (fd, string, strlen (string));}static voidg_messages_prefixed_init (void){ static gboolean initialized = FALSE; if (!initialized) { const gchar *val; initialized = TRUE; val = g_getenv ("G_MESSAGES_PREFIXED"); if (val) { static const GDebugKey keys[] = { { "error", G_LOG_LEVEL_ERROR }, { "critical", G_LOG_LEVEL_CRITICAL }, { "warning", G_LOG_LEVEL_WARNING }, { "message", G_LOG_LEVEL_MESSAGE }, { "info", G_LOG_LEVEL_INFO }, { "debug", G_LOG_LEVEL_DEBUG } }; g_log_msg_prefix = g_parse_debug_string (val, keys, G_N_ELEMENTS (keys)); } }}static GLogDomain*g_log_find_domain_L (const gchar *log_domain){ register GLogDomain *domain; domain = g_log_domains; while (domain) { if (strcmp (domain->log_domain, log_domain) == 0) return domain; domain = domain->next; } return NULL;}static GLogDomain*g_log_domain_new_L (const gchar *log_domain){ register GLogDomain *domain; domain = g_new (GLogDomain, 1); domain->log_domain = g_strdup (log_domain); domain->fatal_mask = G_LOG_FATAL_MASK; domain->handlers = NULL; domain->next = g_log_domains; g_log_domains = domain; return domain;}static voidg_log_domain_check_free_L (GLogDomain *domain){ if (domain->fatal_mask == G_LOG_FATAL_MASK && domain->handlers == NULL) { register GLogDomain *last, *work; last = NULL; work = g_log_domains; while (work) { if (work == domain) { if (last) last->next = domain->next; else g_log_domains = domain->next; g_free (domain->log_domain); g_free (domain); break; } last = work; work = last->next; } }}static GLogFuncg_log_domain_get_handler_L (GLogDomain *domain, GLogLevelFlags log_level, gpointer *data){ if (domain && log_level) { register GLogHandler *handler; handler = domain->handlers; while (handler) { if ((handler->log_level & log_level) == log_level) { *data = handler->data; return handler->log_func; } handler = handler->next; } } return g_log_default_handler;}GLogLevelFlagsg_log_set_always_fatal (GLogLevelFlags fatal_mask){ GLogLevelFlags old_mask; /* restrict the global mask to levels that are known to glib * since this setting applies to all domains */ fatal_mask &= (1 << G_LOG_LEVEL_USER_SHIFT) - 1; /* force errors to be fatal */ fatal_mask |= G_LOG_LEVEL_ERROR; /* remove bogus flag */ fatal_mask &= ~G_LOG_FLAG_FATAL; g_mutex_lock (g_messages_lock); old_mask = g_log_always_fatal; g_log_always_fatal = fatal_mask; g_mutex_unlock (g_messages_lock); return old_mask;}GLogLevelFlagsg_log_set_fatal_mask (const gchar *log_domain, GLogLevelFlags fatal_mask){ GLogLevelFlags old_flags; register GLogDomain *domain; if (!log_domain) log_domain = ""; /* force errors to be fatal */ fatal_mask |= G_LOG_LEVEL_ERROR; /* remove bogus flag */ fatal_mask &= ~G_LOG_FLAG_FATAL; g_mutex_lock (g_messages_lock); domain = g_log_find_domain_L (log_domain); if (!domain) domain = g_log_domain_new_L (log_domain); old_flags = domain->fatal_mask; domain->fatal_mask = fatal_mask; g_log_domain_check_free_L (domain); g_mutex_unlock (g_messages_lock); return old_flags;}guintg_log_set_handler (const gchar *log_domain, GLogLevelFlags log_levels, GLogFunc log_func, gpointer user_data){ static guint handler_id = 0; GLogDomain *domain; GLogHandler *handler; g_return_val_if_fail ((log_levels & G_LOG_LEVEL_MASK) != 0, 0); g_return_val_if_fail (log_func != NULL, 0); if (!log_domain) log_domain = ""; handler = g_new (GLogHandler, 1); g_mutex_lock (g_messages_lock); domain = g_log_find_domain_L (log_domain); if (!domain) domain = g_log_domain_new_L (log_domain); handler->id = ++handler_id; handler->log_level = log_levels; handler->log_func = log_func; handler->data = user_data; handler->next = domain->handlers; domain->handlers = handler; g_mutex_unlock (g_messages_lock); return handler_id;}voidg_log_remove_handler (const gchar *log_domain, guint handler_id){ register GLogDomain *domain; g_return_if_fail (handler_id > 0); if (!log_domain) log_domain = ""; g_mutex_lock (g_messages_lock); domain = g_log_find_domain_L (log_domain); if (domain) { GLogHandler *work, *last; last = NULL; work = domain->handlers; while (work) { if (work->id == handler_id) { if (last) last->next = work->next; else domain->handlers = work->next; g_log_domain_check_free_L (domain); g_mutex_unlock (g_messages_lock); g_free (work); return; } last = work; work = last->next; } } g_mutex_unlock (g_messages_lock); g_warning ("%s: could not find handler with id `%d' for domain \"%s\"", G_STRLOC, handler_id, log_domain);}voidg_logv (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, va_list args1){ gchar buffer[1025]; gboolean was_fatal = (log_level & G_LOG_FLAG_FATAL) != 0; gboolean was_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0; gint i;#ifndef HAVE_VSNPRINTF va_list args2;#endif /* HAVE_VSNPRINTF */ log_level &= G_LOG_LEVEL_MASK; if (!log_level) return; /* we use a stack buffer of fixed size, because we might get called * recursively. */#ifdef HAVE_VSNPRINTF vsnprintf (buffer, 1024, format, args1);#else /* !HAVE_VSNPRINTF */ G_VA_COPY (args2, args1); if (printf_string_upper_bound (format, FALSE, args1) < 1024) vsprintf (buffer, format, args2); else { /* since we might be out of memory, we can't use g_vsnprintf(). */ /* we are out of luck here */ strncpy (buffer, format, 1024); buffer[1024] = 0; } va_end (args2);#endif /* !HAVE_VSNPRINTF */ for (i = g_bit_nth_msf (log_level, -1); i >= 0; i = g_bit_nth_msf (log_level, i)) { register GLogLevelFlags test_level; test_level = 1 << i; if (log_level & test_level) { guint depth = GPOINTER_TO_UINT (g_private_get (g_log_depth)); GLogDomain *domain; GLogFunc log_func; guint domain_fatal_mask; gpointer data = NULL; if (was_fatal) test_level |= G_LOG_FLAG_FATAL; if (was_recursion) test_level |= G_LOG_FLAG_RECURSION; /* check recursion and lookup handler */ g_mutex_lock (g_messages_lock); domain = g_log_find_domain_L (log_domain ? log_domain : ""); if (depth) test_level |= G_LOG_FLAG_RECURSION; depth++; domain_fatal_mask = domain ? domain->fatal_mask : G_LOG_FATAL_MASK; if ((domain_fatal_mask | g_log_always_fatal) & test_level) test_level |= G_LOG_FLAG_FATAL; if (test_level & G_LOG_FLAG_RECURSION) log_func = _g_log_fallback_handler; else log_func = g_log_domain_get_handler_L (domain, test_level, &data); domain = NULL; g_mutex_unlock (g_messages_lock); g_private_set (g_log_depth, GUINT_TO_POINTER (depth)); /* had to defer debug initialization until we can keep track of recursion */ if (!(test_level & G_LOG_FLAG_RECURSION) && !_g_debug_initialized) { guint orig_test_level = test_level; _g_debug_init (); if ((domain_fatal_mask | g_log_always_fatal) & test_level) test_level |= G_LOG_FLAG_FATAL; if (test_level != orig_test_level) { /* need a relookup, not nice, but not too bad either */ g_mutex_lock (g_messages_lock); domain = g_log_find_domain_L (log_domain ? log_domain : ""); log_func = g_log_domain_get_handler_L (domain, test_level, &data); domain = NULL; g_mutex_unlock (g_messages_lock); } } log_func (log_domain, test_level, buffer, data); if (test_level & G_LOG_FLAG_FATAL) {#ifdef G_OS_WIN32 MessageBox (NULL, fatal_msg_buf, NULL, MB_OK);#endif#if defined (G_ENABLE_DEBUG) && (defined (SIGTRAP) || defined (G_OS_WIN32)) if (!(test_level & G_LOG_FLAG_RECURSION)) G_BREAKPOINT (); else abort ();#else /* !G_ENABLE_DEBUG || !(SIGTRAP || G_OS_WIN32) */ abort ();#endif /* !G_ENABLE_DEBUG || !(SIGTRAP || G_OS_WIN32) */ } depth--; g_private_set (g_log_depth, GUINT_TO_POINTER (depth)); } }}voidg_log (const gchar *log_domain, GLogLevelFlags log_level, const gchar *format, ...){ va_list args; va_start (args, format); g_logv (log_domain, log_level, format, args); va_end (args);}#if 0static gchar*strdup_convert (const gchar *string, const gchar *charset){ if (!g_utf8_validate (string, -1, NULL)) return g_strconcat ("[Invalid UTF-8] ", string, NULL); else { GError *err = NULL; gchar *result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, &err); if (result) return result; else { /* Not thread-safe, but doesn't matter if we print the warning twice */ static gboolean warned = FALSE; if (!warned) { warned = TRUE; fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message); } g_error_free (err); return g_strdup (string); } }}#endif/* For a radix of 8 we need at most 3 output bytes for 1 input * byte. Additionally we might need up to 2 output bytes for the * readix prefix and 1 byte for the trailing NULL. */#define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3)static voidformat_unsigned (gchar *buf, gulong num, guint radix){ gulong tmp; gchar c; gint i, n; /* we may not call _any_ GLib functions here (or macros like g_return_if_fail()) */ if (radix != 8 && radix != 10 && radix != 16) { *buf = '\000'; return; } if (!num) { *buf++ = '0'; *buf = '\000'; return; } if (radix == 16) { *buf++ = '0'; *buf++ = 'x'; } else if (radix == 8) { *buf++ = '0'; } n = 0; tmp = num; while (tmp) { tmp /= radix; n++; } i = n; /* Again we can't use g_assert; actually this check should _never_ fail. */ if (n > FORMAT_UNSIGNED_BUFSIZE - 3) { *buf = '\000'; return; } while (num) { i--; c = (num % radix); if (c < 10) buf[i] = c + '0'; else buf[i] = c + 'a' - 10; num /= radix; } buf[n] = '\000';}/* string size big enough to hold level prefix */#define STRING_BUFFER_SIZE (FORMAT_UNSIGNED_BUFSIZE + 32)#define ALERT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)static GFileDescriptormklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE], guint log_level){ gboolean to_stdout = TRUE; /* we may not call _any_ GLib functions here */ switch (log_level & G_LOG_LEVEL_MASK) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -