]> arthur.barton.de Git - netatalk.git/blob - libatalk/unicode/iconv.c
e415be8dfe80593e2c83a9bbec6704a0a5277198
[netatalk.git] / libatalk / unicode / iconv.c
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 2 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, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20    
21    From samba 3.0 beta and GNU libiconv-1.8
22    It's bad but most of the time we can't use libc iconv service:
23    - it doesn't round trip for most encoding
24    - it doesn't know about Apple extension
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif /* HAVE_CONFIG_H */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <atalk/logger.h>
38 #include <errno.h>
39
40 #include <netatalk/endian.h>
41 #include <atalk/unicode.h>
42
43 #ifdef HAVE_USABLE_ICONV
44 #include <iconv.h>
45 #endif
46
47
48 #include "mac_roman.h"
49 #include "mac_hebrew.h"
50
51 /**
52  * @file
53  *
54  * @brief Samba wrapper/stub for iconv character set conversion.
55  *
56  * iconv is the XPG2 interface for converting between character
57  * encodings.  This file provides a Samba wrapper around it, and also
58  * a simple reimplementation that is used if the system does not
59  * implement iconv.
60  *
61  * Samba only works with encodings that are supersets of ASCII: ascii
62  * characters like whitespace can be tested for directly, multibyte
63  * sequences start with a byte with the high bit set, and strings are
64  * terminated by a nul byte.
65  *
66  * Note that the only function provided by iconv is conversion between
67  * characters.  It doesn't directly support operations like
68  * uppercasing or comparison.  We have to convert to UCS-2 and compare
69  * there.
70  *
71  * @sa Samba Developers Guide
72  **/
73
74 static size_t ascii_pull(void *,char **, size_t *, char **, size_t *);
75 static size_t ascii_push(void *,char **, size_t *, char **, size_t *);
76 static size_t  utf8_pull(void *,char **, size_t *, char **, size_t *);
77 static size_t  utf8_push(void *,char **, size_t *, char **, size_t *);
78 static size_t iconv_copy(void *,char **, size_t *, char **, size_t *);
79
80 static size_t   mac_pull(void *,char **, size_t *, char **, size_t *);
81 static size_t   mac_push(void *,char **, size_t *, char **, size_t *);
82
83 static size_t   mac_hebrew_pull(void *,char **, size_t *, char **, size_t *);
84 static size_t   mac_hebrew_push(void *,char **, size_t *, char **, size_t *);
85
86 static struct charset_functions builtin_functions[] = {
87         {"UCS-2LE",   iconv_copy, iconv_copy},
88         {"UTF8",      utf8_pull,  utf8_push},
89         {"UTF-8",     utf8_pull,  utf8_push},
90         {"ASCII",     ascii_pull, ascii_push},
91         {"MAC",       mac_pull,  mac_push},
92         {"MAC-HEBR",  mac_hebrew_pull,  mac_hebrew_push},
93         {NULL, NULL, NULL}
94 };
95
96 #define DLIST_ADD(list, p) \
97 { \
98         if (!(list)) { \
99                 (list) = (p); \
100                 (p)->next = (p)->prev = NULL; \
101         } else { \
102                 (list)->prev = (p); \
103                 (p)->next = (list); \
104                 (p)->prev = NULL; \
105                 (list) = (p); \
106         }\
107 }
108
109
110
111 static struct charset_functions *charsets = NULL;
112
113 static struct charset_functions *find_charset_functions(const char *name) 
114 {
115         struct charset_functions *c = charsets;
116
117         while(c) {
118                 if (strcasecmp(name, c->name) == 0) {
119                         return c;
120                 }
121                 c = c->next;
122         }
123
124         return NULL;
125 }
126
127 int atalk_register_charset(struct charset_functions *funcs) 
128 {
129         if (!funcs) {
130                 return -1;
131         }
132
133         LOG(log_debug, logtype_default, "Attempting to register new charset %s", funcs->name);
134         /* Check whether we already have this charset... */
135         if (find_charset_functions(funcs->name)) {
136                 LOG (log_debug, logtype_default, "Duplicate charset %s, not registering", funcs->name);
137                 return -2;
138         }
139
140         funcs->next = funcs->prev = NULL;
141         LOG(log_debug, logtype_default, "Registered charset %s", funcs->name);
142         DLIST_ADD(charsets, funcs);
143         return 0;
144 }
145
146 void lazy_initialize_iconv(void)
147 {
148         static int initialized = 0;
149         int i;
150
151         if (!initialized) {
152                 initialized = 1;
153                 for(i = 0; builtin_functions[i].name; i++) 
154                         atalk_register_charset(&builtin_functions[i]);
155         }
156 }
157
158 /* if there was an error then reset the internal state,
159    this ensures that we don't have a shift state remaining for
160    character sets like SJIS */
161 static size_t sys_iconv(void *cd, 
162                         char **inbuf, size_t *inbytesleft,
163                         char **outbuf, size_t *outbytesleft)
164 {
165 #ifdef HAVE_USABLE_ICONV
166         size_t ret = iconv((iconv_t)cd, 
167                            inbuf, inbytesleft, 
168                            outbuf, outbytesleft);
169         if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL);
170         return ret;
171 #else
172         errno = EINVAL;
173         return -1;
174 #endif
175 }
176
177 /**
178  * This is a simple portable iconv() implementaion.
179  *
180  * It only knows about a very small number of character sets - just
181  * enough that netatalk works on systems that don't have iconv.
182  **/
183 size_t atalk_iconv(atalk_iconv_t cd, 
184                  const char **inbuf, size_t *inbytesleft,
185                  char **outbuf, size_t *outbytesleft)
186 {
187         char cvtbuf[2048];
188         char *bufp = cvtbuf;
189         size_t bufsize;
190
191         /* in many cases we can go direct */
192         if (cd->direct) {
193                 return cd->direct(cd->cd_direct, 
194                                   (char **)inbuf, inbytesleft, outbuf, outbytesleft);
195         }
196
197
198         /* otherwise we have to do it chunks at a time */
199         while (*inbytesleft > 0) {
200                 bufp = cvtbuf;
201                 bufsize = sizeof(cvtbuf);
202                 
203                 if (cd->pull(cd->cd_pull, (char **)inbuf, inbytesleft, &bufp, &bufsize) == (size_t)-1
204                        && errno != E2BIG) {
205                     return -1;
206                 }
207
208                 bufp = cvtbuf;
209                 bufsize = sizeof(cvtbuf) - bufsize;
210
211                 if (cd->push(cd->cd_push, &bufp, &bufsize, outbuf, outbytesleft) == (size_t)-1) {
212                     return -1;
213                 }
214         }
215
216         return 0;
217 }
218
219
220 size_t atalk_iconv_ignore(atalk_iconv_t cd, 
221                  const char **inbuf, size_t *inbytesleft,
222                  char **outbuf, size_t *outbytesleft, int *ignore)
223 {
224         char cvtbuf[2048];
225         char *bufp = cvtbuf;
226         size_t bufsize;
227         size_t outlen = *outbytesleft;
228         char *o_save;
229         
230         /* we have to do it chunks at a time */
231         while (*inbytesleft > 0) {
232                 bufp = cvtbuf;
233                 bufsize = sizeof(cvtbuf);
234                 
235                 if (cd->pull(cd->cd_pull, (char **)inbuf, inbytesleft, &bufp, &bufsize) == (size_t)-1
236                         && errno != E2BIG) {
237                     return -1;
238                 }
239
240                 bufp = cvtbuf;
241                 bufsize = sizeof(cvtbuf) - bufsize;
242
243                 o_save = *outbuf;
244 convert_push:
245                 if (cd->push(cd->cd_push, 
246                              &bufp, &bufsize, 
247                              outbuf, outbytesleft) == (size_t)-1) {
248                     if (errno == EILSEQ) {
249                         o_save[outlen-*outbytesleft] = '_';
250                         (*outbuf) = o_save + outlen-*outbytesleft+1;
251                         (*outbytesleft) -=1;
252                         bufp += 2;
253                         bufsize -= 2;
254                         //outlen=*outbytesleft;
255                         *ignore = 1;
256                         goto convert_push;
257                     }
258                     else
259                         return (size_t)(-1);
260                 }
261         }
262         return 0;
263 }
264
265 /*
266   simple iconv_open() wrapper
267  */
268 atalk_iconv_t atalk_iconv_open(const char *tocode, const char *fromcode)
269 {
270         atalk_iconv_t ret;
271         struct charset_functions *from, *to;
272
273
274         lazy_initialize_iconv();
275         from = charsets;
276         to = charsets;
277
278         ret = (atalk_iconv_t)malloc(sizeof(*ret));
279         if (!ret) {
280                 errno = ENOMEM;
281                 return (atalk_iconv_t)-1;
282         }
283         memset(ret, 0, sizeof(*ret));
284
285         ret->from_name = strdup(fromcode);
286         ret->to_name = strdup(tocode);
287
288         /* check for the simplest null conversion */
289         if (strcasecmp(fromcode, tocode) == 0) {
290                 ret->direct = iconv_copy;
291                 return ret;
292         }
293
294         /* check if we have a builtin function for this conversion */
295         from = find_charset_functions(fromcode);
296         if(from)ret->pull = from->pull;
297         
298         to = find_charset_functions(tocode);
299         if(to)ret->push = to->push;
300
301         /* check if we can use iconv for this conversion */
302 #ifdef HAVE_USABLE_ICONV
303         if (!ret->pull) {
304                 ret->cd_pull = iconv_open("UCS-2LE", fromcode);
305                 if (ret->cd_pull != (iconv_t)-1)
306                         ret->pull = sys_iconv;
307         }
308
309         if (!ret->push) {
310                 ret->cd_push = iconv_open(tocode, "UCS-2LE");
311                 if (ret->cd_push != (iconv_t)-1)
312                         ret->push = sys_iconv;
313         }
314 #endif
315         
316         if (!ret->push || !ret->pull) {
317                 SAFE_FREE(ret->from_name);
318                 SAFE_FREE(ret->to_name);
319                 SAFE_FREE(ret);
320                 errno = EINVAL;
321                 return (atalk_iconv_t)-1;
322         }
323
324         /* check for conversion to/from ucs2 */
325         if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) {
326                 ret->direct = to->push;
327                 ret->push = ret->pull = NULL;
328                 return ret;
329         }
330
331         if (strcasecmp(tocode, "UCS-2LE") == 0 && from) {
332                 ret->direct = from->pull;
333                 ret->push = ret->pull = NULL;
334                 return ret;
335         }
336
337         /* Check if we can do the conversion direct */
338 #ifdef HAVE_USABLE_ICONV
339         if (strcasecmp(fromcode, "UCS-2LE") == 0) {
340                 ret->direct = sys_iconv;
341                 ret->cd_direct = ret->cd_push;
342                 ret->cd_push = NULL;
343                 return ret;
344         }
345         if (strcasecmp(tocode, "UCS-2LE") == 0) {
346                 ret->direct = sys_iconv;
347                 ret->cd_direct = ret->cd_pull;
348                 ret->cd_pull = NULL;
349                 return ret;
350         }
351 #endif
352
353         return ret;
354 }
355
356 /*
357   simple iconv_close() wrapper
358 */
359 int atalk_iconv_close (atalk_iconv_t cd)
360 {
361 #ifdef HAVE_USABLE_ICONV
362         if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct);
363         if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull);
364         if (cd->cd_push) iconv_close((iconv_t)cd->cd_push);
365 #endif
366
367         SAFE_FREE(cd->from_name);
368         SAFE_FREE(cd->to_name);
369
370         memset(cd, 0, sizeof(*cd));
371         SAFE_FREE(cd);
372         return 0;
373 }
374
375
376 /************************************************************************
377  the following functions implement the builtin character sets in Netatalk
378 *************************************************************************/
379
380 static size_t ascii_pull(void *cd, char **inbuf, size_t *inbytesleft,
381                          char **outbuf, size_t *outbytesleft)
382 {
383         while (*inbytesleft >= 1 && *outbytesleft >= 2) {
384                 (*outbuf)[0] = (*inbuf)[0];
385                 (*outbuf)[1] = 0;
386                 (*inbytesleft)  -= 1;
387                 (*outbytesleft) -= 2;
388                 (*inbuf)  += 1;
389                 (*outbuf) += 2;
390         }
391
392         if (*inbytesleft > 0) {
393                 errno = E2BIG;
394                 return -1;
395         }
396         
397         return 0;
398 }
399
400 static size_t ascii_push(void *cd, char **inbuf, size_t *inbytesleft,
401                          char **outbuf, size_t *outbytesleft)
402 {
403         int ir_count=0;
404
405         while (*inbytesleft >= 2 && *outbytesleft >= 1) {
406                 (*outbuf)[0] = (*inbuf)[0] & 0x7F;
407                 if ((*inbuf)[1]) ir_count++;
408                 (*inbytesleft)  -= 2;
409                 (*outbytesleft) -= 1;
410                 (*inbuf)  += 2;
411                 (*outbuf) += 1;
412         }
413
414         if (*inbytesleft == 1) {
415                 errno = EINVAL;
416                 return -1;
417         }
418
419         if (*inbytesleft > 1) {
420                 errno = E2BIG;
421                 return -1;
422         }
423         
424         return ir_count;
425 }
426
427
428 static size_t iconv_copy(void *cd, char **inbuf, size_t *inbytesleft,
429                          char **outbuf, size_t *outbytesleft)
430 {
431         int n;
432
433         n = MIN(*inbytesleft, *outbytesleft);
434
435         memmove(*outbuf, *inbuf, n);
436
437         (*inbytesleft) -= n;
438         (*outbytesleft) -= n;
439         (*inbuf) += n;
440         (*outbuf) += n;
441
442         if (*inbytesleft > 0) {
443                 errno = E2BIG;
444                 return -1;
445         }
446
447         return 0;
448 }
449
450 /* ------------------------ */
451 static size_t utf8_pull(void *cd, char **inbuf, size_t *inbytesleft,
452                          char **outbuf, size_t *outbytesleft)
453 {
454         while (*inbytesleft >= 1 && *outbytesleft >= 2) {
455                 unsigned char *c = (unsigned char *)*inbuf;
456                 unsigned char *uc = (unsigned char *)*outbuf;
457                 int len = 1;
458
459                 if ((c[0] & 0x80) == 0) {
460                         uc[0] = c[0];
461                         uc[1] = 0;
462                 } else if ((c[0] & 0xf0) == 0xe0) {
463                         if (*inbytesleft < 3) {
464                                 LOG(log_debug, logtype_default, "short utf8 char\n");
465                                 goto badseq;
466                         }
467                         uc[1] = ((c[0]&0xF)<<4) | ((c[1]>>2)&0xF);
468                         uc[0] = (c[1]<<6) | (c[2]&0x3f);
469                         len = 3;
470                 } else if ((c[0] & 0xe0) == 0xc0) {
471                         if (*inbytesleft < 2) {
472                                 LOG(log_debug, logtype_default, "short utf8 char\n");
473                                 goto badseq;
474                         }
475                         uc[1] = (c[0]>>2) & 0x7;
476                         uc[0] = (c[0]<<6) | (c[1]&0x3f);
477                         len = 2;
478                 }
479
480                 (*inbuf)  += len;
481                 (*inbytesleft)  -= len;
482                 (*outbytesleft) -= 2;
483                 (*outbuf) += 2;
484         }
485
486         if (*inbytesleft > 0) {
487                 errno = E2BIG;
488                 return -1;
489         }
490         
491         return 0;
492
493 badseq:
494         errno = EINVAL;
495         return -1;
496 }
497
498 /* ------------------------ */
499 static size_t utf8_push(void *cd, char **inbuf, size_t *inbytesleft,
500                          char **outbuf, size_t *outbytesleft)
501 {
502         while (*inbytesleft >= 2 && *outbytesleft >= 1) {
503                 unsigned char *c = (unsigned char *)*outbuf;
504                 unsigned char *uc = (unsigned char *)*inbuf;
505                 int len=1;
506
507                 if (uc[1] & 0xf8) {
508                         if (*outbytesleft < 3) {
509                                 LOG(log_debug, logtype_default, "short utf8 write\n");
510                                 goto toobig;
511                         }
512                         c[0] = 0xe0 | (uc[1]>>4);
513                         c[1] = 0x80 | ((uc[1]&0xF)<<2) | (uc[0]>>6);
514                         c[2] = 0x80 | (uc[0]&0x3f);
515                         len = 3;
516                 } else if (uc[1] | (uc[0] & 0x80)) {
517                         if (*outbytesleft < 2) {
518                                 LOG(log_debug, logtype_default, "short utf8 write\n");
519                                 goto toobig;
520                         }
521                         c[0] = 0xc0 | (uc[1]<<2) | (uc[0]>>6);
522                         c[1] = 0x80 | (uc[0]&0x3f);
523                         len = 2;
524                 } else {
525                         c[0] = uc[0];
526                 }
527
528
529                 (*inbytesleft)  -= 2;
530                 (*outbytesleft) -= len;
531                 (*inbuf)  += 2;
532                 (*outbuf) += len;
533         }
534
535         if (*inbytesleft == 1) {
536                 errno = EINVAL;
537                 return -1;
538         }
539
540         if (*inbytesleft > 1) {
541                 errno = E2BIG;
542                 return -1;
543         }
544         
545         return 0;
546
547 toobig:
548         errno = E2BIG;
549         return -1;
550 }
551
552 /* ------------------------ */
553 static int
554 char_ucs2_to_mac_roman ( unsigned char *r, ucs2_t wc)
555 {
556         unsigned char c = 0;
557         if (wc < 0x0080) {
558                 *r = wc;
559                 return 1;
560         }
561         else if (wc >= 0x00a0 && wc < 0x0100)
562                 c = mac_roman_page00[wc-0x00a0];
563         else if (wc >= 0x0130 && wc < 0x0198)
564                 c = mac_roman_page01[wc-0x0130];
565         else if (wc >= 0x02c0 && wc < 0x02e0)
566                 c = mac_roman_page02[wc-0x02c0];
567         else if (wc == 0x03c0)
568                 c = 0xb9;
569         else if (wc >= 0x2010 && wc < 0x2048)
570                 c = mac_roman_page20[wc-0x2010];
571         else if (wc >= 0x2120 && wc < 0x2128)
572                 c = mac_roman_page21[wc-0x2120];
573         else if (wc >= 0x2200 && wc < 0x2268)
574                 c = mac_roman_page22[wc-0x2200];
575         else if (wc == 0x25ca)
576                 c = 0xd7;
577         else if (wc >= 0xfb00 && wc < 0xfb08)
578                 c = mac_roman_pagefb[wc-0xfb00];
579         else if (wc == 0xf8ff)
580                 c = 0xf0;
581
582         if (c != 0) {
583                 *r = c;
584                 return 1;
585         }
586         return 0;
587 }
588
589 static size_t mac_push( void *cd, char **inbuf, size_t *inbytesleft,
590                          char **outbuf, size_t *outbytesleft)
591 {
592         int len = 0;
593         unsigned char *tmpptr = (unsigned char *) *outbuf;
594
595         while (*inbytesleft >= 2 && *outbytesleft >= 1) {
596
597                 ucs2_t *inptr = (ucs2_t *) *inbuf;
598                 if (char_ucs2_to_mac_roman ( tmpptr, *inptr)) {
599                         (*inbuf) += 2;
600                         tmpptr++;
601                         len++;
602                         (*inbytesleft)  -= 2;
603                         (*outbytesleft) -= 1;
604                 }
605                 else    
606                 {
607                         errno = EILSEQ;
608                         return (size_t) -1;     
609                 }
610         }
611
612         if (*inbytesleft > 0) {
613                 errno = E2BIG;
614                 return -1;
615         }
616
617         return len;
618 }
619
620 /* ------------------------ */
621 static int
622 char_mac_roman_to_ucs2 (ucs2_t *pwc, const unsigned char *s)
623 {
624         unsigned char c = *s;
625         if (c < 0x80) {
626                 *pwc = (ucs2_t) c;
627                 return 1;
628         }
629         else {
630                 unsigned short wc = mac_roman_2uni[c-0x80];
631                 *pwc = (ucs2_t) wc;
632                 return 1;
633         }
634         return 0;
635 }
636
637 static size_t mac_pull ( void *cd, char **inbuf, size_t *inbytesleft,
638                          char **outbuf, size_t *outbytesleft)
639 {
640         ucs2_t          *temp;
641         unsigned char   *inptr;
642         size_t  len = 0;
643
644         while (*inbytesleft >= 1 && *outbytesleft >= 2) {
645
646                 inptr = (unsigned char *) *inbuf;
647                 temp  = (ucs2_t*) *outbuf;      
648                 if (char_mac_roman_to_ucs2 ( temp, inptr)) {
649                         (*inbuf)        +=1;
650                         (*outbuf)       +=2;
651                         (*inbytesleft) -=1;
652                         (*outbytesleft)-=2;
653                         len++;
654                         
655                 }
656                 else    
657                 {
658                         errno = EILSEQ;
659                         return (size_t) -1;     
660                 }
661         }
662
663         if (*inbytesleft > 0) {
664                 errno = E2BIG;
665                 return (size_t) -1;
666         }
667
668         return len;
669
670 }
671
672 /* ------------------------ 
673  * from unicode to mac hebrew code page
674 */
675 static int
676 char_ucs2_to_mac_hebrew ( unsigned char *r, ucs2_t wc)
677 {
678     unsigned char c = 0;
679     if (wc < 0x0080) {
680        *r = wc;
681        return 1;
682     }
683     else if (wc >= 0x00a0 && wc < 0x0100)
684         c = mac_hebrew_page00[wc-0x00a0];
685     else if (wc >= 0x05b0 && wc < 0x05f0)
686         c = mac_hebrew_page05[wc-0x05b0];
687     else if (wc >= 0x2010 && wc < 0x2028)
688         c = mac_hebrew_page20[wc-0x2010];
689     else if (wc == 0x20aa)
690         c = 0xa6;
691     else if (wc >= 0xfb18 && wc < 0xfb50)
692         c = mac_hebrew_pagefb[wc-0xfb18];
693     if (c != 0) {
694        *r = c;
695        return 1;
696     }
697     return 0;
698 }
699
700 static size_t mac_hebrew_push( void *cd, char **inbuf, size_t *inbytesleft,
701                          char **outbuf, size_t *outbytesleft)
702 {
703     unsigned char c = 0;
704     int len = 0;
705     unsigned char *tmpptr = (unsigned char *) *outbuf;
706
707     while (*inbytesleft >= 2 && *outbytesleft >= 1) {
708         ucs2_t *inptr = (ucs2_t *) *inbuf;
709         if (*inptr == 0x05b8) {
710             (*inbuf) += 2;
711             (*inbytesleft)  -= 2;
712             if (*inbytesleft >= 2 && *((ucs2_t *)*inbuf) == 0xf87f ) {
713                 (*inbuf) += 2;
714                 (*inbytesleft)  -= 2;
715                 c = 0xde;
716             }
717             else {
718                 c = 0xcb;
719             }
720             *tmpptr = c; 
721         }
722         else if (*inptr == 0x05f2 && *inbytesleft >= 4 && *(inptr +1) == 0x05b7) {
723             (*inbuf) += 4;
724             (*inbytesleft)  -= 4;
725             *tmpptr = 0x81;
726         }
727         else if (*inptr == 0xf86a && *inbytesleft >= 6 && *(inptr +1) == 0x05dc && *(inptr +2) == 0x05b9) {
728             (*inbuf) += 6;
729             (*inbytesleft)  -= 6;
730             *tmpptr = 0xc0;
731         }
732         else if (char_ucs2_to_mac_hebrew ( tmpptr, *inptr)) {
733             (*inbuf) += 2;
734             (*inbytesleft)  -= 2;
735         }
736         else {
737             errno = EILSEQ;
738             return (size_t) -1;
739         }
740         (*outbytesleft) -= 1;
741         tmpptr++;
742         len++;
743     }
744
745     if (*inbytesleft > 0) {
746         errno = E2BIG;
747         return -1;
748     }
749
750     return len;
751 }
752
753 /* ------------------------ */
754 static int
755 char_mac_hebrew_to_ucs2 (ucs2_t *pwc, const unsigned char *s)
756 {
757         unsigned char c = *s;
758         if (c < 0x80) {
759                 *pwc = (ucs2_t) c;
760                 return 1;
761         }
762         else {
763                 unsigned short wc = mac_hebrew_2uni[c-0x80];
764                 if (wc != 0xfffd) {
765                     *pwc = (ucs2_t) wc;
766                     return 1;
767                 }
768         }
769         return 0;
770 }
771
772 static size_t mac_hebrew_pull ( void *cd, char **inbuf, size_t *inbytesleft,
773                          char **outbuf, size_t *outbytesleft)
774 {
775     ucs2_t         *temp;
776     unsigned char  *inptr;
777     size_t         len = 0;
778
779     while (*inbytesleft >= 1 && *outbytesleft >= 2) {
780         inptr = (unsigned char *) *inbuf;
781         temp  = (ucs2_t*) *outbuf;      
782         if (char_mac_hebrew_to_ucs2 ( temp, inptr)) {
783             if (*temp == 1) {       /* 0x81 --> 0x05f2+0x05b7 */
784                 if (*outbytesleft < 4) {
785                     errno = EILSEQ;
786                     return (size_t) -1; 
787                 }
788                 *temp = 0x05f2;
789                 *(temp +1) = 0x05b7;
790                 (*outbuf)      +=4;
791                 (*outbytesleft)-=4;
792                 len += 2;
793             }
794             else if (*temp == 2) { /* 0xc0 -> 0xf86a 0x05dc 0x05b9*/
795                 if (*outbytesleft < 6) {
796                     errno = EILSEQ;
797                     return (size_t) -1; 
798                 }
799                 *temp = 0xf86a;
800                 *(temp +1) = 0x05dc;
801                 *(temp +2) = 0x05b9;
802                 (*outbuf)      +=6;
803                 (*outbytesleft)-=6;
804                 len += 3;
805             }
806             else if (*temp == 3) { /* 0xde --> 0x05b8 0xf87f */
807                 if (*outbytesleft < 4) {
808                     errno = EILSEQ;
809                     return (size_t) -1; 
810                 }
811                 *temp = 0x05b8;
812                 *(temp +1) = 0xf87f;
813                 (*outbuf)      +=4;
814                 (*outbytesleft)-=4;
815                 len += 2;
816             }
817             else {
818                 (*outbuf)      +=2;
819                 (*outbytesleft)-=2;
820                 len++;
821             }
822             (*inbuf)        +=1;
823             (*inbytesleft) -=1;
824         }
825         else    
826         {
827             errno = EILSEQ;
828             return (size_t) -1; 
829         }
830     }
831
832     if (*inbytesleft > 0) {
833         errno = E2BIG;
834         return (size_t) -1;
835     }
836     return len;
837 }
838