/src/httpd/srclib/apr/xlate/xlate.c
Line | Count | Source (jump to first uncovered line) |
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 "apr_private.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 | | |
43 | | #if defined(APU_ICONV_INBUF_CONST) |
44 | | #define ICONV_INBUF_TYPE const char ** |
45 | | #else |
46 | | #define ICONV_INBUF_TYPE char ** |
47 | | #endif |
48 | | |
49 | | #ifndef min |
50 | 0 | #define min(x,y) ((x) <= (y) ? (x) : (y)) |
51 | | #endif |
52 | | |
53 | | struct apr_xlate_t { |
54 | | apr_pool_t *pool; |
55 | | char *frompage; |
56 | | char *topage; |
57 | | char *sbcs_table; |
58 | | #if APU_HAVE_ICONV |
59 | | iconv_t ich; |
60 | | #endif |
61 | | }; |
62 | | |
63 | | |
64 | | static const char *handle_special_names(const char *page, apr_pool_t *pool) |
65 | 0 | { |
66 | 0 | if (page == APR_DEFAULT_CHARSET) { |
67 | 0 | return apr_os_default_encoding(pool); |
68 | 0 | } |
69 | 0 | else if (page == APR_LOCALE_CHARSET) { |
70 | 0 | return apr_os_locale_encoding(pool); |
71 | 0 | } |
72 | | #ifdef __MVS__ |
73 | | else if (!strcasecmp(page, "ISO-8859-1")) { |
74 | | return "ISO8859-1"; /* z/OS ASCII name has no dash after ISO */ |
75 | | } |
76 | | #endif |
77 | 0 | else { |
78 | 0 | return page; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | static apr_status_t apr_xlate_cleanup(void *convset) |
83 | 0 | { |
84 | 0 | apr_xlate_t *old = convset; |
85 | |
|
86 | 0 | #if APU_HAVE_ICONV |
87 | 0 | if (old->ich != (iconv_t)-1) { |
88 | 0 | if (iconv_close(old->ich)) { |
89 | 0 | int rv = errno; |
90 | | |
91 | | /* Sometimes, iconv is not good about setting errno. */ |
92 | 0 | return rv ? rv : APR_EINVAL; |
93 | 0 | } |
94 | 0 | } |
95 | 0 | #endif |
96 | | |
97 | 0 | return APR_SUCCESS; |
98 | 0 | } |
99 | | |
100 | | #if APU_HAVE_ICONV |
101 | | static void check_sbcs(apr_xlate_t *convset) |
102 | 0 | { |
103 | 0 | char inbuf[256], outbuf[256]; |
104 | 0 | char *inbufptr = inbuf; |
105 | 0 | char *outbufptr = outbuf; |
106 | 0 | apr_size_t inbytes_left, outbytes_left; |
107 | 0 | int i; |
108 | 0 | apr_size_t translated; |
109 | |
|
110 | 0 | for (i = 0; i < sizeof(inbuf); i++) { |
111 | 0 | inbuf[i] = i; |
112 | 0 | } |
113 | |
|
114 | 0 | inbytes_left = outbytes_left = sizeof(inbuf); |
115 | 0 | translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, |
116 | 0 | &inbytes_left, &outbufptr, &outbytes_left); |
117 | |
|
118 | 0 | if (translated != (apr_size_t)-1 |
119 | 0 | && inbytes_left == 0 |
120 | 0 | && outbytes_left == 0) { |
121 | | /* hurray... this is simple translation; save the table, |
122 | | * close the iconv descriptor |
123 | | */ |
124 | |
|
125 | 0 | convset->sbcs_table = apr_pmemdup(convset->pool, outbuf, sizeof(outbuf)); |
126 | |
|
127 | 0 | iconv_close(convset->ich); |
128 | 0 | convset->ich = (iconv_t)-1; |
129 | | |
130 | | /* TODO: add the table to the cache */ |
131 | 0 | } |
132 | 0 | else { |
133 | | /* reset the iconv descriptor, since it's now in an undefined |
134 | | * state. */ |
135 | 0 | iconv_close(convset->ich); |
136 | 0 | convset->ich = iconv_open(convset->topage, convset->frompage); |
137 | 0 | } |
138 | 0 | } |
139 | | #elif APU_HAVE_APR_ICONV |
140 | | static void check_sbcs(apr_xlate_t *convset) |
141 | | { |
142 | | char inbuf[256], outbuf[256]; |
143 | | char *inbufptr = inbuf; |
144 | | char *outbufptr = outbuf; |
145 | | apr_size_t inbytes_left, outbytes_left; |
146 | | int i; |
147 | | apr_size_t translated; |
148 | | apr_status_t rv; |
149 | | |
150 | | for (i = 0; i < sizeof(inbuf); i++) { |
151 | | inbuf[i] = i; |
152 | | } |
153 | | |
154 | | inbytes_left = outbytes_left = sizeof(inbuf); |
155 | | rv = apr_iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, |
156 | | &inbytes_left, &outbufptr, &outbytes_left, |
157 | | &translated); |
158 | | |
159 | | if ((rv == APR_SUCCESS) |
160 | | && (translated != (apr_size_t)-1) |
161 | | && inbytes_left == 0 |
162 | | && outbytes_left == 0) { |
163 | | /* hurray... this is simple translation; save the table, |
164 | | * close the iconv descriptor |
165 | | */ |
166 | | |
167 | | convset->sbcs_table = apr_palloc(convset->pool, sizeof(outbuf)); |
168 | | memcpy(convset->sbcs_table, outbuf, sizeof(outbuf)); |
169 | | apr_iconv_close(convset->ich, convset->pool); |
170 | | convset->ich = (apr_iconv_t)-1; |
171 | | |
172 | | /* TODO: add the table to the cache */ |
173 | | } |
174 | | else { |
175 | | /* reset the iconv descriptor, since it's now in an undefined |
176 | | * state. */ |
177 | | apr_iconv_close(convset->ich, convset->pool); |
178 | | rv = apr_iconv_open(convset->topage, convset->frompage, |
179 | | convset->pool, &convset->ich); |
180 | | } |
181 | | } |
182 | | #endif /* APU_HAVE_APR_ICONV */ |
183 | | |
184 | | static void make_identity_table(apr_xlate_t *convset) |
185 | 0 | { |
186 | 0 | int i; |
187 | |
|
188 | 0 | convset->sbcs_table = apr_palloc(convset->pool, 256); |
189 | 0 | for (i = 0; i < 256; i++) |
190 | 0 | convset->sbcs_table[i] = i; |
191 | 0 | } |
192 | | |
193 | | APR_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset, |
194 | | const char *topage, |
195 | | const char *frompage, |
196 | | apr_pool_t *pool) |
197 | 0 | { |
198 | 0 | apr_status_t rv; |
199 | 0 | apr_xlate_t *new; |
200 | 0 | int found = 0; |
201 | |
|
202 | 0 | *convset = NULL; |
203 | |
|
204 | 0 | topage = handle_special_names(topage, pool); |
205 | 0 | frompage = handle_special_names(frompage, pool); |
206 | |
|
207 | 0 | new = (apr_xlate_t *)apr_pcalloc(pool, sizeof(apr_xlate_t)); |
208 | 0 | if (!new) { |
209 | 0 | return APR_ENOMEM; |
210 | 0 | } |
211 | | |
212 | 0 | new->pool = pool; |
213 | 0 | new->topage = apr_pstrdup(pool, topage); |
214 | 0 | new->frompage = apr_pstrdup(pool, frompage); |
215 | 0 | if (!new->topage || !new->frompage) { |
216 | 0 | return APR_ENOMEM; |
217 | 0 | } |
218 | | |
219 | | #ifdef TODO |
220 | | /* search cache of codepage pairs; we may be able to avoid the |
221 | | * expensive iconv_open() |
222 | | */ |
223 | | |
224 | | set found to non-zero if found in the cache |
225 | | #endif |
226 | | |
227 | 0 | if ((! found) && (strcmp(topage, frompage) == 0)) { |
228 | | /* to and from are the same */ |
229 | 0 | found = 1; |
230 | 0 | make_identity_table(new); |
231 | 0 | } |
232 | |
|
233 | | #if APU_HAVE_APR_ICONV |
234 | | if (!found) { |
235 | | rv = apr_iconv_open(topage, frompage, pool, &new->ich); |
236 | | if (rv != APR_SUCCESS) { |
237 | | return rv; |
238 | | } |
239 | | found = 1; |
240 | | check_sbcs(new); |
241 | | } else |
242 | | new->ich = (apr_iconv_t)-1; |
243 | | |
244 | | #elif APU_HAVE_ICONV |
245 | 0 | if (!found) { |
246 | 0 | new->ich = iconv_open(topage, frompage); |
247 | 0 | if (new->ich == (iconv_t)-1) { |
248 | 0 | int rv = errno; |
249 | | /* Sometimes, iconv is not good about setting errno. */ |
250 | 0 | return rv ? rv : APR_EINVAL; |
251 | 0 | } |
252 | 0 | found = 1; |
253 | 0 | check_sbcs(new); |
254 | 0 | } else |
255 | 0 | new->ich = (iconv_t)-1; |
256 | 0 | #endif /* APU_HAVE_ICONV */ |
257 | | |
258 | 0 | if (found) { |
259 | 0 | *convset = new; |
260 | 0 | apr_pool_cleanup_register(pool, (void *)new, apr_xlate_cleanup, |
261 | 0 | apr_pool_cleanup_null); |
262 | 0 | rv = APR_SUCCESS; |
263 | 0 | } |
264 | 0 | else { |
265 | 0 | rv = APR_EINVAL; /* iconv() would return EINVAL if it |
266 | | couldn't handle the pair */ |
267 | 0 | } |
268 | |
|
269 | 0 | return rv; |
270 | 0 | } |
271 | | |
272 | | APR_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff) |
273 | 0 | { |
274 | 0 | *onoff = convset->sbcs_table != NULL; |
275 | 0 | return APR_SUCCESS; |
276 | 0 | } |
277 | | |
278 | | APR_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset, |
279 | | const char *inbuf, |
280 | | apr_size_t *inbytes_left, |
281 | | char *outbuf, |
282 | | apr_size_t *outbytes_left) |
283 | 0 | { |
284 | 0 | apr_status_t status = APR_SUCCESS; |
285 | |
|
286 | 0 | #if APU_HAVE_ICONV |
287 | 0 | if (convset->ich != (iconv_t)-1) { |
288 | 0 | const char *inbufptr = inbuf; |
289 | 0 | char *outbufptr = outbuf; |
290 | 0 | apr_size_t translated; |
291 | 0 | translated = iconv(convset->ich, (ICONV_INBUF_TYPE)&inbufptr, |
292 | 0 | inbytes_left, &outbufptr, outbytes_left); |
293 | | |
294 | | /* If everything went fine but we ran out of buffer, don't |
295 | | * report it as an error. Caller needs to look at the two |
296 | | * bytes-left values anyway. |
297 | | * |
298 | | * There are three expected cases where rc is -1. In each of |
299 | | * these cases, *inbytes_left != 0. |
300 | | * a) the non-error condition where we ran out of output |
301 | | * buffer |
302 | | * b) the non-error condition where we ran out of input (i.e., |
303 | | * the last input character is incomplete) |
304 | | * c) the error condition where the input is invalid |
305 | | */ |
306 | 0 | if (translated == (apr_size_t)-1) { |
307 | 0 | int rv = errno; |
308 | 0 | switch (rv) { |
309 | | |
310 | 0 | case E2BIG: /* out of space on output */ |
311 | 0 | status = 0; /* change table lookup code below if you |
312 | | make this an error */ |
313 | 0 | break; |
314 | | |
315 | 0 | case EINVAL: /* input character not complete (yet) */ |
316 | 0 | status = APR_INCOMPLETE; |
317 | 0 | break; |
318 | | |
319 | 0 | case EILSEQ: /* bad input byte */ |
320 | 0 | status = APR_EINVAL; |
321 | 0 | break; |
322 | | |
323 | | /* Sometimes, iconv is not good about setting errno. */ |
324 | 0 | case 0: |
325 | 0 | status = APR_INCOMPLETE; |
326 | 0 | break; |
327 | | |
328 | 0 | default: |
329 | 0 | status = rv; |
330 | 0 | break; |
331 | 0 | } |
332 | 0 | } |
333 | 0 | } |
334 | 0 | else |
335 | 0 | #endif |
336 | | |
337 | 0 | if (inbuf) { |
338 | 0 | apr_size_t to_convert = min(*inbytes_left, *outbytes_left); |
339 | 0 | apr_size_t converted = to_convert; |
340 | 0 | char *table = convset->sbcs_table; |
341 | |
|
342 | 0 | while (to_convert) { |
343 | 0 | *outbuf = table[(unsigned char)*inbuf]; |
344 | 0 | ++outbuf; |
345 | 0 | ++inbuf; |
346 | 0 | --to_convert; |
347 | 0 | } |
348 | 0 | *inbytes_left -= converted; |
349 | 0 | *outbytes_left -= converted; |
350 | 0 | } |
351 | | |
352 | 0 | return status; |
353 | 0 | } |
354 | | |
355 | | APR_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset, |
356 | | unsigned char inchar) |
357 | 0 | { |
358 | 0 | if (convset->sbcs_table) { |
359 | 0 | return convset->sbcs_table[inchar]; |
360 | 0 | } |
361 | 0 | else { |
362 | 0 | return -1; |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | APR_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset) |
367 | 0 | { |
368 | 0 | return apr_pool_cleanup_run(convset->pool, convset, apr_xlate_cleanup); |
369 | 0 | } |
370 | | |
371 | | #else /* !APR_HAS_XLATE */ |
372 | | |
373 | | APR_DECLARE(apr_status_t) apr_xlate_open(apr_xlate_t **convset, |
374 | | const char *topage, |
375 | | const char *frompage, |
376 | | apr_pool_t *pool) |
377 | | { |
378 | | return APR_ENOTIMPL; |
379 | | } |
380 | | |
381 | | APR_DECLARE(apr_status_t) apr_xlate_sb_get(apr_xlate_t *convset, int *onoff) |
382 | | { |
383 | | return APR_ENOTIMPL; |
384 | | } |
385 | | |
386 | | APR_DECLARE(apr_int32_t) apr_xlate_conv_byte(apr_xlate_t *convset, |
387 | | unsigned char inchar) |
388 | | { |
389 | | return (-1); |
390 | | } |
391 | | |
392 | | APR_DECLARE(apr_status_t) apr_xlate_conv_buffer(apr_xlate_t *convset, |
393 | | const char *inbuf, |
394 | | apr_size_t *inbytes_left, |
395 | | char *outbuf, |
396 | | apr_size_t *outbytes_left) |
397 | | { |
398 | | return APR_ENOTIMPL; |
399 | | } |
400 | | |
401 | | APR_DECLARE(apr_status_t) apr_xlate_close(apr_xlate_t *convset) |
402 | | { |
403 | | return APR_ENOTIMPL; |
404 | | } |
405 | | |
406 | | #endif /* APR_HAS_XLATE */ |