1251881Speter/*
2251881Speter * utf.c:  UTF-8 conversion routines
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <stdlib.h>
27251881Speter#include <string.h>
28251881Speter#include <assert.h>
29251881Speter
30251881Speter#include <apr_strings.h>
31251881Speter#include <apr_lib.h>
32251881Speter#include <apr_xlate.h>
33251881Speter#include <apr_atomic.h>
34251881Speter
35251881Speter#include "svn_hash.h"
36251881Speter#include "svn_string.h"
37251881Speter#include "svn_error.h"
38251881Speter#include "svn_pools.h"
39251881Speter#include "svn_ctype.h"
40251881Speter#include "svn_utf.h"
41251881Speter#include "svn_private_config.h"
42251881Speter#include "win32_xlate.h"
43251881Speter
44251881Speter#include "private/svn_utf_private.h"
45251881Speter#include "private/svn_dep_compat.h"
46251881Speter#include "private/svn_string_private.h"
47251881Speter#include "private/svn_mutex.h"
48251881Speter
49251881Speter
50251881Speter
51251881Speter/* Use these static strings to maximize performance on standard conversions.
52251881Speter * Any strings on other locations are still valid, however.
53251881Speter */
54251881Speterstatic const char *SVN_UTF_NTOU_XLATE_HANDLE = "svn-utf-ntou-xlate-handle";
55251881Speterstatic const char *SVN_UTF_UTON_XLATE_HANDLE = "svn-utf-uton-xlate-handle";
56251881Speter
57251881Speterstatic const char *SVN_APR_UTF8_CHARSET = "UTF-8";
58251881Speter
59251881Speterstatic svn_mutex__t *xlate_handle_mutex = NULL;
60251881Speterstatic svn_boolean_t assume_native_charset_is_utf8 = FALSE;
61251881Speter
62251881Speter/* The xlate handle cache is a global hash table with linked lists of xlate
63251881Speter * handles.  In multi-threaded environments, a thread "borrows" an xlate
64251881Speter * handle from the cache during a translation and puts it back afterwards.
65251881Speter * This avoids holding a global lock for all translations.
66251881Speter * If there is no handle for a particular key when needed, a new is
67251881Speter * handle is created and put in the cache after use.
68251881Speter * This means that there will be at most N handles open for a key, where N
69251881Speter * is the number of simultanous handles in use for that key. */
70251881Speter
71251881Spetertypedef struct xlate_handle_node_t {
72251881Speter  apr_xlate_t *handle;
73251881Speter  /* FALSE if the handle is not valid, since its pool is being
74251881Speter     destroyed. */
75251881Speter  svn_boolean_t valid;
76251881Speter  /* The name of a char encoding or APR_LOCALE_CHARSET. */
77251881Speter  const char *frompage, *topage;
78251881Speter  struct xlate_handle_node_t *next;
79251881Speter} xlate_handle_node_t;
80251881Speter
81251881Speter/* This maps const char * userdata_key strings to xlate_handle_node_t **
82251881Speter   handles to the first entry in the linked list of xlate handles.  We don't
83251881Speter   store the pointer to the list head directly in the hash table, since we
84251881Speter   remove/insert entries at the head in the list in the code below, and
85251881Speter   we can't use apr_hash_set() in each character translation because that
86251881Speter   function allocates memory in each call where the value is non-NULL.
87251881Speter   Since these allocations take place in a global pool, this would be a
88251881Speter   memory leak. */
89251881Speterstatic apr_hash_t *xlate_handle_hash = NULL;
90251881Speter
91251881Speter/* "1st level cache" to standard conversion maps. We may access these
92251881Speter * using atomic xchange ops, i.e. without further thread synchronization.
93251881Speter * If the respective item is NULL, fallback to hash lookup.
94251881Speter */
95251881Speterstatic void * volatile xlat_ntou_static_handle = NULL;
96251881Speterstatic void * volatile xlat_uton_static_handle = NULL;
97251881Speter
98251881Speter/* Clean up the xlate handle cache. */
99251881Speterstatic apr_status_t
100251881Speterxlate_cleanup(void *arg)
101251881Speter{
102251881Speter  /* We set the cache variables to NULL so that translation works in other
103251881Speter     cleanup functions, even if it isn't cached then. */
104251881Speter  xlate_handle_hash = NULL;
105251881Speter
106251881Speter  /* ensure no stale objects get accessed */
107251881Speter  xlat_ntou_static_handle = NULL;
108251881Speter  xlat_uton_static_handle = NULL;
109251881Speter
110251881Speter  return APR_SUCCESS;
111251881Speter}
112251881Speter
113251881Speter/* Set the handle of ARG to NULL. */
114251881Speterstatic apr_status_t
115251881Speterxlate_handle_node_cleanup(void *arg)
116251881Speter{
117251881Speter  xlate_handle_node_t *node = arg;
118251881Speter
119251881Speter  node->valid = FALSE;
120251881Speter  return APR_SUCCESS;
121251881Speter}
122251881Speter
123251881Spetervoid
124251881Spetersvn_utf_initialize2(svn_boolean_t assume_native_utf8,
125251881Speter                    apr_pool_t *pool)
126251881Speter{
127251881Speter  if (!xlate_handle_hash)
128251881Speter    {
129251881Speter      /* We create our own subpool, which we protect with the mutex.
130251881Speter         We can't use the pool passed to us by the caller, since we will
131251881Speter         use it for xlate handle allocations, possibly in multiple threads,
132251881Speter         and pool allocation is not thread-safe. */
133251881Speter      apr_pool_t *subpool = svn_pool_create(pool);
134251881Speter      svn_mutex__t *mutex;
135251881Speter      svn_error_t *err = svn_mutex__init(&mutex, TRUE, subpool);
136251881Speter      if (err)
137251881Speter        {
138251881Speter          svn_error_clear(err);
139251881Speter          return;
140251881Speter        }
141251881Speter
142251881Speter      xlate_handle_mutex = mutex;
143251881Speter      xlate_handle_hash = apr_hash_make(subpool);
144251881Speter
145251881Speter      apr_pool_cleanup_register(subpool, NULL, xlate_cleanup,
146251881Speter                                apr_pool_cleanup_null);
147251881Speter    }
148251881Speter
149251881Speter    if (!assume_native_charset_is_utf8)
150251881Speter      assume_native_charset_is_utf8 = assume_native_utf8;
151251881Speter}
152251881Speter
153251881Speter/* Return a unique string key based on TOPAGE and FROMPAGE.  TOPAGE and
154251881Speter * FROMPAGE can be any valid arguments of the same name to
155251881Speter * apr_xlate_open().  Allocate the returned string in POOL. */
156251881Speterstatic const char*
157251881Speterget_xlate_key(const char *topage,
158251881Speter              const char *frompage,
159251881Speter              apr_pool_t *pool)
160251881Speter{
161251881Speter  /* In the cases of SVN_APR_LOCALE_CHARSET and SVN_APR_DEFAULT_CHARSET
162251881Speter   * topage/frompage is really an int, not a valid string.  So generate a
163251881Speter   * unique key accordingly. */
164251881Speter  if (frompage == SVN_APR_LOCALE_CHARSET)
165251881Speter    frompage = "APR_LOCALE_CHARSET";
166251881Speter  else if (frompage == SVN_APR_DEFAULT_CHARSET)
167251881Speter    frompage = "APR_DEFAULT_CHARSET";
168251881Speter
169251881Speter  if (topage == SVN_APR_LOCALE_CHARSET)
170251881Speter    topage = "APR_LOCALE_CHARSET";
171251881Speter  else if (topage == SVN_APR_DEFAULT_CHARSET)
172251881Speter    topage = "APR_DEFAULT_CHARSET";
173251881Speter
174251881Speter  return apr_pstrcat(pool, "svn-utf-", frompage, "to", topage,
175251881Speter                     "-xlate-handle", (char *)NULL);
176251881Speter}
177251881Speter
178251881Speter/* Atomically replace the content in *MEM with NEW_VALUE and return
179251881Speter * the previous content of *MEM. If atomicy cannot be guaranteed,
180251881Speter * *MEM will not be modified and NEW_VALUE is simply returned to
181251881Speter * the caller.
182251881Speter */
183251881Speterstatic APR_INLINE void*
184251881Speteratomic_swap(void * volatile * mem, void *new_value)
185251881Speter{
186251881Speter#if APR_HAS_THREADS
187251881Speter#if APR_VERSION_AT_LEAST(1,3,0)
188251881Speter  /* Cast is necessary because of APR bug:
189251881Speter     https://issues.apache.org/bugzilla/show_bug.cgi?id=50731 */
190251881Speter   return apr_atomic_xchgptr((volatile void **)mem, new_value);
191251881Speter#else
192251881Speter   /* old APRs don't support atomic swaps. Simply return the
193251881Speter    * input to the caller for further proccessing. */
194251881Speter   return new_value;
195251881Speter#endif
196251881Speter#else
197251881Speter   /* no threads - no sync. necessary */
198251881Speter   void *old_value = (void*)*mem;
199251881Speter   *mem = new_value;
200251881Speter   return old_value;
201251881Speter#endif
202251881Speter}
203251881Speter
204251881Speter/* Set *RET to a newly created handle node for converting from FROMPAGE
205251881Speter   to TOPAGE, If apr_xlate_open() returns APR_EINVAL or APR_ENOTIMPL, set
206251881Speter   (*RET)->handle to NULL.  If fail for any other reason, return the error.
207251881Speter   Allocate *RET and its xlate handle in POOL. */
208251881Speterstatic svn_error_t *
209251881Speterxlate_alloc_handle(xlate_handle_node_t **ret,
210251881Speter                   const char *topage, const char *frompage,
211251881Speter                   apr_pool_t *pool)
212251881Speter{
213251881Speter  apr_status_t apr_err;
214251881Speter  apr_xlate_t *handle;
215251881Speter
216251881Speter  /* The error handling doesn't support the following cases, since we don't
217251881Speter     use them currently.  Catch this here. */
218251881Speter  SVN_ERR_ASSERT(frompage != SVN_APR_DEFAULT_CHARSET
219251881Speter                 && topage != SVN_APR_DEFAULT_CHARSET
220251881Speter                 && (frompage != SVN_APR_LOCALE_CHARSET
221251881Speter                     || topage != SVN_APR_LOCALE_CHARSET));
222251881Speter
223251881Speter  /* Try to create a handle. */
224251881Speter#if defined(WIN32)
225251881Speter  apr_err = svn_subr__win32_xlate_open((win32_xlate_t **)&handle, topage,
226251881Speter                                       frompage, pool);
227251881Speter#else
228251881Speter  apr_err = apr_xlate_open(&handle, topage, frompage, pool);
229251881Speter#endif
230251881Speter
231251881Speter  if (APR_STATUS_IS_EINVAL(apr_err) || APR_STATUS_IS_ENOTIMPL(apr_err))
232251881Speter    handle = NULL;
233251881Speter  else if (apr_err != APR_SUCCESS)
234251881Speter    {
235251881Speter      const char *errstr;
236253734Speter      char apr_strerr[512];
237253734Speter
238251881Speter      /* Can't use svn_error_wrap_apr here because it calls functions in
239251881Speter         this file, leading to infinite recursion. */
240251881Speter      if (frompage == SVN_APR_LOCALE_CHARSET)
241251881Speter        errstr = apr_psprintf(pool,
242251881Speter                              _("Can't create a character converter from "
243251881Speter                                "native encoding to '%s'"), topage);
244251881Speter      else if (topage == SVN_APR_LOCALE_CHARSET)
245251881Speter        errstr = apr_psprintf(pool,
246251881Speter                              _("Can't create a character converter from "
247251881Speter                                "'%s' to native encoding"), frompage);
248251881Speter      else
249251881Speter        errstr = apr_psprintf(pool,
250251881Speter                              _("Can't create a character converter from "
251251881Speter                                "'%s' to '%s'"), frompage, topage);
252251881Speter
253253734Speter      /* Just put the error on the stack, since svn_error_create duplicates it
254253734Speter         later.  APR_STRERR will be in the local encoding, not in UTF-8, though.
255253734Speter       */
256253734Speter      svn_strerror(apr_err, apr_strerr, sizeof(apr_strerr));
257253734Speter      return svn_error_create(apr_err,
258253734Speter                              svn_error_create(apr_err, NULL, apr_strerr),
259253734Speter                              errstr);
260251881Speter    }
261251881Speter
262251881Speter  /* Allocate and initialize the node. */
263251881Speter  *ret = apr_palloc(pool, sizeof(xlate_handle_node_t));
264251881Speter  (*ret)->handle = handle;
265251881Speter  (*ret)->valid = TRUE;
266251881Speter  (*ret)->frompage = ((frompage != SVN_APR_LOCALE_CHARSET)
267251881Speter                      ? apr_pstrdup(pool, frompage) : frompage);
268251881Speter  (*ret)->topage = ((topage != SVN_APR_LOCALE_CHARSET)
269251881Speter                    ? apr_pstrdup(pool, topage) : topage);
270251881Speter  (*ret)->next = NULL;
271251881Speter
272251881Speter  /* If we are called from inside a pool cleanup handler, the just created
273251881Speter     xlate handle will be closed when that handler returns by a newly
274251881Speter     registered cleanup handler, however, the handle is still cached by us.
275251881Speter     To prevent this, we register a cleanup handler that will reset the valid
276251881Speter     flag of our node, so we don't use an invalid handle. */
277251881Speter  if (handle)
278251881Speter    apr_pool_cleanup_register(pool, *ret, xlate_handle_node_cleanup,
279251881Speter                              apr_pool_cleanup_null);
280251881Speter
281251881Speter  return SVN_NO_ERROR;
282251881Speter}
283251881Speter
284251881Speter/* Extend xlate_alloc_handle by using USERDATA_KEY as a key in our
285251881Speter   global hash map, if available.
286251881Speter
287251881Speter   Allocate *RET and its xlate handle in POOL if svn_utf_initialize()
288251881Speter   hasn't been called or USERDATA_KEY is NULL.  Else, allocate them
289251881Speter   in the pool of xlate_handle_hash.
290251881Speter
291251881Speter   Note: this function is not thread-safe. Call get_xlate_handle_node
292251881Speter   instead. */
293251881Speterstatic svn_error_t *
294251881Speterget_xlate_handle_node_internal(xlate_handle_node_t **ret,
295251881Speter                               const char *topage, const char *frompage,
296251881Speter                               const char *userdata_key, apr_pool_t *pool)
297251881Speter{
298251881Speter  /* If we already have a handle, just return it. */
299251881Speter  if (userdata_key && xlate_handle_hash)
300251881Speter    {
301251881Speter      xlate_handle_node_t *old_node = NULL;
302251881Speter
303251881Speter      /* 2nd level: hash lookup */
304251881Speter      xlate_handle_node_t **old_node_p = svn_hash_gets(xlate_handle_hash,
305251881Speter                                                       userdata_key);
306251881Speter      if (old_node_p)
307251881Speter        old_node = *old_node_p;
308251881Speter      if (old_node)
309251881Speter        {
310251881Speter          /* Ensure that the handle is still valid. */
311251881Speter          if (old_node->valid)
312251881Speter            {
313251881Speter              /* Remove from the list. */
314251881Speter              *old_node_p = old_node->next;
315251881Speter              old_node->next = NULL;
316251881Speter              *ret = old_node;
317251881Speter              return SVN_NO_ERROR;
318251881Speter            }
319251881Speter        }
320251881Speter    }
321251881Speter
322251881Speter  /* Note that we still have the mutex locked (if it is initialized), so we
323251881Speter     can use the global pool for creating the new xlate handle. */
324251881Speter
325251881Speter  /* Use the correct pool for creating the handle. */
326251881Speter  pool = apr_hash_pool_get(xlate_handle_hash);
327251881Speter
328251881Speter  return xlate_alloc_handle(ret, topage, frompage, pool);
329251881Speter}
330251881Speter
331251881Speter/* Set *RET to a handle node for converting from FROMPAGE to TOPAGE,
332251881Speter   creating the handle node if it doesn't exist in USERDATA_KEY.
333251881Speter   If a node is not cached and apr_xlate_open() returns APR_EINVAL or
334251881Speter   APR_ENOTIMPL, set (*RET)->handle to NULL.  If fail for any other
335251881Speter   reason, return the error.
336251881Speter
337251881Speter   Allocate *RET and its xlate handle in POOL if svn_utf_initialize()
338251881Speter   hasn't been called or USERDATA_KEY is NULL.  Else, allocate them
339251881Speter   in the pool of xlate_handle_hash. */
340251881Speterstatic svn_error_t *
341251881Speterget_xlate_handle_node(xlate_handle_node_t **ret,
342251881Speter                      const char *topage, const char *frompage,
343251881Speter                      const char *userdata_key, apr_pool_t *pool)
344251881Speter{
345251881Speter  xlate_handle_node_t *old_node = NULL;
346251881Speter
347251881Speter  /* If we already have a handle, just return it. */
348251881Speter  if (userdata_key)
349251881Speter    {
350251881Speter      if (xlate_handle_hash)
351251881Speter        {
352251881Speter          /* 1st level: global, static items */
353251881Speter          if (userdata_key == SVN_UTF_NTOU_XLATE_HANDLE)
354251881Speter            old_node = atomic_swap(&xlat_ntou_static_handle, NULL);
355251881Speter          else if (userdata_key == SVN_UTF_UTON_XLATE_HANDLE)
356251881Speter            old_node = atomic_swap(&xlat_uton_static_handle, NULL);
357251881Speter
358251881Speter          if (old_node && old_node->valid)
359251881Speter            {
360251881Speter              *ret = old_node;
361251881Speter              return SVN_NO_ERROR;
362251881Speter            }
363251881Speter        }
364251881Speter      else
365251881Speter        {
366251881Speter          void *p;
367251881Speter          /* We fall back on a per-pool cache instead. */
368251881Speter          apr_pool_userdata_get(&p, userdata_key, pool);
369251881Speter          old_node = p;
370251881Speter          /* Ensure that the handle is still valid. */
371251881Speter          if (old_node && old_node->valid)
372251881Speter            {
373251881Speter              *ret = old_node;
374251881Speter              return SVN_NO_ERROR;
375251881Speter            }
376251881Speter
377251881Speter          return xlate_alloc_handle(ret, topage, frompage, pool);
378251881Speter        }
379251881Speter    }
380251881Speter
381251881Speter  SVN_MUTEX__WITH_LOCK(xlate_handle_mutex,
382251881Speter                       get_xlate_handle_node_internal(ret,
383251881Speter                                                      topage,
384251881Speter                                                      frompage,
385251881Speter                                                      userdata_key,
386251881Speter                                                      pool));
387251881Speter
388251881Speter  return SVN_NO_ERROR;
389251881Speter}
390251881Speter
391251881Speter/* Put back NODE into the xlate handle cache for use by other calls.
392251881Speter
393251881Speter   Note: this function is not thread-safe. Call put_xlate_handle_node
394251881Speter   instead. */
395251881Speterstatic svn_error_t *
396251881Speterput_xlate_handle_node_internal(xlate_handle_node_t *node,
397251881Speter                               const char *userdata_key)
398251881Speter{
399251881Speter  xlate_handle_node_t **node_p = svn_hash_gets(xlate_handle_hash, userdata_key);
400251881Speter  if (node_p == NULL)
401251881Speter    {
402251881Speter      userdata_key = apr_pstrdup(apr_hash_pool_get(xlate_handle_hash),
403251881Speter                                  userdata_key);
404251881Speter      node_p = apr_palloc(apr_hash_pool_get(xlate_handle_hash),
405251881Speter                          sizeof(*node_p));
406251881Speter      *node_p = NULL;
407251881Speter      svn_hash_sets(xlate_handle_hash, userdata_key, node_p);
408251881Speter    }
409251881Speter  node->next = *node_p;
410251881Speter  *node_p = node;
411251881Speter
412251881Speter  return SVN_NO_ERROR;
413251881Speter}
414251881Speter
415251881Speter/* Put back NODE into the xlate handle cache for use by other calls.
416251881Speter   If there is no global cache, store the handle in POOL.
417251881Speter   Ignore errors related to locking/unlocking the mutex. */
418251881Speterstatic svn_error_t *
419251881Speterput_xlate_handle_node(xlate_handle_node_t *node,
420251881Speter                      const char *userdata_key,
421251881Speter                      apr_pool_t *pool)
422251881Speter{
423251881Speter  assert(node->next == NULL);
424251881Speter  if (!userdata_key)
425251881Speter    return SVN_NO_ERROR;
426251881Speter
427251881Speter  /* push previous global node to the hash */
428251881Speter  if (xlate_handle_hash)
429251881Speter    {
430251881Speter      /* 1st level: global, static items */
431251881Speter      if (userdata_key == SVN_UTF_NTOU_XLATE_HANDLE)
432251881Speter        node = atomic_swap(&xlat_ntou_static_handle, node);
433251881Speter      else if (userdata_key == SVN_UTF_UTON_XLATE_HANDLE)
434251881Speter        node = atomic_swap(&xlat_uton_static_handle, node);
435251881Speter      if (node == NULL)
436251881Speter        return SVN_NO_ERROR;
437251881Speter
438251881Speter      SVN_MUTEX__WITH_LOCK(xlate_handle_mutex,
439251881Speter                           put_xlate_handle_node_internal(node,
440251881Speter                                                          userdata_key));
441251881Speter    }
442251881Speter  else
443251881Speter    {
444251881Speter      /* Store it in the per-pool cache. */
445251881Speter      apr_pool_userdata_set(node, userdata_key, apr_pool_cleanup_null, pool);
446251881Speter    }
447251881Speter
448251881Speter  return SVN_NO_ERROR;
449251881Speter}
450251881Speter
451251881Speter/* Return the apr_xlate handle for converting native characters to UTF-8. */
452251881Speterstatic svn_error_t *
453251881Speterget_ntou_xlate_handle_node(xlate_handle_node_t **ret, apr_pool_t *pool)
454251881Speter{
455251881Speter  return get_xlate_handle_node(ret, SVN_APR_UTF8_CHARSET,
456251881Speter                               assume_native_charset_is_utf8
457251881Speter                                 ? SVN_APR_UTF8_CHARSET
458251881Speter                                 : SVN_APR_LOCALE_CHARSET,
459251881Speter                               SVN_UTF_NTOU_XLATE_HANDLE, pool);
460251881Speter}
461251881Speter
462251881Speter
463251881Speter/* Return the apr_xlate handle for converting UTF-8 to native characters.
464251881Speter   Create one if it doesn't exist.  If unable to find a handle, or
465251881Speter   unable to create one because apr_xlate_open returned APR_EINVAL, then
466251881Speter   set *RET to null and return SVN_NO_ERROR; if fail for some other
467251881Speter   reason, return error. */
468251881Speterstatic svn_error_t *
469251881Speterget_uton_xlate_handle_node(xlate_handle_node_t **ret, apr_pool_t *pool)
470251881Speter{
471251881Speter  return get_xlate_handle_node(ret,
472251881Speter                               assume_native_charset_is_utf8
473251881Speter                                 ? SVN_APR_UTF8_CHARSET
474251881Speter                                 : SVN_APR_LOCALE_CHARSET,
475251881Speter                               SVN_APR_UTF8_CHARSET,
476251881Speter                               SVN_UTF_UTON_XLATE_HANDLE, pool);
477251881Speter}
478251881Speter
479251881Speter
480251881Speter/* Copy LEN bytes of SRC, converting non-ASCII and zero bytes to ?\nnn
481251881Speter   sequences, allocating the result in POOL. */
482251881Speterstatic const char *
483251881Speterfuzzy_escape(const char *src, apr_size_t len, apr_pool_t *pool)
484251881Speter{
485251881Speter  const char *src_orig = src, *src_end = src + len;
486251881Speter  apr_size_t new_len = 0;
487251881Speter  char *new;
488251881Speter  const char *new_orig;
489251881Speter
490251881Speter  /* First count how big a dest string we'll need. */
491251881Speter  while (src < src_end)
492251881Speter    {
493251881Speter      if (! svn_ctype_isascii(*src) || *src == '\0')
494251881Speter        new_len += 5;  /* 5 slots, for "?\XXX" */
495251881Speter      else
496251881Speter        new_len += 1;  /* one slot for the 7-bit char */
497251881Speter
498251881Speter      src++;
499251881Speter    }
500251881Speter
501251881Speter  /* Allocate that amount, plus one slot for '\0' character. */
502251881Speter  new = apr_palloc(pool, new_len + 1);
503251881Speter
504251881Speter  new_orig = new;
505251881Speter
506251881Speter  /* And fill it up. */
507251881Speter  while (src_orig < src_end)
508251881Speter    {
509251881Speter      if (! svn_ctype_isascii(*src_orig) || src_orig == '\0')
510251881Speter        {
511251881Speter          /* This is the same format as svn_xml_fuzzy_escape uses, but that
512251881Speter             function escapes different characters.  Please keep in sync!
513251881Speter             ### If we add another fuzzy escape somewhere, we should abstract
514251881Speter             ### this out to a common function. */
515251881Speter          apr_snprintf(new, 6, "?\\%03u", (unsigned char) *src_orig);
516251881Speter          new += 5;
517251881Speter        }
518251881Speter      else
519251881Speter        {
520251881Speter          *new = *src_orig;
521251881Speter          new += 1;
522251881Speter        }
523251881Speter
524251881Speter      src_orig++;
525251881Speter    }
526251881Speter
527251881Speter  *new = '\0';
528251881Speter
529251881Speter  return new_orig;
530251881Speter}
531251881Speter
532251881Speter/* Convert SRC_LENGTH bytes of SRC_DATA in NODE->handle, store the result
533251881Speter   in *DEST, which is allocated in POOL. */
534251881Speterstatic svn_error_t *
535251881Speterconvert_to_stringbuf(xlate_handle_node_t *node,
536251881Speter                     const char *src_data,
537251881Speter                     apr_size_t src_length,
538251881Speter                     svn_stringbuf_t **dest,
539251881Speter                     apr_pool_t *pool)
540251881Speter{
541251881Speter#ifdef WIN32
542251881Speter  apr_status_t apr_err;
543251881Speter
544251881Speter  apr_err = svn_subr__win32_xlate_to_stringbuf((win32_xlate_t *) node->handle,
545251881Speter                                               src_data, src_length,
546251881Speter                                               dest, pool);
547251881Speter#else
548251881Speter  apr_size_t buflen = src_length * 2;
549251881Speter  apr_status_t apr_err;
550251881Speter  apr_size_t srclen = src_length;
551251881Speter  apr_size_t destlen = buflen;
552251881Speter
553251881Speter  /* Initialize *DEST to an empty stringbuf.
554251881Speter     A 1:2 ratio of input bytes to output bytes (as assigned above)
555251881Speter     should be enough for most translations, and if it turns out not
556251881Speter     to be enough, we'll grow the buffer again, sizing it based on a
557251881Speter     1:3 ratio of the remainder of the string. */
558251881Speter  *dest = svn_stringbuf_create_ensure(buflen + 1, pool);
559251881Speter
560251881Speter  /* Not only does it not make sense to convert an empty string, but
561251881Speter     apr-iconv is quite unreasonable about not allowing that. */
562251881Speter  if (src_length == 0)
563251881Speter    return SVN_NO_ERROR;
564251881Speter
565251881Speter  do
566251881Speter    {
567251881Speter      /* Set up state variables for xlate. */
568251881Speter      destlen = buflen - (*dest)->len;
569251881Speter
570251881Speter      /* Attempt the conversion. */
571251881Speter      apr_err = apr_xlate_conv_buffer(node->handle,
572251881Speter                                      src_data + (src_length - srclen),
573251881Speter                                      &srclen,
574251881Speter                                      (*dest)->data + (*dest)->len,
575251881Speter                                      &destlen);
576251881Speter
577251881Speter      /* Now, update the *DEST->len to track the amount of output data
578251881Speter         churned out so far from this loop. */
579251881Speter      (*dest)->len += ((buflen - (*dest)->len) - destlen);
580251881Speter      buflen += srclen * 3; /* 3 is middle ground, 2 wasn't enough
581251881Speter                               for all characters in the buffer, 4 is
582251881Speter                               maximum character size (currently) */
583251881Speter
584251881Speter
585251881Speter    } while (apr_err == APR_SUCCESS && srclen != 0);
586251881Speter#endif
587251881Speter
588251881Speter  /* If we exited the loop with an error, return the error. */
589251881Speter  if (apr_err)
590251881Speter    {
591251881Speter      const char *errstr;
592251881Speter      svn_error_t *err;
593251881Speter
594251881Speter      /* Can't use svn_error_wrap_apr here because it calls functions in
595251881Speter         this file, leading to infinite recursion. */
596251881Speter      if (node->frompage == SVN_APR_LOCALE_CHARSET)
597251881Speter        errstr = apr_psprintf
598251881Speter          (pool, _("Can't convert string from native encoding to '%s':"),
599251881Speter           node->topage);
600251881Speter      else if (node->topage == SVN_APR_LOCALE_CHARSET)
601251881Speter        errstr = apr_psprintf
602251881Speter          (pool, _("Can't convert string from '%s' to native encoding:"),
603251881Speter           node->frompage);
604251881Speter      else
605251881Speter        errstr = apr_psprintf
606251881Speter          (pool, _("Can't convert string from '%s' to '%s':"),
607251881Speter           node->frompage, node->topage);
608251881Speter
609251881Speter      err = svn_error_create(apr_err, NULL, fuzzy_escape(src_data,
610251881Speter                                                         src_length, pool));
611251881Speter      return svn_error_create(apr_err, err, errstr);
612251881Speter    }
613251881Speter  /* Else, exited due to success.  Trim the result buffer down to the
614251881Speter     right length. */
615251881Speter  (*dest)->data[(*dest)->len] = '\0';
616251881Speter
617251881Speter  return SVN_NO_ERROR;
618251881Speter}
619251881Speter
620251881Speter
621251881Speter/* Return APR_EINVAL if the first LEN bytes of DATA contain anything
622251881Speter   other than seven-bit, non-control (except for whitespace) ASCII
623251881Speter   characters, finding the error pool from POOL.  Otherwise, return
624251881Speter   SVN_NO_ERROR. */
625251881Speterstatic svn_error_t *
626251881Spetercheck_non_ascii(const char *data, apr_size_t len, apr_pool_t *pool)
627251881Speter{
628251881Speter  const char *data_start = data;
629251881Speter
630251881Speter  for (; len > 0; --len, data++)
631251881Speter    {
632251881Speter      if ((! svn_ctype_isascii(*data))
633251881Speter          || ((! svn_ctype_isspace(*data))
634251881Speter              && svn_ctype_iscntrl(*data)))
635251881Speter        {
636251881Speter          /* Show the printable part of the data, followed by the
637251881Speter             decimal code of the questionable character.  Because if a
638251881Speter             user ever gets this error, she's going to have to spend
639251881Speter             time tracking down the non-ASCII data, so we want to help
640251881Speter             as much as possible.  And yes, we just call the unsafe
641251881Speter             data "non-ASCII", even though the actual constraint is
642251881Speter             somewhat more complex than that. */
643251881Speter
644251881Speter          if (data - data_start)
645251881Speter            {
646251881Speter              const char *error_data
647251881Speter                = apr_pstrndup(pool, data_start, (data - data_start));
648251881Speter
649251881Speter              return svn_error_createf
650251881Speter                (APR_EINVAL, NULL,
651251881Speter                 _("Safe data '%s' was followed by non-ASCII byte %d: "
652251881Speter                   "unable to convert to/from UTF-8"),
653251881Speter                 error_data, *((const unsigned char *) data));
654251881Speter            }
655251881Speter          else
656251881Speter            {
657251881Speter              return svn_error_createf
658251881Speter                (APR_EINVAL, NULL,
659251881Speter                 _("Non-ASCII character (code %d) detected, "
660251881Speter                   "and unable to convert to/from UTF-8"),
661251881Speter                 *((const unsigned char *) data));
662251881Speter            }
663251881Speter        }
664251881Speter    }
665251881Speter
666251881Speter  return SVN_NO_ERROR;
667251881Speter}
668251881Speter
669251881Speter/* Construct an error with code APR_EINVAL and with a suitable message
670251881Speter * to describe the invalid UTF-8 sequence DATA of length LEN (which
671251881Speter * may have embedded NULLs).  We can't simply print the data, almost
672251881Speter * by definition we don't really know how it is encoded.
673251881Speter */
674251881Speterstatic svn_error_t *
675251881Speterinvalid_utf8(const char *data, apr_size_t len, apr_pool_t *pool)
676251881Speter{
677251881Speter  const char *last = svn_utf__last_valid(data, len);
678251881Speter  const char *valid_txt = "", *invalid_txt = "";
679251881Speter  apr_size_t i;
680251881Speter  size_t valid, invalid;
681251881Speter
682251881Speter  /* We will display at most 24 valid octets (this may split a leading
683251881Speter     multi-byte character) as that should fit on one 80 character line. */
684251881Speter  valid = last - data;
685251881Speter  if (valid > 24)
686251881Speter    valid = 24;
687251881Speter  for (i = 0; i < valid; ++i)
688251881Speter    valid_txt = apr_pstrcat(pool, valid_txt,
689251881Speter                            apr_psprintf(pool, " %02x",
690251881Speter                                         (unsigned char)last[i-valid]),
691251881Speter                                         (char *)NULL);
692251881Speter
693251881Speter  /* 4 invalid octets will guarantee that the faulty octet is displayed */
694251881Speter  invalid = data + len - last;
695251881Speter  if (invalid > 4)
696251881Speter    invalid = 4;
697251881Speter  for (i = 0; i < invalid; ++i)
698251881Speter    invalid_txt = apr_pstrcat(pool, invalid_txt,
699251881Speter                              apr_psprintf(pool, " %02x",
700251881Speter                                           (unsigned char)last[i]),
701251881Speter                                           (char *)NULL);
702251881Speter
703251881Speter  return svn_error_createf(APR_EINVAL, NULL,
704251881Speter                           _("Valid UTF-8 data\n(hex:%s)\n"
705251881Speter                             "followed by invalid UTF-8 sequence\n(hex:%s)"),
706251881Speter                           valid_txt, invalid_txt);
707251881Speter}
708251881Speter
709251881Speter/* Verify that the sequence DATA of length LEN is valid UTF-8.
710251881Speter   If it is not, return an error with code APR_EINVAL. */
711251881Speterstatic svn_error_t *
712251881Spetercheck_utf8(const char *data, apr_size_t len, apr_pool_t *pool)
713251881Speter{
714251881Speter  if (! svn_utf__is_valid(data, len))
715251881Speter    return invalid_utf8(data, len, pool);
716251881Speter  return SVN_NO_ERROR;
717251881Speter}
718251881Speter
719251881Speter/* Verify that the NULL terminated sequence DATA is valid UTF-8.
720251881Speter   If it is not, return an error with code APR_EINVAL. */
721251881Speterstatic svn_error_t *
722251881Spetercheck_cstring_utf8(const char *data, apr_pool_t *pool)
723251881Speter{
724251881Speter
725251881Speter  if (! svn_utf__cstring_is_valid(data))
726251881Speter    return invalid_utf8(data, strlen(data), pool);
727251881Speter  return SVN_NO_ERROR;
728251881Speter}
729251881Speter
730251881Speter
731251881Spetersvn_error_t *
732251881Spetersvn_utf_stringbuf_to_utf8(svn_stringbuf_t **dest,
733251881Speter                          const svn_stringbuf_t *src,
734251881Speter                          apr_pool_t *pool)
735251881Speter{
736251881Speter  xlate_handle_node_t *node;
737251881Speter  svn_error_t *err;
738251881Speter
739251881Speter  SVN_ERR(get_ntou_xlate_handle_node(&node, pool));
740251881Speter
741251881Speter  if (node->handle)
742251881Speter    {
743251881Speter      err = convert_to_stringbuf(node, src->data, src->len, dest, pool);
744251881Speter      if (! err)
745251881Speter        err = check_utf8((*dest)->data, (*dest)->len, pool);
746251881Speter    }
747251881Speter  else
748251881Speter    {
749251881Speter      err = check_non_ascii(src->data, src->len, pool);
750251881Speter      if (! err)
751251881Speter        *dest = svn_stringbuf_dup(src, pool);
752251881Speter    }
753251881Speter
754251881Speter  return svn_error_compose_create(err,
755251881Speter                                  put_xlate_handle_node
756251881Speter                                     (node,
757251881Speter                                      SVN_UTF_NTOU_XLATE_HANDLE,
758251881Speter                                      pool));
759251881Speter}
760251881Speter
761251881Speter
762251881Spetersvn_error_t *
763251881Spetersvn_utf_string_to_utf8(const svn_string_t **dest,
764251881Speter                       const svn_string_t *src,
765251881Speter                       apr_pool_t *pool)
766251881Speter{
767251881Speter  svn_stringbuf_t *destbuf;
768251881Speter  xlate_handle_node_t *node;
769251881Speter  svn_error_t *err;
770251881Speter
771251881Speter  SVN_ERR(get_ntou_xlate_handle_node(&node, pool));
772251881Speter
773251881Speter  if (node->handle)
774251881Speter    {
775251881Speter      err = convert_to_stringbuf(node, src->data, src->len, &destbuf, pool);
776251881Speter      if (! err)
777251881Speter        err = check_utf8(destbuf->data, destbuf->len, pool);
778251881Speter      if (! err)
779251881Speter        *dest = svn_stringbuf__morph_into_string(destbuf);
780251881Speter    }
781251881Speter  else
782251881Speter    {
783251881Speter      err = check_non_ascii(src->data, src->len, pool);
784251881Speter      if (! err)
785251881Speter        *dest = svn_string_dup(src, pool);
786251881Speter    }
787251881Speter
788251881Speter  return svn_error_compose_create(err,
789251881Speter                                  put_xlate_handle_node
790251881Speter                                     (node,
791251881Speter                                      SVN_UTF_NTOU_XLATE_HANDLE,
792251881Speter                                      pool));
793251881Speter}
794251881Speter
795251881Speter
796251881Speter/* Common implementation for svn_utf_cstring_to_utf8,
797251881Speter   svn_utf_cstring_to_utf8_ex, svn_utf_cstring_from_utf8 and
798251881Speter   svn_utf_cstring_from_utf8_ex. Convert SRC to DEST using NODE->handle as
799251881Speter   the translator and allocating from POOL. */
800251881Speterstatic svn_error_t *
801251881Speterconvert_cstring(const char **dest,
802251881Speter                const char *src,
803251881Speter                xlate_handle_node_t *node,
804251881Speter                apr_pool_t *pool)
805251881Speter{
806251881Speter  if (node->handle)
807251881Speter    {
808251881Speter      svn_stringbuf_t *destbuf;
809251881Speter      SVN_ERR(convert_to_stringbuf(node, src, strlen(src),
810251881Speter                                   &destbuf, pool));
811251881Speter      *dest = destbuf->data;
812251881Speter    }
813251881Speter  else
814251881Speter    {
815251881Speter      apr_size_t len = strlen(src);
816251881Speter      SVN_ERR(check_non_ascii(src, len, pool));
817251881Speter      *dest = apr_pstrmemdup(pool, src, len);
818251881Speter    }
819251881Speter  return SVN_NO_ERROR;
820251881Speter}
821251881Speter
822251881Speter
823251881Spetersvn_error_t *
824251881Spetersvn_utf_cstring_to_utf8(const char **dest,
825251881Speter                        const char *src,
826251881Speter                        apr_pool_t *pool)
827251881Speter{
828251881Speter  xlate_handle_node_t *node;
829251881Speter  svn_error_t *err;
830251881Speter
831251881Speter  SVN_ERR(get_ntou_xlate_handle_node(&node, pool));
832251881Speter  err = convert_cstring(dest, src, node, pool);
833251881Speter  SVN_ERR(svn_error_compose_create(err,
834251881Speter                                   put_xlate_handle_node
835251881Speter                                      (node,
836251881Speter                                       SVN_UTF_NTOU_XLATE_HANDLE,
837251881Speter                                       pool)));
838251881Speter  return check_cstring_utf8(*dest, pool);
839251881Speter}
840251881Speter
841251881Speter
842251881Spetersvn_error_t *
843251881Spetersvn_utf_cstring_to_utf8_ex2(const char **dest,
844251881Speter                            const char *src,
845251881Speter                            const char *frompage,
846251881Speter                            apr_pool_t *pool)
847251881Speter{
848251881Speter  xlate_handle_node_t *node;
849251881Speter  svn_error_t *err;
850251881Speter  const char *convset_key = get_xlate_key(SVN_APR_UTF8_CHARSET, frompage,
851251881Speter                                          pool);
852251881Speter
853251881Speter  SVN_ERR(get_xlate_handle_node(&node, SVN_APR_UTF8_CHARSET, frompage,
854251881Speter                                convset_key, pool));
855251881Speter  err = convert_cstring(dest, src, node, pool);
856251881Speter  SVN_ERR(svn_error_compose_create(err,
857251881Speter                                   put_xlate_handle_node
858251881Speter                                      (node,
859251881Speter                                       SVN_UTF_NTOU_XLATE_HANDLE,
860251881Speter                                       pool)));
861251881Speter
862251881Speter  return check_cstring_utf8(*dest, pool);
863251881Speter}
864251881Speter
865251881Speter
866251881Spetersvn_error_t *
867251881Spetersvn_utf_cstring_to_utf8_ex(const char **dest,
868251881Speter                           const char *src,
869251881Speter                           const char *frompage,
870251881Speter                           const char *convset_key,
871251881Speter                           apr_pool_t *pool)
872251881Speter{
873251881Speter  return svn_utf_cstring_to_utf8_ex2(dest, src, frompage, pool);
874251881Speter}
875251881Speter
876251881Speter
877251881Spetersvn_error_t *
878251881Spetersvn_utf_stringbuf_from_utf8(svn_stringbuf_t **dest,
879251881Speter                            const svn_stringbuf_t *src,
880251881Speter                            apr_pool_t *pool)
881251881Speter{
882251881Speter  xlate_handle_node_t *node;
883251881Speter  svn_error_t *err;
884251881Speter
885251881Speter  SVN_ERR(get_uton_xlate_handle_node(&node, pool));
886251881Speter
887251881Speter  if (node->handle)
888251881Speter    {
889251881Speter      err = check_utf8(src->data, src->len, pool);
890251881Speter      if (! err)
891251881Speter        err = convert_to_stringbuf(node, src->data, src->len, dest, pool);
892251881Speter    }
893251881Speter  else
894251881Speter    {
895251881Speter      err = check_non_ascii(src->data, src->len, pool);
896251881Speter      if (! err)
897251881Speter        *dest = svn_stringbuf_dup(src, pool);
898251881Speter    }
899251881Speter
900251881Speter  err = svn_error_compose_create(
901251881Speter          err,
902251881Speter          put_xlate_handle_node(node, SVN_UTF_UTON_XLATE_HANDLE, pool));
903251881Speter
904251881Speter  return err;
905251881Speter}
906251881Speter
907251881Speter
908251881Spetersvn_error_t *
909251881Spetersvn_utf_string_from_utf8(const svn_string_t **dest,
910251881Speter                         const svn_string_t *src,
911251881Speter                         apr_pool_t *pool)
912251881Speter{
913251881Speter  svn_stringbuf_t *dbuf;
914251881Speter  xlate_handle_node_t *node;
915251881Speter  svn_error_t *err;
916251881Speter
917251881Speter  SVN_ERR(get_uton_xlate_handle_node(&node, pool));
918251881Speter
919251881Speter  if (node->handle)
920251881Speter    {
921251881Speter      err = check_utf8(src->data, src->len, pool);
922251881Speter      if (! err)
923251881Speter        err = convert_to_stringbuf(node, src->data, src->len,
924251881Speter                                   &dbuf, pool);
925251881Speter      if (! err)
926251881Speter        *dest = svn_stringbuf__morph_into_string(dbuf);
927251881Speter    }
928251881Speter  else
929251881Speter    {
930251881Speter      err = check_non_ascii(src->data, src->len, pool);
931251881Speter      if (! err)
932251881Speter        *dest = svn_string_dup(src, pool);
933251881Speter    }
934251881Speter
935251881Speter  err = svn_error_compose_create(
936251881Speter          err,
937251881Speter          put_xlate_handle_node(node, SVN_UTF_UTON_XLATE_HANDLE, pool));
938251881Speter
939251881Speter  return err;
940251881Speter}
941251881Speter
942251881Speter
943251881Spetersvn_error_t *
944251881Spetersvn_utf_cstring_from_utf8(const char **dest,
945251881Speter                          const char *src,
946251881Speter                          apr_pool_t *pool)
947251881Speter{
948251881Speter  xlate_handle_node_t *node;
949251881Speter  svn_error_t *err;
950251881Speter
951251881Speter  SVN_ERR(check_cstring_utf8(src, pool));
952251881Speter
953251881Speter  SVN_ERR(get_uton_xlate_handle_node(&node, pool));
954251881Speter  err = convert_cstring(dest, src, node, pool);
955251881Speter  err = svn_error_compose_create(
956251881Speter          err,
957251881Speter          put_xlate_handle_node(node, SVN_UTF_UTON_XLATE_HANDLE, pool));
958251881Speter
959251881Speter  return err;
960251881Speter}
961251881Speter
962251881Speter
963251881Spetersvn_error_t *
964251881Spetersvn_utf_cstring_from_utf8_ex2(const char **dest,
965251881Speter                              const char *src,
966251881Speter                              const char *topage,
967251881Speter                              apr_pool_t *pool)
968251881Speter{
969251881Speter  xlate_handle_node_t *node;
970251881Speter  svn_error_t *err;
971251881Speter  const char *convset_key = get_xlate_key(topage, SVN_APR_UTF8_CHARSET,
972251881Speter                                          pool);
973251881Speter
974251881Speter  SVN_ERR(check_cstring_utf8(src, pool));
975251881Speter
976251881Speter  SVN_ERR(get_xlate_handle_node(&node, topage, SVN_APR_UTF8_CHARSET,
977251881Speter                                convset_key, pool));
978251881Speter  err = convert_cstring(dest, src, node, pool);
979251881Speter  err = svn_error_compose_create(
980251881Speter          err,
981251881Speter          put_xlate_handle_node(node, convset_key, pool));
982251881Speter
983251881Speter  return err;
984251881Speter}
985251881Speter
986251881Speter
987251881Spetersvn_error_t *
988251881Spetersvn_utf_cstring_from_utf8_ex(const char **dest,
989251881Speter                             const char *src,
990251881Speter                             const char *topage,
991251881Speter                             const char *convset_key,
992251881Speter                             apr_pool_t *pool)
993251881Speter{
994251881Speter  return svn_utf_cstring_from_utf8_ex2(dest, src, topage, pool);
995251881Speter}
996251881Speter
997251881Speter
998251881Speterconst char *
999251881Spetersvn_utf__cstring_from_utf8_fuzzy(const char *src,
1000251881Speter                                 apr_pool_t *pool,
1001251881Speter                                 svn_error_t *(*convert_from_utf8)
1002251881Speter                                 (const char **, const char *, apr_pool_t *))
1003251881Speter{
1004251881Speter  const char *escaped, *converted;
1005251881Speter  svn_error_t *err;
1006251881Speter
1007251881Speter  escaped = fuzzy_escape(src, strlen(src), pool);
1008251881Speter
1009251881Speter  /* Okay, now we have a *new* UTF-8 string, one that's guaranteed to
1010251881Speter     contain only 7-bit bytes :-).  Recode to native... */
1011251881Speter  err = convert_from_utf8(((const char **) &converted), escaped, pool);
1012251881Speter
1013251881Speter  if (err)
1014251881Speter    {
1015251881Speter      svn_error_clear(err);
1016251881Speter      return escaped;
1017251881Speter    }
1018251881Speter  else
1019251881Speter    return converted;
1020251881Speter
1021251881Speter  /* ### Check the client locale, maybe we can avoid that second
1022251881Speter   * conversion!  See Ulrich Drepper's patch at
1023251881Speter   * http://subversion.tigris.org/issues/show_bug.cgi?id=807.
1024251881Speter   */
1025251881Speter}
1026251881Speter
1027251881Speter
1028251881Speterconst char *
1029251881Spetersvn_utf_cstring_from_utf8_fuzzy(const char *src,
1030251881Speter                                apr_pool_t *pool)
1031251881Speter{
1032251881Speter  return svn_utf__cstring_from_utf8_fuzzy(src, pool,
1033251881Speter                                          svn_utf_cstring_from_utf8);
1034251881Speter}
1035251881Speter
1036251881Speter
1037251881Spetersvn_error_t *
1038251881Spetersvn_utf_cstring_from_utf8_stringbuf(const char **dest,
1039251881Speter                                    const svn_stringbuf_t *src,
1040251881Speter                                    apr_pool_t *pool)
1041251881Speter{
1042251881Speter  svn_stringbuf_t *destbuf;
1043251881Speter
1044251881Speter  SVN_ERR(svn_utf_stringbuf_from_utf8(&destbuf, src, pool));
1045251881Speter  *dest = destbuf->data;
1046251881Speter
1047251881Speter  return SVN_NO_ERROR;
1048251881Speter}
1049251881Speter
1050251881Speter
1051251881Spetersvn_error_t *
1052251881Spetersvn_utf_cstring_from_utf8_string(const char **dest,
1053251881Speter                                 const svn_string_t *src,
1054251881Speter                                 apr_pool_t *pool)
1055251881Speter{
1056251881Speter  svn_stringbuf_t *dbuf;
1057251881Speter  xlate_handle_node_t *node;
1058251881Speter  svn_error_t *err;
1059251881Speter
1060251881Speter  SVN_ERR(get_uton_xlate_handle_node(&node, pool));
1061251881Speter
1062251881Speter  if (node->handle)
1063251881Speter    {
1064251881Speter      err = check_utf8(src->data, src->len, pool);
1065251881Speter      if (! err)
1066251881Speter        err = convert_to_stringbuf(node, src->data, src->len,
1067251881Speter                                   &dbuf, pool);
1068251881Speter      if (! err)
1069251881Speter        *dest = dbuf->data;
1070251881Speter    }
1071251881Speter  else
1072251881Speter    {
1073251881Speter      err = check_non_ascii(src->data, src->len, pool);
1074251881Speter      if (! err)
1075251881Speter        *dest = apr_pstrmemdup(pool, src->data, src->len);
1076251881Speter    }
1077251881Speter
1078251881Speter  err = svn_error_compose_create(
1079251881Speter          err,
1080251881Speter          put_xlate_handle_node(node, SVN_UTF_UTON_XLATE_HANDLE, pool));
1081251881Speter
1082251881Speter  return err;
1083251881Speter}
1084