diff options
| author | Ulrich Drepper <drepper@redhat.com> | 2009-04-11 05:34:20 +0000 |
|---|---|---|
| committer | Ulrich Drepper <drepper@redhat.com> | 2009-04-11 05:34:20 +0000 |
| commit | 9d26efa90c6dcbcd6b3e586c9927b6058ef4d529 (patch) | |
| tree | fc306e24bf4b956abba8782e78d4118639c46361 | |
| parent | f140a0d53d639c86408ac532091fb4644faa95c0 (diff) | |
| download | glibc-9d26efa90c6dcbcd6b3e586c9927b6058ef4d529.tar.xz glibc-9d26efa90c6dcbcd6b3e586c9927b6058ef4d529.zip | |
* 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.
| -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
@@ -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. @@ -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; + |
