]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/apr-util/xlate/xlate.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / apr-util / xlate / xlate.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  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, 
184                             convset->pool, &convset->ich);
185     }
186 }
187 #endif /* APU_HAVE_APR_ICONV */
188
189 static void make_identity_table(apr_xlate_t *convset)
190 {
191   int i;
192
193   convset->sbcs_table = apr_palloc(convset->pool, 256);
194   for (i = 0; i < 256; i++)
195       convset->sbcs_table[i] = i;
196 }
197
198 APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset,
199                                          const char *topage,
200                                          const char *frompage,
201                                          apr_pool_t *pool)
202 {
203     apr_status_t rv;
204     apr_xlate_t *new;
205     int found = 0;
206
207     *convset = NULL;
208
209     topage = handle_special_names(topage, pool);
210     frompage = handle_special_names(frompage, pool);
211
212     new = (apr_xlate_t *)apr_pcalloc(pool, sizeof(apr_xlate_t));
213     if (!new) {
214         return APR_ENOMEM;
215     }
216
217     new->pool = pool;
218     new->topage = apr_pstrdup(pool, topage);
219     new->frompage = apr_pstrdup(pool, frompage);
220     if (!new->topage || !new->frompage) {
221         return APR_ENOMEM;
222     }
223
224 #ifdef TODO
225     /* search cache of codepage pairs; we may be able to avoid the
226      * expensive iconv_open()
227      */
228
229     set found to non-zero if found in the cache
230 #endif
231
232     if ((! found) && (strcmp(topage, frompage) == 0)) {
233         /* to and from are the same */
234         found = 1;
235         make_identity_table(new);
236     }
237
238 #if APU_HAVE_APR_ICONV
239     if (!found) {
240         rv = apr_iconv_open(topage, frompage, pool, &new->ich);
241         if (rv != APR_SUCCESS) {
242             return rv;
243         }
244         found = 1;
245         check_sbcs(new);
246     } else
247         new->ich = (apr_iconv_t)-1;
248
249 #elif APU_HAVE_ICONV
250     if (!found) {
251         new->ich = iconv_open(topage, frompage);
252         if (new->ich == (iconv_t)-1) {
253             int rv = errno;
254             /* Sometimes, iconv is not good about setting errno. */
255             return rv ? rv : APR_EINVAL;
256         }
257         found = 1;
258         check_sbcs(new);
259     } else
260         new->ich = (iconv_t)-1;
261 #endif /* APU_HAVE_ICONV */
262
263     if (found) {
264         *convset = new;
265         apr_pool_cleanup_register(pool, (void *)new, apr_xlate_cleanup,
266                             apr_pool_cleanup_null);
267         rv = APR_SUCCESS;
268     }
269     else {
270         rv = APR_EINVAL; /* iconv() would return EINVAL if it
271                                 couldn't handle the pair */
272     }
273
274     return rv;
275 }
276
277 APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff)
278 {
279     *onoff = convset->sbcs_table != NULL;
280     return APR_SUCCESS;
281 }
282
283 APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset,
284                                                 const char *inbuf,
285                                                 apr_size_t *inbytes_left,
286                                                 char *outbuf,
287                                                 apr_size_t *outbytes_left)
288 {
289     apr_status_t status = APR_SUCCESS;
290
291 #if APU_HAVE_APR_ICONV
292     if (convset->ich != (apr_iconv_t)-1) {
293         const char *inbufptr = inbuf;
294         apr_size_t translated;
295         char *outbufptr = outbuf;
296         status = apr_iconv(convset->ich, &inbufptr, inbytes_left,
297                            &outbufptr, outbytes_left, &translated);
298
299         /* If everything went fine but we ran out of buffer, don't
300          * report it as an error.  Caller needs to look at the two
301          * bytes-left values anyway.
302          *
303          * There are three expected cases where rc is -1.  In each of
304          * these cases, *inbytes_left != 0.
305          * a) the non-error condition where we ran out of output
306          *    buffer
307          * b) the non-error condition where we ran out of input (i.e.,
308          *    the last input character is incomplete)
309          * c) the error condition where the input is invalid
310          */
311         switch (status) {
312
313             case APR_BADARG:  /* out of space on output */
314                 status = 0; /* change table lookup code below if you
315                                make this an error */
316                 break;
317
318             case APR_EINVAL: /* input character not complete (yet) */
319                 status = APR_INCOMPLETE;
320                 break;
321
322             case APR_BADCH: /* bad input byte */
323                 status = APR_EINVAL;
324                 break;
325
326              /* Sometimes, iconv is not good about setting errno. */
327             case 0:
328                 if (inbytes_left && *inbytes_left)
329                     status = APR_INCOMPLETE;
330                 break;
331
332             default:
333                 break;
334         }
335     }
336     else
337
338 #elif APU_HAVE_ICONV
339     if (convset->ich != (iconv_t)-1) {
340         const char *inbufptr = inbuf;
341         char *outbufptr = outbuf;
342         apr_size_t translated;
343         translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr,
344                            inbytes_left, &outbufptr, outbytes_left);
345
346         /* If everything went fine but we ran out of buffer, don't
347          * report it as an error.  Caller needs to look at the two
348          * bytes-left values anyway.
349          *
350          * There are three expected cases where rc is -1.  In each of
351          * these cases, *inbytes_left != 0.
352          * a) the non-error condition where we ran out of output
353          *    buffer
354          * b) the non-error condition where we ran out of input (i.e.,
355          *    the last input character is incomplete)
356          * c) the error condition where the input is invalid
357          */
358         if (translated == (apr_size_t)-1) {
359             int rv = errno;
360             switch (rv) {
361
362             case E2BIG:  /* out of space on output */
363                 status = 0; /* change table lookup code below if you
364                                make this an error */
365                 break;
366
367             case EINVAL: /* input character not complete (yet) */
368                 status = APR_INCOMPLETE;
369                 break;
370
371             case EILSEQ: /* bad input byte */
372                 status = APR_EINVAL;
373                 break;
374
375              /* Sometimes, iconv is not good about setting errno. */
376             case 0:
377                 status = APR_INCOMPLETE;
378                 break;
379
380             default:
381                 status = rv;
382                 break;
383             }
384         }
385     }
386     else
387 #endif
388
389     if (inbuf) {
390         apr_size_t to_convert = min(*inbytes_left, *outbytes_left);
391         apr_size_t converted = to_convert;
392         char *table = convset->sbcs_table;
393
394         while (to_convert) {
395             *outbuf = table[(unsigned char)*inbuf];
396             ++outbuf;
397             ++inbuf;
398             --to_convert;
399         }
400         *inbytes_left -= converted;
401         *outbytes_left -= converted;
402     }
403
404     return status;
405 }
406
407 APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset,
408                                              unsigned char inchar)
409 {
410     if (convset->sbcs_table) {
411         return convset->sbcs_table[inchar];
412     }
413     else {
414         return -1;
415     }
416 }
417
418 APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset)
419 {
420     return apr_pool_cleanup_run(convset->pool, convset, apr_xlate_cleanup);
421 }
422
423 #else /* !APR_HAS_XLATE */
424
425 APU_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset,
426                                          const char *topage,
427                                          const char *frompage,
428                                          apr_pool_t *pool)
429 {
430     return APR_ENOTIMPL;
431 }
432
433 APU_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff)
434 {
435     return APR_ENOTIMPL;
436 }
437
438 APU_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset,
439                                              unsigned char inchar)
440 {
441     return (-1);
442 }
443
444 APU_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset,
445                                                 const char *inbuf,
446                                                 apr_size_t *inbytes_left,
447                                                 char *outbuf,
448                                                 apr_size_t *outbytes_left)
449 {
450     return APR_ENOTIMPL;
451 }
452
453 APU_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset)
454 {
455     return APR_ENOTIMPL;
456 }
457
458 #endif /* APR_HAS_XLATE */