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