459 lines
13 KiB
C
459 lines
13 KiB
C
|
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
||
|
* contributor license agreements. See the NOTICE file distributed with
|
||
|
* this work for additional information regarding copyright ownership.
|
||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||
|
* (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include "apu.h"
|
||
|
#include "apu_config.h"
|
||
|
#include "apr_lib.h"
|
||
|
#include "apr_strings.h"
|
||
|
#include "apr_portable.h"
|
||
|
#include "apr_xlate.h"
|
||
|
|
||
|
/* If no implementation is available, don't generate code here since
|
||
|
* apr_xlate.h emitted macros which return APR_ENOTIMPL.
|
||
|
*/
|
||
|
|
||
|
#if APR_HAS_XLATE
|
||
|
|
||
|
#ifdef HAVE_STDDEF_H
|
||
|
#include <stddef.h> /* for NULL */
|
||
|
#endif
|
||
|
#if APR_HAVE_STRING_H
|
||
|
#include <string.h>
|
||
|
#endif
|
||
|
#if APR_HAVE_STRINGS_H
|
||
|
#include <strings.h>
|
||
|
#endif
|
||
|
#ifdef HAVE_ICONV_H
|
||
|
#include <iconv.h>
|
||
|
#endif
|
||
|
#if APU_HAVE_APR_ICONV
|
||
|
#include <apr_iconv.h>
|
||
|
#endif
|
||
|
|
||
|
#if defined(APU_ICONV_INBUF_CONST) || APU_HAVE_APR_ICONV
|
||
|
#define ICONV_INBUF_TYPE const char **
|
||
|
#else
|
||
|
#define ICONV_INBUF_TYPE char **
|
||
|
#endif
|
||
|
|
||
|
#ifndef min
|
||
|
#define min(x,y) ((x) <= (y) ? (x) : (y))
|
||
|
#endif
|
||
|
|
||
|
struct apr_xlate_t {
|
||
|
apr_pool_t *pool;
|
||
|
char *frompage;
|
||
|
char *topage;
|
||
|
char *sbcs_table;
|
||
|
#if APU_HAVE_ICONV
|
||
|
iconv_t ich;
|
||
|
#elif APU_HAVE_APR_ICONV
|
||
|
apr_iconv_t ich;
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
|
||
|
static const char *handle_special_names(const char *page, apr_pool_t *pool)
|
||
|
{
|
||
|
if (page == APR_DEFAULT_CHARSET) {
|
||
|
return apr_os_default_encoding(pool);
|
||
|
}
|
||
|
else if (page == APR_LOCALE_CHARSET) {
|
||
|
return apr_os_locale_encoding(pool);
|
||
|
}
|
||
|
else {
|
||
|
return page;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static apr_status_t apr_xlate_cleanup(void *convset)
|
||
|
{
|
||
|
apr_xlate_t *old = convset;
|
||
|
|
||
|
#if APU_HAVE_APR_ICONV
|
||
|
if (old->ich != (apr_iconv_t)-1) {
|
||
|
return apr_iconv_close(old->ich, old->pool);
|
||
|
}
|
||
|
|
||
|
#elif APU_HAVE_ICONV
|
||
|
if (old->ich != (iconv_t)-1) {
|
||
|
if (iconv_close(old->ich)) {
|
||
|
int rv = errno;
|
||
|
|
||
|
/* Sometimes, iconv is not good about setting errno. */
|
||
|
return rv ? rv : APR_EINVAL;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return APR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
#if APU_HAVE_ICONV
|
||
|
static void check_sbcs(apr_xlate_t *convset)
|
||
|
{
|
||
|
char inbuf[256], outbuf[256];
|
||
|
char *inbufptr = inbuf;
|
||
|
char *outbufptr = outbuf;
|
||
|
apr_size_t inbytes_left, outbytes_left;
|
||
|
int i;
|
||
|
apr_size_t translated;
|
||
|
|
||
|
for (i = 0; i < sizeof(inbuf); i++) {
|
||
|
inbuf[i] = i;
|
||
|
}
|
||
|
|
||
|
inbytes_left = outbytes_left = sizeof(inbuf);
|
||
|
translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
|
||
|
&inbytes_left, &outbufptr, &outbytes_left);
|
||
|
|
||
|
if (translated != (apr_size_t)-1
|
||
|
&& inbytes_left == 0
|
||
|
&& outbytes_left == 0) {
|
||
|
/* hurray... this is simple translation; save the table,
|
||
|
* close the iconv descriptor
|
||
|
*/
|
||
|
|
||
|
convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf));
|
||
|
memcpy(convset->sbcs_table, outbuf, sizeof(outbuf));
|
||
|
iconv_close(convset->ich);
|
||
|
convset->ich = (iconv_t)-1;
|
||
|
|
||
|
/* TODO: add the table to the cache */
|
||
|
}
|
||
|
else {
|
||
|
/* reset the iconv descriptor, since it's now in an undefined
|
||
|
* state. */
|
||
|
iconv_close(convset->ich);
|
||
|
convset->ich = iconv_open(convset->topage, convset->frompage);
|
||
|
}
|
||
|
}
|
||
|
#elif APU_HAVE_APR_ICONV
|
||
|
static void check_sbcs(apr_xlate_t *convset)
|
||
|
{
|
||
|
char inbuf[256], outbuf[256];
|
||
|
char *inbufptr = inbuf;
|
||
|
char *outbufptr = outbuf;
|
||
|
apr_size_t inbytes_left, outbytes_left;
|
||
|
int i;
|
||
|
apr_size_t translated;
|
||
|
apr_status_t rv;
|
||
|
|
||
|
for (i = 0; i < sizeof(inbuf); i++) {
|
||
|
inbuf[i] = i;
|
||
|
}
|
||
|
|
||
|
inbytes_left = outbytes_left = sizeof(inbuf);
|
||
|
rv = apr_iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
|
||
|
&inbytes_left, &outbufptr, &outbytes_left,
|
||
|
&translated);
|
||
|
|
||
|
if ((rv == APR_SUCCESS)
|
||
|
&& (translated != (apr_size_t)-1)
|
||
|
&& inbytes_left == 0
|
||
|
&& outbytes_left == 0) {
|
||
|
/* hurray... this is simple translation; save the table,
|
||
|
* close the iconv descriptor
|
||
|
*/
|
||
|
|
||
|
convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf));
|
||
|
memcpy(convset->sbcs_table, outbuf, sizeof(outbuf));
|
||
|
apr_iconv_close(convset->ich, convset->pool);
|
||
|
convset->ich = (apr_iconv_t)-1;
|
||
|
|
||
|
/* TODO: add the table to the cache */
|
||
|
}
|
||
|
else {
|
||
|
/* reset the iconv descriptor, since it's now in an undefined
|
||
|
* state. */
|
||
|
apr_iconv_close(convset->ich, convset->pool);
|
||
|
rv = apr_iconv_open(convset->topage, convset->frompage,
|
||
|
convset->pool, &convset->ich);
|
||
|
}
|
||
|
}
|
||
|
#endif /* APU_HAVE_APR_ICONV */
|
||
|
|
||
|
static void make_identity_table(apr_xlate_t *convset)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
convset->sbcs_table = apr_palloc(convset->pool, 256);
|
||
|
for (i = 0; i < 256; i++)
|
||
|
convset->sbcs_table[i] = i;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset,
|
||
|
const char *topage,
|
||
|
const char *frompage,
|
||
|
apr_pool_t *pool)
|
||
|
{
|
||
|
apr_status_t rv;
|
||
|
apr_xlate_t *new;
|
||
|
int found = 0;
|
||
|
|
||
|
*convset = NULL;
|
||
|
|
||
|
topage = handle_special_names(topage, pool);
|
||
|
frompage = handle_special_names(frompage, pool);
|
||
|
|
||
|
new = (apr_xlate_t *)apr_pcalloc(pool, sizeof(apr_xlate_t));
|
||
|
if (!new) {
|
||
|
return APR_ENOMEM;
|
||
|
}
|
||
|
|
||
|
new->pool = pool;
|
||
|
new->topage = apr_pstrdup(pool, topage);
|
||
|
new->frompage = apr_pstrdup(pool, frompage);
|
||
|
if (!new->topage || !new->frompage) {
|
||
|
return APR_ENOMEM;
|
||
|
}
|
||
|
|
||
|
#ifdef TODO
|
||
|
/* search cache of codepage pairs; we may be able to avoid the
|
||
|
* expensive iconv_open()
|
||
|
*/
|
||
|
|
||
|
set found to non-zero if found in the cache
|
||
|
#endif
|
||
|
|
||
|
if ((! found) && (strcmp(topage, frompage) == 0)) {
|
||
|
/* to and from are the same */
|
||
|
found = 1;
|
||
|
make_identity_table(new);
|
||
|
}
|
||
|
|
||
|
#if APU_HAVE_APR_ICONV
|
||
|
if (!found) {
|
||
|
rv = apr_iconv_open(topage, frompage, pool, &new->ich);
|
||
|
if (rv != APR_SUCCESS) {
|
||
|
return rv;
|
||
|
}
|
||
|
found = 1;
|
||
|
check_sbcs(new);
|
||
|
} else
|
||
|
new->ich = (apr_iconv_t)-1;
|
||
|
|
||
|
#elif APU_HAVE_ICONV
|
||
|
if (!found) {
|
||
|
new->ich = iconv_open(topage, frompage);
|
||
|
if (new->ich == (iconv_t)-1) {
|
||
|
int rv = errno;
|
||
|
/* Sometimes, iconv is not good about setting errno. */
|
||
|
return rv ? rv : APR_EINVAL;
|
||
|
}
|
||
|
found = 1;
|
||
|
check_sbcs(new);
|
||
|
} else
|
||
|
new->ich = (iconv_t)-1;
|
||
|
#endif /* APU_HAVE_ICONV */
|
||
|
|
||
|
if (found) {
|
||
|
*convset = new;
|
||
|
apr_pool_cleanup_register(pool, (void *)new, apr_xlate_cleanup,
|
||
|
apr_pool_cleanup_null);
|
||
|
rv = APR_SUCCESS;
|
||
|
}
|
||
|
else {
|
||
|
rv = APR_EINVAL; /* iconv() would return EINVAL if it
|
||
|
couldn't handle the pair */
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff)
|
||
|
{
|
||
|
*onoff = convset->sbcs_table != NULL;
|
||
|
return APR_SUCCESS;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset,
|
||
|
const char *inbuf,
|
||
|
apr_size_t *inbytes_left,
|
||
|
char *outbuf,
|
||
|
apr_size_t *outbytes_left)
|
||
|
{
|
||
|
apr_status_t status = APR_SUCCESS;
|
||
|
|
||
|
#if APU_HAVE_APR_ICONV
|
||
|
if (convset->ich != (apr_iconv_t)-1) {
|
||
|
const char *inbufptr = inbuf;
|
||
|
apr_size_t translated;
|
||
|
char *outbufptr = outbuf;
|
||
|
status = apr_iconv(convset->ich, &inbufptr, inbytes_left,
|
||
|
&outbufptr, outbytes_left, &translated);
|
||
|
|
||
|
/* If everything went fine but we ran out of buffer, don't
|
||
|
* report it as an error. Caller needs to look at the two
|
||
|
* bytes-left values anyway.
|
||
|
*
|
||
|
* There are three expected cases where rc is -1. In each of
|
||
|
* these cases, *inbytes_left != 0.
|
||
|
* a) the non-error condition where we ran out of output
|
||
|
* buffer
|
||
|
* b) the non-error condition where we ran out of input (i.e.,
|
||
|
* the last input character is incomplete)
|
||
|
* c) the error condition where the input is invalid
|
||
|
*/
|
||
|
switch (status) {
|
||
|
|
||
|
case APR_BADARG: /* out of space on output */
|
||
|
status = 0; /* change table lookup code below if you
|
||
|
make this an error */
|
||
|
break;
|
||
|
|
||
|
case APR_EINVAL: /* input character not complete (yet) */
|
||
|
status = APR_INCOMPLETE;
|
||
|
break;
|
||
|
|
||
|
case APR_BADCH: /* bad input byte */
|
||
|
status = APR_EINVAL;
|
||
|
break;
|
||
|
|
||
|
/* Sometimes, iconv is not good about setting errno. */
|
||
|
case 0:
|
||
|
if (inbytes_left && *inbytes_left)
|
||
|
status = APR_INCOMPLETE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
|
||
|
#elif APU_HAVE_ICONV
|
||
|
if (convset->ich != (iconv_t)-1) {
|
||
|
const char *inbufptr = inbuf;
|
||
|
char *outbufptr = outbuf;
|
||
|
apr_size_t translated;
|
||
|
translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
|
||
|
inbytes_left, &outbufptr, outbytes_left);
|
||
|
|
||
|
/* If everything went fine but we ran out of buffer, don't
|
||
|
* report it as an error. Caller needs to look at the two
|
||
|
* bytes-left values anyway.
|
||
|
*
|
||
|
* There are three expected cases where rc is -1. In each of
|
||
|
* these cases, *inbytes_left != 0.
|
||
|
* a) the non-error condition where we ran out of output
|
||
|
* buffer
|
||
|
* b) the non-error condition where we ran out of input (i.e.,
|
||
|
* the last input character is incomplete)
|
||
|
* c) the error condition where the input is invalid
|
||
|
*/
|
||
|
if (translated == (apr_size_t)-1) {
|
||
|
int rv = errno;
|
||
|
switch (rv) {
|
||
|
|
||
|
case E2BIG: /* out of space on output */
|
||
|
status = 0; /* change table lookup code below if you
|
||
|
make this an error */
|
||
|
break;
|
||
|
|
||
|
case EINVAL: /* input character not complete (yet) */
|
||
|
status = APR_INCOMPLETE;
|
||
|
break;
|
||
|
|
||
|
case EILSEQ: /* bad input byte */
|
||
|
status = APR_EINVAL;
|
||
|
break;
|
||
|
|
||
|
/* Sometimes, iconv is not good about setting errno. */
|
||
|
case 0:
|
||
|
status = APR_INCOMPLETE;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
status = rv;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
#endif
|
||
|
|
||
|
if (inbuf) {
|
||
|
apr_size_t to_convert = min(*inbytes_left, *outbytes_left);
|
||
|
apr_size_t converted = to_convert;
|
||
|
char *table = convset->sbcs_table;
|
||
|
|
||
|
while (to_convert) {
|
||
|
*outbuf = table[(unsigned char)*inbuf];
|
||
|
++outbuf;
|
||
|
++inbuf;
|
||
|
--to_convert;
|
||
|
}
|
||
|
*inbytes_left -= converted;
|
||
|
*outbytes_left -= converted;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset,
|
||
|
unsigned char inchar)
|
||
|
{
|
||
|
if (convset->sbcs_table) {
|
||
|
return convset->sbcs_table[inchar];
|
||
|
}
|
||
|
else {
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset)
|
||
|
{
|
||
|
return apr_pool_cleanup_run(convset->pool, convset, apr_xlate_cleanup);
|
||
|
}
|
||
|
|
||
|
#else /* !APR_HAS_XLATE */
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset,
|
||
|
const char *topage,
|
||
|
const char *frompage,
|
||
|
apr_pool_t *pool)
|
||
|
{
|
||
|
return APR_ENOTIMPL;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff)
|
||
|
{
|
||
|
return APR_ENOTIMPL;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset,
|
||
|
unsigned char inchar)
|
||
|
{
|
||
|
return (-1);
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset,
|
||
|
const char *inbuf,
|
||
|
apr_size_t *inbytes_left,
|
||
|
char *outbuf,
|
||
|
apr_size_t *outbytes_left)
|
||
|
{
|
||
|
return APR_ENOTIMPL;
|
||
|
}
|
||
|
|
||
|
APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset)
|
||
|
{
|
||
|
return APR_ENOTIMPL;
|
||
|
}
|
||
|
|
||
|
#endif /* APR_HAS_XLATE */
|