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