Merge pull request #4621 from alexanderkyte/strdup_env
[mono.git] / mono / utils / strenc.c
1 /**
2  * \file
3  * string encoding conversions
4  *
5  * Author:
6  *      Dick Porter (dick@ximian.com)
7  *
8  * (C) 2003 Ximian, Inc.
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <string.h>
14
15 #include "strenc.h"
16
17 static const char trailingBytesForUTF8[256] = {
18         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
19         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
20         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
21         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
22         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
23         0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
24         1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
25         2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,0,0
26 };
27
28 /**
29  * mono_unicode_from_external:
30  * \param in pointers to the buffer.
31  * \param bytes number of bytes in the string.
32  * Tries to turn a NULL-terminated string into UTF-16.
33  *
34  * First, see if it's valid UTF-8, in which case just turn it directly
35  * into UTF-16.  Next, run through the colon-separated encodings in
36  * \c MONO_EXTERNAL_ENCODINGS and do an \c iconv conversion on each,
37  * returning the first successful conversion to UTF-16.  If no
38  * conversion succeeds, return NULL.
39  *
40  * Callers must free the returned string if not NULL. \p bytes holds the number
41  * of bytes in the returned string, not including the terminator.
42  */
43 gunichar2 *
44 mono_unicode_from_external (const gchar *in, gsize *bytes)
45 {
46         gchar *res=NULL;
47         gchar **encodings;
48         gchar *encoding_list;
49         int i;
50         glong lbytes;
51         
52         if(in==NULL) {
53                 return(NULL);
54         }
55         
56         encoding_list=g_getenv ("MONO_EXTERNAL_ENCODINGS");
57         if(encoding_list==NULL) {
58                 encoding_list = g_strdup("");
59         }
60         
61         encodings=g_strsplit (encoding_list, ":", 0);
62         g_free (encoding_list);
63         for(i=0;encodings[i]!=NULL; i++) {
64                 /* "default_locale" is a special case encoding */
65                 if(!strcmp (encodings[i], "default_locale")) {
66                         gchar *utf8=g_locale_to_utf8 (in, -1, NULL, NULL, NULL);
67                         if(utf8!=NULL) {
68                                 res=(gchar *) g_utf8_to_utf16 (utf8, -1, NULL, &lbytes, NULL);
69                                 *bytes = (gsize) lbytes;
70                         }
71                         g_free (utf8);
72                 } else {
73                         /* Don't use UTF16 here. It returns the <FF FE> prepended to the string */
74                         res = g_convert (in, strlen (in), "UTF8", encodings[i], NULL, bytes, NULL);
75                         if (res != NULL) {
76                                 gchar *ptr = res;
77                                 res = (gchar *) g_utf8_to_utf16 (res, -1, NULL, &lbytes, NULL);
78                                 *bytes = (gsize) lbytes;
79                                 g_free (ptr);
80                         }
81                 }
82
83                 if(res!=NULL) {
84                         g_strfreev (encodings);
85                         *bytes *= 2;
86                         return((gunichar2 *)res);
87                 }
88         }
89         
90         g_strfreev (encodings);
91         
92         if(g_utf8_validate (in, -1, NULL)) {
93                 gunichar2 *unires=g_utf8_to_utf16 (in, -1, NULL, (glong *)bytes, NULL);
94                 *bytes *= 2;
95                 return(unires);
96         }
97
98         return(NULL);
99 }
100
101 /**
102  * mono_utf8_from_external:
103  * \param in pointer to the string buffer.
104  * Tries to turn a NULL-terminated string into UTF8.
105  *
106  * First, see if it's valid UTF-8, in which case there's nothing more
107  * to be done.  Next, run through the colon-separated encodings in
108  * \c MONO_EXTERNAL_ENCODINGS and do an \c iconv conversion on each,
109  * returning the first successful conversion to UTF-8.  If no
110  * conversion succeeds, return NULL.
111  *
112  * Callers must free the returned string if not NULL.
113  *
114  * This function is identical to \c mono_unicode_from_external, apart
115  * from returning UTF-8 not UTF-16; it's handy in a few places to work
116  * in UTF-8.
117  */
118 gchar *mono_utf8_from_external (const gchar *in)
119 {
120         gchar *res=NULL;
121         gchar **encodings;
122         gchar *encoding_list;
123         int i;
124         
125         if(in==NULL) {
126                 return(NULL);
127         }
128         
129         encoding_list=g_getenv ("MONO_EXTERNAL_ENCODINGS");
130         if(encoding_list==NULL) {
131                 encoding_list = g_strdup("");
132         }
133         
134         encodings=g_strsplit (encoding_list, ":", 0);
135         g_free (encoding_list);
136         for(i=0;encodings[i]!=NULL; i++) {
137                 
138                 /* "default_locale" is a special case encoding */
139                 if(!strcmp (encodings[i], "default_locale")) {
140                         res=g_locale_to_utf8 (in, -1, NULL, NULL, NULL);
141                         if(res!=NULL && !g_utf8_validate (res, -1, NULL)) {
142                                 g_free (res);
143                                 res=NULL;
144                         }
145                 } else {
146                         res=g_convert (in, -1, "UTF8", encodings[i], NULL,
147                                        NULL, NULL);
148                 }
149
150                 if(res!=NULL) {
151                         g_strfreev (encodings);
152                         return(res);
153                 }
154         }
155         
156         g_strfreev (encodings);
157         
158         if(g_utf8_validate (in, -1, NULL)) {
159                 return(g_strdup (in));
160         }
161
162         return(NULL);
163 }
164
165 /**
166  * mono_unicode_to_external:
167  * \param uni a UTF-16 string to convert to an external representation.
168  * Turns NULL-terminated UTF-16 into either UTF-8, or the first
169  * working item in \c MONO_EXTERNAL_ENCODINGS if set.  If no conversions
170  * work, then UTF-8 is returned.
171  * Callers must free the returned string.
172  */
173 gchar *mono_unicode_to_external (const gunichar2 *uni)
174 {
175         gchar *utf8;
176         gchar *encoding_list;
177         
178         /* Turn the unicode into utf8 to start with, because its
179          * easier to work with gchar * than gunichar2 *
180          */
181         utf8=g_utf16_to_utf8 (uni, -1, NULL, NULL, NULL);
182         g_assert (utf8!=NULL);
183         
184         encoding_list=g_getenv ("MONO_EXTERNAL_ENCODINGS");
185         if(encoding_list==NULL) {
186                 /* Do UTF8 */
187                 return(utf8);
188         } else {
189                 gchar *res, **encodings;
190                 int i;
191                 
192                 encodings=g_strsplit (encoding_list, ":", 0);
193                 g_free (encoding_list);
194                 for(i=0; encodings[i]!=NULL; i++) {
195                         if(!strcmp (encodings[i], "default_locale")) {
196                                 res=g_locale_from_utf8 (utf8, -1, NULL, NULL,
197                                                         NULL);
198                         } else {
199                                 res=g_convert (utf8, -1, encodings[i], "UTF8",
200                                                NULL, NULL, NULL);
201                         }
202
203                         if(res!=NULL) {
204                                 g_free (utf8);
205                                 g_strfreev (encodings);
206                                 
207                                 return(res);
208                         }
209                 }
210         
211                 g_strfreev (encodings);
212         }
213         
214         /* Nothing else worked, so just return the utf8 */
215         return(utf8);
216 }
217
218 /**
219  * mono_utf8_validate_and_len
220  * \param source Pointer to putative UTF-8 encoded string.
221  * Checks \p source for being valid UTF-8. \p utf is assumed to be
222  * null-terminated.
223  * \returns TRUE if \p source is valid.
224  * \p oEnd will equal the null terminator at the end of the string if valid.
225  * if not valid, it will equal the first charater of the invalid sequence.
226  * \p oLength will equal the length to \p oEnd
227  **/
228 gboolean
229 mono_utf8_validate_and_len (const gchar *source, glong* oLength, const gchar** oEnd)
230 {
231         gboolean retVal = TRUE;
232         gboolean lastRet = TRUE;
233         guchar* ptr = (guchar*) source;
234         guchar* srcPtr;
235         guint length;
236         guchar a;
237         *oLength = 0;
238         while (*ptr != 0) {
239                 length = trailingBytesForUTF8 [*ptr] + 1;
240                 srcPtr = (guchar*) ptr + length;
241                 switch (length) {
242                 default: retVal = FALSE;
243                 /* Everything else falls through when "TRUE"... */
244                 case 4: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
245                                 if ((a == (guchar) 0xBF || a == (guchar) 0xBE) && *(srcPtr-1) == (guchar) 0xBF) {
246                                 if (*(srcPtr-2) == (guchar) 0x8F || *(srcPtr-2) == (guchar) 0x9F ||
247                                         *(srcPtr-2) == (guchar) 0xAF || *(srcPtr-2) == (guchar) 0xBF)
248                                         retVal = FALSE;
249                                 }
250                 case 3: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
251                 case 2: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
252
253                 switch (*ptr) {
254                 /* no fall-through in this inner switch */
255                 case 0xE0: if (a < (guchar) 0xA0) retVal = FALSE; break;
256                 case 0xED: if (a > (guchar) 0x9F) retVal = FALSE; break;
257                 case 0xEF: if (a == (guchar)0xB7 && (*(srcPtr+1) > (guchar) 0x8F && *(srcPtr+1) < 0xB0)) retVal = FALSE;
258                                    if (a == (guchar)0xBF && (*(srcPtr+1) == (guchar) 0xBE || *(srcPtr+1) == 0xBF)) retVal = FALSE; break;
259                 case 0xF0: if (a < (guchar) 0x90) retVal = FALSE; break;
260                 case 0xF4: if (a > (guchar) 0x8F) retVal = FALSE; break;
261                 default:   if (a < (guchar) 0x80) retVal = FALSE;
262                 }
263
264                 case 1: if (*ptr >= (guchar ) 0x80 && *ptr < (guchar) 0xC2) retVal = FALSE;
265                 }
266                 if (*ptr > (guchar) 0xF4)
267                         retVal = FALSE;
268                 //If the string is invalid, set the end to the invalid byte.
269                 if (!retVal && lastRet) {
270                         if (oEnd != NULL)
271                                 *oEnd = (gchar*) ptr;
272                         lastRet = FALSE;
273                 }
274                 ptr += length;
275                 (*oLength)++;
276         }
277         if (retVal && oEnd != NULL)
278                 *oEnd = (gchar*) ptr;
279         return retVal;
280 }
281
282
283 /**
284  * mono_utf8_validate_and_len_with_bounds
285  * \param source: Pointer to putative UTF-8 encoded string.
286  * \param max_bytes: Max number of bytes that can be decoded.
287  *
288  * Checks \p source for being valid UTF-8. \p utf is assumed to be
289  * null-terminated.
290  *
291  * This function returns FALSE if it needs to decode characters beyond \p max_bytes.
292  *
293  * \returns TRUE if \p source is valid.
294  * \p oEnd will equal the null terminator at the end of the string if valid.
295  * if not valid, it will equal the first charater of the invalid sequence.
296  * \p oLength will equal the length to \p oEnd
297  **/
298 gboolean
299 mono_utf8_validate_and_len_with_bounds (const gchar *source, glong max_bytes, glong* oLength, const gchar** oEnd)
300 {
301         gboolean retVal = TRUE;
302         gboolean lastRet = TRUE;
303         guchar* ptr = (guchar*) source;
304         guchar *end = ptr + max_bytes;
305         guchar* srcPtr;
306         guint length;
307         guchar a;
308         *oLength = 0;
309
310         if (max_bytes < 1) {
311                 if (oEnd)
312                         *oEnd = (gchar*) ptr;
313                 return FALSE;
314         }
315
316         while (*ptr != 0) {
317                 length = trailingBytesForUTF8 [*ptr] + 1;
318                 srcPtr = (guchar*) ptr + length;
319                 
320                 /* since *ptr is not zero we must ensure that we can decode the current char + the byte after
321                    srcPtr points to the first byte after the current char.*/
322                 if (srcPtr >= end) {
323                         retVal = FALSE;
324                         break;
325                 }
326                 switch (length) {
327                 default: retVal = FALSE;
328                 /* Everything else falls through when "TRUE"... */
329                 case 4: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
330                                 if ((a == (guchar) 0xBF || a == (guchar) 0xBE) && *(srcPtr-1) == (guchar) 0xBF) {
331                                 if (*(srcPtr-2) == (guchar) 0x8F || *(srcPtr-2) == (guchar) 0x9F ||
332                                         *(srcPtr-2) == (guchar) 0xAF || *(srcPtr-2) == (guchar) 0xBF)
333                                         retVal = FALSE;
334                                 }
335                 case 3: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
336                 case 2: if ((a = (*--srcPtr)) < (guchar) 0x80 || a > (guchar) 0xBF) retVal = FALSE;
337
338                 switch (*ptr) {
339                 /* no fall-through in this inner switch */
340                 case 0xE0: if (a < (guchar) 0xA0) retVal = FALSE; break;
341                 case 0xED: if (a > (guchar) 0x9F) retVal = FALSE; break;
342                 case 0xEF: if (a == (guchar)0xB7 && (*(srcPtr+1) > (guchar) 0x8F && *(srcPtr+1) < 0xB0)) retVal = FALSE;
343                                    if (a == (guchar)0xBF && (*(srcPtr+1) == (guchar) 0xBE || *(srcPtr+1) == 0xBF)) retVal = FALSE; break;
344                 case 0xF0: if (a < (guchar) 0x90) retVal = FALSE; break;
345                 case 0xF4: if (a > (guchar) 0x8F) retVal = FALSE; break;
346                 default:   if (a < (guchar) 0x80) retVal = FALSE;
347                 }
348
349                 case 1: if (*ptr >= (guchar ) 0x80 && *ptr < (guchar) 0xC2) retVal = FALSE;
350                 }
351                 if (*ptr > (guchar) 0xF4)
352                         retVal = FALSE;
353                 //If the string is invalid, set the end to the invalid byte.
354                 if (!retVal && lastRet) {
355                         if (oEnd != NULL)
356                                 *oEnd = (gchar*) ptr;
357                         lastRet = FALSE;
358                 }
359                 ptr += length;
360                 (*oLength)++;
361         }
362         if (retVal && oEnd != NULL)
363                 *oEnd = (gchar*) ptr;
364         return retVal;
365 }
366