Don't use subdir libcharset if libiconv is used.
[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-locale.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     return NULL;
75   assert(utf8_to_locale != (iconv_t) -1);
76   /* make sure we start from an empty state */
77   iconv(utf8_to_locale, NULL, NULL, NULL, NULL);
78   if (conv_fails != NULL) *conv_fails = 0;
79   /* set up output buffer (empty it) */
80   outptr  = outbuffer;
81   outsize = outbufsize;
82   memset(outbuffer, 0, outbufsize);
83   nconv = iconv(utf8_to_locale, &inptr, &insize, &outptr, &outsize);
84   while (nconv == (size_t)-1) {
85     if (errno == E2BIG) {
86       /* grow the output buffer */
87       size_t outlen;
88       outlen     = outptr - outbuffer;
89       outbufsize *= 2;
90       outbuffer  = realloc(outbuffer, outbufsize);
91       outptr     = outbuffer + outlen;
92       outsize    = outbufsize - outlen;
93       memset(outptr, 0, outsize);
94     }
95     else if (errno == EILSEQ) {
96       /* skip over character */
97       const char* unkn_ptr = the_unknown;
98       if (conv_fails != NULL) (*conv_fails)++;
99       if ((*inptr & 0x80) == 0) {
100         /* an ASCII character, just skip one (this case is very improbable) */
101         inptr++; insize--;
102       }
103       else {
104         /* a general UTF-8 character, skip all 0x10xxxxxx bytes */
105         inptr++; insize--;
106         while ((*inptr & 0xC0) == 0x80) {
107           inptr++; insize--;
108         }
109       }
110       /* append the "unknown" string to the output */
111       while (*unkn_ptr) { *outptr++ = *unkn_ptr++; outsize--; }
112     }
113     else {
114       /* EINVAL should not happen, since we convert entire strings */
115       /* EBADF is an error which should be captured by the assert above */
116       return NULL;
117     }
118     nconv = iconv(utf8_to_locale, &inptr, &insize, &outptr, &outsize);
119   }
120   return outbuffer;
121 }
122
123 char* convert_locale_to_utf8(const char* input)
124 {
125   size_t insize  = strlen(input);
126   size_t outsize;
127   ICONV_CONST char *inptr  = (ICONV_CONST char*) input;
128   char   *outptr;
129   size_t nconv;
130
131   if (locale_to_utf8 == (iconv_t) -1 && (open_conversion_contexts() == -1))
132     return NULL;
133   assert(locale_to_utf8 != (iconv_t) -1);
134   /* make sure we start from an empty state */
135   iconv(locale_to_utf8, NULL, NULL, NULL, NULL);
136   /* set up output buffer (empty it) */
137   outptr  = outbuffer;
138   outsize = outbufsize;
139   memset(outbuffer, 0, outbufsize);
140   nconv = iconv(locale_to_utf8, &inptr, &insize, &outptr, &outsize);
141   while (nconv == (size_t)-1) {
142     if (errno == E2BIG) {
143       /* grow the output buffer */
144       size_t outlen;
145       outlen     = outptr - outbuffer;
146       outbufsize *= 2;
147       outbuffer  = realloc(outbuffer, outbufsize);
148       outptr     = outbuffer + outlen;
149       outsize    = outbufsize - outlen;
150       memset(outptr, 0, outsize);
151     }
152     else {
153       /* EILSEQ should not happen, because UTF-8 can represent anything */
154       /* EINVAL should not happen, since we convert entire strings */
155       /* EBADF is an error which should be captured by the assert above */
156       return NULL;
157     }
158     nconv = iconv(locale_to_utf8, &inptr, &insize, &outptr, &outsize);
159   }
160   return outbuffer;
161 }