Header file is renamed.
[gedcom-parse.git] / utf8 / utf8-locale.c
1 /* Encoding utility from UTF-8 to locale and vice versa
2    Copyright (C) 2001, 2002 Peter Verthez
3
4    Permission granted to do anything with this file that you want, as long
5    as the above copyright is retained in all copies.
6    THERE IS NO WARRANTY - USE AT YOUR OWN RISK
7 */
8
9 /* $Id$ */
10 /* $Name$ */
11
12 #include <stdlib.h>
13 #include <iconv.h>
14 #include <assert.h>
15 #include <errno.h>
16 #include <string.h>
17 #include "config.h"
18 #include "libcharset.h"
19 #include "utf8.h"
20
21 #define INITIAL_OUTSIZE 256
22
23 static iconv_t utf8_to_locale = (iconv_t) -1;
24 static iconv_t locale_to_utf8 = (iconv_t) -1;
25 static char*   outbuffer = NULL;
26 static size_t  outbufsize = 0;
27 static const char* the_unknown = "?";
28
29 void convert_set_unknown(const char* unknown)
30 {
31   the_unknown = unknown;
32 }
33
34 void close_conversion_contexts()
35 {
36   iconv_close(utf8_to_locale);
37   iconv_close(locale_to_utf8);
38   utf8_to_locale = (iconv_t) -1;
39   locale_to_utf8 = (iconv_t) -1;
40   free(outbuffer);
41 }
42
43 int open_conversion_contexts()
44 {
45   assert(utf8_to_locale == (iconv_t) -1);
46   assert(locale_to_utf8 == (iconv_t) -1);
47   utf8_to_locale = iconv_open(locale_charset(), "UTF-8");
48   if (utf8_to_locale == (iconv_t) -1)
49     return -1;
50   else {
51     locale_to_utf8 = iconv_open("UTF-8", locale_charset());
52     if (locale_to_utf8 == (iconv_t) -1) {
53       close_conversion_contexts();
54       return -1;
55     }
56     else {
57       outbufsize = INITIAL_OUTSIZE;
58       outbuffer = (char*)malloc(outbufsize);
59       atexit(close_conversion_contexts);
60       return 0;
61     }
62   }
63 }
64
65 char* convert_utf8_to_locale(const char* input, int *conv_fails)
66 {
67   size_t insize  = strlen(input);
68   size_t outsize;
69   ICONV_CONST char *inptr  = (ICONV_CONST char*) input;
70   char   *outptr;
71   size_t nconv;
72
73   if (utf8_to_locale == (iconv_t) -1 && (open_conversion_contexts() == -1)) {
74     if (conv_fails != NULL) *conv_fails = insize;
75     return NULL;
76   }
77   assert(utf8_to_locale != (iconv_t) -1);
78   /* make sure we start from an empty state */
79   iconv(utf8_to_locale, NULL, NULL, NULL, NULL);
80   if (conv_fails != NULL) *conv_fails = 0;
81   /* set up output buffer (empty it) */
82   outptr  = outbuffer;
83   outsize = outbufsize;
84   memset(outbuffer, 0, outbufsize);
85   nconv = iconv(utf8_to_locale, &inptr, &insize, &outptr, &outsize);
86   while (nconv == (size_t)-1) {
87     if (errno == E2BIG) {
88       /* grow the output buffer */
89       size_t outlen;
90       outlen     = outptr - outbuffer;
91       outbufsize *= 2;
92       outbuffer  = realloc(outbuffer, outbufsize);
93       outptr     = outbuffer + outlen;
94       outsize    = outbufsize - outlen;
95       memset(outptr, 0, outsize);
96     }
97     else if (errno == EILSEQ) {
98       /* skip over character */
99       const char* unkn_ptr = the_unknown;
100       if (conv_fails != NULL) (*conv_fails)++;
101       if ((*inptr & 0x80) == 0) {
102         /* an ASCII character, just skip one (this case is very improbable) */
103         inptr++; insize--;
104       }
105       else {
106         /* a general UTF-8 character, skip all 0x10xxxxxx bytes */
107         inptr++; insize--;
108         while ((*inptr & 0xC0) == 0x80) {
109           inptr++; insize--;
110         }
111       }
112       /* append the "unknown" string to the output */
113       while (*unkn_ptr) { *outptr++ = *unkn_ptr++; outsize--; }
114     }
115     else {
116       /* EINVAL should not happen, since we convert entire strings */
117       /* EBADF is an error which should be captured by the assert above */
118       if (conv_fails != NULL) *conv_fails += insize;
119       return NULL;
120     }
121     nconv = iconv(utf8_to_locale, &inptr, &insize, &outptr, &outsize);
122   }
123   return outbuffer;
124 }
125
126 char* convert_locale_to_utf8(const char* input)
127 {
128   size_t insize  = strlen(input);
129   size_t outsize;
130   ICONV_CONST char *inptr  = (ICONV_CONST char*) input;
131   char   *outptr;
132   size_t nconv;
133
134   if (locale_to_utf8 == (iconv_t) -1 && (open_conversion_contexts() == -1))
135     return NULL;
136   assert(locale_to_utf8 != (iconv_t) -1);
137   /* make sure we start from an empty state */
138   iconv(locale_to_utf8, NULL, NULL, NULL, NULL);
139   /* set up output buffer (empty it) */
140   outptr  = outbuffer;
141   outsize = outbufsize;
142   memset(outbuffer, 0, outbufsize);
143   nconv = iconv(locale_to_utf8, &inptr, &insize, &outptr, &outsize);
144   while (nconv == (size_t)-1) {
145     if (errno == E2BIG) {
146       /* grow the output buffer */
147       size_t outlen;
148       outlen     = outptr - outbuffer;
149       outbufsize *= 2;
150       outbuffer  = realloc(outbuffer, outbufsize);
151       outptr     = outbuffer + outlen;
152       outsize    = outbufsize - outlen;
153       memset(outptr, 0, outsize);
154     }
155     else {
156       /* EILSEQ should not happen, because UTF-8 can represent anything */
157       /* EINVAL should not happen, since we convert entire strings */
158       /* EBADF is an error which should be captured by the assert above */
159       return NULL;
160     }
161     nconv = iconv(locale_to_utf8, &inptr, &insize, &outptr, &outsize);
162   }
163   return outbuffer;
164 }