1 | /* GLIB - Library of useful routines for C programming |
---|
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
---|
3 | * |
---|
4 | * This library is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU Lesser General Public |
---|
6 | * License as published by the Free Software Foundation; either |
---|
7 | * version 2 of the License, or (at your option) any later version. |
---|
8 | * |
---|
9 | * This library is distributed in the hope that it will be useful, |
---|
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * Lesser General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU Lesser General Public |
---|
15 | * License along with this library; if not, write to the |
---|
16 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
---|
17 | * Boston, MA 02111-1307, USA. |
---|
18 | */ |
---|
19 | |
---|
20 | /* |
---|
21 | * Modified by the GLib Team and others 1997-2000. See the AUTHORS |
---|
22 | * file for a list of people on the GLib Team. See the ChangeLog |
---|
23 | * files for a list of changes. These files are distributed with |
---|
24 | * GLib at ftp://ftp.gtk.org/pub/gtk/. |
---|
25 | */ |
---|
26 | |
---|
27 | /* |
---|
28 | * MT safe |
---|
29 | */ |
---|
30 | |
---|
31 | #include "config.h" |
---|
32 | |
---|
33 | #include <stdlib.h> |
---|
34 | #include <stdarg.h> |
---|
35 | #include <stdio.h> |
---|
36 | #include <string.h> |
---|
37 | #ifdef HAVE_UNISTD_H |
---|
38 | #include <unistd.h> |
---|
39 | #endif |
---|
40 | #include <signal.h> |
---|
41 | #include <locale.h> |
---|
42 | #include <errno.h> |
---|
43 | |
---|
44 | #include "glib.h" |
---|
45 | #include "gdebug.h" |
---|
46 | #include "gprintfint.h" |
---|
47 | #include "gthreadinit.h" |
---|
48 | |
---|
49 | #ifdef G_OS_WIN32 |
---|
50 | #include <io.h> |
---|
51 | #endif |
---|
52 | |
---|
53 | /* --- structures --- */ |
---|
54 | typedef struct _GLogDomain GLogDomain; |
---|
55 | typedef struct _GLogHandler GLogHandler; |
---|
56 | struct _GLogDomain |
---|
57 | { |
---|
58 | gchar *log_domain; |
---|
59 | GLogLevelFlags fatal_mask; |
---|
60 | GLogHandler *handlers; |
---|
61 | GLogDomain *next; |
---|
62 | }; |
---|
63 | struct _GLogHandler |
---|
64 | { |
---|
65 | guint id; |
---|
66 | GLogLevelFlags log_level; |
---|
67 | GLogFunc log_func; |
---|
68 | gpointer data; |
---|
69 | GLogHandler *next; |
---|
70 | }; |
---|
71 | |
---|
72 | |
---|
73 | /* --- variables --- */ |
---|
74 | static GMutex *g_messages_lock = NULL; |
---|
75 | static GLogDomain *g_log_domains = NULL; |
---|
76 | static GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK; |
---|
77 | static GPrintFunc glib_print_func = NULL; |
---|
78 | static GPrintFunc glib_printerr_func = NULL; |
---|
79 | static GPrivate *g_log_depth = NULL; |
---|
80 | static GLogLevelFlags g_log_msg_prefix = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_DEBUG; |
---|
81 | |
---|
82 | |
---|
83 | /* --- functions --- */ |
---|
84 | #ifdef G_OS_WIN32 |
---|
85 | # define STRICT |
---|
86 | # include <windows.h> |
---|
87 | # undef STRICT |
---|
88 | static gboolean win32_keep_fatal_message = FALSE; |
---|
89 | |
---|
90 | /* This function lifted from GLib HEAD */ |
---|
91 | static guint |
---|
92 | g_win32_get_windows_version (void) |
---|
93 | { |
---|
94 | static gboolean beenhere = FALSE; |
---|
95 | static guint version; |
---|
96 | |
---|
97 | if (!beenhere) |
---|
98 | { |
---|
99 | if (getenv ("G_WIN32_PRETEND_WIN9X")) |
---|
100 | version = 0x80000004; |
---|
101 | else |
---|
102 | version = GetVersion (); |
---|
103 | beenhere = TRUE; |
---|
104 | } |
---|
105 | return version; |
---|
106 | } |
---|
107 | |
---|
108 | #define G_WIN32_IS_NT_BASED() (g_win32_get_windows_version () < 0x80000000) |
---|
109 | #define G_WIN32_HAVE_WIDECHAR_API() (G_WIN32_IS_NT_BASED ()) |
---|
110 | |
---|
111 | /* This default message will usually be overwritten. */ |
---|
112 | /* Yes, a fixed size buffer is bad. So sue me. But g_error() is never |
---|
113 | * called with huge strings, is it? |
---|
114 | */ |
---|
115 | static gchar fatal_msg_buf[1000] = "Unspecified fatal error encountered, aborting."; |
---|
116 | static gchar *fatal_msg_ptr = fatal_msg_buf; |
---|
117 | |
---|
118 | #undef write |
---|
119 | static inline int |
---|
120 | dowrite (int fd, |
---|
121 | const void *buf, |
---|
122 | unsigned int len) |
---|
123 | { |
---|
124 | if (win32_keep_fatal_message) |
---|
125 | { |
---|
126 | memcpy (fatal_msg_ptr, buf, len); |
---|
127 | fatal_msg_ptr += len; |
---|
128 | *fatal_msg_ptr = 0; |
---|
129 | return len; |
---|
130 | } |
---|
131 | |
---|
132 | write (fd, buf, len); |
---|
133 | |
---|
134 | return len; |
---|
135 | } |
---|
136 | #define write(fd, buf, len) dowrite(fd, buf, len) |
---|
137 | |
---|
138 | #endif |
---|
139 | |
---|
140 | static void |
---|
141 | write_string (int fd, |
---|
142 | const gchar *string) |
---|
143 | { |
---|
144 | write (fd, string, strlen (string)); |
---|
145 | } |
---|
146 | |
---|
147 | static void |
---|
148 | g_messages_prefixed_init (void) |
---|
149 | { |
---|
150 | static gboolean initialized = FALSE; |
---|
151 | |
---|
152 | if (!initialized) |
---|
153 | { |
---|
154 | const gchar *val; |
---|
155 | |
---|
156 | initialized = TRUE; |
---|
157 | val = g_getenv ("G_MESSAGES_PREFIXED"); |
---|
158 | |
---|
159 | if (val) |
---|
160 | { |
---|
161 | static const GDebugKey keys[] = { |
---|
162 | { "error", G_LOG_LEVEL_ERROR }, |
---|
163 | { "critical", G_LOG_LEVEL_CRITICAL }, |
---|
164 | { "warning", G_LOG_LEVEL_WARNING }, |
---|
165 | { "message", G_LOG_LEVEL_MESSAGE }, |
---|
166 | { "info", G_LOG_LEVEL_INFO }, |
---|
167 | { "debug", G_LOG_LEVEL_DEBUG } |
---|
168 | }; |
---|
169 | |
---|
170 | g_log_msg_prefix = g_parse_debug_string (val, keys, G_N_ELEMENTS (keys)); |
---|
171 | } |
---|
172 | } |
---|
173 | } |
---|
174 | |
---|
175 | static GLogDomain* |
---|
176 | g_log_find_domain_L (const gchar *log_domain) |
---|
177 | { |
---|
178 | register GLogDomain *domain; |
---|
179 | |
---|
180 | domain = g_log_domains; |
---|
181 | while (domain) |
---|
182 | { |
---|
183 | if (strcmp (domain->log_domain, log_domain) == 0) |
---|
184 | return domain; |
---|
185 | domain = domain->next; |
---|
186 | } |
---|
187 | return NULL; |
---|
188 | } |
---|
189 | |
---|
190 | static GLogDomain* |
---|
191 | g_log_domain_new_L (const gchar *log_domain) |
---|
192 | { |
---|
193 | register GLogDomain *domain; |
---|
194 | |
---|
195 | domain = g_new (GLogDomain, 1); |
---|
196 | domain->log_domain = g_strdup (log_domain); |
---|
197 | domain->fatal_mask = G_LOG_FATAL_MASK; |
---|
198 | domain->handlers = NULL; |
---|
199 | |
---|
200 | domain->next = g_log_domains; |
---|
201 | g_log_domains = domain; |
---|
202 | |
---|
203 | return domain; |
---|
204 | } |
---|
205 | |
---|
206 | static void |
---|
207 | g_log_domain_check_free_L (GLogDomain *domain) |
---|
208 | { |
---|
209 | if (domain->fatal_mask == G_LOG_FATAL_MASK && |
---|
210 | domain->handlers == NULL) |
---|
211 | { |
---|
212 | register GLogDomain *last, *work; |
---|
213 | |
---|
214 | last = NULL; |
---|
215 | |
---|
216 | work = g_log_domains; |
---|
217 | while (work) |
---|
218 | { |
---|
219 | if (work == domain) |
---|
220 | { |
---|
221 | if (last) |
---|
222 | last->next = domain->next; |
---|
223 | else |
---|
224 | g_log_domains = domain->next; |
---|
225 | g_free (domain->log_domain); |
---|
226 | g_free (domain); |
---|
227 | break; |
---|
228 | } |
---|
229 | last = work; |
---|
230 | work = last->next; |
---|
231 | } |
---|
232 | } |
---|
233 | } |
---|
234 | |
---|
235 | static GLogFunc |
---|
236 | g_log_domain_get_handler_L (GLogDomain *domain, |
---|
237 | GLogLevelFlags log_level, |
---|
238 | gpointer *data) |
---|
239 | { |
---|
240 | if (domain && log_level) |
---|
241 | { |
---|
242 | register GLogHandler *handler; |
---|
243 | |
---|
244 | handler = domain->handlers; |
---|
245 | while (handler) |
---|
246 | { |
---|
247 | if ((handler->log_level & log_level) == log_level) |
---|
248 | { |
---|
249 | *data = handler->data; |
---|
250 | return handler->log_func; |
---|
251 | } |
---|
252 | handler = handler->next; |
---|
253 | } |
---|
254 | } |
---|
255 | return g_log_default_handler; |
---|
256 | } |
---|
257 | |
---|
258 | GLogLevelFlags |
---|
259 | g_log_set_always_fatal (GLogLevelFlags fatal_mask) |
---|
260 | { |
---|
261 | GLogLevelFlags old_mask; |
---|
262 | |
---|
263 | /* restrict the global mask to levels that are known to glib |
---|
264 | * since this setting applies to all domains |
---|
265 | */ |
---|
266 | fatal_mask &= (1 << G_LOG_LEVEL_USER_SHIFT) - 1; |
---|
267 | /* force errors to be fatal */ |
---|
268 | fatal_mask |= G_LOG_LEVEL_ERROR; |
---|
269 | /* remove bogus flag */ |
---|
270 | fatal_mask &= ~G_LOG_FLAG_FATAL; |
---|
271 | |
---|
272 | g_mutex_lock (g_messages_lock); |
---|
273 | old_mask = g_log_always_fatal; |
---|
274 | g_log_always_fatal = fatal_mask; |
---|
275 | g_mutex_unlock (g_messages_lock); |
---|
276 | |
---|
277 | return old_mask; |
---|
278 | } |
---|
279 | |
---|
280 | GLogLevelFlags |
---|
281 | g_log_set_fatal_mask (const gchar *log_domain, |
---|
282 | GLogLevelFlags fatal_mask) |
---|
283 | { |
---|
284 | GLogLevelFlags old_flags; |
---|
285 | register GLogDomain *domain; |
---|
286 | |
---|
287 | if (!log_domain) |
---|
288 | log_domain = ""; |
---|
289 | |
---|
290 | /* force errors to be fatal */ |
---|
291 | fatal_mask |= G_LOG_LEVEL_ERROR; |
---|
292 | /* remove bogus flag */ |
---|
293 | fatal_mask &= ~G_LOG_FLAG_FATAL; |
---|
294 | |
---|
295 | g_mutex_lock (g_messages_lock); |
---|
296 | |
---|
297 | domain = g_log_find_domain_L (log_domain); |
---|
298 | if (!domain) |
---|
299 | domain = g_log_domain_new_L (log_domain); |
---|
300 | old_flags = domain->fatal_mask; |
---|
301 | |
---|
302 | domain->fatal_mask = fatal_mask; |
---|
303 | g_log_domain_check_free_L (domain); |
---|
304 | |
---|
305 | g_mutex_unlock (g_messages_lock); |
---|
306 | |
---|
307 | return old_flags; |
---|
308 | } |
---|
309 | |
---|
310 | guint |
---|
311 | g_log_set_handler (const gchar *log_domain, |
---|
312 | GLogLevelFlags log_levels, |
---|
313 | GLogFunc log_func, |
---|
314 | gpointer user_data) |
---|
315 | { |
---|
316 | static guint handler_id = 0; |
---|
317 | GLogDomain *domain; |
---|
318 | GLogHandler *handler; |
---|
319 | |
---|
320 | g_return_val_if_fail ((log_levels & G_LOG_LEVEL_MASK) != 0, 0); |
---|
321 | g_return_val_if_fail (log_func != NULL, 0); |
---|
322 | |
---|
323 | if (!log_domain) |
---|
324 | log_domain = ""; |
---|
325 | |
---|
326 | handler = g_new (GLogHandler, 1); |
---|
327 | |
---|
328 | g_mutex_lock (g_messages_lock); |
---|
329 | |
---|
330 | domain = g_log_find_domain_L (log_domain); |
---|
331 | if (!domain) |
---|
332 | domain = g_log_domain_new_L (log_domain); |
---|
333 | |
---|
334 | handler->id = ++handler_id; |
---|
335 | handler->log_level = log_levels; |
---|
336 | handler->log_func = log_func; |
---|
337 | handler->data = user_data; |
---|
338 | handler->next = domain->handlers; |
---|
339 | domain->handlers = handler; |
---|
340 | |
---|
341 | g_mutex_unlock (g_messages_lock); |
---|
342 | |
---|
343 | return handler_id; |
---|
344 | } |
---|
345 | |
---|
346 | void |
---|
347 | g_log_remove_handler (const gchar *log_domain, |
---|
348 | guint handler_id) |
---|
349 | { |
---|
350 | register GLogDomain *domain; |
---|
351 | |
---|
352 | g_return_if_fail (handler_id > 0); |
---|
353 | |
---|
354 | if (!log_domain) |
---|
355 | log_domain = ""; |
---|
356 | |
---|
357 | g_mutex_lock (g_messages_lock); |
---|
358 | domain = g_log_find_domain_L (log_domain); |
---|
359 | if (domain) |
---|
360 | { |
---|
361 | GLogHandler *work, *last; |
---|
362 | |
---|
363 | last = NULL; |
---|
364 | work = domain->handlers; |
---|
365 | while (work) |
---|
366 | { |
---|
367 | if (work->id == handler_id) |
---|
368 | { |
---|
369 | if (last) |
---|
370 | last->next = work->next; |
---|
371 | else |
---|
372 | domain->handlers = work->next; |
---|
373 | g_log_domain_check_free_L (domain); |
---|
374 | g_mutex_unlock (g_messages_lock); |
---|
375 | g_free (work); |
---|
376 | return; |
---|
377 | } |
---|
378 | last = work; |
---|
379 | work = last->next; |
---|
380 | } |
---|
381 | } |
---|
382 | g_mutex_unlock (g_messages_lock); |
---|
383 | g_warning ("%s: could not find handler with id `%d' for domain \"%s\"", |
---|
384 | G_STRLOC, handler_id, log_domain); |
---|
385 | } |
---|
386 | |
---|
387 | void |
---|
388 | g_logv (const gchar *log_domain, |
---|
389 | GLogLevelFlags log_level, |
---|
390 | const gchar *format, |
---|
391 | va_list args1) |
---|
392 | { |
---|
393 | gboolean was_fatal = (log_level & G_LOG_FLAG_FATAL) != 0; |
---|
394 | gboolean was_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0; |
---|
395 | gint i; |
---|
396 | |
---|
397 | log_level &= G_LOG_LEVEL_MASK; |
---|
398 | if (!log_level) |
---|
399 | return; |
---|
400 | |
---|
401 | for (i = g_bit_nth_msf (log_level, -1); i >= 0; i = g_bit_nth_msf (log_level, i)) |
---|
402 | { |
---|
403 | register GLogLevelFlags test_level; |
---|
404 | |
---|
405 | test_level = 1 << i; |
---|
406 | if (log_level & test_level) |
---|
407 | { |
---|
408 | guint depth = GPOINTER_TO_UINT (g_private_get (g_log_depth)); |
---|
409 | GLogDomain *domain; |
---|
410 | GLogFunc log_func; |
---|
411 | guint domain_fatal_mask; |
---|
412 | gpointer data = NULL; |
---|
413 | |
---|
414 | if (was_fatal) |
---|
415 | test_level |= G_LOG_FLAG_FATAL; |
---|
416 | if (was_recursion) |
---|
417 | test_level |= G_LOG_FLAG_RECURSION; |
---|
418 | |
---|
419 | /* check recursion and lookup handler */ |
---|
420 | g_mutex_lock (g_messages_lock); |
---|
421 | domain = g_log_find_domain_L (log_domain ? log_domain : ""); |
---|
422 | if (depth) |
---|
423 | test_level |= G_LOG_FLAG_RECURSION; |
---|
424 | depth++; |
---|
425 | domain_fatal_mask = domain ? domain->fatal_mask : G_LOG_FATAL_MASK; |
---|
426 | if ((domain_fatal_mask | g_log_always_fatal) & test_level) |
---|
427 | test_level |= G_LOG_FLAG_FATAL; |
---|
428 | if (test_level & G_LOG_FLAG_RECURSION) |
---|
429 | log_func = _g_log_fallback_handler; |
---|
430 | else |
---|
431 | log_func = g_log_domain_get_handler_L (domain, test_level, &data); |
---|
432 | domain = NULL; |
---|
433 | g_mutex_unlock (g_messages_lock); |
---|
434 | |
---|
435 | g_private_set (g_log_depth, GUINT_TO_POINTER (depth)); |
---|
436 | |
---|
437 | /* had to defer debug initialization until we can keep track of recursion */ |
---|
438 | if (!(test_level & G_LOG_FLAG_RECURSION) && !_g_debug_initialized) |
---|
439 | { |
---|
440 | guint orig_test_level = test_level; |
---|
441 | |
---|
442 | _g_debug_init (); |
---|
443 | if ((domain_fatal_mask | g_log_always_fatal) & test_level) |
---|
444 | test_level |= G_LOG_FLAG_FATAL; |
---|
445 | if (test_level != orig_test_level) |
---|
446 | { |
---|
447 | /* need a relookup, not nice, but not too bad either */ |
---|
448 | g_mutex_lock (g_messages_lock); |
---|
449 | domain = g_log_find_domain_L (log_domain ? log_domain : ""); |
---|
450 | log_func = g_log_domain_get_handler_L (domain, test_level, &data); |
---|
451 | domain = NULL; |
---|
452 | g_mutex_unlock (g_messages_lock); |
---|
453 | } |
---|
454 | } |
---|
455 | |
---|
456 | if (test_level & G_LOG_FLAG_RECURSION) |
---|
457 | { |
---|
458 | /* we use a stack buffer of fixed size, since we're likely |
---|
459 | * in an out-of-memory situation |
---|
460 | */ |
---|
461 | gchar buffer[1025]; |
---|
462 | gint size; |
---|
463 | size = _g_vsnprintf (buffer, 1024, format, args1); |
---|
464 | |
---|
465 | log_func (log_domain, test_level, buffer, data); |
---|
466 | } |
---|
467 | else |
---|
468 | { |
---|
469 | gchar *msg = g_strdup_vprintf (format, args1); |
---|
470 | |
---|
471 | log_func (log_domain, test_level, msg, data); |
---|
472 | |
---|
473 | g_free (msg); |
---|
474 | } |
---|
475 | |
---|
476 | if (test_level & G_LOG_FLAG_FATAL) |
---|
477 | { |
---|
478 | #ifdef G_OS_WIN32 |
---|
479 | gchar *locale_msg = g_locale_from_utf8 (fatal_msg_buf, -1, NULL, NULL, NULL); |
---|
480 | |
---|
481 | MessageBox (NULL, locale_msg, NULL, |
---|
482 | MB_ICONERROR|MB_SETFOREGROUND); |
---|
483 | #endif |
---|
484 | #if defined (G_ENABLE_DEBUG) && (defined (SIGTRAP) || defined (G_OS_WIN32)) |
---|
485 | if (!(test_level & G_LOG_FLAG_RECURSION)) |
---|
486 | G_BREAKPOINT (); |
---|
487 | else |
---|
488 | abort (); |
---|
489 | #else /* !G_ENABLE_DEBUG || !(SIGTRAP || G_OS_WIN32) */ |
---|
490 | abort (); |
---|
491 | #endif /* !G_ENABLE_DEBUG || !(SIGTRAP || G_OS_WIN32) */ |
---|
492 | } |
---|
493 | |
---|
494 | depth--; |
---|
495 | g_private_set (g_log_depth, GUINT_TO_POINTER (depth)); |
---|
496 | } |
---|
497 | } |
---|
498 | } |
---|
499 | |
---|
500 | void |
---|
501 | g_log (const gchar *log_domain, |
---|
502 | GLogLevelFlags log_level, |
---|
503 | const gchar *format, |
---|
504 | ...) |
---|
505 | { |
---|
506 | va_list args; |
---|
507 | |
---|
508 | va_start (args, format); |
---|
509 | g_logv (log_domain, log_level, format, args); |
---|
510 | va_end (args); |
---|
511 | } |
---|
512 | |
---|
513 | #define CHAR_IS_SAFE(wc) (!((wc < 0x20 && wc != '\t' && wc != '\n' && wc != '\r') || \ |
---|
514 | (wc == 0x7f) || \ |
---|
515 | (wc >= 0x80 && wc < 0xa0))) |
---|
516 | |
---|
517 | static gchar* |
---|
518 | strdup_convert (const gchar *string, |
---|
519 | const gchar *charset) |
---|
520 | { |
---|
521 | if (!g_utf8_validate (string, -1, NULL)) |
---|
522 | { |
---|
523 | GString *gstring = g_string_new ("[Invalid UTF-8] "); |
---|
524 | guchar *p; |
---|
525 | |
---|
526 | for (p = (guchar *)string; *p; p++) |
---|
527 | { |
---|
528 | if (CHAR_IS_SAFE(*p) && |
---|
529 | !(*p == '\r' && *(p + 1) != '\n') && |
---|
530 | *p < 0x80) |
---|
531 | g_string_append_c (gstring, *p); |
---|
532 | else |
---|
533 | g_string_append_printf (gstring, "\\x%02x", (guint)(guchar)*p); |
---|
534 | } |
---|
535 | |
---|
536 | return g_string_free (gstring, FALSE); |
---|
537 | } |
---|
538 | else |
---|
539 | { |
---|
540 | GError *err = NULL; |
---|
541 | |
---|
542 | gchar *result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, &err); |
---|
543 | if (result) |
---|
544 | return result; |
---|
545 | else |
---|
546 | { |
---|
547 | /* Not thread-safe, but doesn't matter if we print the warning twice |
---|
548 | */ |
---|
549 | static gboolean warned = FALSE; |
---|
550 | if (!warned) |
---|
551 | { |
---|
552 | warned = TRUE; |
---|
553 | _g_fprintf (stderr, "GLib: Cannot convert message: %s\n", err->message); |
---|
554 | } |
---|
555 | g_error_free (err); |
---|
556 | |
---|
557 | return g_strdup (string); |
---|
558 | } |
---|
559 | } |
---|
560 | } |
---|
561 | |
---|
562 | /* For a radix of 8 we need at most 3 output bytes for 1 input |
---|
563 | * byte. Additionally we might need up to 2 output bytes for the |
---|
564 | * readix prefix and 1 byte for the trailing NULL. |
---|
565 | */ |
---|
566 | #define FORMAT_UNSIGNED_BUFSIZE ((GLIB_SIZEOF_LONG * 3) + 3) |
---|
567 | |
---|
568 | static void |
---|
569 | format_unsigned (gchar *buf, |
---|
570 | gulong num, |
---|
571 | guint radix) |
---|
572 | { |
---|
573 | gulong tmp; |
---|
574 | gchar c; |
---|
575 | gint i, n; |
---|
576 | |
---|
577 | /* we may not call _any_ GLib functions here (or macros like g_return_if_fail()) */ |
---|
578 | |
---|
579 | if (radix != 8 && radix != 10 && radix != 16) |
---|
580 | { |
---|
581 | *buf = '\000'; |
---|
582 | return; |
---|
583 | } |
---|
584 | |
---|
585 | if (!num) |
---|
586 | { |
---|
587 | *buf++ = '0'; |
---|
588 | *buf = '\000'; |
---|
589 | return; |
---|
590 | } |
---|
591 | |
---|
592 | if (radix == 16) |
---|
593 | { |
---|
594 | *buf++ = '0'; |
---|
595 | *buf++ = 'x'; |
---|
596 | } |
---|
597 | else if (radix == 8) |
---|
598 | { |
---|
599 | *buf++ = '0'; |
---|
600 | } |
---|
601 | |
---|
602 | n = 0; |
---|
603 | tmp = num; |
---|
604 | while (tmp) |
---|
605 | { |
---|
606 | tmp /= radix; |
---|
607 | n++; |
---|
608 | } |
---|
609 | |
---|
610 | i = n; |
---|
611 | |
---|
612 | /* Again we can't use g_assert; actually this check should _never_ fail. */ |
---|
613 | if (n > FORMAT_UNSIGNED_BUFSIZE - 3) |
---|
614 | { |
---|
615 | *buf = '\000'; |
---|
616 | return; |
---|
617 | } |
---|
618 | |
---|
619 | while (num) |
---|
620 | { |
---|
621 | i--; |
---|
622 | c = (num % radix); |
---|
623 | if (c < 10) |
---|
624 | buf[i] = c + '0'; |
---|
625 | else |
---|
626 | buf[i] = c + 'a' - 10; |
---|
627 | num /= radix; |
---|
628 | } |
---|
629 | |
---|
630 | buf[n] = '\000'; |
---|
631 | } |
---|
632 | |
---|
633 | /* string size big enough to hold level prefix */ |
---|
634 | #define STRING_BUFFER_SIZE (FORMAT_UNSIGNED_BUFSIZE + 32) |
---|
635 | |
---|
636 | #define ALERT_LEVELS (G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING) |
---|
637 | |
---|
638 | static int |
---|
639 | mklevel_prefix (gchar level_prefix[STRING_BUFFER_SIZE], |
---|
640 | guint log_level) |
---|
641 | { |
---|
642 | gboolean to_stdout = TRUE; |
---|
643 | |
---|
644 | /* we may not call _any_ GLib functions here */ |
---|
645 | |
---|
646 | switch (log_level & G_LOG_LEVEL_MASK) |
---|
647 | { |
---|
648 | case G_LOG_LEVEL_ERROR: |
---|
649 | strcpy (level_prefix, "ERROR"); |
---|
650 | to_stdout = FALSE; |
---|
651 | break; |
---|
652 | case G_LOG_LEVEL_CRITICAL: |
---|
653 | strcpy (level_prefix, "CRITICAL"); |
---|
654 | to_stdout = FALSE; |
---|
655 | break; |
---|
656 | case G_LOG_LEVEL_WARNING: |
---|
657 | strcpy (level_prefix, "WARNING"); |
---|
658 | to_stdout = FALSE; |
---|
659 | break; |
---|
660 | case G_LOG_LEVEL_MESSAGE: |
---|
661 | strcpy (level_prefix, "Message"); |
---|
662 | to_stdout = FALSE; |
---|
663 | break; |
---|
664 | case G_LOG_LEVEL_INFO: |
---|
665 | strcpy (level_prefix, "INFO"); |
---|
666 | break; |
---|
667 | case G_LOG_LEVEL_DEBUG: |
---|
668 | strcpy (level_prefix, "DEBUG"); |
---|
669 | break; |
---|
670 | default: |
---|
671 | if (log_level) |
---|
672 | { |
---|
673 | strcpy (level_prefix, "LOG-"); |
---|
674 | format_unsigned (level_prefix + 4, log_level & G_LOG_LEVEL_MASK, 16); |
---|
675 | } |
---|
676 | else |
---|
677 | strcpy (level_prefix, "LOG"); |
---|
678 | break; |
---|
679 | } |
---|
680 | if (log_level & G_LOG_FLAG_RECURSION) |
---|
681 | strcat (level_prefix, " (recursed)"); |
---|
682 | if (log_level & ALERT_LEVELS) |
---|
683 | strcat (level_prefix, " **"); |
---|
684 | |
---|
685 | #ifdef G_OS_WIN32 |
---|
686 | win32_keep_fatal_message = (log_level & G_LOG_FLAG_FATAL) != 0; |
---|
687 | #endif |
---|
688 | return to_stdout ? 1 : 2; |
---|
689 | } |
---|
690 | |
---|
691 | void |
---|
692 | _g_log_fallback_handler (const gchar *log_domain, |
---|
693 | GLogLevelFlags log_level, |
---|
694 | const gchar *message, |
---|
695 | gpointer unused_data) |
---|
696 | { |
---|
697 | gchar level_prefix[STRING_BUFFER_SIZE]; |
---|
698 | #ifndef G_OS_WIN32 |
---|
699 | gchar pid_string[FORMAT_UNSIGNED_BUFSIZE]; |
---|
700 | #endif |
---|
701 | gboolean is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0; |
---|
702 | int fd; |
---|
703 | |
---|
704 | /* we can not call _any_ GLib functions in this fallback handler, |
---|
705 | * which is why we skip UTF-8 conversion, etc. |
---|
706 | * since we either recursed or ran out of memory, we're in a pretty |
---|
707 | * pathologic situation anyways, what we can do is giving the |
---|
708 | * the process ID unconditionally however. |
---|
709 | */ |
---|
710 | |
---|
711 | fd = mklevel_prefix (level_prefix, log_level); |
---|
712 | if (!message) |
---|
713 | message = "(NULL) message"; |
---|
714 | |
---|
715 | #ifndef G_OS_WIN32 |
---|
716 | format_unsigned (pid_string, getpid (), 10); |
---|
717 | #endif |
---|
718 | |
---|
719 | if (log_domain) |
---|
720 | write_string (fd, "\n"); |
---|
721 | else |
---|
722 | write_string (fd, "\n** "); |
---|
723 | |
---|
724 | #ifndef G_OS_WIN32 |
---|
725 | write_string (fd, "(process:"); |
---|
726 | write_string (fd, pid_string); |
---|
727 | write_string (fd, "): "); |
---|
728 | #endif |
---|
729 | |
---|
730 | if (log_domain) |
---|
731 | { |
---|
732 | write_string (fd, log_domain); |
---|
733 | write_string (fd, "-"); |
---|
734 | } |
---|
735 | write_string (fd, level_prefix); |
---|
736 | write_string (fd, ": "); |
---|
737 | write_string (fd, message); |
---|
738 | if (is_fatal) |
---|
739 | write_string (fd, "\naborting...\n"); |
---|
740 | else |
---|
741 | write_string (fd, "\n"); |
---|
742 | } |
---|
743 | |
---|
744 | static void |
---|
745 | escape_string (GString *string) |
---|
746 | { |
---|
747 | const char *p = string->str; |
---|
748 | gunichar wc; |
---|
749 | |
---|
750 | while (p < string->str + string->len) |
---|
751 | { |
---|
752 | gboolean safe; |
---|
753 | |
---|
754 | wc = g_utf8_get_char_validated (p, -1); |
---|
755 | if (wc == (gunichar)-1 || wc == (gunichar)-2) |
---|
756 | { |
---|
757 | gchar *tmp; |
---|
758 | guint pos; |
---|
759 | |
---|
760 | pos = p - string->str; |
---|
761 | |
---|
762 | /* Emit invalid UTF-8 as hex escapes |
---|
763 | */ |
---|
764 | tmp = g_strdup_printf ("\\x%02x", (guint)(guchar)*p); |
---|
765 | g_string_erase (string, pos, 1); |
---|
766 | g_string_insert (string, pos, tmp); |
---|
767 | |
---|
768 | p = string->str + (pos + 4); /* Skip over escape sequence */ |
---|
769 | |
---|
770 | g_free (tmp); |
---|
771 | continue; |
---|
772 | } |
---|
773 | if (wc == '\r') |
---|
774 | { |
---|
775 | safe = *(p + 1) == '\n'; |
---|
776 | } |
---|
777 | else |
---|
778 | { |
---|
779 | safe = CHAR_IS_SAFE (wc); |
---|
780 | } |
---|
781 | |
---|
782 | if (!safe) |
---|
783 | { |
---|
784 | gchar *tmp; |
---|
785 | guint pos; |
---|
786 | |
---|
787 | pos = p - string->str; |
---|
788 | |
---|
789 | /* Largest char we escape is 0x0a, so we don't have to worry |
---|
790 | * about 8-digit \Uxxxxyyyy |
---|
791 | */ |
---|
792 | tmp = g_strdup_printf ("\\u%04x", wc); |
---|
793 | g_string_erase (string, pos, g_utf8_next_char (p) - p); |
---|
794 | g_string_insert (string, pos, tmp); |
---|
795 | g_free (tmp); |
---|
796 | |
---|
797 | p = string->str + (pos + 6); /* Skip over escape sequence */ |
---|
798 | } |
---|
799 | else |
---|
800 | p = g_utf8_next_char (p); |
---|
801 | } |
---|
802 | } |
---|
803 | |
---|
804 | void |
---|
805 | g_log_default_handler (const gchar *log_domain, |
---|
806 | GLogLevelFlags log_level, |
---|
807 | const gchar *message, |
---|
808 | gpointer unused_data) |
---|
809 | { |
---|
810 | gboolean is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0; |
---|
811 | gchar level_prefix[STRING_BUFFER_SIZE], *string; |
---|
812 | GString *gstring; |
---|
813 | int fd; |
---|
814 | |
---|
815 | /* we can be called externally with recursion for whatever reason */ |
---|
816 | if (log_level & G_LOG_FLAG_RECURSION) |
---|
817 | { |
---|
818 | _g_log_fallback_handler (log_domain, log_level, message, unused_data); |
---|
819 | return; |
---|
820 | } |
---|
821 | |
---|
822 | g_messages_prefixed_init (); |
---|
823 | |
---|
824 | fd = mklevel_prefix (level_prefix, log_level); |
---|
825 | |
---|
826 | gstring = g_string_new (NULL); |
---|
827 | if (log_level & ALERT_LEVELS) |
---|
828 | g_string_append (gstring, "\n"); |
---|
829 | if (!log_domain) |
---|
830 | g_string_append (gstring, "** "); |
---|
831 | |
---|
832 | if ((g_log_msg_prefix & log_level) == log_level) |
---|
833 | { |
---|
834 | const gchar *prg_name = g_get_prgname (); |
---|
835 | |
---|
836 | #ifdef G_OS_WIN32 |
---|
837 | if (prg_name) |
---|
838 | prg_name = g_strdup (prg_name); |
---|
839 | else |
---|
840 | { |
---|
841 | if (G_WIN32_HAVE_WIDECHAR_API ()) |
---|
842 | { |
---|
843 | wchar_t buf[MAX_PATH+1]; |
---|
844 | if (GetModuleFileNameW (GetModuleHandle (NULL), |
---|
845 | buf, G_N_ELEMENTS (buf)) > 0) |
---|
846 | { |
---|
847 | gchar *utf8_buf = g_utf16_to_utf8 (buf, -1, |
---|
848 | NULL, NULL, NULL); |
---|
849 | if (utf8_buf) |
---|
850 | { |
---|
851 | prg_name = g_path_get_basename (utf8_buf); |
---|
852 | g_free (utf8_buf); |
---|
853 | } |
---|
854 | } |
---|
855 | } |
---|
856 | else |
---|
857 | { |
---|
858 | gchar buf[MAX_PATH+1]; |
---|
859 | if (GetModuleFileNameA (GetModuleHandle (NULL), |
---|
860 | buf, G_N_ELEMENTS (buf)) > 0) |
---|
861 | { |
---|
862 | gchar *locale_buf = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); |
---|
863 | if (locale_buf) |
---|
864 | { |
---|
865 | prg_name = g_path_get_basename (locale_buf); |
---|
866 | g_free (locale_buf); |
---|
867 | } |
---|
868 | } |
---|
869 | } |
---|
870 | } |
---|
871 | |
---|
872 | if (prg_name) |
---|
873 | { |
---|
874 | g_string_append_printf (gstring, "(%s): ", prg_name); |
---|
875 | g_free ((gchar *) prg_name); |
---|
876 | } |
---|
877 | #else |
---|
878 | if (!prg_name) |
---|
879 | g_string_append_printf (gstring, "(process:%lu): ", (gulong)getpid ()); |
---|
880 | else |
---|
881 | g_string_append_printf (gstring, "(%s:%lu): ", prg_name, (gulong)getpid ()); |
---|
882 | #endif |
---|
883 | } |
---|
884 | |
---|
885 | if (log_domain) |
---|
886 | { |
---|
887 | g_string_append (gstring, log_domain); |
---|
888 | g_string_append_c (gstring, '-'); |
---|
889 | } |
---|
890 | g_string_append (gstring, level_prefix); |
---|
891 | |
---|
892 | g_string_append (gstring, ": "); |
---|
893 | if (!message) |
---|
894 | g_string_append (gstring, "(NULL) message"); |
---|
895 | else |
---|
896 | { |
---|
897 | GString *msg; |
---|
898 | const gchar *charset; |
---|
899 | |
---|
900 | msg = g_string_new (message); |
---|
901 | escape_string (msg); |
---|
902 | |
---|
903 | if (g_get_charset (&charset)) |
---|
904 | g_string_append (gstring, msg->str); /* charset is UTF-8 already */ |
---|
905 | else |
---|
906 | { |
---|
907 | string = strdup_convert (msg->str, charset); |
---|
908 | g_string_append (gstring, string); |
---|
909 | g_free (string); |
---|
910 | } |
---|
911 | |
---|
912 | g_string_free (msg, TRUE); |
---|
913 | } |
---|
914 | if (is_fatal) |
---|
915 | g_string_append (gstring, "\naborting...\n"); |
---|
916 | else |
---|
917 | g_string_append (gstring, "\n"); |
---|
918 | |
---|
919 | string = g_string_free (gstring, FALSE); |
---|
920 | |
---|
921 | write_string (fd, string); |
---|
922 | g_free (string); |
---|
923 | } |
---|
924 | |
---|
925 | GPrintFunc |
---|
926 | g_set_print_handler (GPrintFunc func) |
---|
927 | { |
---|
928 | GPrintFunc old_print_func; |
---|
929 | |
---|
930 | g_mutex_lock (g_messages_lock); |
---|
931 | old_print_func = glib_print_func; |
---|
932 | glib_print_func = func; |
---|
933 | g_mutex_unlock (g_messages_lock); |
---|
934 | |
---|
935 | return old_print_func; |
---|
936 | } |
---|
937 | |
---|
938 | void |
---|
939 | g_print (const gchar *format, |
---|
940 | ...) |
---|
941 | { |
---|
942 | va_list args; |
---|
943 | gchar *string; |
---|
944 | GPrintFunc local_glib_print_func; |
---|
945 | |
---|
946 | g_return_if_fail (format != NULL); |
---|
947 | |
---|
948 | va_start (args, format); |
---|
949 | string = g_strdup_vprintf (format, args); |
---|
950 | va_end (args); |
---|
951 | |
---|
952 | g_mutex_lock (g_messages_lock); |
---|
953 | local_glib_print_func = glib_print_func; |
---|
954 | g_mutex_unlock (g_messages_lock); |
---|
955 | |
---|
956 | if (local_glib_print_func) |
---|
957 | local_glib_print_func (string); |
---|
958 | else |
---|
959 | { |
---|
960 | const gchar *charset; |
---|
961 | |
---|
962 | if (g_get_charset (&charset)) |
---|
963 | fputs (string, stdout); /* charset is UTF-8 already */ |
---|
964 | else |
---|
965 | { |
---|
966 | gchar *lstring = strdup_convert (string, charset); |
---|
967 | |
---|
968 | fputs (lstring, stdout); |
---|
969 | g_free (lstring); |
---|
970 | } |
---|
971 | fflush (stdout); |
---|
972 | } |
---|
973 | g_free (string); |
---|
974 | } |
---|
975 | |
---|
976 | GPrintFunc |
---|
977 | g_set_printerr_handler (GPrintFunc func) |
---|
978 | { |
---|
979 | GPrintFunc old_printerr_func; |
---|
980 | |
---|
981 | g_mutex_lock (g_messages_lock); |
---|
982 | old_printerr_func = glib_printerr_func; |
---|
983 | glib_printerr_func = func; |
---|
984 | g_mutex_unlock (g_messages_lock); |
---|
985 | |
---|
986 | return old_printerr_func; |
---|
987 | } |
---|
988 | |
---|
989 | void |
---|
990 | g_printerr (const gchar *format, |
---|
991 | ...) |
---|
992 | { |
---|
993 | va_list args; |
---|
994 | gchar *string; |
---|
995 | GPrintFunc local_glib_printerr_func; |
---|
996 | |
---|
997 | g_return_if_fail (format != NULL); |
---|
998 | |
---|
999 | va_start (args, format); |
---|
1000 | string = g_strdup_vprintf (format, args); |
---|
1001 | va_end (args); |
---|
1002 | |
---|
1003 | g_mutex_lock (g_messages_lock); |
---|
1004 | local_glib_printerr_func = glib_printerr_func; |
---|
1005 | g_mutex_unlock (g_messages_lock); |
---|
1006 | |
---|
1007 | if (local_glib_printerr_func) |
---|
1008 | local_glib_printerr_func (string); |
---|
1009 | else |
---|
1010 | { |
---|
1011 | const gchar *charset; |
---|
1012 | |
---|
1013 | if (g_get_charset (&charset)) |
---|
1014 | fputs (string, stderr); /* charset is UTF-8 already */ |
---|
1015 | else |
---|
1016 | { |
---|
1017 | gchar *lstring = strdup_convert (string, charset); |
---|
1018 | |
---|
1019 | fputs (lstring, stderr); |
---|
1020 | g_free (lstring); |
---|
1021 | } |
---|
1022 | fflush (stderr); |
---|
1023 | } |
---|
1024 | g_free (string); |
---|
1025 | } |
---|
1026 | |
---|
1027 | gsize |
---|
1028 | g_printf_string_upper_bound (const gchar *format, |
---|
1029 | va_list args) |
---|
1030 | { |
---|
1031 | gchar c; |
---|
1032 | return _g_vsnprintf (&c, 1, format, args) + 1; |
---|
1033 | } |
---|
1034 | |
---|
1035 | void |
---|
1036 | _g_messages_thread_init (void) |
---|
1037 | { |
---|
1038 | g_messages_lock = g_mutex_new (); |
---|
1039 | g_messages_prefixed_init (); |
---|
1040 | _g_debug_init (); |
---|
1041 | } |
---|
1042 | |
---|
1043 | void |
---|
1044 | _g_messages_thread_private_init (void) |
---|
1045 | { |
---|
1046 | g_assert (g_log_depth == NULL); |
---|
1047 | g_log_depth = g_private_new (NULL); |
---|
1048 | } |
---|
1049 | |
---|
1050 | gboolean _g_debug_initialized = FALSE; |
---|
1051 | guint _g_debug_flags = 0; |
---|
1052 | |
---|
1053 | void |
---|
1054 | _g_debug_init (void) |
---|
1055 | { |
---|
1056 | const gchar *val; |
---|
1057 | |
---|
1058 | _g_debug_initialized = TRUE; |
---|
1059 | |
---|
1060 | val = g_getenv ("G_DEBUG"); |
---|
1061 | if (val != NULL) |
---|
1062 | { |
---|
1063 | static const GDebugKey keys[] = { |
---|
1064 | {"fatal_warnings", G_DEBUG_FATAL_WARNINGS} |
---|
1065 | }; |
---|
1066 | |
---|
1067 | _g_debug_flags = g_parse_debug_string (val, keys, G_N_ELEMENTS (keys)); |
---|
1068 | } |
---|
1069 | |
---|
1070 | if (_g_debug_flags & G_DEBUG_FATAL_WARNINGS) |
---|
1071 | { |
---|
1072 | GLogLevelFlags fatal_mask; |
---|
1073 | |
---|
1074 | fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); |
---|
1075 | fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; |
---|
1076 | g_log_set_always_fatal (fatal_mask); |
---|
1077 | } |
---|
1078 | } |
---|