📄 utf.c
字号:
/* * utf.c: UTF-8 conversion routines * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <string.h>#include <assert.h>#include <apr_strings.h>#include <apr_lib.h>#include <apr_xlate.h>#include "svn_string.h"#include "svn_error.h"#include "svn_pools.h"#include "svn_ctype.h"#include "svn_utf.h"#include "utf_impl.h"#include "svn_private_config.h"#define SVN_UTF_NTOU_XLATE_HANDLE "svn-utf-ntou-xlate-handle"#define SVN_UTF_UTON_XLATE_HANDLE "svn-utf-uton-xlate-handle"#ifndef AS400#define SVN_APR_UTF8_CHARSET "UTF-8"#else#define SVN_APR_UTF8_CHARSET (const char*)1208#endif#if APR_HAS_THREADSstatic apr_thread_mutex_t *xlate_handle_mutex = NULL;#endif/* The xlate handle cache is a global hash table with linked lists of xlate * handles. In multi-threaded environments, a thread "borrows" an xlate * handle from the cache during a translation and puts it back afterwards. * This avoids holding a global lock for all translations. * If there is no handle for a particular key when needed, a new is * handle is created and put in the cache after use. * This means that there will be at most N handles open for a key, where N * is the number of simultanous handles in use for that key. */typedef struct xlate_handle_node_t { apr_xlate_t *handle; /* FALSE if the handle is not valid, since its pool is being destroyed. */ svn_boolean_t valid; /* The name of a char encoding or APR_LOCALE_CHARSET. */ const char *frompage, *topage; struct xlate_handle_node_t *next;} xlate_handle_node_t;/* This maps const char * userdata_key strings to xlate_handle_node_t ** handles to the first entry in the linked list of xlate handles. We don't store the pointer to the list head directly in the hash table, since we remove/insert entries at the head in the list in the code below, and we can't use apr_hash_set() in each character translation because that function allocates memory in each call where the value is non-NULL. Since these allocations take place in a global pool, this would be a memory leak. */static apr_hash_t *xlate_handle_hash = NULL;/* Clean up the xlate handle cache. */static apr_status_txlate_cleanup(void *arg){ /* We set the cache variables to NULL so that translation works in other cleanup functions, even if it isn't cached then. */#if APR_HAS_THREADS apr_thread_mutex_destroy(xlate_handle_mutex); xlate_handle_mutex = NULL;#endif xlate_handle_hash = NULL; return APR_SUCCESS;}/* Set the handle of ARG to NULL. */static apr_status_txlate_handle_node_cleanup(void *arg){ xlate_handle_node_t *node = arg; node->valid = FALSE; return APR_SUCCESS;}voidsvn_utf_initialize(apr_pool_t *pool){ apr_pool_t *subpool;#if APR_HAS_THREADS apr_thread_mutex_t *mutex;#endif if (!xlate_handle_hash) { /* We create our own subpool, which we protect with the mutex. We can't use the pool passed to us by the caller, since we will use it for xlate handle allocations, possibly in multiple threads, and pool allocation is not thread-safe. */ subpool = svn_pool_create(pool);#if APR_HAS_THREADS if (apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_DEFAULT, subpool) == APR_SUCCESS) xlate_handle_mutex = mutex; else return;#endif xlate_handle_hash = apr_hash_make(subpool); apr_pool_cleanup_register(subpool, NULL, xlate_cleanup, apr_pool_cleanup_null); }}/* Return a unique string key based on TOPAGE and FROMPAGE. TOPAGE and * FROMPAGE can be any valid arguments of the same name to * apr_xlate_open(). Allocate the returned string in POOL. */static const char*get_xlate_key(const char *topage, const char *frompage, apr_pool_t *pool){#ifndef AS400 /* In the cases of SVN_APR_LOCALE_CHARSET and SVN_APR_DEFAULT_CHARSET * topage/frompage is really an int, not a valid string. So generate a * unique key accordingly. */ if (frompage == SVN_APR_LOCALE_CHARSET) frompage = "APR_LOCALE_CHARSET"; else if (frompage == SVN_APR_DEFAULT_CHARSET) frompage = "APR_DEFAULT_CHARSET"; if (topage == SVN_APR_LOCALE_CHARSET) topage = "APR_LOCALE_CHARSET"; else if (topage == SVN_APR_DEFAULT_CHARSET) topage = "APR_DEFAULT_CHARSET"; return apr_pstrcat(pool, "svn-utf-", frompage, "to", topage, "-xlate-handle", NULL);#else /* OS400 code pages are always ints. */ return apr_psprintf(pool, "svn-utf-%dto%d-xlate-handle", (int)frompage, (int)topage);#endif}/* Set *RET to a handle node for converting from FROMPAGE to TOPAGE, creating the handle node if it doesn't exist in USERDATA_KEY. If a node is not cached and apr_xlate_open() returns APR_EINVAL or APR_ENOTIMPL, set (*RET)->handle to NULL. If fail for any other reason, return the error. Allocate *RET and its xlate handle in POOL if svn_utf_initialize() hasn't been called or USERDATA_KEY is NULL. Else, allocate them in the pool of xlate_handle_hash. */static svn_error_t *get_xlate_handle_node(xlate_handle_node_t **ret, const char *topage, const char *frompage, const char *userdata_key, apr_pool_t *pool){ xlate_handle_node_t **old_node_p; xlate_handle_node_t *old_node = NULL; apr_status_t apr_err; apr_xlate_t *handle; svn_error_t *err = NULL; /* If we already have a handle, just return it. */ if (userdata_key) { if (xlate_handle_hash) {#if APR_HAS_THREADS apr_err = apr_thread_mutex_lock(xlate_handle_mutex); if (apr_err != APR_SUCCESS) return svn_error_create(apr_err, NULL, _("Can't lock charset translation mutex"));#endif old_node_p = apr_hash_get(xlate_handle_hash, userdata_key, APR_HASH_KEY_STRING); if (old_node_p) old_node = *old_node_p; if (old_node) { /* Ensure that the handle is still valid. */ if (old_node->valid) { /* Remove from the list. */ *old_node_p = old_node->next; old_node->next = NULL;#if APR_HAS_THREADS apr_err = apr_thread_mutex_unlock(xlate_handle_mutex); if (apr_err != APR_SUCCESS) return svn_error_create(apr_err, NULL, _("Can't unlock charset " "translation mutex"));#endif *ret = old_node; return SVN_NO_ERROR; } } } else { void *p; /* We fall back on a per-pool cache instead. */ apr_pool_userdata_get(&p, userdata_key, pool); old_node = p; /* Ensure that the handle is still valid. */ if (old_node && old_node->valid) { *ret = old_node; return SVN_NO_ERROR; } } } /* Note that we still have the mutex locked (if it is initialized), so we can use the global pool for creating the new xlate handle. */ /* The error handling doesn't support the following cases, since we don't use them currently. Catch this here. */#ifndef AS400 /* On OS400 V5R4 with UTF support, APR_DEFAULT_CHARSET and * APR_LOCALE_CHARSET are both UTF-8 (CCSID 1208), so we won't get far * with this assert active. */ assert(frompage != SVN_APR_DEFAULT_CHARSET && topage != SVN_APR_DEFAULT_CHARSET && (frompage != SVN_APR_LOCALE_CHARSET || topage != SVN_APR_LOCALE_CHARSET));#endif /* Use the correct pool for creating the handle. */ if (userdata_key && xlate_handle_hash) pool = apr_hash_pool_get(xlate_handle_hash); /* Try to create a handle. */#ifndef AS400 apr_err = apr_xlate_open(&handle, topage, frompage, pool);#else apr_err = apr_xlate_open(&handle, (int)topage, (int)frompage, pool);#endif if (APR_STATUS_IS_EINVAL(apr_err) || APR_STATUS_IS_ENOTIMPL(apr_err)) handle = NULL; else if (apr_err != APR_SUCCESS) { const char *errstr; /* Can't use svn_error_wrap_apr here because it calls functions in this file, leading to infinite recursion. */#ifndef AS400 if (frompage == SVN_APR_LOCALE_CHARSET) errstr = apr_psprintf(pool, _("Can't create a character converter from " "native encoding to '%s'"), topage); else if (topage == SVN_APR_LOCALE_CHARSET) errstr = apr_psprintf(pool, _("Can't create a character converter from " "'%s' to native encoding"), frompage); else errstr = apr_psprintf(pool, _("Can't create a character converter from " "'%s' to '%s'"), frompage, topage);#else /* Handle the error condition normally prevented by the assert * above. */ errstr = apr_psprintf(pool, _("Can't create a character converter from " "'%i' to '%i'"), frompage, topage);#endif err = svn_error_create(apr_err, NULL, errstr); goto cleanup; } /* Allocate and initialize the node. */ *ret = apr_palloc(pool, sizeof(xlate_handle_node_t)); (*ret)->handle = handle; (*ret)->valid = TRUE; (*ret)->frompage = ((frompage != SVN_APR_LOCALE_CHARSET) ? apr_pstrdup(pool, frompage) : frompage); (*ret)->topage = ((topage != SVN_APR_LOCALE_CHARSET) ? apr_pstrdup(pool, topage) : topage); (*ret)->next = NULL; /* If we are called from inside a pool cleanup handler, the just created xlate handle will be closed when that handler returns by a newly registered cleanup handler, however, the handle is still cached by us. To prevent this, we register a cleanup handler that will reset the valid flag of our node, so we don't use an invalid handle. */ if (handle) apr_pool_cleanup_register(pool, *ret, xlate_handle_node_cleanup, apr_pool_cleanup_null); cleanup: /* Don't need the lock anymore. */#if APR_HAS_THREADS if (userdata_key && xlate_handle_hash) { apr_status_t unlock_err = apr_thread_mutex_unlock(xlate_handle_mutex); if (unlock_err != APR_SUCCESS) return svn_error_create(unlock_err, NULL, _("Can't unlock charset translation mutex")); }#endif return err;}/* Put back NODE into the xlate handle cache for use by other calls. If there is no global cache, store the handle in POOL. Ignore errors related to locking/unlocking the mutex. ### Mutex errors here are very weird. Should we handle them "correctly"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -