diff options
Diffstat (limited to 'REORG.TODO/iconv/gconv_cache.c')
-rw-r--r-- | REORG.TODO/iconv/gconv_cache.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/REORG.TODO/iconv/gconv_cache.c b/REORG.TODO/iconv/gconv_cache.c new file mode 100644 index 0000000000..0cadea3638 --- /dev/null +++ b/REORG.TODO/iconv/gconv_cache.c @@ -0,0 +1,472 @@ +/* Cache handling for iconv modules. + Copyright (C) 2001-2017 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 2001. + + 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 <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/stat.h> + +#include <gconv_int.h> +#include <iconvconfig.h> +#include <not-cancel.h> + +#include "../intl/hash-string.h" + +static void *gconv_cache; +static size_t cache_size; +static int cache_malloced; + + +void * +__gconv_get_cache (void) +{ + return gconv_cache; +} + + +int +internal_function +__gconv_load_cache (void) +{ + int fd; + struct stat64 st; + struct gconvcache_header *header; + + /* We cannot use the cache if the GCONV_PATH environment variable is + set. */ + __gconv_path_envvar = getenv ("GCONV_PATH"); + if (__gconv_path_envvar != NULL) + return -1; + + /* See whether the cache file exists. */ + fd = open_not_cancel (GCONV_MODULES_CACHE, O_RDONLY, 0); + if (__builtin_expect (fd, 0) == -1) + /* Not available. */ + return -1; + + /* Get information about the file. */ + if (__builtin_expect (__fxstat64 (_STAT_VER, fd, &st), 0) < 0 + /* We do not have to start looking at the file if it cannot contain + at least the cache header. */ + || (size_t) st.st_size < sizeof (struct gconvcache_header)) + { + close_and_exit: + close_not_cancel_no_status (fd); + return -1; + } + + /* Make the file content available. */ + cache_size = st.st_size; +#ifdef _POSIX_MAPPED_FILES + gconv_cache = __mmap (NULL, cache_size, PROT_READ, MAP_SHARED, fd, 0); + if (__glibc_unlikely (gconv_cache == MAP_FAILED)) +#endif + { + size_t already_read; + + gconv_cache = malloc (cache_size); + if (gconv_cache == NULL) + goto close_and_exit; + + already_read = 0; + do + { + ssize_t n = __read (fd, (char *) gconv_cache + already_read, + cache_size - already_read); + if (__builtin_expect (n, 0) == -1) + { + free (gconv_cache); + gconv_cache = NULL; + goto close_and_exit; + } + + already_read += n; + } + while (already_read < cache_size); + + cache_malloced = 1; + } + + /* We don't need the file descriptor anymore. */ + close_not_cancel_no_status (fd); + + /* Check the consistency. */ + header = (struct gconvcache_header *) gconv_cache; + if (__builtin_expect (header->magic, GCONVCACHE_MAGIC) != GCONVCACHE_MAGIC + || __builtin_expect (header->string_offset >= cache_size, 0) + || __builtin_expect (header->hash_offset >= cache_size, 0) + || __builtin_expect (header->hash_size == 0, 0) + || __builtin_expect ((header->hash_offset + + header->hash_size * sizeof (struct hash_entry)) + > cache_size, 0) + || __builtin_expect (header->module_offset >= cache_size, 0) + || __builtin_expect (header->otherconv_offset > cache_size, 0)) + { + if (cache_malloced) + { + free (gconv_cache); + cache_malloced = 0; + } +#ifdef _POSIX_MAPPED_FILES + else + __munmap (gconv_cache, cache_size); +#endif + gconv_cache = NULL; + + return -1; + } + + /* That worked. */ + return 0; +} + + +static int +internal_function +find_module_idx (const char *str, size_t *idxp) +{ + unsigned int idx; + unsigned int hval; + unsigned int hval2; + const struct gconvcache_header *header; + const char *strtab; + const struct hash_entry *hashtab; + unsigned int limit; + + header = (const struct gconvcache_header *) gconv_cache; + strtab = (char *) gconv_cache + header->string_offset; + hashtab = (struct hash_entry *) ((char *) gconv_cache + + header->hash_offset); + + hval = __hash_string (str); + idx = hval % header->hash_size; + hval2 = 1 + hval % (header->hash_size - 2); + + limit = cache_size - header->string_offset; + while (hashtab[idx].string_offset != 0) + if (hashtab[idx].string_offset < limit + && strcmp (str, strtab + hashtab[idx].string_offset) == 0) + { + *idxp = hashtab[idx].module_idx; + return 0; + } + else + if ((idx += hval2) >= header->hash_size) + idx -= header->hash_size; + + /* Nothing found. */ + return -1; +} + + +#ifndef STATIC_GCONV +static int +internal_function +find_module (const char *directory, const char *filename, + struct __gconv_step *result) +{ + size_t dirlen = strlen (directory); + size_t fnamelen = strlen (filename) + 1; + char fullname[dirlen + fnamelen]; + int status = __GCONV_NOCONV; + + memcpy (__mempcpy (fullname, directory, dirlen), filename, fnamelen); + + result->__shlib_handle = __gconv_find_shlib (fullname); + if (result->__shlib_handle != NULL) + { + status = __GCONV_OK; + + result->__modname = NULL; + result->__fct = result->__shlib_handle->fct; + result->__init_fct = result->__shlib_handle->init_fct; + result->__end_fct = result->__shlib_handle->end_fct; + + /* These settings can be overridden by the init function. */ + result->__btowc_fct = NULL; + result->__data = NULL; + + /* Call the init function. */ + if (result->__init_fct != NULL) + { + __gconv_init_fct init_fct = result->__init_fct; +#ifdef PTR_DEMANGLE + PTR_DEMANGLE (init_fct); +#endif + status = DL_CALL_FCT (init_fct, (result)); + +#ifdef PTR_MANGLE + if (result->__btowc_fct != NULL) + PTR_MANGLE (result->__btowc_fct); +#endif + } + } + + return status; +} +#endif + + +int +internal_function +__gconv_compare_alias_cache (const char *name1, const char *name2, int *result) +{ + size_t name1_idx; + size_t name2_idx; + + if (gconv_cache == NULL) + return -1; + + if (find_module_idx (name1, &name1_idx) != 0 + || find_module_idx (name2, &name2_idx) != 0) + *result = strcmp (name1, name2); + else + *result = (int) (name1_idx - name2_idx); + + return 0; +} + + +int +internal_function +__gconv_lookup_cache (const char *toset, const char *fromset, + struct __gconv_step **handle, size_t *nsteps, int flags) +{ + const struct gconvcache_header *header; + const char *strtab; + size_t fromidx; + size_t toidx; + const struct module_entry *modtab; + const struct module_entry *from_module; + const struct module_entry *to_module; + struct __gconv_step *result; + + if (gconv_cache == NULL) + /* We have no cache available. */ + return __GCONV_NODB; + + header = (const struct gconvcache_header *) gconv_cache; + strtab = (char *) gconv_cache + header->string_offset; + modtab = (const struct module_entry *) ((char *) gconv_cache + + header->module_offset); + + if (find_module_idx (fromset, &fromidx) != 0 + || (header->module_offset + (fromidx + 1) * sizeof (struct module_entry) + > cache_size)) + return __GCONV_NOCONV; + from_module = &modtab[fromidx]; + + if (find_module_idx (toset, &toidx) != 0 + || (header->module_offset + (toidx + 1) * sizeof (struct module_entry) + > cache_size)) + return __GCONV_NOCONV; + to_module = &modtab[toidx]; + + /* Avoid copy-only transformations if the user requests. */ + if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0) && fromidx == toidx) + return __GCONV_NULCONV; + + /* If there are special conversions available examine them first. */ + if (fromidx != 0 && toidx != 0 + && __builtin_expect (from_module->extra_offset, 0) != 0) + { + /* Search through the list to see whether there is a module + matching the destination character set. */ + const struct extra_entry *extra; + + /* Note the -1. This is due to the offset added in iconvconfig. + See there for more explanations. */ + extra = (const struct extra_entry *) ((char *) gconv_cache + + header->otherconv_offset + + from_module->extra_offset - 1); + while (extra->module_cnt != 0 + && extra->module[extra->module_cnt - 1].outname_offset != toidx) + extra = (const struct extra_entry *) ((char *) extra + + sizeof (struct extra_entry) + + (extra->module_cnt + * sizeof (struct extra_entry_module))); + + if (extra->module_cnt != 0) + { + /* Use the extra module. First determine how many steps. */ + char *fromname; + int idx; + + *nsteps = extra->module_cnt; + *handle = result = + (struct __gconv_step *) malloc (extra->module_cnt + * sizeof (struct __gconv_step)); + if (result == NULL) + return __GCONV_NOMEM; + + fromname = (char *) strtab + from_module->canonname_offset; + idx = 0; + do + { + result[idx].__from_name = fromname; + fromname = result[idx].__to_name = + (char *) strtab + modtab[extra->module[idx].outname_offset].canonname_offset; + + result[idx].__counter = 1; + result[idx].__data = NULL; + +#ifndef STATIC_GCONV + if (strtab[extra->module[idx].dir_offset] != '\0') + { + /* Load the module, return handle for it. */ + int res; + + res = find_module (strtab + extra->module[idx].dir_offset, + strtab + extra->module[idx].name_offset, + &result[idx]); + if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK) + { + /* Something went wrong. */ + free (result); + goto try_internal; + } + } + else +#endif + /* It's a builtin transformation. */ + __gconv_get_builtin_trans (strtab + + extra->module[idx].name_offset, + &result[idx]); + + } + while (++idx < extra->module_cnt); + + return __GCONV_OK; + } + } + + try_internal: + /* See whether we can convert via the INTERNAL charset. */ + if ((fromidx != 0 && __builtin_expect (from_module->fromname_offset, 1) == 0) + || (toidx != 0 && __builtin_expect (to_module->toname_offset, 1) == 0) + || (fromidx == 0 && toidx == 0)) + /* Not possible. Nothing we can do. */ + return __GCONV_NOCONV; + + /* We will use up to two modules. Always allocate room for two. */ + result = (struct __gconv_step *) malloc (2 * sizeof (struct __gconv_step)); + if (result == NULL) + return __GCONV_NOMEM; + + *handle = result; + *nsteps = 0; + + /* Generate data structure for conversion to INTERNAL. */ + if (fromidx != 0) + { + result[0].__from_name = (char *) strtab + from_module->canonname_offset; + result[0].__to_name = (char *) "INTERNAL"; + + result[0].__counter = 1; + result[0].__data = NULL; + +#ifndef STATIC_GCONV + if (strtab[from_module->todir_offset] != '\0') + { + /* Load the module, return handle for it. */ + int res = find_module (strtab + from_module->todir_offset, + strtab + from_module->toname_offset, + &result[0]); + if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK) + { + /* Something went wrong. */ + free (result); + return res; + } + } + else +#endif + /* It's a builtin transformation. */ + __gconv_get_builtin_trans (strtab + from_module->toname_offset, + &result[0]); + + ++*nsteps; + } + + /* Generate data structure for conversion from INTERNAL. */ + if (toidx != 0) + { + int idx = *nsteps; + + result[idx].__from_name = (char *) "INTERNAL"; + result[idx].__to_name = (char *) strtab + to_module->canonname_offset; + + result[idx].__counter = 1; + result[idx].__data = NULL; + +#ifndef STATIC_GCONV + if (strtab[to_module->fromdir_offset] != '\0') + { + /* Load the module, return handle for it. */ + int res = find_module (strtab + to_module->fromdir_offset, + strtab + to_module->fromname_offset, + &result[idx]); + if (__builtin_expect (res, __GCONV_OK) != __GCONV_OK) + { + /* Something went wrong. */ + if (idx != 0) + __gconv_release_step (&result[0]); + free (result); + return res; + } + } + else +#endif + /* It's a builtin transformation. */ + __gconv_get_builtin_trans (strtab + to_module->fromname_offset, + &result[idx]); + + ++*nsteps; + } + + return __GCONV_OK; +} + + +/* Free memory allocated for the transformation record. */ +void +internal_function +__gconv_release_cache (struct __gconv_step *steps, size_t nsteps) +{ + if (gconv_cache != NULL) + /* The only thing we have to deallocate is the record with the + steps. */ + free (steps); +} + + +/* Free all resources if necessary. */ +libc_freeres_fn (free_mem) +{ + if (cache_malloced) + free (gconv_cache); +#ifdef _POSIX_MAPPED_FILES + else if (gconv_cache != NULL) + __munmap (gconv_cache, cache_size); +#endif +} |