Ok, I'm a moron. When I originally implemented ENABLE_GC_FRIENDLY, I
[platform/upstream/glib.git] / glib / gmessages.c
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 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include "glib.h"
40 #ifdef HAVE_UNISTD_H
41 #include <unistd.h>
42 #endif
43 #include <signal.h>
44 #include <locale.h>
45 #include <errno.h>
46
47
48 /* --- structures --- */
49 typedef struct _GLogDomain      GLogDomain;
50 typedef struct _GLogHandler     GLogHandler;
51 struct _GLogDomain
52 {
53   gchar         *log_domain;
54   GLogLevelFlags fatal_mask;
55   GLogHandler   *handlers;
56   GLogDomain    *next;
57 };
58 struct _GLogHandler
59 {
60   guint          id;
61   GLogLevelFlags log_level;
62   GLogFunc       log_func;
63   gpointer       data;
64   GLogHandler   *next;
65 };
66
67
68 /* --- prototypes --- */
69 static inline guint printf_string_upper_bound (const gchar *format,
70                                                gboolean     may_warn,
71                                                va_list      args);
72
73
74 /* --- variables --- */
75
76 static GMutex* g_messages_lock = NULL;
77
78 const gchar          *g_log_domain_glib = "GLib";
79 static GLogDomain    *g_log_domains = NULL;
80 static GLogLevelFlags g_log_always_fatal = G_LOG_FATAL_MASK;
81 static GPrintFunc     glib_print_func = NULL;
82 static GPrintFunc     glib_printerr_func = NULL;
83 static GErrorFunc     glib_error_func = NULL;
84 static GWarningFunc   glib_warning_func = NULL;
85 static GPrintFunc     glib_message_func = NULL;
86
87 static GPrivate* g_log_depth = NULL;
88
89
90 /* --- functions --- */
91 #ifdef G_OS_WIN32
92 #  define STRICT
93 #  include <windows.h>
94 #  include <process.h>          /* For _getpid() */
95
96 static gboolean alloc_console_called = FALSE;
97
98 /* Just use stdio. If we're out of memory, we're hosed anyway. */
99 #undef write
100 static inline int
101 dowrite (FILE        *fd,
102          const void  *buf,
103          unsigned int len)
104 {
105   fwrite (buf, len, 1, fd);
106   fflush (fd);
107
108   return len;
109 }
110
111 #define write(fd, buf, len) dowrite(fd, buf, len)
112
113 static void
114 ensure_stdout_valid (void)
115 {
116   HANDLE handle;
117
118   if (!alloc_console_called)
119     {
120       handle = GetStdHandle (STD_OUTPUT_HANDLE);
121   
122       if (handle == INVALID_HANDLE_VALUE)
123         {
124           AllocConsole ();
125           alloc_console_called = TRUE;
126           freopen ("CONOUT$", "w", stdout);
127         }
128     }
129 }
130 #else
131 #define ensure_stdout_valid()   /* Define as empty */
132 #endif
133         
134 static inline GLogDomain*
135 g_log_find_domain (const gchar *log_domain)
136 {
137   register GLogDomain *domain;
138   
139   g_mutex_lock (g_messages_lock);
140   domain = g_log_domains;
141   while (domain)
142     {
143       if (strcmp (domain->log_domain, log_domain) == 0)
144         {
145           g_mutex_unlock (g_messages_lock);
146           return domain;
147         }
148       domain = domain->next;
149     }
150   g_mutex_unlock (g_messages_lock);
151   return NULL;
152 }
153
154 static inline GLogDomain*
155 g_log_domain_new (const gchar *log_domain)
156 {
157   register GLogDomain *domain;
158
159   domain = g_new (GLogDomain, 1);
160   domain->log_domain = g_strdup (log_domain);
161   domain->fatal_mask = G_LOG_FATAL_MASK;
162   domain->handlers = NULL;
163   
164   g_mutex_lock (g_messages_lock);
165   domain->next = g_log_domains;
166   g_log_domains = domain;
167   g_mutex_unlock (g_messages_lock);
168   
169   return domain;
170 }
171
172 static inline void
173 g_log_domain_check_free (GLogDomain *domain)
174 {
175   if (domain->fatal_mask == G_LOG_FATAL_MASK &&
176       domain->handlers == NULL)
177     {
178       register GLogDomain *last, *work;
179       
180       last = NULL;  
181
182       g_mutex_lock (g_messages_lock);
183       work = g_log_domains;
184       while (work)
185         {
186           if (work == domain)
187             {
188               if (last)
189                 last->next = domain->next;
190               else
191                 g_log_domains = domain->next;
192               g_free (domain->log_domain);
193               g_free (domain);
194               break;
195             }
196           last = work;
197           work = last->next;
198         }  
199       g_mutex_unlock (g_messages_lock);
200     }
201 }
202
203 static inline GLogFunc
204 g_log_domain_get_handler (GLogDomain    *domain,
205                           GLogLevelFlags log_level,
206                           gpointer      *data)
207 {
208   if (domain && log_level)
209     {
210       register GLogHandler *handler;
211       
212       handler = domain->handlers;
213       while (handler)
214         {
215           if ((handler->log_level & log_level) == log_level)
216             {
217               *data = handler->data;
218               return handler->log_func;
219             }
220           handler = handler->next;
221         }
222     }
223   return g_log_default_handler;
224 }
225
226 GLogLevelFlags
227 g_log_set_always_fatal (GLogLevelFlags fatal_mask)
228 {
229   GLogLevelFlags old_mask;
230
231   /* restrict the global mask to levels that are known to glib */
232   fatal_mask &= (1 << G_LOG_LEVEL_USER_SHIFT) - 1;
233   /* force errors to be fatal */
234   fatal_mask |= G_LOG_LEVEL_ERROR;
235   /* remove bogus flag */
236   fatal_mask &= ~G_LOG_FLAG_FATAL;
237
238   g_mutex_lock (g_messages_lock);
239   old_mask = g_log_always_fatal;
240   g_log_always_fatal = fatal_mask;
241   g_mutex_unlock (g_messages_lock);
242
243   return old_mask;
244 }
245
246 GLogLevelFlags
247 g_log_set_fatal_mask (const gchar    *log_domain,
248                       GLogLevelFlags  fatal_mask)
249 {
250   GLogLevelFlags old_flags;
251   register GLogDomain *domain;
252   
253   if (!log_domain)
254     log_domain = "";
255   
256   /* force errors to be fatal */
257   fatal_mask |= G_LOG_LEVEL_ERROR;
258   /* remove bogus flag */
259   fatal_mask &= ~G_LOG_FLAG_FATAL;
260   
261   domain = g_log_find_domain (log_domain);
262   if (!domain)
263     domain = g_log_domain_new (log_domain);
264   old_flags = domain->fatal_mask;
265   
266   domain->fatal_mask = fatal_mask;
267   g_log_domain_check_free (domain);
268   
269   return old_flags;
270 }
271
272 guint
273 g_log_set_handler (const gchar    *log_domain,
274                    GLogLevelFlags  log_levels,
275                    GLogFunc        log_func,
276                    gpointer        user_data)
277 {
278   register GLogDomain *domain;
279   register GLogHandler *handler;
280   static guint handler_id = 0;
281   
282   g_return_val_if_fail ((log_levels & G_LOG_LEVEL_MASK) != 0, 0);
283   g_return_val_if_fail (log_func != NULL, 0);
284   
285   if (!log_domain)
286     log_domain = "";
287   
288   domain = g_log_find_domain (log_domain);
289   if (!domain)
290     domain = g_log_domain_new (log_domain);
291   
292   handler = g_new (GLogHandler, 1);
293   g_mutex_lock (g_messages_lock);
294   handler->id = ++handler_id;
295   g_mutex_unlock (g_messages_lock);
296   handler->log_level = log_levels;
297   handler->log_func = log_func;
298   handler->data = user_data;
299   handler->next = domain->handlers;
300   domain->handlers = handler;
301   
302   return handler_id;
303 }
304
305 void
306 g_log_remove_handler (const gchar *log_domain,
307                       guint        handler_id)
308 {
309   register GLogDomain *domain;
310   
311   g_return_if_fail (handler_id > 0);
312   
313   if (!log_domain)
314     log_domain = "";
315   
316   domain = g_log_find_domain (log_domain);
317   if (domain)
318     {
319       register GLogHandler *work, *last;
320       
321       last = NULL;
322       work = domain->handlers;
323       while (work)
324         {
325           if (work->id == handler_id)
326             {
327               if (last)
328                 last->next = work->next;
329               else
330                 domain->handlers = work->next;
331               g_free (work);
332               g_log_domain_check_free (domain);
333               return;
334             }
335           last = work;
336           work = last->next;
337         }
338     }
339   g_warning ("g_log_remove_handler(): could not find handler with id `%d' for domain \"%s\"",
340              handler_id,
341              log_domain);
342 }
343
344 void
345 g_logv (const gchar   *log_domain,
346         GLogLevelFlags log_level,
347         const gchar   *format,
348         va_list        args1)
349 {
350   va_list args2;
351   gchar buffer[1025];
352   register gint i;
353   
354   log_level &= G_LOG_LEVEL_MASK;
355   if (!log_level)
356     return;
357   
358   /* we use a stack buffer of fixed size, because we might get called
359    * recursively.
360    */
361   G_VA_COPY (args2, args1);
362   if (printf_string_upper_bound (format, FALSE, args1) < 1024)
363     vsprintf (buffer, format, args2);
364   else
365     {
366       /* since we might be out of memory, we can't use g_vsnprintf(). */
367 #ifdef  HAVE_VSNPRINTF
368       vsnprintf (buffer, 1024, format, args2);
369 #else   /* !HAVE_VSNPRINTF */
370       /* we are out of luck here */
371       strncpy (buffer, format, 1024);
372 #endif  /* !HAVE_VSNPRINTF */
373       buffer[1024] = 0;
374     }
375   va_end (args2);
376   
377   for (i = g_bit_nth_msf (log_level, -1); i >= 0; i = g_bit_nth_msf (log_level, i))
378     {
379       register GLogLevelFlags test_level;
380       
381       test_level = 1 << i;
382       if (log_level & test_level)
383         {
384           guint depth = GPOINTER_TO_UINT (g_private_get (g_log_depth));
385           GLogDomain *domain;
386           GLogFunc log_func;
387           gpointer data = NULL;
388           
389           domain = g_log_find_domain (log_domain ? log_domain : "");
390           
391           if (depth)
392             test_level |= G_LOG_FLAG_RECURSION;
393           
394           depth++;
395           g_private_set (g_log_depth, GUINT_TO_POINTER (depth));
396
397           g_mutex_lock (g_messages_lock);
398           if ((((domain ? domain->fatal_mask : G_LOG_FATAL_MASK) | 
399                 g_log_always_fatal) & test_level) != 0)
400             test_level |= G_LOG_FLAG_FATAL;  
401           g_mutex_unlock (g_messages_lock);
402
403           log_func = g_log_domain_get_handler (domain, test_level, &data);
404           log_func (log_domain, test_level, buffer, data);
405           
406           /* *domain can be cluttered now */
407           
408           if (test_level & G_LOG_FLAG_FATAL)
409             {
410 #if defined (G_ENABLE_DEBUG) && defined (SIGTRAP)
411               if (!(test_level & G_LOG_FLAG_RECURSION))
412                 raise (SIGTRAP);
413               else
414                 abort ();
415 #else /* !G_ENABLE_DEBUG || !SIGTRAP */
416               abort ();
417 #endif /* !G_ENABLE_DEBUG || !SIGTRAP */
418             }
419           
420           depth--;
421           g_private_set (g_log_depth, GUINT_TO_POINTER (depth));
422         }
423     }
424 }
425
426 void
427 g_log (const gchar    *log_domain,
428        GLogLevelFlags  log_level,
429        const gchar    *format,
430        ...)
431 {
432   va_list args;
433   
434   va_start (args, format);
435   g_logv (log_domain, log_level, format, args);
436   va_end (args);
437 }
438
439 void
440 g_log_default_handler (const gchar    *log_domain,
441                        GLogLevelFlags  log_level,
442                        const gchar    *message,
443                        gpointer        unused_data)
444 {
445 #ifdef G_OS_WIN32
446   FILE *fd;
447 #else
448   gint fd;
449 #endif
450   gboolean in_recursion;
451   gboolean is_fatal;  
452   GErrorFunc local_glib_error_func;
453   GWarningFunc local_glib_warning_func;
454   GPrintFunc local_glib_message_func;
455   gchar prg_pid[64], *prg_name = g_get_prgname ();
456
457   in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
458   is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
459   log_level &= G_LOG_LEVEL_MASK;
460   
461   if (!message)
462     message = "g_log_default_handler(): (NULL) message";
463   if (!prg_name)
464     {
465       prg_name = "(process";
466       sprintf (prg_pid, ":%u): ", getpid ());
467     }
468   else
469     sprintf (prg_pid, " (pid:%u): ", getpid ());
470   
471 #ifdef G_OS_WIN32
472   /* Use just stdout as stderr is hard to get redirected from the
473    * DOS prompt.
474    */
475   fd = stdout;
476 #else
477   fd = (log_level > G_LOG_LEVEL_MESSAGE) ? 1 : 2;
478 #endif
479   
480   g_mutex_lock (g_messages_lock);
481   local_glib_error_func = glib_error_func;
482   local_glib_warning_func = glib_warning_func;
483   local_glib_message_func = glib_message_func;
484   g_mutex_unlock (g_messages_lock);
485
486   switch (log_level)
487     {
488     case G_LOG_LEVEL_ERROR:
489       if (!log_domain && local_glib_error_func)
490         {
491           /* compatibility code */
492           local_glib_error_func (message);  
493           return;
494         }
495       /* use write(2) for output, in case we are out of memeory */
496       ensure_stdout_valid ();
497       write (fd, "\n", 1);
498 #ifdef G_ENABLE_MSG_PREFIX
499       write (fd, prg_name, strlen (prg_name));
500       write (fd, prg_pid, strlen (prg_pid));
501 #endif /* G_ENABLE_MSG_PREFIX */
502       if (log_domain)
503         {
504           write (fd, log_domain, strlen (log_domain));
505           write (fd, "-", 1);
506         }
507       else
508         write (fd, "** ", 3);
509       if (in_recursion)
510         write (fd, "ERROR (recursed) **: ", 21);
511       else
512         write (fd, "ERROR **: ", 10);
513       write (fd, message, strlen (message));
514       if (is_fatal)
515         write (fd, "\naborting...\n", 13);
516       else
517         write (fd, "\n", 1);
518       break;
519     case G_LOG_LEVEL_CRITICAL:
520       ensure_stdout_valid ();
521       write (fd, "\n", 1);
522 #ifdef G_ENABLE_MSG_PREFIX
523       write (fd, prg_name, strlen (prg_name));
524       write (fd, prg_pid, strlen (prg_pid));
525 #endif /* G_ENABLE_MSG_PREFIX */
526       if (log_domain)
527         {
528           write (fd, log_domain, strlen (log_domain));
529           write (fd, "-", 1);
530         }
531       else
532         write (fd, "** ", 3);
533       if (in_recursion)
534         write (fd, "CRITICAL (recursed) **: ", 24);
535       else
536         write (fd, "CRITICAL **: ", 13);
537       write (fd, message, strlen (message));
538       if (is_fatal)
539         write (fd, "\naborting...\n", 13);
540       else
541         write (fd, "\n", 1);
542       break;
543     case G_LOG_LEVEL_WARNING:
544       if (!log_domain && local_glib_warning_func)
545         {
546           /* compatibility code */
547           local_glib_warning_func (message);
548           return;
549         }
550       ensure_stdout_valid ();
551       write (fd, "\n", 1);
552 #ifdef G_ENABLE_MSG_PREFIX
553       write (fd, prg_name, strlen (prg_name));
554       write (fd, prg_pid, strlen (prg_pid));
555 #endif /* G_ENABLE_MSG_PREFIX */
556       if (log_domain)
557         {
558           write (fd, log_domain, strlen (log_domain));
559           write (fd, "-", 1);
560         }
561       else
562         write (fd, "** ", 3);
563       if (in_recursion)
564         write (fd, "WARNING (recursed) **: ", 23);
565       else
566         write (fd, "WARNING **: ", 12);
567       write (fd, message, strlen (message));
568       if (is_fatal)
569         write (fd, "\naborting...\n", 13);
570       else
571         write (fd, "\n", 1);
572       break;
573     case G_LOG_LEVEL_MESSAGE:
574       if (!log_domain && local_glib_message_func)
575         {
576           /* compatibility code */
577           local_glib_message_func (message);
578           return;
579         }
580       ensure_stdout_valid ();
581 #ifdef G_ENABLE_MSG_PREFIX
582       write (fd, prg_name, strlen (prg_name));
583       write (fd, prg_pid, strlen (prg_pid));
584 #endif /* G_ENABLE_MSG_PREFIX */
585       if (log_domain)
586         {
587           write (fd, log_domain, strlen (log_domain));
588           write (fd, "-", 1);
589         }
590       if (in_recursion)
591         write (fd, "Message (recursed): ", 20);
592       else
593         write (fd, "Message: ", 9);
594       write (fd, message, strlen (message));
595       if (is_fatal)
596         write (fd, "\naborting...\n", 13);
597       else
598         write (fd, "\n", 1);
599       break;
600     case G_LOG_LEVEL_INFO:
601       ensure_stdout_valid ();
602 #ifdef G_ENABLE_MSG_PREFIX
603       write (fd, prg_name, strlen (prg_name));
604       write (fd, prg_pid, strlen (prg_pid));
605 #endif /* G_ENABLE_MSG_PREFIX */
606       if (log_domain)
607         {
608           write (fd, log_domain, strlen (log_domain));
609           write (fd, "-", 1);
610         }
611       if (in_recursion)
612         write (fd, "INFO (recursed): ", 17);
613       else
614         write (fd, "INFO: ", 6);
615       write (fd, message, strlen (message));
616       if (is_fatal)
617         write (fd, "\naborting...\n", 13);
618       else
619         write (fd, "\n", 1);
620       break;
621     case G_LOG_LEVEL_DEBUG:
622       ensure_stdout_valid ();
623 #ifdef G_ENABLE_MSG_PREFIX
624       write (fd, prg_name, strlen (prg_name));
625       write (fd, prg_pid, strlen (prg_pid));
626 #endif /* G_ENABLE_MSG_PREFIX */
627       if (log_domain)
628         {
629           write (fd, log_domain, strlen (log_domain));
630           write (fd, "-", 1);
631         }
632       if (in_recursion)
633         write (fd, "DEBUG (recursed): ", 18);
634       else
635         write (fd, "DEBUG: ", 7);
636       write (fd, message, strlen (message));
637       if (is_fatal)
638         write (fd, "\naborting...\n", 13);
639       else
640         write (fd, "\n", 1);
641       break;
642     default:
643       /* we are used for a log level that is not defined by GLib itself,
644        * try to make the best out of it.
645        */
646       ensure_stdout_valid ();
647 #ifdef G_ENABLE_MSG_PREFIX
648       write (fd, prg_name, strlen (prg_name));
649       write (fd, prg_pid, strlen (prg_pid));
650 #endif /* G_ENABLE_MSG_PREFIX */
651       if (log_domain)
652         {
653           write (fd, log_domain, strlen (log_domain));
654           if (in_recursion)
655             write (fd, "-LOG (recursed:", 15);
656           else
657             write (fd, "-LOG (", 6);
658         }
659       else if (in_recursion)
660         write (fd, "LOG (recursed:", 14);
661       else
662         write (fd, "LOG (", 5);
663       if (log_level)
664         {
665           gchar string[] = "0x00): ";
666           gchar *p = string + 2;
667           guint i;
668           
669           i = g_bit_nth_msf (log_level, -1);
670           *p = i >> 4;
671           p++;
672           *p = '0' + (i & 0xf);
673           if (*p > '9')
674             *p += 'A' - '9' - 1;
675           
676           write (fd, string, 7);
677         }
678       else
679         write (fd, "): ", 3);
680       write (fd, message, strlen (message));
681       if (is_fatal)
682         write (fd, "\naborting...\n", 13);
683       else
684         write (fd, "\n", 1);
685       break;
686     }
687 }
688
689 GPrintFunc
690 g_set_print_handler (GPrintFunc func)
691 {
692   GPrintFunc old_print_func;
693   
694   g_mutex_lock (g_messages_lock);
695   old_print_func = glib_print_func;
696   glib_print_func = func;
697   g_mutex_unlock (g_messages_lock);
698   
699   return old_print_func;
700 }
701
702 void
703 g_print (const gchar *format,
704          ...)
705 {
706   va_list args;
707   gchar *string;
708   GPrintFunc local_glib_print_func;
709   
710   g_return_if_fail (format != NULL);
711   
712   va_start (args, format);
713   string = g_strdup_vprintf (format, args);
714   va_end (args);
715   
716   g_mutex_lock (g_messages_lock);
717   local_glib_print_func = glib_print_func;
718   g_mutex_unlock (g_messages_lock);
719
720   if (local_glib_print_func)
721     local_glib_print_func (string);
722   else
723     {
724       ensure_stdout_valid ();
725       fputs (string, stdout);
726       fflush (stdout);
727     }
728   g_free (string);
729 }
730
731 GPrintFunc
732 g_set_printerr_handler (GPrintFunc func)
733 {
734   GPrintFunc old_printerr_func;
735   
736   g_mutex_lock (g_messages_lock);
737   old_printerr_func = glib_printerr_func;
738   glib_printerr_func = func;
739   g_mutex_unlock (g_messages_lock);
740   
741   return old_printerr_func;
742 }
743
744 void
745 g_printerr (const gchar *format,
746             ...)
747 {
748   va_list args;
749   gchar *string;
750   GPrintFunc local_glib_printerr_func;
751   
752   g_return_if_fail (format != NULL);
753   
754   va_start (args, format);
755   string = g_strdup_vprintf (format, args);
756   va_end (args);
757   
758   g_mutex_lock (g_messages_lock);
759   local_glib_printerr_func = glib_printerr_func;
760   g_mutex_unlock (g_messages_lock);
761
762   if (local_glib_printerr_func)
763     local_glib_printerr_func (string);
764   else
765     {
766       fputs (string, stderr);
767       fflush (stderr);
768     }
769   g_free (string);
770 }
771
772 /* compatibility code */
773 GErrorFunc
774 g_set_error_handler (GErrorFunc func)
775 {
776   GErrorFunc old_error_func;
777   
778   g_mutex_lock (g_messages_lock);
779   old_error_func = glib_error_func;
780   glib_error_func = func;
781   g_mutex_unlock (g_messages_lock);
782  
783   return old_error_func;
784 }
785
786 /* compatibility code */
787 GWarningFunc
788 g_set_warning_handler (GWarningFunc func)
789 {
790   GWarningFunc old_warning_func;
791   
792   g_mutex_lock (g_messages_lock);
793   old_warning_func = glib_warning_func;
794   glib_warning_func = func;
795   g_mutex_unlock (g_messages_lock);
796   
797   return old_warning_func;
798 }
799
800 /* compatibility code */
801 GPrintFunc
802 g_set_message_handler (GPrintFunc func)
803 {
804   GPrintFunc old_message_func;
805   
806   g_mutex_lock (g_messages_lock);
807   old_message_func = glib_message_func;
808   glib_message_func = func;
809   g_mutex_unlock (g_messages_lock);
810   
811   return old_message_func;
812 }
813
814 #ifndef MB_LEN_MAX
815 #  define MB_LEN_MAX 8
816 #endif
817
818 typedef struct
819 {
820   guint min_width;
821   guint precision;
822   gboolean alternate_format, zero_padding, adjust_left, locale_grouping;
823   gboolean add_space, add_sign, possible_sign, seen_precision;
824   gboolean mod_half, mod_long, mod_extra_long;
825 } PrintfArgSpec;
826
827 static inline guint
828 printf_string_upper_bound (const gchar *format,
829                            gboolean     may_warn,
830                            va_list      args)
831 {
832   static const gboolean honour_longs = SIZEOF_LONG > 4 || SIZEOF_VOID_P > 4;
833   guint len = 1;
834
835   if (!format)
836     return len;
837
838   while (*format)
839     {
840       register gchar c = *format++;
841
842       if (c != '%')
843         len += 1;
844       else /* (c == '%') */
845         {
846           PrintfArgSpec spec = { 0, };
847           gboolean seen_l = FALSE, conv_done = FALSE;
848           guint conv_len = 0;
849           const gchar *spec_start = format;
850
851           do
852             {
853               c = *format++;
854               switch (c)
855                 {
856                   GDoubleIEEE754 u_double;
857                   guint v_uint;
858                   gint v_int;
859                   gchar *v_string;
860
861                   /* beware of positional parameters
862                    */
863                 case '$':
864                   if (may_warn)
865                     g_warning (G_GNUC_PRETTY_FUNCTION
866                                "(): unable to handle positional parameters (%%n$)");
867                   len += 1024; /* try adding some safety padding */
868                   break;
869
870                   /* parse flags
871                    */
872                 case '#':
873                   spec.alternate_format = TRUE;
874                   break;
875                 case '0':
876                   spec.zero_padding = TRUE;
877                   break;
878                 case '-':
879                   spec.adjust_left = TRUE;
880                   break;
881                 case ' ':
882                   spec.add_space = TRUE;
883                   break;
884                 case '+':
885                   spec.add_sign = TRUE;
886                   break;
887                 case '\'':
888                   spec.locale_grouping = TRUE;
889                   break;
890
891                   /* parse output size specifications
892                    */
893                 case '.':
894                   spec.seen_precision = TRUE;
895                   break;
896                 case '1':
897                 case '2':
898                 case '3':
899                 case '4':
900                 case '5':
901                 case '6':
902                 case '7':
903                 case '8':
904                 case '9':
905                   v_uint = c - '0';
906                   c = *format;
907                   while (c >= '0' && c <= '9')
908                     {
909                       format++;
910                       v_uint = v_uint * 10 + c - '0';
911                       c = *format;
912                     }
913                   if (spec.seen_precision)
914                     spec.precision = MAX (spec.precision, v_uint);
915                   else
916                     spec.min_width = MAX (spec.min_width, v_uint);
917                   break;
918                 case '*':
919                   v_int = va_arg (args, int);
920                   if (spec.seen_precision)
921                     {
922                       /* forget about negative precision */
923                       if (v_int >= 0)
924                         spec.precision = MAX (spec.precision, v_int);
925                     }
926                   else
927                     {
928                       if (v_int < 0)
929                         {
930                           v_int = - v_int;
931                           spec.adjust_left = TRUE;
932                         }
933                       spec.min_width = MAX (spec.min_width, v_int);
934                     }
935                   break;
936
937                   /* parse type modifiers
938                    */
939                 case 'h':
940                   spec.mod_half = TRUE;
941                   break;
942                 case 'l':
943                   if (!seen_l)
944                     {
945                       spec.mod_long = TRUE;
946                       seen_l = TRUE;
947                       break;
948                     }
949                   /* else, fall through */
950                 case 'L':
951                 case 'q':
952                   spec.mod_long = TRUE;
953                   spec.mod_extra_long = TRUE;
954                   break;
955                 case 'z':
956                 case 'Z':
957 #if GLIB_SIZEOF_SIZE_T > 4
958                   spec.mod_long = TRUE;
959                   spec.mod_extra_long = TRUE;
960 #endif /* GLIB_SIZEOF_SIZE_T > 4 */
961                   break;
962                 case 't':
963 #if GLIB_SIZEOF_PTRDIFF_T > 4
964                   spec.mod_long = TRUE;
965                   spec.mod_extra_long = TRUE;
966 #endif /* GLIB_SIZEOF_PTRDIFF_T > 4 */
967                   break;
968                 case 'j':
969 #if GLIB_SIZEOF_INTMAX_T > 4
970                   spec.mod_long = TRUE;
971                   spec.mod_extra_long = TRUE;
972 #endif /* GLIB_SIZEOF_INTMAX_T > 4 */
973                   break;
974
975                   /* parse output conversions
976                    */
977                 case '%':
978                   conv_len += 1;
979                   break;
980                 case 'O':
981                 case 'D':
982                 case 'I':
983                 case 'U':
984                   /* some C libraries feature long variants for these as well? */
985                   spec.mod_long = TRUE;
986                   /* fall through */
987                 case 'o':
988                   conv_len += 2;
989                   /* fall through */
990                 case 'd':
991                 case 'i':
992                   conv_len += 1; /* sign */
993                   /* fall through */
994                 case 'u':
995                   conv_len += 4;
996                   /* fall through */
997                 case 'x':
998                 case 'X':
999                   spec.possible_sign = TRUE;
1000                   conv_len += 10;
1001                   if (spec.mod_long && honour_longs)
1002                     conv_len *= 2;
1003                   if (spec.mod_extra_long)
1004                     conv_len *= 2;
1005                   if (spec.mod_extra_long)
1006                     {
1007 #ifdef G_HAVE_GINT64
1008                       (void) va_arg (args, gint64);
1009 #else /* !G_HAVE_GINT64 */
1010                       (void) va_arg (args, long);
1011 #endif /* !G_HAVE_GINT64 */
1012                     }
1013                   else if (spec.mod_long)
1014                     (void) va_arg (args, long);
1015                   else
1016                     (void) va_arg (args, int);
1017                   break;
1018                 case 'A':
1019                 case 'a':
1020                   /*          0x */
1021                   conv_len += 2;
1022                   /* fall through */
1023                 case 'g':
1024                 case 'G':
1025                 case 'e':
1026                 case 'E':
1027                 case 'f':
1028                   spec.possible_sign = TRUE;
1029                   /*          n   .   dddddddddddddddddddddddd   E   +-  eeee */
1030                   conv_len += 1 + 1 + MAX (24, spec.precision) + 1 + 1 + 4;
1031                   if (may_warn && spec.mod_extra_long)
1032                     g_warning (G_GNUC_PRETTY_FUNCTION
1033                                "(): unable to handle long double, collecting double only");
1034 #ifdef HAVE_LONG_DOUBLE
1035 #error need to implement special handling for long double
1036 #endif
1037                   u_double.v_double = va_arg (args, double);
1038                   /* %f can expand up to all significant digits before '.' (308) */
1039                   if (c == 'f' &&
1040                       u_double.mpn.biased_exponent > 0 && u_double.mpn.biased_exponent < 2047)
1041                     {
1042                       gint exp = u_double.mpn.biased_exponent;
1043
1044                       exp -= G_IEEE754_DOUBLE_BIAS;
1045                       exp = exp * G_LOG_2_BASE_10 + 1;
1046                       conv_len += exp;
1047                     }
1048                   /* some printf() implementations require extra padding for rounding */
1049                   conv_len += 2;
1050                   /* we can't really handle locale specific grouping here */
1051                   if (spec.locale_grouping)
1052                     conv_len *= 2;
1053                   break;
1054                 case 'C':
1055                   spec.mod_long = TRUE;
1056                   /* fall through */
1057                 case 'c':
1058                   conv_len += spec.mod_long ? MB_LEN_MAX : 1;
1059                   (void) va_arg (args, int);
1060                   break;
1061                 case 'S':
1062                   spec.mod_long = TRUE;
1063                   /* fall through */
1064                 case 's':
1065                   v_string = va_arg (args, char*);
1066                   if (!v_string)
1067                     conv_len += 8; /* hold "(null)" */
1068                   else if (spec.seen_precision)
1069                     conv_len += spec.precision;
1070                   else
1071                     conv_len += strlen (v_string);
1072                   conv_done = TRUE;
1073                   if (spec.mod_long)
1074                     {
1075                       if (may_warn)
1076                         g_warning (G_GNUC_PRETTY_FUNCTION
1077                                    "(): unable to handle wide char strings");
1078                       len += 1024; /* try adding some safety padding */
1079                     }
1080                   break;
1081                 case 'P': /* do we actually need this? */
1082                   /* fall through */
1083                 case 'p':
1084                   spec.alternate_format = TRUE;
1085                   conv_len += 10;
1086                   if (honour_longs)
1087                     conv_len *= 2;
1088                   /* fall through */
1089                 case 'n':
1090                   conv_done = TRUE;
1091                   (void) va_arg (args, void*);
1092                   break;
1093                 case 'm':
1094                   /* there's not much we can do to be clever */
1095                   v_string = g_strerror (errno);
1096                   v_uint = v_string ? strlen (v_string) : 0;
1097                   conv_len += MAX (256, v_uint);
1098                   break;
1099
1100                   /* handle invalid cases
1101                    */
1102                 case '\000':
1103                   /* no conversion specification, bad bad */
1104                   conv_len += format - spec_start;
1105                   break;
1106                 default:
1107                   if (may_warn)
1108                     g_warning (G_GNUC_PRETTY_FUNCTION
1109                                "(): unable to handle `%c' while parsing format",
1110                                c);
1111                   break;
1112                 }
1113               conv_done |= conv_len > 0;
1114             }
1115           while (!conv_done);
1116           /* handle width specifications */
1117           conv_len = MAX (conv_len, MAX (spec.precision, spec.min_width));
1118           /* handle flags */
1119           conv_len += spec.alternate_format ? 2 : 0;
1120           conv_len += (spec.add_space || spec.add_sign || spec.possible_sign);
1121           /* finally done */
1122           len += conv_len;
1123         } /* else (c == '%') */
1124     } /* while (*format) */
1125
1126   return len;
1127 }
1128
1129 guint
1130 g_printf_string_upper_bound (const gchar *format,
1131                              va_list      args)
1132 {
1133   return printf_string_upper_bound (format, TRUE, args);
1134 }
1135
1136 void
1137 g_messages_init (void)
1138 {
1139   g_messages_lock = g_mutex_new();
1140   g_log_depth = g_private_new(NULL);
1141 }