1251876Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251876Speter * contributor license agreements. See the NOTICE file distributed with 3251876Speter * this work for additional information regarding copyright ownership. 4251876Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251876Speter * (the "License"); you may not use this file except in compliance with 6251876Speter * the License. You may obtain a copy of the License at 7251876Speter * 8251876Speter * http://www.apache.org/licenses/LICENSE-2.0 9251876Speter * 10251876Speter * Unless required by applicable law or agreed to in writing, software 11251876Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251876Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251876Speter * See the License for the specific language governing permissions and 14251876Speter * limitations under the License. 15251876Speter */ 16251876Speter 17251876Speter#include "apu.h" 18251876Speter#include "apu_config.h" 19251876Speter#include "apr_lib.h" 20251876Speter#include "apr_strings.h" 21251876Speter#include "apr_portable.h" 22251876Speter#include "apr_xlate.h" 23251876Speter 24251876Speter/* If no implementation is available, don't generate code here since 25251876Speter * apr_xlate.h emitted macros which return APR_ENOTIMPL. 26251876Speter */ 27251876Speter 28251876Speter#if APR_HAS_XLATE 29251876Speter 30251876Speter#ifdef HAVE_STDDEF_H 31251876Speter#include <stddef.h> /* for NULL */ 32251876Speter#endif 33251876Speter#if APR_HAVE_STRING_H 34251876Speter#include <string.h> 35251876Speter#endif 36251876Speter#if APR_HAVE_STRINGS_H 37251876Speter#include <strings.h> 38251876Speter#endif 39251876Speter#ifdef HAVE_ICONV_H 40251876Speter#include <iconv.h> 41251876Speter#endif 42251876Speter#if APU_HAVE_APR_ICONV 43251876Speter#include <apr_iconv.h> 44251876Speter#endif 45251876Speter 46251876Speter#if defined(APU_ICONV_INBUF_CONST) || APU_HAVE_APR_ICONV 47251876Speter#define ICONV_INBUF_TYPE const char ** 48251876Speter#else 49251876Speter#define ICONV_INBUF_TYPE char ** 50251876Speter#endif 51251876Speter 52251876Speter#ifndef min 53251876Speter#define min(x,y) ((x) <= (y) ? (x) : (y)) 54251876Speter#endif 55251876Speter 56251876Speterstruct apr_xlate_t { 57251876Speter apr_pool_t *pool; 58251876Speter char *frompage; 59251876Speter char *topage; 60251876Speter char *sbcs_table; 61251876Speter#if APU_HAVE_ICONV 62251876Speter iconv_t ich; 63251876Speter#elif APU_HAVE_APR_ICONV 64251876Speter apr_iconv_t ich; 65251876Speter#endif 66251876Speter}; 67251876Speter 68251876Speter 69251876Speterstatic const char *handle_special_names(const char *page, apr_pool_t *pool) 70251876Speter{ 71251876Speter if (page == APR_DEFAULT_CHARSET) { 72251876Speter return apr_os_default_encoding(pool); 73251876Speter } 74251876Speter else if (page == APR_LOCALE_CHARSET) { 75251876Speter return apr_os_locale_encoding(pool); 76251876Speter } 77251876Speter else { 78251876Speter return page; 79251876Speter } 80251876Speter} 81251876Speter 82251876Speterstatic apr_status_t apr_xlate_cleanup(void *convset) 83251876Speter{ 84251876Speter apr_xlate_t *old = convset; 85251876Speter 86251876Speter#if APU_HAVE_APR_ICONV 87251876Speter if (old->ich != (apr_iconv_t)-1) { 88251876Speter return apr_iconv_close(old->ich, old->pool); 89251876Speter } 90251876Speter 91251876Speter#elif APU_HAVE_ICONV 92251876Speter if (old->ich != (iconv_t)-1) { 93251876Speter if (iconv_close(old->ich)) { 94251876Speter int rv = errno; 95251876Speter 96251876Speter /* Sometimes, iconv is not good about setting errno. */ 97251876Speter return rv ? rv : APR_EINVAL; 98251876Speter } 99251876Speter } 100251876Speter#endif 101251876Speter 102251876Speter return APR_SUCCESS; 103251876Speter} 104251876Speter 105251876Speter#if APU_HAVE_ICONV 106251876Speterstatic void check_sbcs(apr_xlate_t *convset) 107251876Speter{ 108251876Speter char inbuf[256], outbuf[256]; 109251876Speter char *inbufptr = inbuf; 110251876Speter char *outbufptr = outbuf; 111251876Speter apr_size_t inbytes_left, outbytes_left; 112251876Speter int i; 113251876Speter apr_size_t translated; 114251876Speter 115251876Speter for (i = 0; i < sizeof(inbuf); i++) { 116251876Speter inbuf[i] = i; 117251876Speter } 118251876Speter 119251876Speter inbytes_left = outbytes_left = sizeof(inbuf); 120251876Speter translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, 121251876Speter &inbytes_left, &outbufptr, &outbytes_left); 122251876Speter 123251876Speter if (translated != (apr_size_t)-1 124251876Speter && inbytes_left == 0 125251876Speter && outbytes_left == 0) { 126251876Speter /* hurray... this is simple translation; save the table, 127251876Speter * close the iconv descriptor 128251876Speter */ 129251876Speter 130251876Speter convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf)); 131251876Speter memcpy(convset->sbcs_table, outbuf, sizeof(outbuf)); 132251876Speter iconv_close(convset->ich); 133251876Speter convset->ich = (iconv_t)-1; 134251876Speter 135251876Speter /* TODO: add the table to the cache */ 136251876Speter } 137251876Speter else { 138251876Speter /* reset the iconv descriptor, since it's now in an undefined 139251876Speter * state. */ 140251876Speter iconv_close(convset->ich); 141251876Speter convset->ich = iconv_open(convset->topage, convset->frompage); 142251876Speter } 143251876Speter} 144251876Speter#elif APU_HAVE_APR_ICONV 145251876Speterstatic void check_sbcs(apr_xlate_t *convset) 146251876Speter{ 147251876Speter char inbuf[256], outbuf[256]; 148251876Speter char *inbufptr = inbuf; 149251876Speter char *outbufptr = outbuf; 150251876Speter apr_size_t inbytes_left, outbytes_left; 151251876Speter int i; 152251876Speter apr_size_t translated; 153251876Speter apr_status_t rv; 154251876Speter 155251876Speter for (i = 0; i < sizeof(inbuf); i++) { 156251876Speter inbuf[i] = i; 157251876Speter } 158251876Speter 159251876Speter inbytes_left = outbytes_left = sizeof(inbuf); 160251876Speter rv = apr_iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, 161251876Speter &inbytes_left, &outbufptr, &outbytes_left, 162251876Speter &translated); 163251876Speter 164251876Speter if ((rv == APR_SUCCESS) 165251876Speter && (translated != (apr_size_t)-1) 166251876Speter && inbytes_left == 0 167251876Speter && outbytes_left == 0) { 168251876Speter /* hurray... this is simple translation; save the table, 169251876Speter * close the iconv descriptor 170251876Speter */ 171251876Speter 172251876Speter convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf)); 173251876Speter memcpy(convset->sbcs_table, outbuf, sizeof(outbuf)); 174251876Speter apr_iconv_close(convset->ich, convset->pool); 175251876Speter convset->ich = (apr_iconv_t)-1; 176251876Speter 177251876Speter /* TODO: add the table to the cache */ 178251876Speter } 179251876Speter else { 180251876Speter /* reset the iconv descriptor, since it's now in an undefined 181251876Speter * state. */ 182251876Speter apr_iconv_close(convset->ich, convset->pool); 183251876Speter rv = apr_iconv_open(convset->topage, convset->frompage, 184251876Speter convset->pool, &convset->ich); 185251876Speter } 186251876Speter} 187251876Speter#endif /* APU_HAVE_APR_ICONV */ 188251876Speter 189251876Speterstatic void make_identity_table(apr_xlate_t *convset) 190251876Speter{ 191251876Speter int i; 192251876Speter 193251876Speter convset->sbcs_table = apr_palloc(convset->pool, 256); 194251876Speter for (i = 0; i < 256; i++) 195251876Speter convset->sbcs_table[i] = i; 196251876Speter} 197251876Speter 198251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset, 199251876Speter const char *topage, 200251876Speter const char *frompage, 201251876Speter apr_pool_t *pool) 202251876Speter{ 203251876Speter apr_status_t rv; 204251876Speter apr_xlate_t *new; 205251876Speter int found = 0; 206251876Speter 207251876Speter *convset = NULL; 208251876Speter 209251876Speter topage = handle_special_names(topage, pool); 210251876Speter frompage = handle_special_names(frompage, pool); 211251876Speter 212251876Speter new = (apr_xlate_t *)apr_pcalloc(pool, sizeof(apr_xlate_t)); 213251876Speter if (!new) { 214251876Speter return APR_ENOMEM; 215251876Speter } 216251876Speter 217251876Speter new->pool = pool; 218251876Speter new->topage = apr_pstrdup(pool, topage); 219251876Speter new->frompage = apr_pstrdup(pool, frompage); 220251876Speter if (!new->topage || !new->frompage) { 221251876Speter return APR_ENOMEM; 222251876Speter } 223251876Speter 224251876Speter#ifdef TODO 225251876Speter /* search cache of codepage pairs; we may be able to avoid the 226251876Speter * expensive iconv_open() 227251876Speter */ 228251876Speter 229251876Speter set found to non-zero if found in the cache 230251876Speter#endif 231251876Speter 232251876Speter if ((! found) && (strcmp(topage, frompage) == 0)) { 233251876Speter /* to and from are the same */ 234251876Speter found = 1; 235251876Speter make_identity_table(new); 236251876Speter } 237251876Speter 238251876Speter#if APU_HAVE_APR_ICONV 239251876Speter if (!found) { 240251876Speter rv = apr_iconv_open(topage, frompage, pool, &new->ich); 241251876Speter if (rv != APR_SUCCESS) { 242251876Speter return rv; 243251876Speter } 244251876Speter found = 1; 245251876Speter check_sbcs(new); 246251876Speter } else 247251876Speter new->ich = (apr_iconv_t)-1; 248251876Speter 249251876Speter#elif APU_HAVE_ICONV 250251876Speter if (!found) { 251251876Speter new->ich = iconv_open(topage, frompage); 252251876Speter if (new->ich == (iconv_t)-1) { 253251876Speter int rv = errno; 254251876Speter /* Sometimes, iconv is not good about setting errno. */ 255251876Speter return rv ? rv : APR_EINVAL; 256251876Speter } 257251876Speter found = 1; 258251876Speter check_sbcs(new); 259251876Speter } else 260251876Speter new->ich = (iconv_t)-1; 261251876Speter#endif /* APU_HAVE_ICONV */ 262251876Speter 263251876Speter if (found) { 264251876Speter *convset = new; 265251876Speter apr_pool_cleanup_register(pool, (void *)new, apr_xlate_cleanup, 266251876Speter apr_pool_cleanup_null); 267251876Speter rv = APR_SUCCESS; 268251876Speter } 269251876Speter else { 270251876Speter rv = APR_EINVAL; /* iconv() would return EINVAL if it 271251876Speter couldn't handle the pair */ 272251876Speter } 273251876Speter 274251876Speter return rv; 275251876Speter} 276251876Speter 277251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff) 278251876Speter{ 279251876Speter *onoff = convset->sbcs_table != NULL; 280251876Speter return APR_SUCCESS; 281251876Speter} 282251876Speter 283251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset, 284251876Speter const char *inbuf, 285251876Speter apr_size_t *inbytes_left, 286251876Speter char *outbuf, 287251876Speter apr_size_t *outbytes_left) 288251876Speter{ 289251876Speter apr_status_t status = APR_SUCCESS; 290251876Speter 291251876Speter#if APU_HAVE_APR_ICONV 292251876Speter if (convset->ich != (apr_iconv_t)-1) { 293251876Speter const char *inbufptr = inbuf; 294251876Speter apr_size_t translated; 295251876Speter char *outbufptr = outbuf; 296251876Speter status = apr_iconv(convset->ich, &inbufptr, inbytes_left, 297251876Speter &outbufptr, outbytes_left, &translated); 298251876Speter 299251876Speter /* If everything went fine but we ran out of buffer, don't 300251876Speter * report it as an error. Caller needs to look at the two 301251876Speter * bytes-left values anyway. 302251876Speter * 303251876Speter * There are three expected cases where rc is -1. In each of 304251876Speter * these cases, *inbytes_left != 0. 305251876Speter * a) the non-error condition where we ran out of output 306251876Speter * buffer 307251876Speter * b) the non-error condition where we ran out of input (i.e., 308251876Speter * the last input character is incomplete) 309251876Speter * c) the error condition where the input is invalid 310251876Speter */ 311251876Speter switch (status) { 312251876Speter 313251876Speter case APR_BADARG: /* out of space on output */ 314251876Speter status = 0; /* change table lookup code below if you 315251876Speter make this an error */ 316251876Speter break; 317251876Speter 318251876Speter case APR_EINVAL: /* input character not complete (yet) */ 319251876Speter status = APR_INCOMPLETE; 320251876Speter break; 321251876Speter 322251876Speter case APR_BADCH: /* bad input byte */ 323251876Speter status = APR_EINVAL; 324251876Speter break; 325251876Speter 326251876Speter /* Sometimes, iconv is not good about setting errno. */ 327251876Speter case 0: 328251876Speter if (inbytes_left && *inbytes_left) 329251876Speter status = APR_INCOMPLETE; 330251876Speter break; 331251876Speter 332251876Speter default: 333251876Speter break; 334251876Speter } 335251876Speter } 336251876Speter else 337251876Speter 338251876Speter#elif APU_HAVE_ICONV 339251876Speter if (convset->ich != (iconv_t)-1) { 340251876Speter const char *inbufptr = inbuf; 341251876Speter char *outbufptr = outbuf; 342251876Speter apr_size_t translated; 343251876Speter translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, 344251876Speter inbytes_left, &outbufptr, outbytes_left); 345251876Speter 346251876Speter /* If everything went fine but we ran out of buffer, don't 347251876Speter * report it as an error. Caller needs to look at the two 348251876Speter * bytes-left values anyway. 349251876Speter * 350251876Speter * There are three expected cases where rc is -1. In each of 351251876Speter * these cases, *inbytes_left != 0. 352251876Speter * a) the non-error condition where we ran out of output 353251876Speter * buffer 354251876Speter * b) the non-error condition where we ran out of input (i.e., 355251876Speter * the last input character is incomplete) 356251876Speter * c) the error condition where the input is invalid 357251876Speter */ 358251876Speter if (translated == (apr_size_t)-1) { 359251876Speter int rv = errno; 360251876Speter switch (rv) { 361251876Speter 362251876Speter case E2BIG: /* out of space on output */ 363251876Speter status = 0; /* change table lookup code below if you 364251876Speter make this an error */ 365251876Speter break; 366251876Speter 367251876Speter case EINVAL: /* input character not complete (yet) */ 368251876Speter status = APR_INCOMPLETE; 369251876Speter break; 370251876Speter 371251876Speter case EILSEQ: /* bad input byte */ 372251876Speter status = APR_EINVAL; 373251876Speter break; 374251876Speter 375251876Speter /* Sometimes, iconv is not good about setting errno. */ 376251876Speter case 0: 377251876Speter status = APR_INCOMPLETE; 378251876Speter break; 379251876Speter 380251876Speter default: 381251876Speter status = rv; 382251876Speter break; 383251876Speter } 384251876Speter } 385251876Speter } 386251876Speter else 387251876Speter#endif 388251876Speter 389251876Speter if (inbuf) { 390251876Speter apr_size_t to_convert = min(*inbytes_left, *outbytes_left); 391251876Speter apr_size_t converted = to_convert; 392251876Speter char *table = convset->sbcs_table; 393251876Speter 394251876Speter while (to_convert) { 395251876Speter *outbuf = table[(unsigned char)*inbuf]; 396251876Speter ++outbuf; 397251876Speter ++inbuf; 398251876Speter --to_convert; 399251876Speter } 400251876Speter *inbytes_left -= converted; 401251876Speter *outbytes_left -= converted; 402251876Speter } 403251876Speter 404251876Speter return status; 405251876Speter} 406251876Speter 407251876SpeterAPU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset, 408251876Speter unsigned char inchar) 409251876Speter{ 410251876Speter if (convset->sbcs_table) { 411251876Speter return convset->sbcs_table[inchar]; 412251876Speter } 413251876Speter else { 414251876Speter return -1; 415251876Speter } 416251876Speter} 417251876Speter 418251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset) 419251876Speter{ 420251876Speter return apr_pool_cleanup_run(convset->pool, convset, apr_xlate_cleanup); 421251876Speter} 422251876Speter 423251876Speter#else /* !APR_HAS_XLATE */ 424251876Speter 425251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset, 426251876Speter const char *topage, 427251876Speter const char *frompage, 428251876Speter apr_pool_t *pool) 429251876Speter{ 430251876Speter return APR_ENOTIMPL; 431251876Speter} 432251876Speter 433251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff) 434251876Speter{ 435251876Speter return APR_ENOTIMPL; 436251876Speter} 437251876Speter 438251876SpeterAPU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset, 439251876Speter unsigned char inchar) 440251876Speter{ 441251876Speter return (-1); 442251876Speter} 443251876Speter 444251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset, 445251876Speter const char *inbuf, 446251876Speter apr_size_t *inbytes_left, 447251876Speter char *outbuf, 448251876Speter apr_size_t *outbytes_left) 449251876Speter{ 450251876Speter return APR_ENOTIMPL; 451251876Speter} 452251876Speter 453251876SpeterAPU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset) 454251876Speter{ 455251876Speter return APR_ENOTIMPL; 456251876Speter} 457251876Speter 458251876Speter#endif /* APR_HAS_XLATE */ 459