source: branches/samba-3.3.x/source/lib/iconv.c@ 460

Last change on this file since 460 was 339, checked in by Herwig Bauernfeind, 16 years ago

divers fix for Ticket #68 in 3.3

File size: 19.2 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 minimal iconv implementation
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Jelmer Vernooij 2002,2003
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#include "includes.h"
22
23/*
24 * We have to use strcasecmp here as the character conversions
25 * haven't been initialised yet. JRA.
26 */
27
28#undef strcasecmp
29
30/**
31 * @file
32 *
33 * @brief Samba wrapper/stub for iconv character set conversion.
34 *
35 * iconv is the XPG2 interface for converting between character
36 * encodings. This file provides a Samba wrapper around it, and also
37 * a simple reimplementation that is used if the system does not
38 * implement iconv.
39 *
40 * Samba only works with encodings that are supersets of ASCII: ascii
41 * characters like whitespace can be tested for directly, multibyte
42 * sequences start with a byte with the high bit set, and strings are
43 * terminated by a nul byte.
44 *
45 * Note that the only function provided by iconv is conversion between
46 * characters. It doesn't directly support operations like
47 * uppercasing or comparison. We have to convert to UCS-2 and compare
48 * there.
49 *
50 * @sa Samba Developers Guide
51 **/
52
53static_decl_charset;
54
55static size_t ascii_pull(void *,const char **, size_t *, char **, size_t *);
56static size_t ascii_push(void *,const char **, size_t *, char **, size_t *);
57static size_t latin1_push(void *,const char **, size_t *, char **, size_t *);
58static size_t utf8_pull(void *,const char **, size_t *, char **, size_t *);
59static size_t utf8_push(void *,const char **, size_t *, char **, size_t *);
60static size_t ucs2hex_pull(void *,const char **, size_t *, char **, size_t *);
61static size_t ucs2hex_push(void *,const char **, size_t *, char **, size_t *);
62static size_t iconv_copy(void *,const char **, size_t *, char **, size_t *);
63static size_t iconv_swab (void *,const char **, size_t *, char **, size_t *);
64
65static struct charset_functions builtin_functions[] = {
66 /* windows is really neither UCS-2 not UTF-16 */
67 {"UCS-2LE", iconv_copy, iconv_copy},
68 {"UTF-16LE", iconv_copy, iconv_copy},
69 {"UCS-2BE", iconv_swab, iconv_swab},
70 {"UTF-16BE", iconv_swab, iconv_swab},
71
72 /* we include the UTF-8 alias to cope with differing locale settings */
73 {"UTF8", utf8_pull, utf8_push},
74 {"UTF-8", utf8_pull, utf8_push},
75 {"ASCII", ascii_pull, ascii_push},
76 {"646", ascii_pull, ascii_push},
77 {"ISO-8859-1", ascii_pull, latin1_push},
78 {"UCS2-HEX", ucs2hex_pull, ucs2hex_push},
79 {NULL, NULL, NULL}
80};
81
82static struct charset_functions *charsets = NULL;
83
84static struct charset_functions *find_charset_functions(const char *name)
85{
86 struct charset_functions *c = charsets;
87
88 while(c) {
89 if (strcasecmp(name, c->name) == 0) {
90 return c;
91 }
92 c = c->next;
93 }
94
95 return NULL;
96}
97
98NTSTATUS smb_register_charset(struct charset_functions *funcs)
99{
100 if (!funcs) {
101 return NT_STATUS_INVALID_PARAMETER;
102 }
103
104 DEBUG(5, ("Attempting to register new charset %s\n", funcs->name));
105 /* Check whether we already have this charset... */
106 if (find_charset_functions(funcs->name)) {
107 DEBUG(0, ("Duplicate charset %s, not registering\n", funcs->name));
108 return NT_STATUS_OBJECT_NAME_COLLISION;
109 }
110
111 funcs->next = funcs->prev = NULL;
112 DEBUG(5, ("Registered charset %s\n", funcs->name));
113 DLIST_ADD(charsets, funcs);
114 return NT_STATUS_OK;
115}
116
117static void lazy_initialize_iconv(void)
118{
119 static bool initialized;
120 int i;
121
122 if (!initialized) {
123 initialized = True;
124 for(i = 0; builtin_functions[i].name; i++)
125 smb_register_charset(&builtin_functions[i]);
126 static_init_charset;
127 }
128}
129
130#ifdef __OS2__
131// i could have done a static variable w/o this function. but i feel it's nicer this way. SCS
132// the purpose of this function is to save the to_name to get the korean and japanese code set working
133char * save_toname(char *toname, bool what)
134{
135 static char *to_name=NULL;
136
137 if ( what == 0 )
138 to_name = SMB_STRDUP(toname);
139
140 return to_name;
141}
142#endif
143
144#ifdef HAVE_NATIVE_ICONV
145/* if there was an error then reset the internal state,
146 this ensures that we don't have a shift state remaining for
147 character sets like SJIS */
148static size_t sys_iconv(void *cd,
149 const char **inbuf, size_t *inbytesleft,
150 char **outbuf, size_t *outbytesleft)
151{
152#ifdef __OS2__
153 uint16 *outbuf_uc = ( uint16 * )*outbuf;
154 char *to_name = save_toname(NULL, 1);
155#endif
156
157 size_t ret = iconv((iconv_t)cd,
158 (void *)inbuf, inbytesleft,
159 outbuf, outbytesleft);
160 if (ret == (size_t)-1) {
161 int saved_errno = errno;
162 iconv(cd, NULL, NULL, NULL, NULL);
163 errno = saved_errno;
164 }
165#ifdef __OS2__
166 /* Workaround for path separator on OS/2 */
167 else
168 {
169 if( (strstr(to_name, "949") != NULL) || /* Korean CP */
170 (strstr(to_name, "932") != NULL) || /* Japanese CP */
171 (strstr(to_name, "942") != NULL) || /* Japanese CP */
172 (strstr(to_name, "943") != NULL) ) /* Japanese CP */
173 {
174 while(( char * )outbuf_uc < *outbuf )
175 {
176 if( *outbuf_uc == 0x20a9 || /* Korean WON */
177 *outbuf_uc == 0x00a5 ) /* Japanese YEN */
178 *outbuf_uc = '\\';
179
180 outbuf_uc++;
181 }
182 }
183 }
184#endif
185 return ret;
186}
187#endif
188
189/**
190 * This is a simple portable iconv() implementaion.
191 *
192 * It only knows about a very small number of character sets - just
193 * enough that Samba works on systems that don't have iconv.
194 **/
195size_t smb_iconv(smb_iconv_t cd,
196 const char **inbuf, size_t *inbytesleft,
197 char **outbuf, size_t *outbytesleft)
198{
199 char cvtbuf[2048];
200 char *bufp = cvtbuf;
201 size_t bufsize;
202
203#ifdef __OS2__
204 save_toname(cd->to_name, 0);
205#endif
206
207 /* in many cases we can go direct */
208 if (cd->direct) {
209 return cd->direct(cd->cd_direct,
210 inbuf, inbytesleft, outbuf, outbytesleft);
211 }
212
213
214 /* otherwise we have to do it chunks at a time */
215 while (*inbytesleft > 0) {
216 bufp = cvtbuf;
217 bufsize = sizeof(cvtbuf);
218
219 if (cd->pull(cd->cd_pull,
220 inbuf, inbytesleft, &bufp, &bufsize) == -1
221 && errno != E2BIG)
222 return -1;
223
224 bufp = cvtbuf;
225 bufsize = sizeof(cvtbuf) - bufsize;
226
227 if (cd->push(cd->cd_push,
228 (const char **)&bufp, &bufsize,
229 outbuf, outbytesleft) == -1)
230 return -1;
231 }
232
233 return 0;
234}
235
236
237static bool is_utf16(const char *name)
238{
239 return strcasecmp(name, "UCS-2LE") == 0 ||
240 strcasecmp(name, "UTF-16LE") == 0;
241}
242
243/*
244 simple iconv_open() wrapper
245 */
246smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode)
247{
248 smb_iconv_t ret;
249 struct charset_functions *from, *to;
250
251 lazy_initialize_iconv();
252 from = charsets;
253 to = charsets;
254
255 ret = SMB_MALLOC_P(struct _smb_iconv_t);
256 if (!ret) {
257 errno = ENOMEM;
258 return (smb_iconv_t)-1;
259 }
260 memset(ret, 0, sizeof(struct _smb_iconv_t));
261
262 ret->from_name = SMB_STRDUP(fromcode);
263 ret->to_name = SMB_STRDUP(tocode);
264
265 /* check for the simplest null conversion */
266 if (strcasecmp(fromcode, tocode) == 0) {
267 ret->direct = iconv_copy;
268 return ret;
269 }
270
271 /* check if we have a builtin function for this conversion */
272 from = find_charset_functions(fromcode);
273 if(from)ret->pull = from->pull;
274
275 to = find_charset_functions(tocode);
276 if(to)ret->push = to->push;
277
278 /* check if we can use iconv for this conversion */
279#ifdef HAVE_NATIVE_ICONV
280 if (!ret->pull) {
281 ret->cd_pull = iconv_open("UTF-16LE", fromcode);
282 if (ret->cd_pull == (iconv_t)-1)
283 ret->cd_pull = iconv_open("UCS-2LE", fromcode);
284 if (ret->cd_pull != (iconv_t)-1)
285 ret->pull = sys_iconv;
286 }
287
288 if (!ret->push) {
289 ret->cd_push = iconv_open(tocode, "UTF-16LE");
290 if (ret->cd_push == (iconv_t)-1)
291 ret->cd_push = iconv_open(tocode, "UCS-2LE");
292 if (ret->cd_push != (iconv_t)-1)
293 ret->push = sys_iconv;
294 }
295#endif
296
297 /* check if there is a module available that can do this conversion */
298 if (!ret->pull && NT_STATUS_IS_OK(smb_probe_module("charset", fromcode))) {
299 if(!(from = find_charset_functions(fromcode)))
300 DEBUG(0, ("Module %s doesn't provide charset %s!\n", fromcode, fromcode));
301 else
302 ret->pull = from->pull;
303 }
304
305 if (!ret->push && NT_STATUS_IS_OK(smb_probe_module("charset", tocode))) {
306 if(!(to = find_charset_functions(tocode)))
307 DEBUG(0, ("Module %s doesn't provide charset %s!\n", tocode, tocode));
308 else
309 ret->push = to->push;
310 }
311
312 if (!ret->push || !ret->pull) {
313 SAFE_FREE(ret->from_name);
314 SAFE_FREE(ret->to_name);
315 SAFE_FREE(ret);
316 errno = EINVAL;
317 return (smb_iconv_t)-1;
318 }
319
320 /* check for conversion to/from ucs2 */
321 if (is_utf16(fromcode) && to) {
322 ret->direct = to->push;
323 ret->push = ret->pull = NULL;
324 return ret;
325 }
326
327 if (is_utf16(tocode) && from) {
328 ret->direct = from->pull;
329 ret->push = ret->pull = NULL;
330 return ret;
331 }
332
333 /* Check if we can do the conversion direct */
334#ifdef HAVE_NATIVE_ICONV
335 if (is_utf16(fromcode)) {
336 ret->direct = sys_iconv;
337 ret->cd_direct = ret->cd_push;
338 ret->cd_push = NULL;
339 return ret;
340 }
341 if (is_utf16(tocode)) {
342 ret->direct = sys_iconv;
343 ret->cd_direct = ret->cd_pull;
344 ret->cd_pull = NULL;
345 return ret;
346 }
347#endif
348
349 return ret;
350}
351
352/*
353 simple iconv_close() wrapper
354*/
355int smb_iconv_close (smb_iconv_t cd)
356{
357#ifdef HAVE_NATIVE_ICONV
358 if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct);
359 if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull);
360 if (cd->cd_push) iconv_close((iconv_t)cd->cd_push);
361#endif
362
363 SAFE_FREE(cd->from_name);
364 SAFE_FREE(cd->to_name);
365
366 memset(cd, 0, sizeof(*cd));
367 SAFE_FREE(cd);
368 return 0;
369}
370
371
372/**********************************************************************
373 the following functions implement the builtin character sets in Samba
374 and also the "test" character sets that are designed to test
375 multi-byte character set support for english users
376***********************************************************************/
377
378static size_t ascii_pull(void *cd, const char **inbuf, size_t *inbytesleft,
379 char **outbuf, size_t *outbytesleft)
380{
381 while (*inbytesleft >= 1 && *outbytesleft >= 2) {
382 (*outbuf)[0] = (*inbuf)[0];
383 (*outbuf)[1] = 0;
384 (*inbytesleft) -= 1;
385 (*outbytesleft) -= 2;
386 (*inbuf) += 1;
387 (*outbuf) += 2;
388 }
389
390 if (*inbytesleft > 0) {
391 errno = E2BIG;
392 return -1;
393 }
394
395 return 0;
396}
397
398static size_t ascii_push(void *cd, const char **inbuf, size_t *inbytesleft,
399 char **outbuf, size_t *outbytesleft)
400{
401 int ir_count=0;
402
403 while (*inbytesleft >= 2 && *outbytesleft >= 1) {
404 (*outbuf)[0] = (*inbuf)[0] & 0x7F;
405 if ((*inbuf)[1]) ir_count++;
406 (*inbytesleft) -= 2;
407 (*outbytesleft) -= 1;
408 (*inbuf) += 2;
409 (*outbuf) += 1;
410 }
411
412 if (*inbytesleft == 1) {
413 errno = EINVAL;
414 return -1;
415 }
416
417 if (*inbytesleft > 1) {
418 errno = E2BIG;
419 return -1;
420 }
421
422 return ir_count;
423}
424
425static size_t latin1_push(void *cd, const char **inbuf, size_t *inbytesleft,
426 char **outbuf, size_t *outbytesleft)
427{
428 int ir_count=0;
429
430 while (*inbytesleft >= 2 && *outbytesleft >= 1) {
431 (*outbuf)[0] = (*inbuf)[0];
432 if ((*inbuf)[1]) ir_count++;
433 (*inbytesleft) -= 2;
434 (*outbytesleft) -= 1;
435 (*inbuf) += 2;
436 (*outbuf) += 1;
437 }
438
439 if (*inbytesleft == 1) {
440 errno = EINVAL;
441 return -1;
442 }
443
444 if (*inbytesleft > 1) {
445 errno = E2BIG;
446 return -1;
447 }
448
449 return ir_count;
450}
451
452static size_t ucs2hex_pull(void *cd, const char **inbuf, size_t *inbytesleft,
453 char **outbuf, size_t *outbytesleft)
454{
455 while (*inbytesleft >= 1 && *outbytesleft >= 2) {
456 unsigned v;
457
458 if ((*inbuf)[0] != '@') {
459 /* seven bit ascii case */
460 (*outbuf)[0] = (*inbuf)[0];
461 (*outbuf)[1] = 0;
462 (*inbytesleft) -= 1;
463 (*outbytesleft) -= 2;
464 (*inbuf) += 1;
465 (*outbuf) += 2;
466 continue;
467 }
468 /* it's a hex character */
469 if (*inbytesleft < 5) {
470 errno = EINVAL;
471 return -1;
472 }
473
474 if (sscanf(&(*inbuf)[1], "%04x", &v) != 1) {
475 errno = EILSEQ;
476 return -1;
477 }
478
479 (*outbuf)[0] = v&0xff;
480 (*outbuf)[1] = v>>8;
481 (*inbytesleft) -= 5;
482 (*outbytesleft) -= 2;
483 (*inbuf) += 5;
484 (*outbuf) += 2;
485 }
486
487 if (*inbytesleft > 0) {
488 errno = E2BIG;
489 return -1;
490 }
491
492 return 0;
493}
494
495static size_t ucs2hex_push(void *cd, const char **inbuf, size_t *inbytesleft,
496 char **outbuf, size_t *outbytesleft)
497{
498 while (*inbytesleft >= 2 && *outbytesleft >= 1) {
499 char buf[6];
500
501 if ((*inbuf)[1] == 0 &&
502 ((*inbuf)[0] & 0x80) == 0 &&
503 (*inbuf)[0] != '@') {
504 (*outbuf)[0] = (*inbuf)[0];
505 (*inbytesleft) -= 2;
506 (*outbytesleft) -= 1;
507 (*inbuf) += 2;
508 (*outbuf) += 1;
509 continue;
510 }
511 if (*outbytesleft < 5) {
512 errno = E2BIG;
513 return -1;
514 }
515 snprintf(buf, 6, "@%04x", SVAL(*inbuf, 0));
516 memcpy(*outbuf, buf, 5);
517 (*inbytesleft) -= 2;
518 (*outbytesleft) -= 5;
519 (*inbuf) += 2;
520 (*outbuf) += 5;
521 }
522
523 if (*inbytesleft == 1) {
524 errno = EINVAL;
525 return -1;
526 }
527
528 if (*inbytesleft > 1) {
529 errno = E2BIG;
530 return -1;
531 }
532
533 return 0;
534}
535
536static size_t iconv_swab(void *cd, const char **inbuf, size_t *inbytesleft,
537 char **outbuf, size_t *outbytesleft)
538{
539 int n;
540
541 n = MIN(*inbytesleft, *outbytesleft);
542
543 swab(*inbuf, *outbuf, (n&~1));
544 if (n&1) {
545 (*outbuf)[n-1] = 0;
546 }
547
548 (*inbytesleft) -= n;
549 (*outbytesleft) -= n;
550 (*inbuf) += n;
551 (*outbuf) += n;
552
553 if (*inbytesleft > 0) {
554 errno = E2BIG;
555 return -1;
556 }
557
558 return 0;
559}
560
561static size_t iconv_copy(void *cd, const char **inbuf, size_t *inbytesleft,
562 char **outbuf, size_t *outbytesleft)
563{
564 int n;
565
566 n = MIN(*inbytesleft, *outbytesleft);
567
568 memmove(*outbuf, *inbuf, n);
569
570 (*inbytesleft) -= n;
571 (*outbytesleft) -= n;
572 (*inbuf) += n;
573 (*outbuf) += n;
574
575 if (*inbytesleft > 0) {
576 errno = E2BIG;
577 return -1;
578 }
579
580 return 0;
581}
582
583static size_t utf8_pull(void *cd, const char **inbuf, size_t *inbytesleft,
584 char **outbuf, size_t *outbytesleft)
585{
586 size_t in_left=*inbytesleft, out_left=*outbytesleft;
587 const uint8 *c = (const uint8 *)*inbuf;
588 uint8 *uc = (uint8 *)*outbuf;
589
590 while (in_left >= 1 && out_left >= 2) {
591 unsigned int codepoint;
592
593 if ((c[0] & 0x80) == 0) {
594 uc[0] = c[0];
595 uc[1] = 0;
596 c += 1;
597 in_left -= 1;
598 out_left -= 2;
599 uc += 2;
600 continue;
601 }
602
603 if ((c[0] & 0xe0) == 0xc0) {
604 if (in_left < 2 ||
605 (c[1] & 0xc0) != 0x80) {
606 errno = EILSEQ;
607 goto error;
608 }
609 codepoint = (c[1]&0x3f) | ((c[0]&0x1f)<<6);
610 if (codepoint < 0x80) {
611 /* don't accept UTF-8 characters that are not minimally packed */
612 errno = EILSEQ;
613 goto error;
614 }
615 uc[1] = codepoint >> 8;
616 uc[0] = codepoint & 0xff;
617 c += 2;
618 in_left -= 2;
619 out_left -= 2;
620 uc += 2;
621 continue;
622 }
623
624 if ((c[0] & 0xf0) == 0xe0) {
625 if (in_left < 3 ||
626 (c[1] & 0xc0) != 0x80 ||
627 (c[2] & 0xc0) != 0x80) {
628 errno = EILSEQ;
629 goto error;
630 }
631 codepoint = (c[2]&0x3f) | ((c[1]&0x3f)<<6) | ((c[0]&0xf)<<12);
632 if (codepoint < 0x800) {
633 /* don't accept UTF-8 characters that are not minimally packed */
634 errno = EILSEQ;
635 goto error;
636 }
637 uc[1] = codepoint >> 8;
638 uc[0] = codepoint & 0xff;
639 c += 3;
640 in_left -= 3;
641 out_left -= 2;
642 uc += 2;
643 continue;
644 }
645
646 if ((c[0] & 0xf8) == 0xf0) {
647 if (in_left < 4 ||
648 (c[1] & 0xc0) != 0x80 ||
649 (c[2] & 0xc0) != 0x80 ||
650 (c[3] & 0xc0) != 0x80) {
651 errno = EILSEQ;
652 goto error;
653 }
654 codepoint =
655 (c[3]&0x3f) |
656 ((c[2]&0x3f)<<6) |
657 ((c[1]&0x3f)<<12) |
658 ((c[0]&0x7)<<18);
659 if (codepoint < 0x10000 || codepoint > 0x10ffff) {
660 /* don't accept UTF-8 characters that are not minimally packed */
661 errno = EILSEQ;
662 goto error;
663 }
664
665 codepoint -= 0x10000;
666
667 if (out_left < 4) {
668 errno = E2BIG;
669 goto error;
670 }
671
672 uc[0] = (codepoint>>10) & 0xFF;
673 uc[1] = (codepoint>>18) | 0xd8;
674 uc[2] = codepoint & 0xFF;
675 uc[3] = ((codepoint>>8) & 0x3) | 0xdc;
676 c += 4;
677 in_left -= 4;
678 out_left -= 4;
679 uc += 4;
680 continue;
681 }
682
683 /* we don't handle 5 byte sequences */
684 errno = EINVAL;
685 goto error;
686 }
687
688 if (in_left > 0) {
689 errno = E2BIG;
690 goto error;
691 }
692
693 *inbytesleft = in_left;
694 *outbytesleft = out_left;
695 *inbuf = (char *)c;
696 *outbuf = (char *)uc;
697 return 0;
698
699error:
700 *inbytesleft = in_left;
701 *outbytesleft = out_left;
702 *inbuf = (char *)c;
703 *outbuf = (char *)uc;
704 return -1;
705}
706
707static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft,
708 char **outbuf, size_t *outbytesleft)
709{
710 size_t in_left=*inbytesleft, out_left=*outbytesleft;
711 uint8 *c = (uint8 *)*outbuf;
712 const uint8 *uc = (const uint8 *)*inbuf;
713
714 while (in_left >= 2 && out_left >= 1) {
715 unsigned int codepoint;
716
717 if (uc[1] == 0 && !(uc[0] & 0x80)) {
718 /* simplest case */
719 c[0] = uc[0];
720 in_left -= 2;
721 out_left -= 1;
722 uc += 2;
723 c += 1;
724 continue;
725 }
726
727 if ((uc[1]&0xf8) == 0) {
728 /* next simplest case */
729 if (out_left < 2) {
730 errno = E2BIG;
731 goto error;
732 }
733 c[0] = 0xc0 | (uc[0]>>6) | (uc[1]<<2);
734 c[1] = 0x80 | (uc[0] & 0x3f);
735 in_left -= 2;
736 out_left -= 2;
737 uc += 2;
738 c += 2;
739 continue;
740 }
741
742 if ((uc[1] & 0xfc) == 0xdc) {
743 /* its the second part of a 4 byte sequence. Illegal */
744 if (in_left < 4) {
745 errno = EINVAL;
746 } else {
747 errno = EILSEQ;
748 }
749 goto error;
750 }
751
752 if ((uc[1] & 0xfc) != 0xd8) {
753 codepoint = uc[0] | (uc[1]<<8);
754 if (out_left < 3) {
755 errno = E2BIG;
756 goto error;
757 }
758 c[0] = 0xe0 | (codepoint >> 12);
759 c[1] = 0x80 | ((codepoint >> 6) & 0x3f);
760 c[2] = 0x80 | (codepoint & 0x3f);
761
762 in_left -= 2;
763 out_left -= 3;
764 uc += 2;
765 c += 3;
766 continue;
767 }
768
769 /* its the first part of a 4 byte sequence */
770 if (in_left < 4) {
771 errno = EINVAL;
772 goto error;
773 }
774 if ((uc[3] & 0xfc) != 0xdc) {
775 errno = EILSEQ;
776 goto error;
777 }
778 codepoint = 0x10000 + (uc[2] | ((uc[3] & 0x3)<<8) |
779 (uc[0]<<10) | ((uc[1] & 0x3)<<18));
780
781 if (out_left < 4) {
782 errno = E2BIG;
783 goto error;
784 }
785 c[0] = 0xf0 | (codepoint >> 18);
786 c[1] = 0x80 | ((codepoint >> 12) & 0x3f);
787 c[2] = 0x80 | ((codepoint >> 6) & 0x3f);
788 c[3] = 0x80 | (codepoint & 0x3f);
789
790 in_left -= 4;
791 out_left -= 4;
792 uc += 4;
793 c += 4;
794 }
795
796 if (in_left == 1) {
797 errno = EINVAL;
798 goto error;
799 }
800
801 if (in_left > 1) {
802 errno = E2BIG;
803 goto error;
804 }
805
806 *inbytesleft = in_left;
807 *outbytesleft = out_left;
808 *inbuf = (char *)uc;
809 *outbuf = (char *)c;
810
811 return 0;
812
813error:
814 *inbytesleft = in_left;
815 *outbytesleft = out_left;
816 *inbuf = (char *)uc;
817 *outbuf = (char *)c;
818 return -1;
819}
820
Note: See TracBrowser for help on using the repository browser.