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 : 8032 : safe_utf8_collate (const char * da, const char * db)
39 : : {
40 : 8032 : if (da && !(*da))
41 : 0 : da = NULL;
42 : 8032 : if (db && !(*db))
43 : 0 : db = NULL;
44 : :
45 : 8032 : if (da && db)
46 : 8032 : 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 : : int
55 : 0 : safe_utf8_collate_natural (const char * da, const char * db)
56 : : {
57 : 0 : if (da && !(*da))
58 : 0 : da = NULL;
59 : :
60 : 0 : if (db && !(*db))
61 : 0 : db = NULL;
62 : :
63 : 0 : if (da && db)
64 : : {
65 : 0 : gchar *a = g_utf8_collate_key_for_filename(da ? da : "", -1);
66 : 0 : gchar *b = g_utf8_collate_key_for_filename(db ? db : "", -1);
67 : :
68 : 0 : int result = strcmp(a, b);
69 : :
70 : 0 : g_free(a);
71 : 0 : g_free(b);
72 : :
73 : 0 : return result;
74 : : }
75 : 0 : else if (da)
76 : 0 : return 1;
77 : 0 : else if (db)
78 : 0 : return -1;
79 : :
80 : 0 : return 0;
81 : : }
82 : :
83 : : /********************************************************************
84 : : * The following definitions are from gutf8.c, for use by
85 : : * gnc_utf8_validate(). These are all verbatim copies, except for
86 : : * UNICODE_VALID() which has been modified to look for the strict
87 : : * subset of UTF-8 that is valid XML text.
88 : : */
89 : :
90 : : #define UTF8_COMPUTE(Char, Mask, Len) \
91 : : if (Char < 128) \
92 : : { \
93 : : Len = 1; \
94 : : Mask = 0x7f; \
95 : : } \
96 : : else if ((Char & 0xe0) == 0xc0) \
97 : : { \
98 : : Len = 2; \
99 : : Mask = 0x1f; \
100 : : } \
101 : : else if ((Char & 0xf0) == 0xe0) \
102 : : { \
103 : : Len = 3; \
104 : : Mask = 0x0f; \
105 : : } \
106 : : else if ((Char & 0xf8) == 0xf0) \
107 : : { \
108 : : Len = 4; \
109 : : Mask = 0x07; \
110 : : } \
111 : : else if ((Char & 0xfc) == 0xf8) \
112 : : { \
113 : : Len = 5; \
114 : : Mask = 0x03; \
115 : : } \
116 : : else if ((Char & 0xfe) == 0xfc) \
117 : : { \
118 : : Len = 6; \
119 : : Mask = 0x01; \
120 : : } \
121 : : else \
122 : : Len = -1;
123 : :
124 : : #define UTF8_LENGTH(Char) \
125 : : ((Char) < 0x80 ? 1 : \
126 : : ((Char) < 0x800 ? 2 : \
127 : : ((Char) < 0x10000 ? 3 : \
128 : : ((Char) < 0x200000 ? 4 : \
129 : : ((Char) < 0x4000000 ? 5 : 6)))))
130 : :
131 : :
132 : : #define UTF8_GET(Result, Chars, Count, Mask, Len) \
133 : : (Result) = (Chars)[0] & (Mask); \
134 : : for ((Count) = 1; (Count) < (Len); ++(Count)) \
135 : : { \
136 : : if (((Chars)[(Count)] & 0xc0) != 0x80) \
137 : : { \
138 : : (Result) = -1; \
139 : : break; \
140 : : } \
141 : : (Result) <<= 6; \
142 : : (Result) |= ((Chars)[(Count)] & 0x3f); \
143 : : }
144 : :
145 : : #define UNICODE_VALID(Char) \
146 : : ((Char) < 0x110000 && \
147 : : (((Char) & 0xFFFFF800) != 0xD800) && \
148 : : ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \
149 : : ((Char) >= 0x20 || (Char) == 0x09 || (Char) == 0x0A || (Char) == 0x0D) && \
150 : : ((Char) & 0xFFFE) != 0xFFFE)
151 : :
152 : : gboolean
153 : 5 : gnc_utf8_validate(const gchar *str,
154 : : gssize max_len,
155 : : const gchar **end)
156 : : {
157 : :
158 : : const gchar *p;
159 : :
160 : 5 : g_return_val_if_fail (str != NULL, FALSE);
161 : :
162 : 5 : if (end)
163 : 5 : *end = str;
164 : :
165 : 5 : p = str;
166 : :
167 : 214 : while ((max_len < 0 || (p - str) < max_len) && *p)
168 : : {
169 : 212 : int i, mask = 0, len;
170 : : gunichar result;
171 : 212 : unsigned char c = (unsigned char) * p;
172 : :
173 : 212 : UTF8_COMPUTE (c, mask, len);
174 : :
175 : 212 : if (len == -1)
176 : 1 : break;
177 : :
178 : : /* check that the expected number of bytes exists in str */
179 : 211 : if (max_len >= 0 &&
180 : 0 : ((max_len - (p - str)) < len))
181 : 0 : break;
182 : :
183 : 382 : UTF8_GET (result, p, i, mask, len);
184 : :
185 : 211 : if (UTF8_LENGTH (result) != len) /* Check for overlong UTF-8 */
186 : 1 : break;
187 : :
188 : 210 : if (result == (gunichar) - 1)
189 : 0 : break;
190 : :
191 : 210 : if (!UNICODE_VALID (result))
192 : : break;
193 : :
194 : 209 : p += len;
195 : : }
196 : :
197 : 5 : if (end)
198 : 5 : *end = p;
199 : :
200 : : /* See that we covered the entire length if a length was
201 : : * passed in, or that we ended on a nul if not
202 : : */
203 : 5 : if (max_len >= 0 &&
204 : 0 : p != (str + max_len))
205 : 0 : return FALSE;
206 : 5 : else if (max_len < 0 &&
207 : 5 : *p != '\0')
208 : 3 : return FALSE;
209 : : else
210 : 2 : return TRUE;
211 : : }
212 : :
213 : : void
214 : 2 : gnc_utf8_strip_invalid (gchar *str)
215 : : {
216 : : gchar *end;
217 : : gint len;
218 : :
219 : 2 : g_return_if_fail(str);
220 : :
221 : 2 : if (gnc_utf8_validate(str, -1, (const gchar **)&end))
222 : 0 : return;
223 : :
224 : 2 : g_warning("Invalid utf8 string: %s", str);
225 : : do
226 : : {
227 : 3 : len = strlen(end);
228 : 3 : memmove(end, end + 1, len); /* shuffle the remainder one byte */
229 : : }
230 : 3 : while (!gnc_utf8_validate(str, -1, (const gchar **)&end));
231 : : }
232 : :
233 : : gchar *
234 : 0 : gnc_utf8_strip_invalid_strdup(const gchar* str)
235 : : {
236 : 0 : gchar *result = g_strdup (str);
237 : 0 : gnc_utf8_strip_invalid (result);
238 : 0 : return result;
239 : : }
240 : :
241 : : void
242 : 2 : gnc_utf8_strip_invalid_and_controls (gchar *str)
243 : : {
244 : 2 : gchar *c = NULL;
245 : 2 : const gchar *controls = "\b\f\n\r\t\v";
246 : 2 : g_return_if_fail (str != NULL && strlen (str) > 0);
247 : 2 : gnc_utf8_strip_invalid (str); /* First fix the UTF-8 */
248 : 189 : for(c = str + strlen (str) - 1; c != str; --c)
249 : : {
250 : 187 : gboolean line_control = ((unsigned char)(*c) < 0x20);
251 : 187 : if (line_control || strchr(controls, *c) != NULL)
252 : 2 : *c = ' '; /*replace controls with a single space. */
253 : : }
254 : : }
255 : :
256 : : gchar *
257 : 0 : gnc_locale_from_utf8(const gchar* str)
258 : : {
259 : : gchar * locale_str;
260 : 0 : gsize bytes_written = 0;
261 : 0 : GError * err = NULL;
262 : :
263 : : /* Convert from UTF-8 to the encoding used in the current locale. */
264 : 0 : locale_str = g_locale_from_utf8(str, -1, NULL, &bytes_written, &err);
265 : 0 : if (err)
266 : : {
267 : 0 : g_warning("g_locale_from_utf8 failed: %s", err->message);
268 : 0 : g_error_free(err);
269 : : }
270 : :
271 : 0 : return locale_str;
272 : : }
273 : :
274 : : gchar *
275 : 0 : gnc_locale_to_utf8(const gchar* str)
276 : : {
277 : : gchar * utf8_str;
278 : 0 : gsize bytes_written = 0;
279 : 0 : GError * err = NULL;
280 : :
281 : : /* Convert to UTF-8 from the encoding used in the current locale. */
282 : 0 : utf8_str = g_locale_to_utf8(str, -1, NULL, &bytes_written, &err);
283 : 0 : if (err)
284 : : {
285 : 0 : g_warning("g_locale_to_utf8 failed: %s", err->message);
286 : 0 : g_error_free(err);
287 : : }
288 : :
289 : 0 : return utf8_str;
290 : : }
291 : :
292 : : GList*
293 : 17 : gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data)
294 : : {
295 : 17 : GList *rtn = NULL;
296 : 33 : for (; list != NULL; list = list->next)
297 : : {
298 : 16 : rtn = g_list_prepend (rtn, (*fn)(list->data, user_data));
299 : : }
300 : 17 : return g_list_reverse (rtn);
301 : : }
302 : :
303 : : void
304 : 0 : gnc_g_list_cut(GList **list, GList *cut_point)
305 : : {
306 : 0 : if (list == NULL || *list == NULL)
307 : 0 : return;
308 : :
309 : : // if it's the first element.
310 : 0 : if (cut_point->prev == NULL)
311 : : {
312 : 0 : *list = NULL;
313 : 0 : return;
314 : : }
315 : :
316 : 0 : cut_point->prev->next = NULL;
317 : 0 : cut_point->prev = NULL;
318 : : }
319 : :
320 : : static bool
321 : 6 : utf8_strstr(char **needle, char *haystack)
322 : : {
323 : 6 : char *tmp = g_utf8_normalize (*needle, -1, G_NORMALIZE_NFC);
324 : 6 : if (haystack && *haystack)
325 : : {
326 : 5 : char *place = strstr(haystack, tmp);
327 : 5 : if (place)
328 : : {
329 : 2 : g_free (tmp);
330 : 2 : return false;
331 : : }
332 : : }
333 : 4 : *needle = tmp; //so that haystack is already normalized
334 : 4 : return true;
335 : : }
336 : :
337 : : static gchar *
338 : 14 : gnc_g_list_stringjoin_internal (GList *list_of_strings, const gchar *sep, bool testdups)
339 : : {
340 : 14 : gint seplen = sep ? strlen(sep) : 0;
341 : 14 : gint length = -seplen;
342 : : gchar *retval, *p;
343 : :
344 : 50 : for (GList *n = list_of_strings; n; n = n->next)
345 : : {
346 : 36 : gchar *str = n->data;
347 : 36 : if (str && *str)
348 : 30 : length += strlen (str) + seplen;
349 : : }
350 : :
351 : 14 : if (length <= 0)
352 : 2 : return NULL;
353 : :
354 : 12 : p = retval = (gchar*) g_malloc0 (length * sizeof (gchar) + 1);
355 : 48 : for (GList *n = list_of_strings; n; n = n->next)
356 : : {
357 : 36 : gchar *str = n->data;
358 : 36 : if (!str || !str[0])
359 : 6 : continue;
360 : 30 : if (!testdups || utf8_strstr (&str, retval))
361 : : {
362 : 28 : if (sep && (p != retval))
363 : 13 : p = g_stpcpy (p, sep);
364 : 28 : p = g_stpcpy (p, str);
365 : 28 : if (testdups)
366 : 4 : g_free (str);
367 : : }
368 : : }
369 : :
370 : 12 : return retval;
371 : : }
372 : :
373 : : gchar *
374 : 13 : gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep)
375 : : {
376 : 13 : return gnc_g_list_stringjoin_internal (list_of_strings, sep, false);
377 : : }
378 : :
379 : : gchar *
380 : 1 : gnc_g_list_stringjoin_nodups (GList *list_of_strings, const gchar *sep)
381 : : {
382 : 1 : return gnc_g_list_stringjoin_internal (list_of_strings, sep, true);
383 : : }
384 : :
385 : : gint
386 : 8 : gnc_list_length_cmp (const GList *list, size_t len)
387 : : {
388 : 15 : for (GList *lst = (GList*) list;; lst = g_list_next (lst), len--)
389 : : {
390 : 15 : if (!lst) return (len ? -1 : 0);
391 : 9 : if (!len) return 1;
392 : : }
393 : : }
|