Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gnc-glib-utils.c -- utility functions based on glib functions *
3 : : * Copyright (C) 2006 David Hampton <hampton@employees.org> *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : \********************************************************************/
23 : :
24 : : #include <config.h>
25 : : #include <errno.h>
26 : : #include <stdio.h>
27 : : #include <signal.h>
28 : : #include <string.h>
29 : : #include <stdbool.h>
30 : :
31 : : #include "gnc-glib-utils.h"
32 : :
33 : : #ifdef G_OS_WIN32
34 : : #include <windows.h>
35 : : #endif
36 : :
37 : : int
38 : 90263 : safe_utf8_collate (const char * da, const char * db)
39 : : {
40 : 90263 : if (da && !(*da))
41 : 0 : da = NULL;
42 : 90263 : if (db && !(*db))
43 : 0 : db = NULL;
44 : :
45 : 90263 : if (da && db)
46 : 90263 : return g_utf8_collate(da, db);
47 : 0 : if (da)
48 : 0 : return 1;
49 : 0 : if (db)
50 : 0 : return -1;
51 : 0 : return 0;
52 : : }
53 : :
54 : : /********************************************************************
55 : : * The following definitions are from gutf8.c, for use by
56 : : * gnc_utf8_validate(). These are all verbatim copies, except for
57 : : * UNICODE_VALID() which has been modified to look for the strict
58 : : * subset of UTF-8 that is valid XML text.
59 : : */
60 : :
61 : : #define UTF8_COMPUTE(Char, Mask, Len) \
62 : : if (Char < 128) \
63 : : { \
64 : : Len = 1; \
65 : : Mask = 0x7f; \
66 : : } \
67 : : else if ((Char & 0xe0) == 0xc0) \
68 : : { \
69 : : Len = 2; \
70 : : Mask = 0x1f; \
71 : : } \
72 : : else if ((Char & 0xf0) == 0xe0) \
73 : : { \
74 : : Len = 3; \
75 : : Mask = 0x0f; \
76 : : } \
77 : : else if ((Char & 0xf8) == 0xf0) \
78 : : { \
79 : : Len = 4; \
80 : : Mask = 0x07; \
81 : : } \
82 : : else if ((Char & 0xfc) == 0xf8) \
83 : : { \
84 : : Len = 5; \
85 : : Mask = 0x03; \
86 : : } \
87 : : else if ((Char & 0xfe) == 0xfc) \
88 : : { \
89 : : Len = 6; \
90 : : Mask = 0x01; \
91 : : } \
92 : : else \
93 : : Len = -1;
94 : :
95 : : #define UTF8_LENGTH(Char) \
96 : : ((Char) < 0x80 ? 1 : \
97 : : ((Char) < 0x800 ? 2 : \
98 : : ((Char) < 0x10000 ? 3 : \
99 : : ((Char) < 0x200000 ? 4 : \
100 : : ((Char) < 0x4000000 ? 5 : 6)))))
101 : :
102 : :
103 : : #define UTF8_GET(Result, Chars, Count, Mask, Len) \
104 : : (Result) = (Chars)[0] & (Mask); \
105 : : for ((Count) = 1; (Count) < (Len); ++(Count)) \
106 : : { \
107 : : if (((Chars)[(Count)] & 0xc0) != 0x80) \
108 : : { \
109 : : (Result) = -1; \
110 : : break; \
111 : : } \
112 : : (Result) <<= 6; \
113 : : (Result) |= ((Chars)[(Count)] & 0x3f); \
114 : : }
115 : :
116 : : #define UNICODE_VALID(Char) \
117 : : ((Char) < 0x110000 && \
118 : : (((Char) & 0xFFFFF800) != 0xD800) && \
119 : : ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \
120 : : ((Char) >= 0x20 || (Char) == 0x09 || (Char) == 0x0A || (Char) == 0x0D) && \
121 : : ((Char) & 0xFFFE) != 0xFFFE)
122 : :
123 : : gboolean
124 : 5 : gnc_utf8_validate(const gchar *str,
125 : : gssize max_len,
126 : : const gchar **end)
127 : : {
128 : :
129 : : const gchar *p;
130 : :
131 : 5 : g_return_val_if_fail (str != NULL, FALSE);
132 : :
133 : 5 : if (end)
134 : 5 : *end = str;
135 : :
136 : 5 : p = str;
137 : :
138 : 214 : while ((max_len < 0 || (p - str) < max_len) && *p)
139 : : {
140 : 212 : int i, mask = 0, len;
141 : : gunichar result;
142 : 212 : unsigned char c = (unsigned char) * p;
143 : :
144 : 212 : UTF8_COMPUTE (c, mask, len);
145 : :
146 : 212 : if (len == -1)
147 : 1 : break;
148 : :
149 : : /* check that the expected number of bytes exists in str */
150 : 211 : if (max_len >= 0 &&
151 : 0 : ((max_len - (p - str)) < len))
152 : 0 : break;
153 : :
154 : 382 : UTF8_GET (result, p, i, mask, len);
155 : :
156 : 211 : if (UTF8_LENGTH (result) != len) /* Check for overlong UTF-8 */
157 : 1 : break;
158 : :
159 : 210 : if (result == (gunichar) - 1)
160 : 0 : break;
161 : :
162 : 210 : if (!UNICODE_VALID (result))
163 : : break;
164 : :
165 : 209 : p += len;
166 : : }
167 : :
168 : 5 : if (end)
169 : 5 : *end = p;
170 : :
171 : : /* See that we covered the entire length if a length was
172 : : * passed in, or that we ended on a nul if not
173 : : */
174 : 5 : if (max_len >= 0 &&
175 : 0 : p != (str + max_len))
176 : 0 : return FALSE;
177 : 5 : else if (max_len < 0 &&
178 : 5 : *p != '\0')
179 : 3 : return FALSE;
180 : : else
181 : 2 : return TRUE;
182 : : }
183 : :
184 : : void
185 : 2 : gnc_utf8_strip_invalid (gchar *str)
186 : : {
187 : : gchar *end;
188 : : gint len;
189 : :
190 : 2 : g_return_if_fail(str);
191 : :
192 : 2 : if (gnc_utf8_validate(str, -1, (const gchar **)&end))
193 : 0 : return;
194 : :
195 : 2 : g_warning("Invalid utf8 string: %s", str);
196 : : do
197 : : {
198 : 3 : len = strlen(end);
199 : 3 : memmove(end, end + 1, len); /* shuffle the remainder one byte */
200 : : }
201 : 3 : while (!gnc_utf8_validate(str, -1, (const gchar **)&end));
202 : : }
203 : :
204 : : gchar *
205 : 0 : gnc_utf8_strip_invalid_strdup(const gchar* str)
206 : : {
207 : 0 : gchar *result = g_strdup (str);
208 : 0 : gnc_utf8_strip_invalid (result);
209 : 0 : return result;
210 : : }
211 : :
212 : : void
213 : 2 : gnc_utf8_strip_invalid_and_controls (gchar *str)
214 : : {
215 : 2 : gchar *c = NULL;
216 : 2 : const gchar *controls = "\b\f\n\r\t\v";
217 : 2 : g_return_if_fail (str != NULL && strlen (str) > 0);
218 : 2 : gnc_utf8_strip_invalid (str); /* First fix the UTF-8 */
219 : 189 : for(c = str + strlen (str) - 1; c != str; --c)
220 : : {
221 : 187 : gboolean line_control = ((unsigned char)(*c) < 0x20);
222 : 187 : if (line_control || strchr(controls, *c) != NULL)
223 : 2 : *c = ' '; /*replace controls with a single space. */
224 : : }
225 : : }
226 : :
227 : : gchar *
228 : 0 : gnc_locale_from_utf8(const gchar* str)
229 : : {
230 : : gchar * locale_str;
231 : 0 : gsize bytes_written = 0;
232 : 0 : GError * err = NULL;
233 : :
234 : : /* Convert from UTF-8 to the encoding used in the current locale. */
235 : 0 : locale_str = g_locale_from_utf8(str, -1, NULL, &bytes_written, &err);
236 : 0 : if (err)
237 : : {
238 : 0 : g_warning("g_locale_from_utf8 failed: %s", err->message);
239 : 0 : g_error_free(err);
240 : : }
241 : :
242 : 0 : return locale_str;
243 : : }
244 : :
245 : : gchar *
246 : 0 : gnc_locale_to_utf8(const gchar* str)
247 : : {
248 : : gchar * utf8_str;
249 : 0 : gsize bytes_written = 0;
250 : 0 : GError * err = NULL;
251 : :
252 : : /* Convert to UTF-8 from the encoding used in the current locale. */
253 : 0 : utf8_str = g_locale_to_utf8(str, -1, NULL, &bytes_written, &err);
254 : 0 : if (err)
255 : : {
256 : 0 : g_warning("g_locale_to_utf8 failed: %s", err->message);
257 : 0 : g_error_free(err);
258 : : }
259 : :
260 : 0 : return utf8_str;
261 : : }
262 : :
263 : : GList*
264 : 17 : gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data)
265 : : {
266 : 17 : GList *rtn = NULL;
267 : 33 : for (; list != NULL; list = list->next)
268 : : {
269 : 16 : rtn = g_list_prepend (rtn, (*fn)(list->data, user_data));
270 : : }
271 : 17 : return g_list_reverse (rtn);
272 : : }
273 : :
274 : : void
275 : 0 : gnc_g_list_cut(GList **list, GList *cut_point)
276 : : {
277 : 0 : if (list == NULL || *list == NULL)
278 : 0 : return;
279 : :
280 : : // if it's the first element.
281 : 0 : if (cut_point->prev == NULL)
282 : : {
283 : 0 : *list = NULL;
284 : 0 : return;
285 : : }
286 : :
287 : 0 : cut_point->prev->next = NULL;
288 : 0 : cut_point->prev = NULL;
289 : : }
290 : :
291 : : static bool
292 : 6 : utf8_strstr(char **needle, char *haystack)
293 : : {
294 : 6 : char *tmp = g_utf8_normalize (*needle, -1, G_NORMALIZE_NFC);
295 : 6 : if (haystack && *haystack)
296 : : {
297 : 5 : char *place = strstr(haystack, tmp);
298 : 5 : if (place)
299 : : {
300 : 2 : g_free (tmp);
301 : 2 : return false;
302 : : }
303 : : }
304 : 4 : *needle = tmp; //so that haystack is already normalized
305 : 4 : return true;
306 : : }
307 : :
308 : : static gchar *
309 : 14 : gnc_g_list_stringjoin_internal (GList *list_of_strings, const gchar *sep, bool testdups)
310 : : {
311 : 14 : gint seplen = sep ? strlen(sep) : 0;
312 : 14 : gint length = -seplen;
313 : : gchar *retval, *p;
314 : :
315 : 50 : for (GList *n = list_of_strings; n; n = n->next)
316 : : {
317 : 36 : gchar *str = n->data;
318 : 36 : if (str && *str)
319 : 30 : length += strlen (str) + seplen;
320 : : }
321 : :
322 : 14 : if (length <= 0)
323 : 2 : return NULL;
324 : :
325 : 12 : p = retval = (gchar*) g_malloc0 (length * sizeof (gchar) + 1);
326 : 48 : for (GList *n = list_of_strings; n; n = n->next)
327 : : {
328 : 36 : gchar *str = n->data;
329 : 36 : if (!str || !str[0])
330 : 6 : continue;
331 : 30 : if (!testdups || utf8_strstr (&str, retval))
332 : : {
333 : 28 : if (sep && (p != retval))
334 : 13 : p = g_stpcpy (p, sep);
335 : 28 : p = g_stpcpy (p, str);
336 : 28 : if (testdups)
337 : 4 : g_free (str);
338 : : }
339 : : }
340 : :
341 : 12 : return retval;
342 : : }
343 : :
344 : : gchar *
345 : 13 : gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep)
346 : : {
347 : 13 : return gnc_g_list_stringjoin_internal (list_of_strings, sep, false);
348 : : }
349 : :
350 : : gchar *
351 : 1 : gnc_g_list_stringjoin_nodups (GList *list_of_strings, const gchar *sep)
352 : : {
353 : 1 : return gnc_g_list_stringjoin_internal (list_of_strings, sep, true);
354 : : }
355 : :
356 : : gint
357 : 8 : gnc_list_length_cmp (const GList *list, size_t len)
358 : : {
359 : 15 : for (GList *lst = (GList*) list;; lst = g_list_next (lst), len--)
360 : : {
361 : 15 : if (!lst) return (len ? -1 : 0);
362 : 9 : if (!len) return 1;
363 : : }
364 : : }
|