GnuCash fec5643+
Loading...
Searching...
No Matches
gnc-glib-utils.c
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
37int
38safe_utf8_collate (const char * da, const char * db)
39{
40 if (da && !(*da))
41 da = NULL;
42 if (db && !(*db))
43 db = NULL;
44
45 if (da && db)
46 return g_utf8_collate(da, db);
47 if (da)
48 return 1;
49 if (db)
50 return -1;
51 return 0;
52}
53
54int
55safe_utf8_collate_natural (const char * da, const char * db)
56{
57 if (da && !(*da))
58 da = NULL;
59
60 if (db && !(*db))
61 db = NULL;
62
63 if (da && db)
64 {
65 gchar *a = g_utf8_collate_key_for_filename(da ? da : "", -1);
66 gchar *b = g_utf8_collate_key_for_filename(db ? db : "", -1);
67
68 int result = strcmp(a, b);
69
70 g_free(a);
71 g_free(b);
72
73 return result;
74 }
75 else if (da)
76 return 1;
77 else if (db)
78 return -1;
79
80 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
152gboolean
153gnc_utf8_validate(const gchar *str,
154 gssize max_len,
155 const gchar **end)
156{
157
158 const gchar *p;
159
160 g_return_val_if_fail (str != NULL, FALSE);
161
162 if (end)
163 *end = str;
164
165 p = str;
166
167 while ((max_len < 0 || (p - str) < max_len) && *p)
168 {
169 int i, mask = 0, len;
170 gunichar result;
171 unsigned char c = (unsigned char) * p;
172
173 UTF8_COMPUTE (c, mask, len);
174
175 if (len == -1)
176 break;
177
178 /* check that the expected number of bytes exists in str */
179 if (max_len >= 0 &&
180 ((max_len - (p - str)) < len))
181 break;
182
183 UTF8_GET (result, p, i, mask, len);
184
185 if (UTF8_LENGTH (result) != len) /* Check for overlong UTF-8 */
186 break;
187
188 if (result == (gunichar) - 1)
189 break;
190
191 if (!UNICODE_VALID (result))
192 break;
193
194 p += len;
195 }
196
197 if (end)
198 *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 if (max_len >= 0 &&
204 p != (str + max_len))
205 return FALSE;
206 else if (max_len < 0 &&
207 *p != '\0')
208 return FALSE;
209 else
210 return TRUE;
211}
212
213void
215{
216 gchar *end;
217 gint len;
218
219 g_return_if_fail(str);
220
221 if (gnc_utf8_validate(str, -1, (const gchar **)&end))
222 return;
223
224 g_warning("Invalid utf8 string: %s", str);
225 do
226 {
227 len = strlen(end);
228 memmove(end, end + 1, len); /* shuffle the remainder one byte */
229 }
230 while (!gnc_utf8_validate(str, -1, (const gchar **)&end));
231}
232
233gchar *
235{
236 gchar *result = g_strdup (str);
237 gnc_utf8_strip_invalid (result);
238 return result;
239}
240
241void
243{
244 gchar *c = NULL;
245 const gchar *controls = "\b\f\n\r\t\v";
246 g_return_if_fail (str != NULL && strlen (str) > 0);
247 gnc_utf8_strip_invalid (str); /* First fix the UTF-8 */
248 for(c = str + strlen (str) - 1; c != str; --c)
249 {
250 gboolean line_control = ((unsigned char)(*c) < 0x20);
251 if (line_control || strchr(controls, *c) != NULL)
252 *c = ' '; /*replace controls with a single space. */
253 }
254}
255
256gchar *
257gnc_locale_from_utf8(const gchar* str)
258{
259 gchar * locale_str;
260 gsize bytes_written = 0;
261 GError * err = NULL;
262
263 /* Convert from UTF-8 to the encoding used in the current locale. */
264 locale_str = g_locale_from_utf8(str, -1, NULL, &bytes_written, &err);
265 if (err)
266 {
267 g_warning("g_locale_from_utf8 failed: %s", err->message);
268 g_error_free(err);
269 }
270
271 return locale_str;
272}
273
274gchar *
275gnc_locale_to_utf8(const gchar* str)
276{
277 gchar * utf8_str;
278 gsize bytes_written = 0;
279 GError * err = NULL;
280
281 /* Convert to UTF-8 from the encoding used in the current locale. */
282 utf8_str = g_locale_to_utf8(str, -1, NULL, &bytes_written, &err);
283 if (err)
284 {
285 g_warning("g_locale_to_utf8 failed: %s", err->message);
286 g_error_free(err);
287 }
288
289 return utf8_str;
290}
291
292GList*
293gnc_g_list_map(GList* list, GncGMapFunc fn, gpointer user_data)
294{
295 GList *rtn = NULL;
296 for (; list != NULL; list = list->next)
297 {
298 rtn = g_list_prepend (rtn, (*fn)(list->data, user_data));
299 }
300 return g_list_reverse (rtn);
301}
302
303void
304gnc_g_list_cut(GList **list, GList *cut_point)
305{
306 if (list == NULL || *list == NULL)
307 return;
308
309 // if it's the first element.
310 if (cut_point->prev == NULL)
311 {
312 *list = NULL;
313 return;
314 }
315
316 cut_point->prev->next = NULL;
317 cut_point->prev = NULL;
318}
319
320static bool
321utf8_strstr(char **needle, char *haystack)
322{
323 char *tmp = g_utf8_normalize (*needle, -1, G_NORMALIZE_NFC);
324 if (haystack && *haystack)
325 {
326 char *place = strstr(haystack, tmp);
327 if (place)
328 {
329 g_free (tmp);
330 return false;
331 }
332 }
333 *needle = tmp; //so that haystack is already normalized
334 return true;
335}
336
337static gchar *
338gnc_g_list_stringjoin_internal (GList *list_of_strings, const gchar *sep, bool testdups)
339{
340 gint seplen = sep ? strlen(sep) : 0;
341 gint length = -seplen;
342 gchar *retval, *p;
343
344 for (GList *n = list_of_strings; n; n = n->next)
345 {
346 gchar *str = n->data;
347 if (str && *str)
348 length += strlen (str) + seplen;
349 }
350
351 if (length <= 0)
352 return NULL;
353
354 p = retval = (gchar*) g_malloc0 (length * sizeof (gchar) + 1);
355 for (GList *n = list_of_strings; n; n = n->next)
356 {
357 gchar *str = n->data;
358 if (!str || !str[0])
359 continue;
360 if (!testdups || utf8_strstr (&str, retval))
361 {
362 if (sep && (p != retval))
363 p = g_stpcpy (p, sep);
364 p = g_stpcpy (p, str);
365 if (testdups)
366 g_free (str);
367 }
368 }
369
370 return retval;
371}
372
373gchar *
374gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep)
375{
376 return gnc_g_list_stringjoin_internal (list_of_strings, sep, false);
377}
378
379gchar *
380gnc_g_list_stringjoin_nodups (GList *list_of_strings, const gchar *sep)
381{
382 return gnc_g_list_stringjoin_internal (list_of_strings, sep, true);
383}
384
385gint
386gnc_list_length_cmp (const GList *list, size_t len)
387{
388 for (GList *lst = (GList*) list;; lst = g_list_next (lst), len--)
389 {
390 if (!lst) return (len ? -1 : 0);
391 if (!len) return 1;
392 }
393}
GLib helper routines.
gchar * gnc_g_list_stringjoin_nodups(GList *list_of_strings, const gchar *sep)
Like stringjoin but ensures that the string to be added isn't already part of the return string.
gboolean gnc_utf8_validate(const gchar *str, gssize max_len, const gchar **end)
Validates UTF-8 encoded text for use in GnuCash.
gchar * gnc_utf8_strip_invalid_strdup(const gchar *str)
Returns a newly allocated copy of the given string but with any non-UTF-8 character stripped from it.
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
void gnc_utf8_strip_invalid(gchar *str)
Strip any non-UTF-8 characters from a string.
void gnc_g_list_cut(GList **list, GList *cut_point)
Cut a GList into two parts; the cut_point is the beginning of the new list; list may need to be modif...
int safe_utf8_collate(const char *da, const char *db)
Collate two UTF-8 strings.
void gnc_utf8_strip_invalid_and_controls(gchar *str)
Strip any non-utf8 characters and any control characters (everything < 0x20, , \f,...
GList * gnc_g_list_map(GList *list, GncGMapFunc fn, gpointer user_data)
gchar * gnc_locale_to_utf8(const gchar *str)
Converts a string to UTF-8 from the encoding used for strings in the current locale.
gint gnc_list_length_cmp(const GList *list, size_t len)
Scans the GList elements the minimum number of iterations required to test it against a specified siz...
gchar * gnc_locale_from_utf8(const gchar *str)
Converts a string from UTF-8 to the encoding used for strings in the current locale.
int safe_utf8_collate_natural(const char *da, const char *db)
Collate two UTF-8 strings naturally.