Use ICONV_CONST to have const correctness.
[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 <langinfo.h>
15 #include <assert.h>
16 #include <errno.h>
17 #include "config.h"
18 #include "utf8-locale.h"
19
20 #define INITIAL_OUTSIZE 256
21
22 static iconv_t utf8_to_locale = (iconv_t) -1;
23 static iconv_t locale_to_utf8 = (iconv_t) -1;
24 static char*   outbuffer = NULL;
25 static size_t  outbufsize = 0;
26 static const char* the_unknown = "?";
27
28 void convert_set_unknown(const char* unknown)
29 {
30   the_unknown = unknown;
31 }
32
33 void close_conversion_contexts()
34 {
35   iconv_close(utf8_to_locale);
36   iconv_close(locale_to_utf8);
37   utf8_to_locale = (iconv_t) -1;
38   locale_to_utf8 = (iconv_t) -1;
39   free(outbuffer);
40 }
41
42 int open_conversion_contexts()
43 {
44   assert(utf8_to_locale == (iconv_t) -1);
45   assert(locale_to_utf8 == (iconv_t) -1);
46   utf8_to_locale = iconv_open(nl_langinfo(CODESET), "UTF-8");
47   if (utf8_to_locale == (iconv_t) -1)
48     return -1;
49   else {
50     locale_to_utf8 = iconv_open("UTF-8", nl_langinfo(CODESET));
51     if (locale_to_utf8 == (iconv_t) -1) {
52       close_conversion_contexts();
53       return -1;
54     }
55     else {
56       outbufsize = INITIAL_OUTSIZE;
57       outbuffer = (char*)malloc(outbufsize);
58       atexit(close_conversion_contexts);
59       return 0;
60     }
61   }
62 }
63
64 char* convert_utf8_to_locale(const char* input, int *conv_fails)
65 {
66   size_t insize  = strlen(input);
67   size_t outsize;
68   ICONV_CONST char *inptr  = (ICONV_CONST char*) input;
69   char   *outptr;
70   size_t nconv;
71
72   if (utf8_to_locale == (iconv_t) -1 && (open_conversion_contexts() == -1))
73     return NULL;
74   assert(utf8_to_locale != (iconv_t) -1);
75   /* make sure we start from an empty state */
76   iconv(utf8_to_locale, NULL, NULL, NULL, NULL);
77   if (conv_fails != NULL) *conv_fails = 0;
78   /* set up output buffer (empty it) */
79   outptr  = outbuffer;
80   outsize = outbufsize;
81   memset(outbuffer, 0, outbufsize);
82   nconv = iconv(utf8_to_locale, &inptr, &insize, &outptr, &outsize);
83   while (nconv == (size_t)-1) {
84     if (errno == E2BIG) {
85       /* grow the output buffer */
86       size_t outlen;
87       outlen     = outptr - outbuffer;
88       outbufsize *= 2;
89       outbuffer  = realloc(outbuffer, outbufsize);
90       outptr     = outbuffer + outlen;
91       outsize    = outbufsize - outlen;
92       memset(outptr, 0, outsize);
93     }
94     else if (errno == EILSEQ) {
95       /* skip over character */
96       const char* unkn_ptr = the_unknown;
97       if (conv_fails != NULL) (*conv_fails)++;
98       if ((*inptr & 0x80) == 0) {
99         /* an ASCII character, just skip one (this case is very improbable) */
100         inptr++; insize--;
101       }
102       else {
103         /* a general UTF-8 character, skip all 0x10xxxxxx bytes */
104         inptr++; insize--;
105         while ((*inptr & 0xC0) == 0x80) {
106           inptr++; insize--;
107         }
108       }
109       /* append the "unknown" string to the output */
110       while (*unkn_ptr) { *outptr++ = *unkn_ptr++; outsize--; }
111     }
112     else {
113       /* EINVAL should not happen, since we convert entire strings */
114       /* EBADF is an error which should be captured by the assert above */
115       return NULL;
116     }
117     nconv = iconv(utf8_to_locale, &inptr, &insize, &outptr, &outsize);
118   }
119   return outbuffer;
120 }
121
122 char* convert_locale_to_utf8(const char* input)
123 {
124   size_t insize  = strlen(input);
125   size_t outsize;
126   ICONV_CONST char *inptr  = (ICONV_CONST char*) input;
127   char   *outptr;
128   size_t nconv;
129
130   if (locale_to_utf8 == (iconv_t) -1 && (open_conversion_contexts() == -1))
131     return NULL;
132   assert(locale_to_utf8 != (iconv_t) -1);
133   /* make sure we start from an empty state */
134   iconv(locale_to_utf8, NULL, NULL, NULL, NULL);
135   /* set up output buffer (empty it) */
136   outptr  = outbuffer;
137   outsize = outbufsize;
138   memset(outbuffer, 0, outbufsize);
139   nconv = iconv(locale_to_utf8, &inptr, &insize, &outptr, &outsize);
140   while (nconv == (size_t)-1) {
141     if (errno == E2BIG) {
142       /* grow the output buffer */
143       size_t outlen;
144       outlen     = outptr - outbuffer;
145       outbufsize *= 2;
146       outbuffer  = realloc(outbuffer, outbufsize);
147       outptr     = outbuffer + outlen;
148       outsize    = outbufsize - outlen;
149       memset(outptr, 0, outsize);
150     }
151     else {
152       /* EILSEQ should not happen, because UTF-8 can represent anything */
153       /* EINVAL should not happen, since we convert entire strings */
154       /* EBADF is an error which should be captured by the assert above */
155       return NULL;
156     }
157     nconv = iconv(locale_to_utf8, &inptr, &insize, &outptr, &outsize);
158   }
159   return outbuffer;
160 }