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