2001-07-23 01:47:08 +08:00
|
|
|
/* Cache handling for iconv modules.
|
2024-01-02 02:12:26 +08:00
|
|
|
Copyright (C) 2001-2024 Free Software Foundation, Inc.
|
2001-07-23 01:47:08 +08:00
|
|
|
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
|
2012-02-10 07:18:22 +08:00
|
|
|
License along with the GNU C Library; if not, see
|
Prefer https to http for gnu.org and fsf.org URLs
Also, change sources.redhat.com to sourceware.org.
This patch was automatically generated by running the following shell
script, which uses GNU sed, and which avoids modifying files imported
from upstream:
sed -ri '
s,(http|ftp)(://(.*\.)?(gnu|fsf|sourceware)\.org($|[^.]|\.[^a-z])),https\2,g
s,(http|ftp)(://(.*\.)?)sources\.redhat\.com($|[^.]|\.[^a-z]),https\2sourceware.org\4,g
' \
$(find $(git ls-files) -prune -type f \
! -name '*.po' \
! -name 'ChangeLog*' \
! -path COPYING ! -path COPYING.LIB \
! -path manual/fdl-1.3.texi ! -path manual/lgpl-2.1.texi \
! -path manual/texinfo.tex ! -path scripts/config.guess \
! -path scripts/config.sub ! -path scripts/install-sh \
! -path scripts/mkinstalldirs ! -path scripts/move-if-change \
! -path INSTALL ! -path locale/programs/charmap-kw.h \
! -path po/libc.pot ! -path sysdeps/gnu/errlist.c \
! '(' -name configure \
-execdir test -f configure.ac -o -f configure.in ';' ')' \
! '(' -name preconfigure \
-execdir test -f preconfigure.ac ';' ')' \
-print)
and then by running 'make dist-prepare' to regenerate files built
from the altered files, and then executing the following to cleanup:
chmod a+x sysdeps/unix/sysv/linux/riscv/configure
# Omit irrelevant whitespace and comment-only changes,
# perhaps from a slightly-different Autoconf version.
git checkout -f \
sysdeps/csky/configure \
sysdeps/hppa/configure \
sysdeps/riscv/configure \
sysdeps/unix/sysv/linux/csky/configure
# Omit changes that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/powerpc/powerpc64/ppc-mcount.S: trailing lines
git checkout -f \
sysdeps/powerpc/powerpc64/ppc-mcount.S \
sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
# Omit change that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S: last line does not end in newline
git checkout -f sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S
2019-09-07 13:40:42 +08:00
|
|
|
<https://www.gnu.org/licenses/>. */
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
#include <dlfcn.h>
|
2003-04-20 00:57:17 +08:00
|
|
|
#include <errno.h>
|
2001-07-23 01:47:08 +08:00
|
|
|
#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>
|
2003-04-20 00:57:17 +08:00
|
|
|
#include <not-cancel.h>
|
2022-10-18 23:00:07 +08:00
|
|
|
#include <pointer_guard.h>
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
#include "../intl/hash-string.h"
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
static void *gconv_cache;
|
2001-07-23 01:47:08 +08:00
|
|
|
static size_t cache_size;
|
|
|
|
static int cache_malloced;
|
|
|
|
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
void *
|
|
|
|
__gconv_get_cache (void)
|
|
|
|
{
|
|
|
|
return gconv_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-23 01:47:08 +08:00
|
|
|
int
|
|
|
|
__gconv_load_cache (void)
|
|
|
|
{
|
|
|
|
int fd;
|
2021-03-03 04:06:02 +08:00
|
|
|
struct __stat64_t64 st;
|
2001-07-23 01:47:08 +08:00
|
|
|
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. */
|
2023-04-20 00:02:03 +08:00
|
|
|
fd = __open_nocancel (GCONV_MODULES_CACHE, O_RDONLY | O_CLOEXEC, 0);
|
2001-07-23 01:47:08 +08:00
|
|
|
if (__builtin_expect (fd, 0) == -1)
|
|
|
|
/* Not available. */
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Get information about the file. */
|
2021-03-03 04:06:02 +08:00
|
|
|
if (__glibc_unlikely (__fstat64_time64 (fd, &st) < 0)
|
2001-07-23 01:47:08 +08:00
|
|
|
/* We do not have to start looking at the file if it cannot contain
|
|
|
|
at least the cache header. */
|
2002-08-25 12:51:08 +08:00
|
|
|
|| (size_t) st.st_size < sizeof (struct gconvcache_header))
|
2001-07-23 01:47:08 +08:00
|
|
|
{
|
|
|
|
close_and_exit:
|
2017-07-04 02:22:58 +08:00
|
|
|
__close_nocancel_nostatus (fd);
|
2001-07-23 01:47:08 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make the file content available. */
|
|
|
|
cache_size = st.st_size;
|
|
|
|
#ifdef _POSIX_MAPPED_FILES
|
2002-08-05 07:32:14 +08:00
|
|
|
gconv_cache = __mmap (NULL, cache_size, PROT_READ, MAP_SHARED, fd, 0);
|
2014-02-10 21:45:42 +08:00
|
|
|
if (__glibc_unlikely (gconv_cache == MAP_FAILED))
|
2001-07-23 01:47:08 +08:00
|
|
|
#endif
|
|
|
|
{
|
|
|
|
size_t already_read;
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
gconv_cache = malloc (cache_size);
|
|
|
|
if (gconv_cache == NULL)
|
2001-07-23 01:47:08 +08:00
|
|
|
goto close_and_exit;
|
|
|
|
|
|
|
|
already_read = 0;
|
|
|
|
do
|
|
|
|
{
|
2002-08-05 07:32:14 +08:00
|
|
|
ssize_t n = __read (fd, (char *) gconv_cache + already_read,
|
2001-07-23 01:47:08 +08:00
|
|
|
cache_size - already_read);
|
|
|
|
if (__builtin_expect (n, 0) == -1)
|
|
|
|
{
|
2002-08-05 07:32:14 +08:00
|
|
|
free (gconv_cache);
|
|
|
|
gconv_cache = NULL;
|
2001-07-23 01:47:08 +08:00
|
|
|
goto close_and_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_read += n;
|
|
|
|
}
|
|
|
|
while (already_read < cache_size);
|
|
|
|
|
|
|
|
cache_malloced = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We don't need the file descriptor anymore. */
|
2017-07-04 02:22:58 +08:00
|
|
|
__close_nocancel_nostatus (fd);
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
/* Check the consistency. */
|
2002-08-05 07:32:14 +08:00
|
|
|
header = (struct gconvcache_header *) gconv_cache;
|
2001-07-23 01:47:08 +08:00
|
|
|
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)
|
|
|
|
{
|
2002-08-05 07:32:14 +08:00
|
|
|
free (gconv_cache);
|
2001-07-23 01:47:08 +08:00
|
|
|
cache_malloced = 0;
|
|
|
|
}
|
|
|
|
#ifdef _POSIX_MAPPED_FILES
|
|
|
|
else
|
2002-08-05 07:32:14 +08:00
|
|
|
__munmap (gconv_cache, cache_size);
|
2001-07-23 01:47:08 +08:00
|
|
|
#endif
|
2002-08-05 07:32:14 +08:00
|
|
|
gconv_cache = NULL;
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* That worked. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
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;
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
header = (const struct gconvcache_header *) gconv_cache;
|
|
|
|
strtab = (char *) gconv_cache + header->string_offset;
|
|
|
|
hashtab = (struct hash_entry *) ((char *) gconv_cache
|
2001-09-02 03:03:22 +08:00
|
|
|
+ header->hash_offset);
|
2001-07-23 01:47:08 +08:00
|
|
|
|
2003-06-12 05:57:23 +08:00
|
|
|
hval = __hash_string (str);
|
2001-07-23 01:47:08 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-08-16 13:39:18 +08:00
|
|
|
#ifndef STATIC_GCONV
|
2001-07-23 01:47:08 +08:00
|
|
|
static int
|
|
|
|
find_module (const char *directory, const char *filename,
|
|
|
|
struct __gconv_step *result)
|
|
|
|
{
|
|
|
|
size_t dirlen = strlen (directory);
|
|
|
|
size_t fnamelen = strlen (filename) + 1;
|
2001-07-25 05:30:18 +08:00
|
|
|
char fullname[dirlen + fnamelen];
|
2001-07-24 07:01:08 +08:00
|
|
|
int status = __GCONV_NOCONV;
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
memcpy (__mempcpy (fullname, directory, dirlen), filename, fnamelen);
|
|
|
|
|
|
|
|
result->__shlib_handle = __gconv_find_shlib (fullname);
|
2001-07-24 07:01:08 +08:00
|
|
|
if (result->__shlib_handle != NULL)
|
|
|
|
{
|
|
|
|
status = __GCONV_OK;
|
|
|
|
|
2001-07-25 05:30:18 +08:00
|
|
|
result->__modname = NULL;
|
2001-07-24 07:01:08 +08:00
|
|
|
result->__fct = result->__shlib_handle->fct;
|
|
|
|
result->__init_fct = result->__shlib_handle->init_fct;
|
|
|
|
result->__end_fct = result->__shlib_handle->end_fct;
|
2001-07-23 01:47:08 +08:00
|
|
|
|
2002-12-03 06:39:58 +08:00
|
|
|
/* These settings can be overridden by the init function. */
|
|
|
|
result->__btowc_fct = NULL;
|
2001-07-24 07:01:08 +08:00
|
|
|
result->__data = NULL;
|
2002-12-03 06:39:58 +08:00
|
|
|
|
|
|
|
/* Call the init function. */
|
2017-08-29 21:53:28 +08:00
|
|
|
__gconv_init_fct init_fct = result->__init_fct;
|
|
|
|
PTR_DEMANGLE (init_fct);
|
|
|
|
if (init_fct != NULL)
|
|
|
|
{
|
2005-12-21 00:35:25 +08:00
|
|
|
status = DL_CALL_FCT (init_fct, (result));
|
2017-08-29 21:53:28 +08:00
|
|
|
PTR_MANGLE (result->__btowc_fct);
|
2005-12-21 00:35:25 +08:00
|
|
|
}
|
2001-07-24 07:01:08 +08:00
|
|
|
}
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
2001-08-16 13:39:18 +08:00
|
|
|
#endif
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
|
2001-07-28 01:26:26 +08:00
|
|
|
int
|
|
|
|
__gconv_compare_alias_cache (const char *name1, const char *name2, int *result)
|
|
|
|
{
|
|
|
|
size_t name1_idx;
|
|
|
|
size_t name2_idx;
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
if (gconv_cache == NULL)
|
2001-07-28 01:26:26 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-23 01:47:08 +08:00
|
|
|
int
|
|
|
|
__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;
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
if (gconv_cache == NULL)
|
2001-07-23 01:47:08 +08:00
|
|
|
/* We have no cache available. */
|
|
|
|
return __GCONV_NODB;
|
|
|
|
|
2002-08-05 07:32:14 +08:00
|
|
|
header = (const struct gconvcache_header *) gconv_cache;
|
|
|
|
strtab = (char *) gconv_cache + header->string_offset;
|
|
|
|
modtab = (const struct module_entry *) ((char *) gconv_cache
|
2001-07-23 01:47:08 +08:00
|
|
|
+ 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))
|
2001-07-24 15:54:33 +08:00
|
|
|
return __GCONV_NOCONV;
|
2001-07-23 01:47:08 +08:00
|
|
|
to_module = &modtab[toidx];
|
|
|
|
|
|
|
|
/* Avoid copy-only transformations if the user requests. */
|
|
|
|
if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0) && fromidx == toidx)
|
2007-07-29 03:02:07 +08:00
|
|
|
return __GCONV_NULCONV;
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
/* If there are special conversions available examine them first. */
|
2001-07-24 07:01:08 +08:00
|
|
|
if (fromidx != 0 && toidx != 0
|
|
|
|
&& __builtin_expect (from_module->extra_offset, 0) != 0)
|
2001-07-23 01:47:08 +08:00
|
|
|
{
|
|
|
|
/* 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. */
|
2002-08-05 07:32:14 +08:00
|
|
|
extra = (const struct extra_entry *) ((char *) gconv_cache
|
2001-07-23 01:47:08 +08:00
|
|
|
+ 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;
|
|
|
|
|
2001-07-23 17:10:36 +08:00
|
|
|
result[idx].__counter = 1;
|
|
|
|
result[idx].__data = NULL;
|
|
|
|
|
2001-07-23 01:47:08 +08:00
|
|
|
#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. */
|
2001-07-24 07:01:08 +08:00
|
|
|
if ((fromidx != 0 && __builtin_expect (from_module->fromname_offset, 1) == 0)
|
2001-07-25 02:49:46 +08:00
|
|
|
|| (toidx != 0 && __builtin_expect (to_module->toname_offset, 1) == 0)
|
|
|
|
|| (fromidx == 0 && toidx == 0))
|
2001-07-23 01:47:08 +08:00
|
|
|
/* Not possible. Nothing we can do. */
|
|
|
|
return __GCONV_NOCONV;
|
|
|
|
|
2001-07-25 02:49:46 +08:00
|
|
|
/* We will use up to two modules. Always allocate room for two. */
|
2001-07-23 01:47:08 +08:00
|
|
|
result = (struct __gconv_step *) malloc (2 * sizeof (struct __gconv_step));
|
|
|
|
if (result == NULL)
|
|
|
|
return __GCONV_NOMEM;
|
|
|
|
|
|
|
|
*handle = result;
|
2001-07-24 07:01:08 +08:00
|
|
|
*nsteps = 0;
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
/* Generate data structure for conversion to INTERNAL. */
|
2001-07-24 07:01:08 +08:00
|
|
|
if (fromidx != 0)
|
|
|
|
{
|
|
|
|
result[0].__from_name = (char *) strtab + from_module->canonname_offset;
|
|
|
|
result[0].__to_name = (char *) "INTERNAL";
|
2001-07-23 01:47:08 +08:00
|
|
|
|
2001-07-24 07:01:08 +08:00
|
|
|
result[0].__counter = 1;
|
|
|
|
result[0].__data = NULL;
|
2001-07-23 17:10:36 +08:00
|
|
|
|
2001-07-23 01:47:08 +08:00
|
|
|
#ifndef STATIC_GCONV
|
2001-07-24 07:01:08 +08:00
|
|
|
if (strtab[from_module->todir_offset] != '\0')
|
2001-07-23 01:47:08 +08:00
|
|
|
{
|
2001-07-24 07:01:08 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
2001-07-23 01:47:08 +08:00
|
|
|
}
|
2001-07-24 07:01:08 +08:00
|
|
|
else
|
2001-07-23 01:47:08 +08:00
|
|
|
#endif
|
2001-07-24 07:01:08 +08:00
|
|
|
/* It's a builtin transformation. */
|
|
|
|
__gconv_get_builtin_trans (strtab + from_module->toname_offset,
|
|
|
|
&result[0]);
|
|
|
|
|
|
|
|
++*nsteps;
|
|
|
|
}
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
/* Generate data structure for conversion from INTERNAL. */
|
2001-07-24 07:01:08 +08:00
|
|
|
if (toidx != 0)
|
|
|
|
{
|
|
|
|
int idx = *nsteps;
|
2001-07-23 01:47:08 +08:00
|
|
|
|
2001-07-24 07:01:08 +08:00
|
|
|
result[idx].__from_name = (char *) "INTERNAL";
|
|
|
|
result[idx].__to_name = (char *) strtab + to_module->canonname_offset;
|
|
|
|
|
|
|
|
result[idx].__counter = 1;
|
|
|
|
result[idx].__data = NULL;
|
2001-07-23 17:10:36 +08:00
|
|
|
|
2001-07-23 01:47:08 +08:00
|
|
|
#ifndef STATIC_GCONV
|
2001-07-24 07:01:08 +08:00
|
|
|
if (strtab[to_module->fromdir_offset] != '\0')
|
2001-07-23 01:47:08 +08:00
|
|
|
{
|
2001-07-24 07:01:08 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
2001-07-23 01:47:08 +08:00
|
|
|
}
|
2001-07-24 07:01:08 +08:00
|
|
|
else
|
2001-07-23 01:47:08 +08:00
|
|
|
#endif
|
2001-07-24 07:01:08 +08:00
|
|
|
/* It's a builtin transformation. */
|
|
|
|
__gconv_get_builtin_trans (strtab + to_module->fromname_offset,
|
|
|
|
&result[idx]);
|
|
|
|
|
|
|
|
++*nsteps;
|
|
|
|
}
|
2001-07-23 01:47:08 +08:00
|
|
|
|
|
|
|
return __GCONV_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-25 05:30:18 +08:00
|
|
|
/* Free memory allocated for the transformation record. */
|
|
|
|
void
|
|
|
|
__gconv_release_cache (struct __gconv_step *steps, size_t nsteps)
|
|
|
|
{
|
2019-07-31 17:43:59 +08:00
|
|
|
if (gconv_cache != NULL)
|
|
|
|
/* The only thing we have to deallocate is the record with the
|
|
|
|
steps. */
|
2001-07-25 05:30:18 +08:00
|
|
|
free (steps);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-07-23 01:47:08 +08:00
|
|
|
/* Free all resources if necessary. */
|
2022-12-28 05:11:42 +08:00
|
|
|
void
|
|
|
|
__gconv_cache_freemem (void)
|
2001-07-23 01:47:08 +08:00
|
|
|
{
|
|
|
|
if (cache_malloced)
|
2002-08-05 07:32:14 +08:00
|
|
|
free (gconv_cache);
|
2001-07-23 01:47:08 +08:00
|
|
|
#ifdef _POSIX_MAPPED_FILES
|
2006-01-11 15:08:29 +08:00
|
|
|
else if (gconv_cache != NULL)
|
2002-08-05 07:32:14 +08:00
|
|
|
__munmap (gconv_cache, cache_size);
|
2001-07-23 01:47:08 +08:00
|
|
|
#endif
|
|
|
|
}
|