| /* Copyright (C) 2009-2014 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, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include <errno.h> |
| #include <limits.h> |
| #include <printf.h> |
| #include <stdlib.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 (const wchar_t *str) |
| { |
| if (str[0] == L'\0') |
| { |
| einval: |
| __set_errno (EINVAL); |
| return -1; |
| } |
| |
| const 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); |
| } |
| } |