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