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