summaryrefslogtreecommitdiff
path: root/gmessages.c
diff options
context:
space:
mode:
authorTim Janik <timj@gtk.org>1999-10-12 12:08:13 +0000
committerTim Janik <timj@src.gnome.org>1999-10-12 12:08:13 +0000
commit30a1e1addb131e3e200f91e9f93e3fc0709f3809 (patch)
treeba667ef80c93bc9da537f3a0bd7654f401b3c107 /gmessages.c
parentacbe34e51a2903d50ee99cb46552c360905322ea (diff)
downloadglib-30a1e1addb131e3e200f91e9f93e3fc0709f3809.tar.gz
completly new implementation for printf string upper bounds calculation.
Tue Oct 12 12:16:12 1999 Tim Janik <timj@gtk.org> * gmessages.c (g_printf_string_upper_bound): completly new implementation for printf string upper bounds calculation. we handle all glibc 2.1 format specifiers now, except for positional parameters (%nn$...) and wide char strings, plus some obscure upper case variants of the standard conversions. this fixes a lot of bugs in the old code, i.e. - NULL format strings - floats with exponents >+24 - %G - precision specifications in general - negative field widths - %p for SIZEOF_VOID_P > 4 platforms we now issue warnigns in places where the old code would have caused buffer overruns anyways. warnings are suppressed when invoked from glogv(), to avoid infinite recursions if someone passes a log message that comes with really obscure format specifications. Tue Oct 12 11:49:00 1999 Tim Janik <timj@gtk.org> * gstrfuncs.c: nuked old g_printf_string_upper_bound() version. Tue Oct 12 03:34:40 1999 Tim Janik <timj@gtk.org> * glib.h: added GFloatIEEE754 and GDoubleIEEE754 unions to access sign, mantissa and exponent of IEEE floats and doubles (required by the new version of g_printf_string_upper_bound). the unions are endian specific, we handle G_LITTLE_ENDIAN and G_BIG_ENDIAN as of currently. ieee floats and doubles are supported (used for storage) by at least intel, ppc and sparc, reference: http://twister.ou.edu/workshop.docs/common-tools/numerical_comp_guide/ncg_math.doc.html Mon Oct 11 18:01:49 1999 Tim Janik <timj@gtk.org> * configure.in: added additional checks to figure sizes of size_t, ptrdiff_t and intmax_t (required by g_printf_string_upper_bound).
Diffstat (limited to 'gmessages.c')
-rw-r--r--gmessages.c413
1 files changed, 370 insertions, 43 deletions
diff --git a/gmessages.c b/gmessages.c
index 4179f73f9..5f60218bf 100644
--- a/gmessages.c
+++ b/gmessages.c
@@ -41,42 +41,9 @@
#include <unistd.h>
#endif
#include <signal.h>
+#include <locale.h>
+#include <errno.h>
-#ifdef G_OS_WIN32
-# define STRICT
-# include <windows.h>
-# include <process.h> /* For _getpid() */
-
-/* Just use stdio. If we're out of memory, we're hosed anyway. */
-#undef write
-
-static inline int
-write (FILE *fd,
- const char *buf,
- int len)
-{
- fwrite (buf, len, 1, fd);
-
- return len;
-}
-
-static void
-ensure_stdout_valid (void)
-{
- HANDLE handle;
-
- handle = GetStdHandle (STD_OUTPUT_HANDLE);
-
- if (handle == INVALID_HANDLE_VALUE)
- {
- AllocConsole ();
- freopen ("CONOUT$", "w", stdout);
- }
-}
-#else
-#define ensure_stdout_valid() /* Define as empty */
-#endif
-
/* --- structures --- */
typedef struct _GLogDomain GLogDomain;
@@ -98,6 +65,12 @@ struct _GLogHandler
};
+/* --- prototypes --- */
+static inline guint printf_string_upper_bound (const gchar *format,
+ gboolean may_warn,
+ va_list args);
+
+
/* --- variables --- */
static GMutex* g_messages_lock = NULL;
@@ -115,8 +88,40 @@ static GPrivate* g_log_depth = NULL;
/* --- functions --- */
+#ifdef G_OS_WIN32
+# define STRICT
+# include <windows.h>
+# include <process.h> /* For _getpid() */
+/* Just use stdio. If we're out of memory, we're hosed anyway. */
+#undef write
+static inline int
+write (FILE *fd,
+ const char *buf,
+ int len)
+{
+ fwrite (buf, len, 1, fd);
+
+ return len;
+}
+static void
+ensure_stdout_valid (void)
+{
+ HANDLE handle;
+
+ handle = GetStdHandle (STD_OUTPUT_HANDLE);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ {
+ AllocConsole ();
+ freopen ("CONOUT$", "w", stdout);
+ }
+}
+#else
+#define ensure_stdout_valid() /* Define as empty */
+#endif
+
static inline GLogDomain*
-g_log_find_domain (const gchar *log_domain)
+g_log_find_domain (const gchar *log_domain)
{
register GLogDomain *domain;
@@ -286,8 +291,8 @@ g_log_set_handler (const gchar *log_domain,
}
void
-g_log_remove_handler (const gchar *log_domain,
- guint handler_id)
+g_log_remove_handler (const gchar *log_domain,
+ guint handler_id)
{
register GLogDomain *domain;
@@ -324,10 +329,10 @@ g_log_remove_handler (const gchar *log_domain,
}
void
-g_logv (const gchar *log_domain,
- GLogLevelFlags log_level,
- const gchar *format,
- va_list args1)
+g_logv (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *format,
+ va_list args1)
{
va_list args2;
gchar buffer[1025];
@@ -341,7 +346,7 @@ g_logv (const gchar *log_domain,
* recursively.
*/
G_VA_COPY (args2, args1);
- if (g_printf_string_upper_bound (format, args1) < 1024)
+ if (printf_string_upper_bound (format, FALSE, args1) < 1024)
vsprintf (buffer, format, args2);
else
{
@@ -793,6 +798,328 @@ g_set_message_handler (GPrintFunc func)
return old_message_func;
}
+#ifndef MB_LEN_MAX
+# define MB_LEN_MAX 8
+#endif
+
+typedef struct
+{
+ guint min_width;
+ guint precision;
+ gboolean alternate_format, zero_padding, adjust_left, locale_grouping;
+ gboolean add_space, add_sign, possible_sign, seen_precision;
+ gboolean mod_half, mod_long, mod_extra_long;
+} PrintfArgSpec;
+
+static inline guint
+printf_string_upper_bound (const gchar *format,
+ gboolean may_warn,
+ va_list args)
+{
+ static const gboolean honour_longs = SIZEOF_LONG > 4 || SIZEOF_VOID_P > 4;
+ guint len = 1;
+
+ if (!format)
+ return len;
+
+ while (*format)
+ {
+ register gchar c = *format++;
+
+ if (c != '%')
+ len += 1;
+ else /* (c == '%') */
+ {
+ PrintfArgSpec spec = { 0, };
+ gboolean seen_l = FALSE, conv_done = FALSE;
+ guint conv_len = 0;
+ const gchar *spec_start = format;
+
+ do
+ {
+ c = *format++;
+ switch (c)
+ {
+ GDoubleIEEE754 u_double;
+ guint v_uint;
+ gint v_int;
+ gchar *v_string;
+
+ /* beware of positional parameters
+ */
+ case '$':
+ if (may_warn)
+ g_warning (G_GNUC_PRETTY_FUNCTION
+ "(): unable to handle positional parameters (%%n$)");
+ len += 1024; /* try adding some safety padding */
+ break;
+
+ /* parse flags
+ */
+ case '#':
+ spec.alternate_format = TRUE;
+ break;
+ case '0':
+ spec.zero_padding = TRUE;
+ break;
+ case '-':
+ spec.adjust_left = TRUE;
+ break;
+ case ' ':
+ spec.add_space = TRUE;
+ break;
+ case '+':
+ spec.add_sign = TRUE;
+ break;
+ case '\'':
+ spec.locale_grouping = TRUE;
+ break;
+
+ /* parse output size specifications
+ */
+ case '.':
+ spec.seen_precision = TRUE;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ v_uint = c - '0';
+ c = *format;
+ while (c >= '0' && c <= '9')
+ {
+ format++;
+ v_uint = v_uint * 10 + c - '0';
+ c = *format;
+ }
+ if (spec.seen_precision)
+ spec.precision = MAX (spec.precision, v_uint);
+ else
+ spec.min_width = MAX (spec.min_width, v_uint);
+ break;
+ case '*':
+ v_int = va_arg (args, int);
+ if (spec.seen_precision)
+ {
+ /* forget about negative precision */
+ if (v_int >= 0)
+ spec.precision = MAX (spec.precision, v_int);
+ }
+ else
+ {
+ if (v_int < 0)
+ {
+ v_int = - v_int;
+ spec.adjust_left = TRUE;
+ }
+ spec.min_width = MAX (spec.min_width, v_int);
+ }
+ break;
+
+ /* parse type modifiers
+ */
+ case 'h':
+ spec.mod_half = TRUE;
+ break;
+ case 'l':
+ if (!seen_l)
+ {
+ spec.mod_long = TRUE;
+ seen_l = TRUE;
+ break;
+ }
+ /* else, fall through */
+ case 'L':
+ case 'q':
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+ break;
+ case 'z':
+ case 'Z':
+#if GLIB_SIZEOF_SIZE_T > 4
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+#endif /* GLIB_SIZEOF_SIZE_T > 4 */
+ break;
+ case 't':
+#if GLIB_SIZEOF_PTRDIFF_T > 4
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+#endif /* GLIB_SIZEOF_PTRDIFF_T > 4 */
+ break;
+ case 'j':
+#if GLIB_SIZEOF_INTMAX_T > 4
+ spec.mod_long = TRUE;
+ spec.mod_extra_long = TRUE;
+#endif /* GLIB_SIZEOF_INTMAX_T > 4 */
+ break;
+
+ /* parse output conversions
+ */
+ case '%':
+ conv_len += 1;
+ break;
+ case 'O':
+ case 'D':
+ case 'I':
+ case 'U':
+ /* some C libraries feature long variants for these as well? */
+ spec.mod_long = TRUE;
+ /* fall through */
+ case 'o':
+ conv_len += 2;
+ /* fall through */
+ case 'd':
+ case 'i':
+ conv_len += 1; /* sign */
+ /* fall through */
+ case 'u':
+ conv_len += 4;
+ /* fall through */
+ case 'x':
+ case 'X':
+ spec.possible_sign = TRUE;
+ conv_len += 10;
+ if (spec.mod_long && honour_longs)
+ conv_len *= 2;
+ if (spec.mod_extra_long)
+ conv_len *= 2;
+ if (spec.mod_extra_long)
+ {
+#ifdef G_HAVE_GINT64
+ (void) va_arg (args, gint64);
+#else /* !G_HAVE_GINT64 */
+ (void) va_arg (args, long);
+#endif /* !G_HAVE_GINT64 */
+ }
+ else if (spec.mod_long)
+ (void) va_arg (args, long);
+ else
+ (void) va_arg (args, int);
+ break;
+ case 'A':
+ case 'a':
+ /* 0x */
+ conv_len += 2;
+ /* fall through */
+ case 'g':
+ case 'G':
+ case 'e':
+ case 'E':
+ case 'f':
+ spec.possible_sign = TRUE;
+ /* n . dddddddddddddddddddddddd E +- eeee */
+ conv_len += 1 + 1 + MAX (24, spec.precision) + 1 + 1 + 4;
+ if (may_warn && spec.mod_extra_long)
+ g_warning (G_GNUC_PRETTY_FUNCTION
+ "(): unable to handle long double, collecting double only");
+#ifdef HAVE_LONG_DOUBLE
+#error need to implement special handling for long double
+#endif
+ u_double.v_double = va_arg (args, double);
+ /* %f can expand up to all significant digits before '.' (308) */
+ if (c == 'f' &&
+ u_double.mpn.biased_exponent > 0 && u_double.mpn.biased_exponent < 2047)
+ {
+ gint exp = u_double.mpn.biased_exponent;
+
+ exp -= G_IEEE754_DOUBLE_BIAS;
+ exp = exp * G_LOG_2_BASE_10 + 1;
+ conv_len += exp;
+ }
+ /* some printf() implementations require extra padding for rounding */
+ conv_len += 2;
+ /* we can't really handle locale specific grouping here */
+ if (spec.locale_grouping)
+ conv_len *= 2;
+ break;
+ case 'C':
+ spec.mod_long = TRUE;
+ /* fall through */
+ case 'c':
+ conv_len += spec.mod_long ? MB_LEN_MAX : 1;
+ (void) va_arg (args, int);
+ break;
+ case 'S':
+ spec.mod_long = TRUE;
+ /* fall through */
+ case 's':
+ v_string = va_arg (args, char*);
+ if (!v_string)
+ conv_len += 8; /* hold "(null)" */
+ else if (spec.seen_precision)
+ conv_len += spec.precision;
+ else
+ conv_len += strlen (v_string);
+ conv_done = TRUE;
+ if (spec.mod_long)
+ {
+ if (may_warn)
+ g_warning (G_GNUC_PRETTY_FUNCTION
+ "(): unable to handle wide char strings");
+ len += 1024; /* try adding some safety padding */
+ }
+ break;
+ case 'P': /* do we actually need this? */
+ /* fall through */
+ case 'p':
+ spec.alternate_format = TRUE;
+ conv_len += 10;
+ if (honour_longs)
+ conv_len *= 2;
+ /* fall through */
+ case 'n':
+ conv_done = TRUE;
+ (void) va_arg (args, void*);
+ break;
+ case 'm':
+ /* there's not much we can do to be clever */
+ v_string = g_strerror (errno);
+ v_uint = v_string ? strlen (v_string) : 0;
+ conv_len += MAX (256, v_uint);
+ break;
+
+ /* handle invalid cases
+ */
+ case '\000':
+ /* no conversion specification, bad bad */
+ conv_len += format - spec_start;
+ break;
+ default:
+ if (may_warn)
+ g_warning (G_GNUC_PRETTY_FUNCTION
+ "(): unable to handle `%c' while parsing format",
+ c);
+ break;
+ }
+ conv_done |= conv_len > 0;
+ }
+ while (!conv_done);
+ /* handle width specifications */
+ conv_len = MAX (conv_len, MAX (spec.precision, spec.min_width));
+ /* handle flags */
+ conv_len += spec.alternate_format ? 2 : 0;
+ conv_len += (spec.add_space || spec.add_sign || spec.possible_sign);
+ /* finally done */
+ len += conv_len;
+ } /* else (c == '%') */
+ } /* while (*format) */
+
+ return len;
+}
+
+guint
+g_printf_string_upper_bound (const gchar *format,
+ va_list args)
+{
+ return printf_string_upper_bound (format, TRUE, args);
+}
+
void
g_messages_init (void)
{