GnuCash  5.6-150-g038405b370+
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 
37 int
38 safe_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 
54 int
55 safe_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 
152 gboolean
153 gnc_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 
213 void
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 
233 gchar *
235 {
236  gchar *result = g_strdup (str);
237  gnc_utf8_strip_invalid (result);
238  return result;
239 }
240 
241 void
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 
256 gchar *
257 gnc_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 
274 gchar *
275 gnc_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 
292 GList*
293 gnc_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 
303 void
304 gnc_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 
320 static bool
321 utf8_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 
337 static gchar *
338 gnc_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 
373 gchar *
374 gnc_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 
379 gchar *
380 gnc_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 
385 gint
386 gnc_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 }
GList * gnc_g_list_map(GList *list, GncGMapFunc fn, gpointer user_data)
gchar * gnc_g_list_stringjoin(GList *list_of_strings, const gchar *sep)
Return a string joining a GList whose elements are gchar* strings.
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, , , ...
gchar * gnc_locale_from_utf8(const gchar *str)
Converts a string from UTF-8 to the encoding used for strings in the current locale.
gboolean gnc_utf8_validate(const gchar *str, gssize max_len, const gchar **end)
Validates UTF-8 encoded text for use in GnuCash.
int safe_utf8_collate_natural(const char *da, const char *db)
Collate two UTF-8 strings naturally.
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...
void gnc_utf8_strip_invalid(gchar *str)
Strip any non-UTF-8 characters from a string.
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&#39;t already part of the return string...
gchar * gnc_locale_to_utf8(const gchar *str)
Converts a string to UTF-8 from the encoding used for strings in the current locale.
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...
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...