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