1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4 * GThumb
5 *
6 * Copyright (C) 2019 Free Software Foundation, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <config.h>
23 #include <glib.h>
24 #include <glib/gi18n.h>
25 #include "str-utils.h"
26
27
28 gboolean
_g_str_equal(const char * str1,const char * str2)29 _g_str_equal (const char *str1,
30 const char *str2)
31 {
32 return g_strcmp0 (str1, str2) == 0;
33 }
34
35
36 gboolean
_g_str_n_equal(const char * str1,const char * str2,gsize size)37 _g_str_n_equal (const char *str1,
38 const char *str2,
39 gsize size)
40 {
41 if ((str1 == NULL) && (str2 == NULL))
42 return TRUE;
43 if ((str1 == NULL) || (str2 == NULL))
44 return FALSE;
45 return strncmp (str1, str2, size);
46 }
47
48
49 void
_g_str_set(char ** str,const char * value)50 _g_str_set (char **str,
51 const char *value)
52 {
53 if (*str == value)
54 return;
55
56 if (*str != NULL) {
57 g_free (*str);
58 *str = NULL;
59 }
60
61 if (value != NULL)
62 *str = g_strdup (value);
63 }
64
65
66 char **
_g_strv_take_from_str_list(GList * str_list,int size)67 _g_strv_take_from_str_list (GList *str_list,
68 int size)
69 {
70 char **str_v;
71 GList *scan;
72 int i;
73
74 if (size < 0)
75 size = g_list_length (str_list);
76
77 str_v = g_new0 (char *, size + 1);
78 for (scan = g_list_last (str_list), i = 0; scan && (i < size); scan = scan->prev, i++)
79 str_v[i] = (char *) scan->data;
80 str_v[i] = NULL;
81
82 return str_v;
83 }
84
85
86 char *
_g_str_random(int len)87 _g_str_random (int len)
88 {
89 static char *alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
90 static int letters_only = 52;
91 static int whole_alphabet = 62;
92
93 GRand *generator;
94 char *s;
95 int i;
96
97 generator = g_rand_new ();
98
99 s = g_new (char, len + 1);
100 for (i = 0; i < len; i++)
101 s[i] = alphabet[g_rand_int_range (generator, 0, (i == 0) ? letters_only : whole_alphabet)];
102 s[len] = 0;
103
104 g_rand_free (generator);
105
106 return s;
107 }
108
109
110 char *
_g_str_remove_suffix(const char * str,const char * suffix)111 _g_str_remove_suffix (const char *str,
112 const char *suffix)
113 {
114 int s_len;
115 int suffix_len;
116
117 if (str == NULL)
118 return NULL;
119
120 if (suffix == NULL)
121 return g_strdup (str);
122
123 s_len = strlen (str);
124 suffix_len = strlen (suffix);
125
126 if (suffix_len >= s_len)
127 return g_strdup ("");
128 else
129 return g_strndup (str, s_len - suffix_len);
130 }
131
132
133 GHashTable *static_strings = NULL;
134 static GMutex static_strings_mutex;
135
136
137 const char *
_g_str_get_static(const char * str)138 _g_str_get_static (const char *str)
139 {
140 const char *result;
141
142 if (str == NULL)
143 return NULL;
144
145 g_mutex_lock (&static_strings_mutex);
146
147 if (static_strings == NULL)
148 static_strings = g_hash_table_new_full (g_str_hash,
149 g_str_equal,
150 g_free,
151 NULL);
152
153 if (! g_hash_table_lookup_extended (static_strings,
154 str,
155 (gpointer) &result,
156 NULL))
157 {
158 result = g_strdup (str);
159 g_hash_table_insert (static_strings,
160 (gpointer) result,
161 GINT_TO_POINTER (1));
162 }
163
164 g_mutex_unlock (&static_strings_mutex);
165
166 return result;
167 }
168
169
170 GHashTable *
_g_str_split_as_hash_table(const char * str)171 _g_str_split_as_hash_table (const char *str)
172 {
173 GHashTable *htable;
174
175 htable = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
176
177 if (str != NULL) {
178 char **strv;
179 int i;
180
181 strv = g_strsplit (str, ",", -1);
182 for (i = 0; strv[i] != NULL; i++)
183 if (strv[i] != 0)
184 g_hash_table_add (htable, g_strdup (strv[i]));
185
186 g_strfreev (strv);
187 }
188
189 return htable;
190 }
191
192
193 /* StrV utils */
194
195
196 int
_g_strv_find(char ** strv,const char * str)197 _g_strv_find (char **strv,
198 const char *str)
199 {
200 int i;
201
202 for (i = 0; strv[i] != NULL; i++) {
203 if (strcmp (strv[i], str) == 0)
204 return i;
205 }
206
207 return -1;
208 }
209
210
211 gboolean
_g_strv_contains(char ** strv,const char * str)212 _g_strv_contains (char **strv,
213 const char *str)
214 {
215 return (_g_strv_find (strv, str) >= 0);
216 }
217
218
219 char **
_g_strv_prepend(char ** strv,const char * str)220 _g_strv_prepend (char **strv,
221 const char *str)
222 {
223 char **result;
224 int i;
225 int j;
226
227 result = g_new (char *, g_strv_length (strv) + 1);
228 i = 0;
229 result[i++] = g_strdup (str);
230 for (j = 0; strv[j] != NULL; j++)
231 result[i++] = g_strdup (strv[j]);
232 result[i] = NULL;
233
234 return result;
235 }
236
237
238 char **
_g_strv_concat(char ** strv1,char ** strv2)239 _g_strv_concat (char **strv1,
240 char **strv2)
241 {
242 char **result;
243 int i, j;
244
245 result = g_new (char *, g_strv_length (strv1) + g_strv_length (strv2) + 1);
246 i = 0;
247 for (j = 0; strv1[j] != NULL; j++)
248 result[i++] = g_strdup (strv1[j]);
249 for (j = 0; strv2[j] != NULL; j++)
250 result[i++] = g_strdup (strv2[j]);
251 result[i] = NULL;
252
253 return result;
254 }
255
256
257 gboolean
_g_strv_remove(char ** strv,const char * str)258 _g_strv_remove (char **strv,
259 const char *str)
260 {
261 int i;
262 int j;
263
264 if (str == NULL)
265 return FALSE;
266
267 for (i = 0; strv[i] != NULL; i++)
268 if (strcmp (strv[i], str) == 0)
269 break;
270
271 if (strv[i] == NULL)
272 return FALSE;
273
274 for (j = i; strv[j] != NULL; j++)
275 strv[j] = strv[j + 1];
276
277 return TRUE;
278 }
279
280
281 /* UTF-8 utils */
282
283
284 char *
_g_utf8_strndup(const char * str,gssize size)285 _g_utf8_strndup (const char *str,
286 gssize size)
287 {
288 char *new_str;
289
290 if ((str == NULL) || (size == 0))
291 return NULL;
292
293 if (size < 0)
294 size = g_utf8_strlen (str, -1);
295
296 new_str = g_new (char, size * 4 + 1);
297 g_utf8_strncpy (new_str, str, size);
298
299 return new_str;
300 }
301
302
303 const char *
_g_utf8_find_str(const char * haystack,const char * needle)304 _g_utf8_find_str (const char *haystack,
305 const char *needle)
306 {
307 glong haystack_len;
308 glong needle_len;
309 glong needle_size;
310 glong i;
311
312 if ((haystack == NULL) || (needle == NULL))
313 return NULL;
314
315 haystack_len = g_utf8_strlen (haystack, -1);
316 needle_len = g_utf8_strlen (needle, -1);
317 needle_size = strlen (needle);
318
319 if (needle_len == 0)
320 return NULL;
321
322 for (i = 0; i <= haystack_len - needle_len; i++) {
323 if (strncmp (haystack, needle, needle_size) == 0)
324 return haystack;
325 haystack = g_utf8_next_char (haystack);
326 }
327
328 return NULL;
329 }
330
331
332 /* -- _g_utf8_split -- */
333
334
335 char **
_g_utf8_split(const char * str,const char * sep,int max_tokens)336 _g_utf8_split (const char *str,
337 const char *sep,
338 int max_tokens)
339 {
340 glong sep_size;
341 GList *chunk_list;
342 int chunk_n;
343 const char *p;
344 char **chunk_v;
345
346 sep_size = (sep != NULL) ? strlen (sep) : 0;
347 chunk_list = NULL;
348 chunk_n = 0;
349 p = str;
350 while ((p != NULL) && (max_tokens != 0) && (max_tokens != 1)) {
351 const char *sep_p = _g_utf8_find_str (p, sep);
352 char *chunk;
353
354 if (sep_p == NULL) {
355 if ((p == str) && (*p == 0)) {
356 /* Special case: when splitting an emtpy string
357 * return an emtpy string. */
358
359 chunk = g_strdup ("");
360 chunk_list = g_list_prepend (chunk_list, chunk);
361 chunk_n++;
362 if (max_tokens > 0) max_tokens--;
363 p = NULL;
364 }
365 else if ((sep != NULL) && (sep_size == 0)) {
366
367 /* Special case: when the separator is an
368 * empty string, split each character. */
369
370 chunk = _g_utf8_strndup (p, 1);
371 chunk_list = g_list_prepend (chunk_list, chunk);
372 chunk_n++;
373 if (max_tokens > 0) max_tokens--;
374 p = g_utf8_next_char (p);
375 }
376 else {
377 chunk = g_strdup (p);
378 chunk_list = g_list_prepend (chunk_list, chunk);
379 chunk_n++;
380 if (max_tokens > 0) max_tokens--;
381 p = NULL;
382 }
383 }
384 else if (sep_p > p) {
385 chunk = g_strndup (p, sep_p - p);
386 chunk_list = g_list_prepend (chunk_list, chunk);
387 chunk_n++;
388 if (max_tokens > 0) max_tokens--;
389 p = sep_p + sep_size;
390 }
391 else
392 p = sep_p + sep_size;
393
394 if ((p != NULL) && (g_utf8_get_char (p) == 0))
395 break;
396 }
397
398 if ((p != NULL) && (max_tokens == 1)) {
399 chunk_list = g_list_prepend (chunk_list, g_strdup (p));
400 chunk_n++;
401 }
402
403 chunk_v = _g_strv_take_from_str_list (chunk_list, chunk_n);
404 g_list_free (chunk_list);
405
406 return chunk_v;
407 }
408
409
410 /* -- _g_utf8_split_template -- */
411
412
413 typedef enum {
414 SPLIT_TMPL_STATE_START,
415 SPLIT_TMPL_STATE_READING_SHARPS,
416 SPLIT_TMPL_STATE_READING_LITERAL
417 } SplitTmplState;
418
419
420 /**
421 * example 1 : "xxx##yy#" --> [0] = xxx
422 * [1] = ##
423 * [2] = yy
424 * [3] = #
425 * [4] = NULL
426 *
427 * example 2 : "" --> [0] = NULL
428 **/
429 char **
_g_utf8_split_template(const char * tmpl)430 _g_utf8_split_template (const char *tmpl)
431 {
432 SplitTmplState state;
433 GList *chunk_list;
434 int chunk_n;
435 const char *p;
436 const char *chunk_start;
437 const char *chunk_end;
438 char **chunk_v;
439
440 state = SPLIT_TMPL_STATE_START;
441 chunk_list = NULL;
442 chunk_n = 0;
443 p = tmpl;
444 while (p != NULL) {
445 gunichar ch = g_utf8_get_char (p);
446 gboolean save_chunk = FALSE;
447
448 switch (state) {
449 case SPLIT_TMPL_STATE_START:
450 chunk_start = chunk_end = p;
451 if (ch == '#')
452 state = SPLIT_TMPL_STATE_READING_SHARPS;
453 else
454 state = SPLIT_TMPL_STATE_READING_LITERAL;
455 break;
456
457 case SPLIT_TMPL_STATE_READING_SHARPS:
458 if (ch != '#') {
459 state = SPLIT_TMPL_STATE_READING_LITERAL;
460 save_chunk = TRUE;
461 }
462 else
463 chunk_end = p;
464 break;
465
466 case SPLIT_TMPL_STATE_READING_LITERAL:
467 if ((ch == '#') || (ch == 0)) {
468 state = SPLIT_TMPL_STATE_READING_SHARPS;
469 save_chunk = TRUE;
470 }
471 else
472 chunk_end = p;
473 break;
474 }
475
476 if (save_chunk) {
477 glong chunk_size;
478 char *chunk;
479
480 chunk_size = chunk_end - chunk_start + 1;
481 chunk = _g_utf8_strndup (chunk_start, chunk_size);
482 chunk_list = g_list_prepend (chunk_list, chunk);
483 chunk_n++;
484 chunk_start = chunk_end = p;
485 }
486
487 if (ch == 0)
488 break;
489
490 p = g_utf8_next_char (p);
491 }
492
493 chunk_v = _g_strv_take_from_str_list (chunk_list, chunk_n);
494 g_list_free (chunk_list);
495
496 return chunk_v;
497 }
498
499
500 char *
_g_utf8_replace_str(const char * str,const char * old_str,const char * new_str)501 _g_utf8_replace_str (const char *str,
502 const char *old_str,
503 const char *new_str)
504 {
505 GString *result;
506 size_t old_str_size;
507 const char *p;
508
509 if (str == NULL)
510 return NULL;
511
512 result = g_string_new ("");
513 old_str_size = (old_str != NULL) ? strlen (old_str) : 0;
514 p = str;
515 while ((p != NULL) && (g_utf8_get_char (p) != 0)) {
516 const char *sep = _g_utf8_find_str (p, old_str);
517
518 if (sep == NULL) {
519 g_string_append (result, p);
520 p = NULL;
521 }
522 else {
523 g_string_append_len (result, p, sep - p);
524 if (new_str != NULL)
525 g_string_append (result, new_str);
526 p = sep + old_str_size;
527 }
528 }
529
530 return g_string_free (result, FALSE);
531 }
532
533
534 char *
_g_utf8_replace_pattern(const char * str,const char * pattern,const char * replacement)535 _g_utf8_replace_pattern (const char *str,
536 const char *pattern,
537 const char *replacement)
538 {
539 GRegex *regex;
540 char *result;
541
542 if (str == NULL)
543 return NULL;
544
545 regex = g_regex_new (pattern, 0, 0, NULL);
546 if (regex == NULL)
547 return NULL;
548
549 result = g_regex_replace_literal (regex, str, -1, 0, replacement, 0, NULL);
550
551 g_regex_unref (regex);
552
553 return result;
554 }
555
556
557 char *
_g_utf8_last_char(const char * str,glong * p_size)558 _g_utf8_last_char (const char *str,
559 glong *p_size)
560 {
561 glong len;
562
563 if (str == NULL) {
564 if (p_size) *p_size = 0;
565 return NULL;
566 }
567
568 len = strlen (str);
569 if (p_size) *p_size = len;
570
571 if (len == 0)
572 return NULL;
573
574 return g_utf8_find_prev_char (str, str + len);
575 }
576
577
578 gboolean
_g_utf8_n_equal(const char * str1,const char * str2,glong size)579 _g_utf8_n_equal (const char *str1,
580 const char *str2,
581 glong size)
582 {
583 const char *p1;
584 const char *p2;
585
586 p1 = str1;
587 p2 = str2;
588 while ((size > 0) && (p1 != NULL) && (p2 != NULL)) {
589 gunichar c1 = g_utf8_get_char (p1);
590 gunichar c2 = g_utf8_get_char (p2);
591
592 if ((c1 == 0) || (c2 == 0) || (c1 != c2))
593 break;
594
595 size--;
596 p1 = g_utf8_next_char (p1);
597 p2 = g_utf8_next_char (p2);
598 }
599
600 return size == 0;
601 }
602
603
604 const char *
_g_utf8_after_ascii_space(const char * str)605 _g_utf8_after_ascii_space (const char *str)
606 {
607 while (str != NULL) {
608 gunichar c = g_utf8_get_char (str);
609
610 if (c == 0)
611 break;
612
613 if (c == ' ')
614 return g_utf8_next_char (str);
615
616 str = g_utf8_next_char (str);
617 }
618
619 return NULL;
620 }
621
622
623 gboolean
_g_utf8_has_prefix(const char * str,const char * prefix)624 _g_utf8_has_prefix (const char *str,
625 const char *prefix)
626 {
627 if (str == NULL)
628 return FALSE;
629
630 if (prefix == NULL)
631 return FALSE;
632
633 while ((str != NULL) && (prefix != NULL)) {
634 gunichar str_ch = g_utf8_get_char (str);
635 gunichar prefix_ch = g_utf8_get_char (prefix);
636
637 if (prefix_ch == 0)
638 return TRUE;
639
640 if (str_ch == 0)
641 return FALSE;
642
643 if (str_ch != prefix_ch)
644 return FALSE;
645
646 str = g_utf8_next_char (str);
647 prefix = g_utf8_next_char (prefix);
648 }
649
650 return FALSE;
651 }
652
653
654 gboolean
_g_utf8_all_spaces(const char * str)655 _g_utf8_all_spaces (const char *str)
656 {
657 while (str != NULL) {
658 gunichar ch = g_utf8_get_char (str);
659
660 if (ch == 0)
661 break;
662
663 if (! g_unichar_isspace (ch))
664 return FALSE;
665
666 str = g_utf8_next_char (str);
667 }
668
669 return TRUE;
670 }
671
672
673 char *
_g_utf8_try_from_any(const char * str)674 _g_utf8_try_from_any (const char *str)
675 {
676 char *utf8_str;
677
678 if (str == NULL)
679 return NULL;
680
681 if (! g_utf8_validate (str, -1, NULL))
682 utf8_str = g_locale_to_utf8 (str, -1, NULL, NULL, NULL);
683 else
684 utf8_str = g_strdup (str);
685
686 return utf8_str;
687 }
688
689
690 char *
_g_utf8_from_any(const char * str)691 _g_utf8_from_any (const char *str)
692 {
693 char *utf8_str;
694
695 if (str == NULL)
696 return NULL;
697
698 utf8_str = _g_utf8_try_from_any (str);
699 if (utf8_str == NULL)
700 utf8_str = g_strdup (_("(invalid value)"));
701
702 return utf8_str;
703 }
704
705
706 /* -- _g_utf8_strip_func -- */
707
708
709 typedef enum {
710 STRIP_STATE_HEADING_SPACE,
711 STRIP_STATE_REST
712 } StripState;
713
714
715 char *
_g_utf8_strip_func(const char * str,StripFunc is_space_func)716 _g_utf8_strip_func (const char *str,
717 StripFunc is_space_func)
718 {
719 const char *first_non_space = NULL;
720 const char *last_non_space = NULL;
721 StripState state = STRIP_STATE_HEADING_SPACE;
722
723 if (str == NULL)
724 return NULL;
725
726 if (is_space_func == NULL)
727 return g_strdup ("");
728
729 while (str != NULL) {
730 gunichar ch = g_utf8_get_char (str);
731 gboolean is_space = is_space_func (ch) || (ch == 0);
732
733 switch (state) {
734 case STRIP_STATE_HEADING_SPACE:
735 if (! is_space) {
736 state = STRIP_STATE_REST;
737 first_non_space = last_non_space = str;
738 }
739 break;
740
741 case STRIP_STATE_REST:
742 if (! is_space)
743 last_non_space = str;
744 break;
745 }
746
747 if (ch == 0)
748 break;
749
750 str = g_utf8_next_char (str);
751 }
752
753 if (first_non_space == NULL)
754 return g_strdup ("");
755
756 g_assert (last_non_space != NULL);
757
758 return g_strndup (first_non_space, g_utf8_next_char (last_non_space) - first_non_space);
759 }
760
761
762 char *
_g_utf8_strip(const char * str)763 _g_utf8_strip (const char *str)
764 {
765 return _g_utf8_strip_func (str, g_unichar_isspace);
766 }
767
768
769 /* -- _g_utf8_rstrip_func -- */
770
771
772 typedef enum {
773 RSTRIP_STATE_ONLY_SPACES, /* String contains only spaces. */
774 RSTRIP_STATE_NON_SPACE, /* Reading non space characters. */
775 RSTRIP_STATE_TRAILING_SPACE /* Reading possible trailing spaces. */
776 } RStripState;
777
778
779 char *
_g_utf8_rstrip_func(const char * str,StripFunc is_space_func)780 _g_utf8_rstrip_func (const char *str,
781 StripFunc is_space_func)
782 {
783 const char *first_trail_space;
784 RStripState state;
785 const char *p;
786
787 if (str == NULL)
788 return NULL;
789
790 if (is_space_func == NULL)
791 return g_strdup (str);
792
793 first_trail_space = NULL;
794 state = RSTRIP_STATE_ONLY_SPACES;
795 p = str;
796 while (p != NULL) {
797 gunichar ch = g_utf8_get_char (p);
798 gboolean is_space = is_space_func (ch);
799
800 switch (state) {
801 case RSTRIP_STATE_ONLY_SPACES:
802 if (! is_space && (ch != 0))
803 state = RSTRIP_STATE_NON_SPACE;
804 break;
805
806 case RSTRIP_STATE_NON_SPACE:
807 if (is_space) {
808 state = RSTRIP_STATE_TRAILING_SPACE;
809 first_trail_space = p;
810 }
811 break;
812
813 case RSTRIP_STATE_TRAILING_SPACE:
814 if (! is_space && (ch != 0)) {
815 state = RSTRIP_STATE_NON_SPACE;
816 first_trail_space = NULL;
817 }
818 break;
819 }
820
821 if (ch == 0)
822 break;
823
824 p = g_utf8_next_char (p);
825 }
826
827 if (state == RSTRIP_STATE_ONLY_SPACES)
828 return g_strdup ("");
829
830 if (state == RSTRIP_STATE_NON_SPACE)
831 return g_strdup (str);
832
833 g_assert (first_trail_space != NULL);
834
835 return g_strndup (str, first_trail_space - str);
836 }
837
838
839 char *
_g_utf8_rstrip(const char * str)840 _g_utf8_rstrip (const char *str)
841 {
842 return _g_utf8_rstrip_func (str, g_unichar_isspace);
843 }
844
845
846 /* -- _g_utf8_translate -- */
847
848
849 static gboolean
_g_unichar_equal(gconstpointer v1,gconstpointer v2)850 _g_unichar_equal (gconstpointer v1,
851 gconstpointer v2)
852 {
853 return *((const gunichar*) v1) == *((const gunichar*) v2);
854 }
855
856
857 static guint
_g_unichar_hash(gconstpointer v)858 _g_unichar_hash (gconstpointer v)
859 {
860 return (guint) *(const gunichar*) v;
861 }
862
863
864 /* Substitute each occurrence of a character with a string. */
865 char *
_g_utf8_translate(const char * str,...)866 _g_utf8_translate (const char *str,
867 ...)
868 {
869 va_list args;
870 GHashTable *translation;
871 const char *arg;
872 GString *regexp;
873
874 if (str == NULL)
875 return NULL;
876
877 translation = g_hash_table_new_full (_g_unichar_hash, _g_unichar_equal, NULL, g_free);
878 va_start (args, str);
879 while ((arg = va_arg (args, const char *)) != NULL) {
880 gunichar from_ch;
881 const char *to_str;
882
883 from_ch = g_utf8_get_char (arg);
884 to_str = va_arg (args, const char *);
885 if (to_str == NULL)
886 break;
887
888 g_hash_table_insert (translation, &from_ch, g_strdup (to_str));
889 }
890 va_end (args);
891
892 if (g_hash_table_size (translation) == 0) {
893 g_hash_table_unref (translation);
894 return g_strdup (str);
895 }
896
897 regexp = g_string_new ("");
898 while (str != NULL) {
899 gunichar ch = g_utf8_get_char (str);
900 char *replacement;
901
902 if (ch == 0)
903 break;
904
905 replacement = g_hash_table_lookup (translation, &ch);
906 if (replacement != NULL)
907 g_string_append (regexp, replacement);
908 else
909 g_string_append_unichar (regexp, ch);
910
911 str = g_utf8_next_char (str);
912 }
913
914 g_hash_table_unref (translation);
915
916 return g_string_free (regexp, FALSE);
917 }
918
919
920 /* -- _g_utf8_text_escape_xml -- */
921
922
923 typedef enum {
924 XML_ESCAPE_DEFAULT = 1 << 0,
925 XML_ESCAPE_TEXT = 1 << 1
926 } XmlEscFlags;
927
928
929 static char *
_g_utf8_escape_xml_flags(const char * text,XmlEscFlags flags)930 _g_utf8_escape_xml_flags (const char *text,
931 XmlEscFlags flags)
932 {
933 GString *str;
934 gboolean for_text;
935
936 if (text == NULL)
937 return NULL;
938
939 str = g_string_sized_new (strlen (text));
940 for_text = (flags & XML_ESCAPE_TEXT) != 0;
941
942 while (text != NULL) {
943 gunichar ch = g_utf8_get_char (text);
944
945 if (ch == 0)
946 break;
947
948 switch (ch) {
949 case '&':
950 g_string_append (str, "&");
951 break;
952
953 case '<':
954 g_string_append (str, "<");
955 break;
956
957 case '>':
958 g_string_append (str, ">");
959 break;
960
961 case '\'':
962 g_string_append (str, "'");
963 break;
964
965 case '"':
966 g_string_append (str, """);
967 break;
968
969 default:
970 if (for_text && (ch == '\n'))
971 g_string_append (str, "<br>");
972 else if ((ch > 127) || ! g_ascii_isprint ((char) ch))
973 g_string_append_printf (str, "&#%d;", ch);
974 else
975 g_string_append_unichar (str, ch);
976 break;
977 }
978
979 text = g_utf8_next_char (text);
980 }
981
982 return g_string_free (str, FALSE);
983 }
984
985
986 char *
_g_utf8_escape_xml(const char * str)987 _g_utf8_escape_xml (const char *str)
988 {
989 return _g_utf8_escape_xml_flags (str, XML_ESCAPE_DEFAULT);
990 }
991
992
993 char *
_g_utf8_text_escape_xml(const char * str)994 _g_utf8_text_escape_xml (const char *str)
995 {
996 return _g_utf8_escape_xml_flags (str, XML_ESCAPE_TEXT);
997 }
998