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