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