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