diff options
-rw-r--r-- | ChangeLog | 31 | ||||
-rw-r--r-- | NEWS | 8 | ||||
-rw-r--r-- | stdio-common/Makefile | 1 | ||||
-rw-r--r-- | stdio-common/Versions | 1 | ||||
-rw-r--r-- | stdio-common/printf-parse.h | 23 | ||||
-rw-r--r-- | stdio-common/printf-parsemb.c | 123 | ||||
-rw-r--r-- | stdio-common/printf-prs.c | 7 | ||||
-rw-r--r-- | stdio-common/printf.h | 49 | ||||
-rw-r--r-- | stdio-common/reg-modifier.c | 202 | ||||
-rw-r--r-- | stdio-common/reg-printf.c | 46 | ||||
-rw-r--r-- | stdio-common/reg-type.c | 62 | ||||
-rw-r--r-- | stdio-common/vfprintf.c | 65 |
12 files changed, 528 insertions, 90 deletions
diff --git a/ChangeLog b/ChangeLog index 6a28967a24..6c36c13fec 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,34 @@ +2009-04-10 Ulrich Drepper <drepper@redhat.com> + + * stdio-common/printf.h (struct printf_info): Add user element. + New types printf_arginfo_size_function, printf_va_arg_function. + Declare register_printf_specifier, register_printf_modifier, + register_printf_type. + * stdio-common/printf-parse.h (struct printf_spec): Add size element. + (union printf_arg): Add pa_user element. + Adjust __printf_arginfo_table type. + Add __printf_va_arg_table, __printf_modifier_table, + __handle_registered_modifier_mb, and __handle_registered_modifier_wc + declarations. + * stdio-common/printf-parsemb.c: Recognize registered modifiers. + If registered arginfo call failed try normal specifier. + * stdio-common/printf-prs.c: Pass additional parameter to arginfo + function. + * stdio-common/Makefile (routines): Add reg-modifier and reg-type. + * stdio-common/Versions: Export register_printf_modifier, + register_printf_type, and register_printf_specifier for GLIBC_2.10. + * stdio-common/reg-modifier.c: New file. + * stdio-common/reg-type.c: New file. + * stdio-common/reg-printf.c (__register_printf_specifier): New + function. Mostly the old __register_printf_function function but + uses locking and type of third parameter changed. + (__register_printf_function): Implement using + __register_printf_specifier. + * stdio-common/vfprintf.c (vfprintf): Collect argument sizes in + calls to arginfo functions. Allocate enough memory for user-defined + types. Call new va_arg functions to get user-defined types. + Try installed handlers even for existing format specifiers first. + 2009-04-09 Ulrich Drepper <drepper@redhat.com> * sysdeps/x86_64/rawmemchr.S: New file. diff --git a/NEWS b/NEWS index 74feed6f3b..4be57cac97 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -GNU C Library NEWS -- history of user-visible changes. 2009-4-8 +GNU C Library NEWS -- history of user-visible changes. 2009-4-10 Copyright (C) 1992-2008, 2009 Free Software Foundation, Inc. See the end for copying conditions. @@ -37,7 +37,11 @@ Version 2.10 * New locales: nan_TW@latin, ks_IN -* Faster strlen, strchr, strchrnul, and memchr for x86-64. +* Faster strlen, strchr, strchrnul, memchr, and rawmemchr for x86-64. + Implemented by Ulrich Drepper. + +* Extended printf hook support. It is possible to use user-defined types + and extend existing format specifiers. Implemented by Ulrich Drepper. diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 6d0b8ab9e1..9cbf14385c 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -27,6 +27,7 @@ routines := \ ctermid cuserid \ _itoa _itowa itoa-digits itoa-udigits itowa-digits \ vfprintf vprintf printf_fp reg-printf printf-prs printf_fphex \ + reg-modifier reg-type \ printf_size fprintf printf snprintf sprintf asprintf dprintf \ vfwprintf vfscanf vfwscanf \ fscanf scanf sscanf \ diff --git a/stdio-common/Versions b/stdio-common/Versions index af693fff52..3503a84e6b 100644 --- a/stdio-common/Versions +++ b/stdio-common/Versions @@ -55,6 +55,7 @@ libc { } GLIBC_2.10 { psiginfo; + register_printf_modifier; register_printf_type; register_printf_specifier; } GLIBC_PRIVATE { # global variables diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h index f6ad71cd3b..555ad78f33 100644 --- a/stdio-common/printf-parse.h +++ b/stdio-common/printf-parse.h @@ -1,5 +1,5 @@ /* Internal header for parsing printf format strings. - Copyright (C) 1995-1999, 2000, 2002, 2003, 2007 + Copyright (C) 1995-1999, 2000, 2002, 2003, 2007, 2009 Free Software Foundation, Inc. This file is part of th GNU C Library. @@ -42,6 +42,8 @@ struct printf_spec int data_arg_type; /* Type of first argument. */ /* Number of arguments consumed by this format specifier. */ size_t ndata_args; + /* Size of the parameter for PA_USER type. */ + int size; }; @@ -60,6 +62,7 @@ union printf_arg const char *pa_string; const wchar_t *pa_wstring; void *pa_pointer; + void *pa_user; }; @@ -83,8 +86,9 @@ read_int (const UCHAR_T * *pstr) /* These are defined in reg-printf.c. */ -extern printf_arginfo_function **__printf_arginfo_table attribute_hidden; +extern printf_arginfo_size_function **__printf_arginfo_table attribute_hidden; extern printf_function **__printf_function_table attribute_hidden; +extern printf_va_arg_function **__printf_va_arg_table attribute_hidden; /* Find the next spec in FORMAT, or the end of the string. Returns @@ -114,3 +118,18 @@ extern size_t __parse_one_specmb (const unsigned char *format, size_t posn, extern size_t __parse_one_specwc (const unsigned int *format, size_t posn, struct printf_spec *spec, size_t *max_ref_arg) attribute_hidden; + + + +/* This variable is defined in reg-modifier.c. */ +struct printf_modifier_record; +extern struct printf_modifier_record **__printf_modifier_table + attribute_hidden; + +/* Handle registered modifiers. */ +extern int __handle_registered_modifier_mb (const unsigned char **format, + struct printf_info *info) + attribute_hidden; +extern int __handle_registered_modifier_wc (const unsigned int **format, + struct printf_info *info) + attribute_hidden; diff --git a/stdio-common/printf-parsemb.c b/stdio-common/printf-parsemb.c index da6fd3edb0..efd1eca3a2 100644 --- a/stdio-common/printf-parsemb.c +++ b/stdio-common/printf-parsemb.c @@ -1,5 +1,5 @@ /* Helper functions for parsing printf format strings. - Copyright (C) 1995-2000,2002,2003,2004,2006 Free Software Foundation, Inc. + Copyright (C) 1995-2000,2002-2004,2006,2009 Free Software Foundation, Inc. This file is part of th GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -31,12 +31,14 @@ # define INT_T int # define L_(Str) Str # define ISDIGIT(Ch) isdigit (Ch) +# define HANDLE_REGISTERED_MODIFIER __handle_registered_modifier_mb #else # define CHAR_T wchar_t # define UCHAR_T unsigned int # define INT_T wint_t # define L_(Str) L##Str # define ISDIGIT(Ch) iswdigit (Ch) +# define HANDLE_REGISTERED_MODIFIER __handle_registered_modifier_wc #endif #include "printf-parse.h" @@ -223,72 +225,79 @@ __parse_one_specmb (const UCHAR_T *format, size_t posn, spec->info.is_short = 0; spec->info.is_long = 0; spec->info.is_char = 0; - - switch (*format++) - { - case L_('h'): - /* ints are short ints or chars. */ - if (*format != L_('h')) - spec->info.is_short = 1; - else - { - ++format; - spec->info.is_char = 1; - } - break; - case L_('l'): - /* ints are long ints. */ - spec->info.is_long = 1; - if (*format != L_('l')) + spec->info.user = 0; + + if (__builtin_expect (__printf_modifier_table == NULL, 1) + || __printf_modifier_table[*format] == NULL + || HANDLE_REGISTERED_MODIFIER (&format, &spec->info) != 0) + switch (*format++) + { + case L_('h'): + /* ints are short ints or chars. */ + if (*format != L_('h')) + spec->info.is_short = 1; + else + { + ++format; + spec->info.is_char = 1; + } break; - ++format; - /* FALLTHROUGH */ - case L_('L'): - /* doubles are long doubles, and ints are long long ints. */ - case L_('q'): - /* 4.4 uses this for long long. */ - spec->info.is_long_double = 1; - break; - case L_('z'): - case L_('Z'): - /* ints are size_ts. */ - assert (sizeof (size_t) <= sizeof (unsigned long long int)); + case L_('l'): + /* ints are long ints. */ + spec->info.is_long = 1; + if (*format != L_('l')) + break; + ++format; + /* FALLTHROUGH */ + case L_('L'): + /* doubles are long doubles, and ints are long long ints. */ + case L_('q'): + /* 4.4 uses this for long long. */ + spec->info.is_long_double = 1; + break; + case L_('z'): + case L_('Z'): + /* ints are size_ts. */ + assert (sizeof (size_t) <= sizeof (unsigned long long int)); #if LONG_MAX != LONG_LONG_MAX - spec->info.is_long_double = sizeof (size_t) > sizeof (unsigned long int); + spec->info.is_long_double = (sizeof (size_t) + > sizeof (unsigned long int)); #endif - spec->info.is_long = sizeof (size_t) > sizeof (unsigned int); - break; - case L_('t'): - assert (sizeof (ptrdiff_t) <= sizeof (long long int)); + spec->info.is_long = sizeof (size_t) > sizeof (unsigned int); + break; + case L_('t'): + assert (sizeof (ptrdiff_t) <= sizeof (long long int)); #if LONG_MAX != LONG_LONG_MAX - spec->info.is_long_double = (sizeof (ptrdiff_t) > sizeof (long int)); + spec->info.is_long_double = (sizeof (ptrdiff_t) > sizeof (long int)); #endif - spec->info.is_long = sizeof (ptrdiff_t) > sizeof (int); - break; - case L_('j'): - assert (sizeof (uintmax_t) <= sizeof (unsigned long long int)); + spec->info.is_long = sizeof (ptrdiff_t) > sizeof (int); + break; + case L_('j'): + assert (sizeof (uintmax_t) <= sizeof (unsigned long long int)); #if LONG_MAX != LONG_LONG_MAX - spec->info.is_long_double = (sizeof (uintmax_t) - > sizeof (unsigned long int)); + spec->info.is_long_double = (sizeof (uintmax_t) + > sizeof (unsigned long int)); #endif - spec->info.is_long = sizeof (uintmax_t) > sizeof (unsigned int); - break; - default: - /* Not a recognized modifier. Backup. */ - --format; - break; - } + spec->info.is_long = sizeof (uintmax_t) > sizeof (unsigned int); + break; + default: + /* Not a recognized modifier. Backup. */ + --format; + break; + } /* Get the format specification. */ spec->info.spec = (wchar_t) *format++; - if (__builtin_expect (__printf_function_table != NULL, 0) - && spec->info.spec <= UCHAR_MAX - && __printf_arginfo_table[spec->info.spec] != NULL) - /* We don't try to get the types for all arguments if the format - uses more than one. The normal case is covered though. */ - spec->ndata_args = (*__printf_arginfo_table[spec->info.spec]) - (&spec->info, 1, &spec->data_arg_type); - else + spec->size = -1; + if (__builtin_expect (__printf_function_table == NULL, 1) + || spec->info.spec > UCHAR_MAX + || __printf_arginfo_table[spec->info.spec] == NULL + /* We don't try to get the types for all arguments if the format + uses more than one. The normal case is covered though. If + the call returns -1 we continue with the normal specifiers. */ + || (spec->ndata_args = (*__printf_arginfo_table[spec->info.spec]) + (&spec->info, 1, &spec->data_arg_type, + &spec->size)) < 0) { /* Find the data argument types of a built-in spec. */ spec->ndata_args = 1; diff --git a/stdio-common/printf-prs.c b/stdio-common/printf-prs.c index aabc9ed85a..e8d84b3be0 100644 --- a/stdio-common/printf-prs.c +++ b/stdio-common/printf-prs.c @@ -1,5 +1,5 @@ -/* Copyright (C) 1991, 1992, 1995, 1996, 1999, 2000, 2002, 2003, 2004, 2005, - 2007 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1992, 1995, 1996, 1999, 2000, 2002-2005, 2007, 2009 + Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -97,7 +97,8 @@ parse_printf_format (fmt, n, argtypes) /* We have more than one argument for this format spec. We must call the arginfo function again to determine all the types. */ (void) (*__printf_arginfo_table[spec.info.spec]) - (&spec.info, n - spec.data_arg, &argtypes[spec.data_arg]); + (&spec.info, n - spec.data_arg, &argtypes[spec.data_arg], + &spec.size); break; } } diff --git a/stdio-common/printf.h b/stdio-common/printf.h index 360cdcce1d..a11af02274 100644 --- a/stdio-common/printf.h +++ b/stdio-common/printf.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-1993,1995-1999,2000,2001,2006 +/* Copyright (C) 1991-1993,1995-2001,2006,2009 Free Software Foundation, Inc. This file is part of the GNU C Library. @@ -29,6 +29,7 @@ __BEGIN_DECLS #define __need_size_t #define __need_wchar_t #include <stddef.h> +#include <stdarg.h> struct printf_info @@ -48,6 +49,8 @@ struct printf_info unsigned int is_char:1; /* hh flag. */ unsigned int wide:1; /* Nonzero for wide character streams. */ unsigned int i18n:1; /* I flag. */ + unsigned int __pad:4; /* Unused so far. */ + unsigned short int user; /* Bits for user-installed modifiers. */ wchar_t pad; /* Padding character. */ }; @@ -68,18 +71,55 @@ typedef int printf_function (FILE *__stream, /* Type of a printf specifier-arginfo function. INFO gives information about the format specification. - N, ARGTYPES, and return value are as for parse_printf_format. */ + N, ARGTYPES, *SIZE has to contain the size of the parameter for + user-defined types, and return value are as for parse_printf_format + except that -1 should be returned if the handler cannot handle + this case. This allows to partially overwrite the functionality + of existing format specifiers. */ + +typedef int printf_arginfo_size_function (__const struct printf_info *__info, + size_t __n, int *__argtypes, + int *__size); + +/* Old version of 'printf_arginfo_function' without a SIZE parameter. */ typedef int printf_arginfo_function (__const struct printf_info *__info, size_t __n, int *__argtypes); +/* Type of a function to get a value of a user-defined from the + variable argument list. */ +typedef void printf_va_arg_function (void *__mem, va_list *__ap); + /* Register FUNC to be called to format SPEC specifiers; ARGINFO must be specified to determine how many arguments a SPEC conversion requires and what their types are. */ +extern int register_printf_specifier (int __spec, printf_function __func, + printf_arginfo_size_function __arginfo) + __THROW; + + +/* Obsolete interface similar to register_printf_specifier. It can only + handle basic data types because the ARGINFO callback does not return + information on the size of the user-defined type. */ + extern int register_printf_function (int __spec, printf_function __func, - printf_arginfo_function __arginfo); + printf_arginfo_function __arginfo) + __THROW __attribute_deprecated__; + + +/* Register a new modifier character sequence. If the call succeeds + it returns a positive value representing the bit set in the USER + field in 'struct printf_info'. */ + +extern int register_printf_modifier (wchar_t *__str) __wur __THROW; + + +/* Register variable argument handler for user type. The return value + is to be used in ARGINFO functions to signal the use of the + type. */ +extern int register_printf_type (printf_va_arg_function __fct) __wur __THROW; /* Parse FMT, and fill in N elements of ARGTYPES with the @@ -100,7 +140,8 @@ extern size_t parse_printf_format (__const char *__restrict __fmt, size_t __n, /* Codes returned by `parse_printf_format' for basic types. These values cover all the standard format specifications. - Users can add new values after PA_LAST for their own types. */ + Users can reserve new values after PA_LAST for their own types + using 'register_printf_type'. */ enum { /* C type: */ diff --git a/stdio-common/reg-modifier.c b/stdio-common/reg-modifier.c new file mode 100644 index 0000000000..beec2ecbe3 --- /dev/null +++ b/stdio-common/reg-modifier.c @@ -0,0 +1,202 @@ +/* Copyright (C) 2009 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <limits.h> +#include <printf.h> +#include <wchar.h> +#include <bits/libc-lock.h> + + +struct printf_modifier_record +{ + struct printf_modifier_record *next; + int bit; + wchar_t str[0]; +}; + +struct printf_modifier_record **__printf_modifier_table attribute_hidden; + +__libc_lock_define_initialized (static, lock) + +/* Bits to hand out. */ +static int next_bit; + + +int +__register_printf_modifier (wchar_t *str) +{ + if (str[0] == L'\0') + { + einval: + __set_errno (EINVAL); + return -1; + } + + wchar_t *wc = str; + while (*wc != L'\0') + if (*wc < 0 || *wc > (wchar_t) UCHAR_MAX) + goto einval; + else + ++wc; + + if (next_bit / 8 == sizeof (((struct printf_info *) NULL)->user)) + { + __set_errno (ENOSPC); + return -1; + } + + int result = -1; + __libc_lock_lock (lock); + + if (__printf_modifier_table == NULL) + { + __printf_modifier_table = calloc (UCHAR_MAX, + sizeof (*__printf_modifier_table)); + if (__printf_modifier_table == NULL) + goto out; + } + + /* Create enough room for the string. But we don't need the first + character. */ + struct printf_modifier_record *newp = malloc (sizeof (*newp) + + ((wc - str) + * sizeof (wchar_t))); + if (newp == NULL) + goto out; + + newp->next = __printf_modifier_table[(unsigned char) *str]; + newp->bit = 1 << next_bit++; + wmemcpy (newp->str, str + 1, wc - str); + + __printf_modifier_table[(unsigned char) *str] = newp; + + result = newp->bit; + + out: + __libc_lock_unlock (lock); + + return result; +} +weak_alias (__register_printf_modifier, register_printf_modifier) + + +#include <stdio.h> +int +attribute_hidden +__handle_registered_modifier_mb (const unsigned char **format, + struct printf_info *info) +{ + struct printf_modifier_record *runp = __printf_modifier_table[**format]; + + int best_bit = 0; + int best_len = 0; + const unsigned char *best_cp = NULL; + + while (runp != NULL) + { + const unsigned char *cp = *format + 1; + wchar_t *fcp = runp->str; + + while (*cp != '\0' && *fcp != L'\0') + if (*cp != *fcp) + break; + else + ++cp, ++fcp; + + if (*fcp == L'\0' && cp - *format > best_len) + { + best_cp = cp; + best_len = cp - *format; + best_bit = runp->bit; + } + + runp = runp->next; + } + + if (best_bit != 0) + { + info->user |= best_bit; + *format = best_cp; + return 0; + } + + return 1; +} + + +int +attribute_hidden +__handle_registered_modifier_wc (const unsigned int **format, + struct printf_info *info) +{ + struct printf_modifier_record *runp = __printf_modifier_table[**format]; + + int best_bit = 0; + int best_len = 0; + const unsigned int *best_cp = NULL; + + while (runp != NULL) + { + const unsigned int *cp = *format + 1; + wchar_t *fcp = runp->str; + + while (*cp != '\0' && *fcp != L'\0') + if (*cp != *fcp) + break; + else + ++cp, ++fcp; + + if (*fcp == L'\0' && cp - *format > best_len) + { + best_cp = cp; + best_len = cp - *format; + best_bit = runp->bit; + } + + runp = runp->next; + } + + if (best_bit != 0) + { + info->user |= best_bit; + *format = best_cp; + return 0; + } + + return 1; +} + + +libc_freeres_fn (free_mem) +{ + if (__printf_modifier_table != NULL) + { + for (int i = 0; i < UCHAR_MAX; ++i) + { + struct printf_modifier_record *runp = __printf_modifier_table[i]; + while (runp != NULL) + { + struct printf_modifier_record *oldp = runp; + runp = runp->next; + free (oldp); + } + } + free (__printf_modifier_table); + } +} diff --git a/stdio-common/reg-printf.c b/stdio-common/reg-printf.c index dbb6234e5f..b5cab679de 100644 --- a/stdio-common/reg-printf.c +++ b/stdio-common/reg-printf.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991,1996,1997,2002,2003,2004 Free Software Foundation, Inc. +/* Copyright (C) 1991,1996,1997,2002-2004,2009 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -21,21 +21,28 @@ #include <printf.h> #include <stddef.h> #include <stdlib.h> +#include <bits/libc-lock.h> + /* Array of functions indexed by format character. */ -libc_freeres_ptr (printf_arginfo_function **__printf_arginfo_table) +libc_freeres_ptr (printf_arginfo_size_function **__printf_arginfo_table) attribute_hidden; printf_function **__printf_function_table attribute_hidden; +__libc_lock_define_initialized (static, lock) + +int __register_printf_specifier (int, printf_function, + printf_arginfo_size_function); int __register_printf_function (int, printf_function, - printf_arginfo_function) __THROW; + printf_arginfo_function); + /* Register FUNC to be called to format SPEC specifiers. */ int -__register_printf_function (spec, converter, arginfo) +__register_printf_specifier (spec, converter, arginfo) int spec; printf_function converter; - printf_arginfo_function arginfo; + printf_arginfo_size_function arginfo; { if (spec < 0 || spec > (int) UCHAR_MAX) { @@ -43,12 +50,19 @@ __register_printf_function (spec, converter, arginfo) return -1; } + int result = 0; + __libc_lock_lock (lock); + if (__printf_function_table == NULL) { - __printf_arginfo_table = (printf_arginfo_function **) + __printf_arginfo_table = (printf_arginfo_size_function **) calloc (UCHAR_MAX + 1, sizeof (void *) * 2); if (__printf_arginfo_table == NULL) - return -1; + { + result = -1; + goto out; + } + __printf_function_table = (printf_function **) (__printf_arginfo_table + UCHAR_MAX + 1); } @@ -56,6 +70,22 @@ __register_printf_function (spec, converter, arginfo) __printf_function_table[spec] = converter; __printf_arginfo_table[spec] = arginfo; - return 0; + out: + __libc_lock_unlock (lock); + + return result; +} +weak_alias (__register_printf_specifier, register_printf_specifier) + + +/* Register FUNC to be called to format SPEC specifiers. */ +int +__register_printf_function (spec, converter, arginfo) + int spec; + printf_function converter; + printf_arginfo_function arginfo; +{ + return __register_printf_specifier (spec, converter, + (printf_arginfo_size_function*) arginfo); } weak_alias (__register_printf_function, register_printf_function) diff --git a/stdio-common/reg-type.c b/stdio-common/reg-type.c new file mode 100644 index 0000000000..7a96b2893a --- /dev/null +++ b/stdio-common/reg-type.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2009 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <errno.h> +#include <printf.h> +#include <stdlib.h> +#include <bits/libc-lock.h> + + +/* Array of functions indexed by format character. */ +libc_freeres_ptr (printf_va_arg_function **__printf_va_arg_table) + attribute_hidden; + +__libc_lock_define_initialized (static, lock); + +/* Last type allocated. */ +static int pa_next_type = PA_LAST; + + +int +__register_printf_type (printf_va_arg_function fct) +{ + int result = -1; + __libc_lock_lock (lock); + + if (__printf_va_arg_table == NULL) + { + __printf_va_arg_table = (printf_va_arg_function **) + calloc (0x100 - PA_LAST, sizeof (void *)); + if (__printf_va_arg_table == NULL) + goto out; + } + + if (pa_next_type == 0x100) + __set_errno (ENOSPC); + else + { + result = pa_next_type++; + __printf_va_arg_table[result - PA_LAST] = fct; + } + + out: + __libc_lock_unlock (lock); + + return result; +} +weak_alias (__register_printf_type, register_printf_type) diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index 5a24e725d5..38ba8ffdcd 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -238,7 +238,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) /* This table maps a character into a number representing a class. In each step there is a destination label for each class. */ - static const int jump_table[] = + static const uint8_t jump_table[] = { /* ' ' */ 1, 0, 0, /* '#' */ 4, 0, /* '%' */ 14, 0, /* '\''*/ 6, @@ -1630,6 +1630,7 @@ do_positional: size_t nargs = 0; int *args_type; union printf_arg *args_value = NULL; + int *args_size; /* Positional parameters refer to arguments directly. This could also determine the maximum number of arguments. Track the @@ -1684,6 +1685,7 @@ do_positional: memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0', nargs * sizeof (int)); args_value = alloca (nargs * sizeof (union printf_arg)); + args_size = alloca (nargs * sizeof (int)); /* XXX Could do sanity check here: If any element in ARGS_TYPE is still zero after this loop, format is invalid. For now we @@ -1704,8 +1706,10 @@ do_positional: { case 0: /* No arguments. */ break; - case 1: /* One argument; we already have the type. */ + case 1: /* One argument; we already have the + type and size. */ args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type; + args_size[specs[cnt].data_arg] = specs[cnt].size; break; default: /* We have more than one argument for this format spec. @@ -1713,7 +1717,8 @@ do_positional: all the types. */ (void) (*__printf_arginfo_table[specs[cnt].info.spec]) (&specs[cnt].info, - specs[cnt].ndata_args, &args_type[specs[cnt].data_arg]); + specs[cnt].ndata_args, &args_type[specs[cnt].data_arg], + &args_size[specs[cnt].data_arg]); break; } } @@ -1760,6 +1765,13 @@ do_positional: default: if ((args_type[cnt] & PA_FLAG_PTR) != 0) args_value[cnt].pa_pointer = va_arg (ap_save, void *); + else if (__builtin_expect (__printf_va_arg_table != NULL, 0) + && __printf_va_arg_table[args_type[cnt] - PA_LAST] != NULL) + { + args_value[cnt].pa_user = alloca (args_size[cnt]); + (*__printf_va_arg_table[args_type[cnt] - PA_LAST]) + (args_value[cnt].pa_user, &ap_save); + } else args_value[cnt].pa_long_double = 0.0; break; @@ -1863,6 +1875,40 @@ do_positional: /* Process format specifiers. */ while (1) { + extern printf_function **__printf_function_table; + int function_done; + + if (spec <= UCHAR_MAX + && __printf_function_table != NULL + && __printf_function_table[(size_t) spec] != NULL) + { + const void **ptr = alloca (specs[nspecs_done].ndata_args + * sizeof (const void *)); + + /* Fill in an array of pointers to the argument values. */ + for (unsigned int i = 0; i < specs[nspecs_done].ndata_args; + ++i) + ptr[i] = &args_value[specs[nspecs_done].data_arg + i]; + + /* Call the function. */ + function_done = __printf_function_table[(size_t) spec] + (s, &specs[nspecs_done].info, ptr); + + if (function_done != -2) + { + /* If an error occurred we don't have information + about # of chars. */ + if (function_done < 0) + { + done = -1; + goto all_done; + } + + done_add (function_done); + break; + } + } + JUMP (spec, step4_jumps); process_arg ((&specs[nspecs_done])); @@ -1870,19 +1916,9 @@ do_positional: LABEL (form_unknown): { - extern printf_function **__printf_function_table; - int function_done; - printf_function *function; unsigned int i; const void **ptr; - function = - (__printf_function_table == NULL ? NULL : - __printf_function_table[specs[nspecs_done].info.spec]); - - if (function == NULL) - function = &printf_unknown; - ptr = alloca (specs[nspecs_done].ndata_args * sizeof (const void *)); @@ -1891,7 +1927,8 @@ do_positional: ptr[i] = &args_value[specs[nspecs_done].data_arg + i]; /* Call the function. */ - function_done = (*function) (s, &specs[nspecs_done].info, ptr); + function_done = printf_unknown (s, &specs[nspecs_done].info, + ptr); /* If an error occurred we don't have information about # of chars. */ |