upload http
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / srclib / apr-util / xlate / xlate.c
1 /* Copyright 2000-2005 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "apu.h"
18 #include "apu_config.h"
19 #include "apr_lib.h"
20 #include "apr_strings.h"
21 #include "apr_portable.h"
22 #include "apr_xlate.h"
23
24 /* If no implementation is available, don't generate code here since
25  * apr_xlate.h emitted macros which return APR_ENOTIMPL.
26  */
27
28 #if APR_HAS_XLATE
29
30 #ifdef HAVE_STDDEF_H
31 #include <stddef.h> /* for NULL */
32 #endif
33 #if APR_HAVE_STRING_H
34 #include <string.h>
35 #endif
36 #if APR_HAVE_STRINGS_H
37 #include <strings.h>
38 #endif
39 #ifdef HAVE_ICONV_H
40 #include <iconv.h>
41 #endif
42 #if APU_HAVE_APR_ICONV
43 #include <apr_iconv.h>
44 #endif
45
46 #if defined(APU_ICONV_INBUF_CONST) || APU_HAVE_APR_ICONV
47 #define ICONV_INBUF_TYPE const char **
48 #else
49 #define ICONV_INBUF_TYPE char **
50 #endif
51
52 #ifndef min
53 #define min(x,y) ((x) <= (y) ? (x) : (y))
54 #endif
55
56 struct apr_xlate_t {
57     apr_pool_t *pool;
58     char *frompage;
59     char *topage;
60     char *sbcs_table;
61 #if APU_HAVE_ICONV
62     iconv_t ich;
63 #elif APU_HAVE_APR_ICONV
64     apr_iconv_t ich;
65 #endif
66 };
67
68
69 static const char *handle_special_names(const char *page, apr_pool_t *pool)
70 {
71     if (page == APR_DEFAULT_CHARSET) {
72         return apr_os_default_encoding(pool);
73     }
74     else if (page == APR_LOCALE_CHARSET) {
75         return apr_os_locale_encoding(pool);
76     }
77     else {
78         return page;
79     }
80 }
81
82 static apr_status_t apr_xlate_cleanup(void *convset)
83 {
84     apr_xlate_t *old = convset;
85
86 #if APU_HAVE_APR_ICONV
87     if (old->ich != (apr_iconv_t)-1) {
88         return apr_iconv_close(old->ich, old->pool);
89     }
90
91 #elif APU_HAVE_ICONV
92     if (old->ich != (iconv_t)-1) {
93         if (iconv_close(old->ich)) {
94             int rv = errno;
95
96             /* Sometimes, iconv is not good about setting errno. */
97             return rv ? rv : APR_EINVAL;
98         }
99     }
100 #endif
101
102     return APR_SUCCESS;
103 }
104
105 #if APU_HAVE_ICONV
106 static void check_sbcs(apr_xlate_t *convset)
107 {
108     char inbuf[256], outbuf[256];
109     char *inbufptr = inbuf;
110     char *outbufptr = outbuf;
111     apr_size_t inbytes_left, outbytes_left;
112     int i;
113     apr_size_t translated;
114
115     for (i = 0; i < sizeof(inbuf); i++) {
116         inbuf[i] = i;
117     }
118
119     inbytes_left = outbytes_left = sizeof(inbuf);
120     translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
121                        &inbytes_left, &outbufptr, &outbytes_left);
122
123     if (translated != (apr_size_t)-1
124         && inbytes_left == 0
125         && outbytes_left == 0) {
126         /* hurray... this is simple translation; save the table,
127          * close the iconv descriptor
128          */
129
130         convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf));
131         memcpy(convset->sbcs_table, outbuf, sizeof(outbuf));
132         iconv_close(convset->ich);
133         convset->ich = (iconv_t)-1;
134
135         /* TODO: add the table to the cache */
136     }
137     else {
138         /* reset the iconv descriptor, since it's now in an undefined
139          * state. */
140         iconv_close(convset->ich);
141         convset->ich = iconv_open(convset->topage, convset->frompage);
142     }
143 }
144 #elif APU_HAVE_APR_ICONV
145 static void check_sbcs(apr_xlate_t *convset)
146 {
147     char inbuf[256], outbuf[256];
148     char *inbufptr = inbuf;
149     char *outbufptr = outbuf;
150     apr_size_t inbytes_left, outbytes_left;
151     int i;
152     apr_size_t translated;
153     apr_status_t rv;
154
155     for (i = 0; i < sizeof(inbuf); i++) {
156         inbuf[i] = i;
157     }
158
159     inbytes_left = outbytes_left = sizeof(inbuf);
160     rv = apr_iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
161                    &inbytes_left, &outbufptr, &outbytes_left,
162                    &translated);
163
164     if ((rv == APR_SUCCESS)
165         && (translated != (apr_size_t)-1)
166         && inbytes_left == 0
167         && outbytes_left == 0) {
168         /* hurray... this is simple translation; save the table,
169          * close the iconv descriptor
170          */
171
172         convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf));
173         memcpy(convset->sbcs_table, outbuf, sizeof(outbuf));
174         apr_iconv_close(convset->ich, convset->pool);
175         convset->ich = (apr_iconv_t)-1;
176
177         /* TODO: add the table to the cache */
178     }
179     else {
180         /* reset the iconv descriptor, since it's now in an undefined
181          * state. */
182         apr_iconv_close(convset->ich, convset->pool);
183         rv = apr_iconv_open(convset->topage, convset->frompage, convset->pool, &convset->ich);
184      }
185 }
186 #endif /* APU_HAVE_APR_ICONV */
187
188 static void make_identity_table(apr_xlate_t *convset)
189 {
190   int i;
191
192   convset->sbcs_table = apr_palloc(convset->pool, 256);
193   for (i = 0; i < 256; i++)
194       convset->sbcs_table[i] = i;
195 }
196
197 APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset,
198                                          const char *topage,
199                                          const char *frompage,
200                                          apr_pool_t *pool)
201 {
202     apr_status_t rv;
203     apr_xlate_t *new;
204     int found = 0;
205
206     *convset = NULL;
207
208     topage = handle_special_names(topage, pool);
209     frompage = handle_special_names(frompage, pool);
210
211     new = (apr_xlate_t *)apr_pcalloc(pool, sizeof(apr_xlate_t));
212     if (!new) {
213         return APR_ENOMEM;
214     }
215
216     new->pool = pool;
217     new->topage = apr_pstrdup(pool, topage);
218     new->frompage = apr_pstrdup(pool, frompage);
219     if (!new->topage || !new->frompage) {
220         return APR_ENOMEM;
221     }
222
223 #ifdef TODO
224     /* search cache of codepage pairs; we may be able to avoid the
225      * expensive iconv_open()
226      */
227
228     set found to non-zero if found in the cache
229 #endif
230
231     if ((! found) && (strcmp(topage, frompage) == 0)) {
232         /* to and from are the same */
233         found = 1;
234         make_identity_table(new);
235     }
236
237 #if APU_HAVE_APR_ICONV
238     if (!found) {
239         rv = apr_iconv_open(topage, frompage, pool, &new->ich);
240         if (rv != APR_SUCCESS) {
241             return rv;
242         }
243         found = 1;
244         check_sbcs(new);
245     } else
246         new->ich = (apr_iconv_t)-1;
247
248 #elif APU_HAVE_ICONV
249     if (!found) {
250         new->ich = iconv_open(topage, frompage);
251         if (new->ich == (iconv_t)-1) {
252             int rv = errno;
253             /* Sometimes, iconv is not good about setting errno. */
254             return rv ? rv : APR_EINVAL;
255         }
256         found = 1;
257         check_sbcs(new);
258     } else
259         new->ich = (iconv_t)-1;
260 #endif /* APU_HAVE_ICONV */
261
262     if (found) {
263         *convset = new;
264         apr_pool_cleanup_register(pool, (void *)new, apr_xlate_cleanup,
265                             apr_pool_cleanup_null);
266         rv = APR_SUCCESS;
267     }
268     else {
269         rv = APR_EINVAL; /* iconv() would return EINVAL if it
270                                 couldn't handle the pair */
271     }
272
273     return rv;
274 }
275
276 APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff)
277 {
278     *onoff = convset->sbcs_table != NULL;
279     return APR_SUCCESS;
280 }
281
282 APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset,
283                                                 const char *inbuf,
284                                                 apr_size_t *inbytes_left,
285                                                 char *outbuf,
286                                                 apr_size_t *outbytes_left)
287 {
288     apr_status_t status = APR_SUCCESS;
289
290 #if APU_HAVE_APR_ICONV
291     if (convset->ich != (apr_iconv_t)-1) {
292         const char *inbufptr = inbuf;
293         apr_size_t translated;
294         char *outbufptr = outbuf;
295         status = apr_iconv(convset->ich, &inbufptr, inbytes_left,
296                            &outbufptr, outbytes_left, &translated);
297
298         /* If everything went fine but we ran out of buffer, don't
299          * report it as an error.  Caller needs to look at the two
300          * bytes-left values anyway.
301          *
302          * There are three expected cases where rc is -1.  In each of
303          * these cases, *inbytes_left != 0.
304          * a) the non-error condition where we ran out of output
305          *    buffer
306          * b) the non-error condition where we ran out of input (i.e.,
307          *    the last input character is incomplete)
308          * c) the error condition where the input is invalid
309          */
310         switch (status) {
311
312             case E2BIG:  /* out of space on output */
313                 status = 0; /* change table lookup code below if you
314                                make this an error */
315                 break;
316
317             case EINVAL: /* input character not complete (yet) */
318                 status = APR_INCOMPLETE;
319                 break;
320
321             case EILSEQ: /* bad input byte */
322                 status = APR_EINVAL;
323                 break;
324
325              /* Sometimes, iconv is not good about setting errno. */
326             case 0:
327                 if (inbytes_left && *inbytes_left)
328                     status = APR_INCOMPLETE;
329                 break;
330
331             default:
332                 break;
333         }
334     }
335     else
336
337 #elif APU_HAVE_ICONV
338     if (convset->ich != (iconv_t)-1) {
339         const char *inbufptr = inbuf;
340         char *outbufptr = outbuf;
341         apr_size_t translated;
342         translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
343                            inbytes_left, &outbufptr, outbytes_left);
344
345         /* If everything went fine but we ran out of buffer, don't
346          * report it as an error.  Caller needs to look at the two
347          * bytes-left values anyway.
348          *
349          * There are three expected cases where rc is -1.  In each of
350          * these cases, *inbytes_left != 0.
351          * a) the non-error condition where we ran out of output
352          *    buffer
353          * b) the non-error condition where we ran out of input (i.e.,
354          *    the last input character is incomplete)
355          * c) the error condition where the input is invalid
356          */
357         if (translated == (apr_size_t)-1) {
358             int rv = errno;
359             switch (rv) {
360
361             case E2BIG:  /* out of space on output */
362                 status = 0; /* change table lookup code below if you
363                                make this an error */
364                 break;
365
366             case EINVAL: /* input character not complete (yet) */
367                 status = APR_INCOMPLETE;
368                 break;
369
370             case EILSEQ: /* bad input byte */
371                 status = APR_EINVAL;
372                 break;
373
374              /* Sometimes, iconv is not good about setting errno. */
375             case 0:
376                 status = APR_INCOMPLETE;
377                 break;
378
379             default:
380                 status = rv;
381                 break;
382             }
383         }
384     }
385     else
386 #endif
387
388     {
389         int to_convert = min(*inbytes_left, *outbytes_left);
390         int converted = to_convert;
391         char *table = convset->sbcs_table;
392
393         while (to_convert) {
394             *outbuf = table[(unsigned char)*inbuf];
395             ++outbuf;
396             ++inbuf;
397             --to_convert;
398         }
399         *inbytes_left -= converted;
400         *outbytes_left -= converted;
401     }
402
403     return status;
404 }
405
406 APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset,
407                                              unsigned char inchar)
408 {
409     if (convset->sbcs_table) {
410         return convset->sbcs_table[inchar];
411     }
412     else {
413         return -1;
414     }
415 }
416
417 APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset)
418 {
419     return apr_pool_cleanup_run(convset->pool, convset, apr_xlate_cleanup);
420 }
421
422 #else /* !APR_HAS_XLATE */
423
424 APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset,
425                                          const char *topage,
426                                          const char *frompage,
427                                          apr_pool_t *pool)
428 {
429     return APR_ENOTIMPL;
430 }
431
432 APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff)
433 {
434     return APR_ENOTIMPL;
435 }
436
437 APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset,
438                                              unsigned char inchar)
439 {
440     return (-1);
441 }
442
443 APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset,
444                                                 const char *inbuf,
445                                                 apr_size_t *inbytes_left,
446                                                 char *outbuf,
447                                                 apr_size_t *outbytes_left)
448 {
449     return APR_ENOTIMPL;
450 }
451
452 APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset)
453 {
454     return APR_ENOTIMPL;
455 }
456
457 #endif /* APR_HAS_XLATE */
458
459 /* Deprecated
460  */
461 APU_DECLARE(apr_status_t) apr_xlate_get_sb(apr_xlate_t *convset, int *onoff)
462 {
463     return apr_xlate_sb_get(convset, onoff);
464 }