1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2 
3    eel-string.c: String routines to augment <string.h>.
4 
5    Copyright (C) 2000 Eazel, Inc.
6 
7    The Gnome Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11 
12    The Gnome Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16 
17    You should have received a copy of the GNU Library General Public
18    License along with the Gnome Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
20    Boston, MA 02110-1335, USA.
21 
22    Authors: Darin Adler <darin@eazel.com>
23 */
24 
25 #include <config.h>
26 #include "eel-string.h"
27 
28 #include <errno.h>
29 #include <locale.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <eel-glib-extensions.h>
33 
34 #if !defined (EEL_OMIT_SELF_CHECK)
35 #include "eel-lib-self-check-functions.h"
36 #endif
37 
38 char *
eel_str_double_underscores(const char * string)39 eel_str_double_underscores (const char *string)
40 {
41 	int underscores;
42 	const char *p;
43 	char *q;
44 	char *escaped;
45 
46 	if (string == NULL) {
47 		return NULL;
48 	}
49 
50 	underscores = 0;
51 	for (p = string; *p != '\0'; p++) {
52 		underscores += (*p == '_');
53 	}
54 
55 	if (underscores == 0) {
56 		return g_strdup (string);
57 	}
58 
59 	escaped = g_new (char, strlen (string) + underscores + 1);
60 	for (p = string, q = escaped; *p != '\0'; p++, q++) {
61 		/* Add an extra underscore. */
62 		if (*p == '_') {
63 			*q++ = '_';
64 		}
65 		*q = *p;
66 	}
67 	*q = '\0';
68 
69 	return escaped;
70 }
71 
72 char *
eel_str_escape_spaces(const char * string)73 eel_str_escape_spaces (const char *string)
74 {
75     int spaces;
76     const char *p;
77     char *q;
78     char *escaped;
79 
80     if (string == NULL) {
81         return NULL;
82     }
83 
84     spaces = 0;
85     for (p = string; *p != '\0'; p++) {
86         spaces += (*p == ' ');
87     }
88 
89     if (spaces == 0) {
90         return g_strdup (string);
91     }
92 
93     escaped = g_new (char, strlen (string) + spaces + 1);
94     for (p = string, q = escaped; *p != '\0'; p++, q++) {
95         /* Add an extra underscore. */
96         if (*p == ' ') {
97             *q++ = '\\';
98         }
99         *q = *p;
100     }
101     *q = '\0';
102 
103     return escaped;
104 }
105 
106 char *
eel_str_escape_quotes(const char * string)107 eel_str_escape_quotes (const char *string)
108 {
109     int quotes;
110     const char *p;
111     char *q;
112     char *escaped;
113 
114     if (string == NULL) {
115         return NULL;
116     }
117 
118     quotes = 0;
119     for (p = string; *p != '\0'; p++) {
120         quotes += (*p == '\'') || (*p == '\"');
121     }
122 
123     if (quotes == 0) {
124         return g_strdup (string);
125     }
126 
127     escaped = g_new (char, strlen (string) + quotes + 1);
128     for (p = string, q = escaped; *p != '\0'; p++, q++) {
129         if ((*p == '\'') || (*p == '\"')) {
130             *q++ = '\\';
131         }
132         *q = *p;
133     }
134     *q = '\0';
135 
136     return escaped;
137 }
138 
139 char *
eel_str_capitalize(const char * string)140 eel_str_capitalize (const char *string)
141 {
142 	char *capitalized;
143 
144 	if (string == NULL) {
145 		return NULL;
146 	}
147 
148 	capitalized = g_strdup (string);
149 
150 	capitalized[0] = g_ascii_toupper (capitalized[0]);
151 
152 	return capitalized;
153 }
154 
155 /* Note: eel_string_ellipsize_* that use a length in pixels
156  * rather than characters can be found in eel_gdk_extensions.h
157  *
158  * FIXME bugzilla.eazel.com 5089:
159  * we should coordinate the names of eel_string_ellipsize_*
160  * and eel_str_*_truncate so that they match better and reflect
161  * their different behavior.
162  */
163 char *
eel_str_middle_truncate(const char * string,guint truncate_length)164 eel_str_middle_truncate (const char *string,
165 			 guint truncate_length)
166 {
167 	char *truncated;
168 	guint length;
169 	guint num_left_chars;
170 	guint num_right_chars;
171 
172 	if (string == NULL) {
173 		return NULL;
174 	}
175 	const char delimter[] = "...";
176 	const guint delimter_length = strlen (delimter);
177 	const guint min_truncate_length = delimter_length + 2;
178 
179 	/* It doesnt make sense to truncate strings to less than
180 	 * the size of the delimiter plus 2 characters (one on each
181 	 * side)
182 	 */
183 	if (truncate_length < min_truncate_length) {
184 		return g_strdup (string);
185 	}
186 
187 	length = g_utf8_strlen (string, -1);
188 
189 	/* Make sure the string is not already small enough. */
190 	if (length <= truncate_length) {
191 		return g_strdup (string);
192 	}
193 
194 	/* Find the 'middle' where the truncation will occur. */
195 	num_left_chars = (truncate_length - delimter_length) / 2;
196 	num_right_chars = truncate_length - num_left_chars - delimter_length;
197 
198 	truncated = g_new (char, strlen (string) + 1);
199 
200 	g_utf8_strncpy (truncated, string, num_left_chars);
201 	strcat (truncated, delimter);
202 	strcat (truncated, g_utf8_offset_to_pointer  (string, length - num_right_chars));
203 
204 	return truncated;
205 }
206 
207 char *
eel_str_strip_substring_and_after(const char * string,const char * substring)208 eel_str_strip_substring_and_after (const char *string,
209 					const char *substring)
210 {
211 	const char *substring_position;
212 
213 	g_return_val_if_fail (substring != NULL, g_strdup (string));
214 	g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
215 
216 	if (string == NULL) {
217 		return NULL;
218 	}
219 
220 	substring_position = strstr (string, substring);
221 	if (substring_position == NULL) {
222 		return g_strdup (string);
223 	}
224 
225 	return g_strndup (string,
226 			  substring_position - string);
227 }
228 
229 char *
eel_str_replace_substring(const char * string,const char * substring,const char * replacement)230 eel_str_replace_substring (const char *string,
231 				const char *substring,
232 				const char *replacement)
233 {
234 	int substring_length, replacement_length, result_length, remaining_length;
235 	const char *p, *substring_position;
236 	char *result, *result_position;
237 
238 	g_return_val_if_fail (substring != NULL, g_strdup (string));
239 	g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
240 
241 	if (string == NULL) {
242 		return NULL;
243 	}
244 
245 	substring_length = substring ? strlen (substring) : 0;
246 	replacement_length = replacement ? strlen (replacement) : 0;
247 
248 	result_length = strlen (string);
249 	for (p = string; ; p = substring_position + substring_length) {
250 		substring_position = strstr (p, substring);
251 		if (substring_position == NULL) {
252 			break;
253 		}
254 		result_length += replacement_length - substring_length;
255 	}
256 
257 	result = g_malloc (result_length + 1);
258 
259 	result_position = result;
260 	for (p = string; ; p = substring_position + substring_length) {
261 		substring_position = strstr (p, substring);
262 		if (substring_position == NULL) {
263 			remaining_length = strlen (p);
264 			memcpy (result_position, p, remaining_length);
265 			result_position += remaining_length;
266 			break;
267 		}
268 		memcpy (result_position, p, substring_position - p);
269 		result_position += substring_position - p;
270 		memcpy (result_position, replacement, replacement_length);
271 		result_position += replacement_length;
272 	}
273 	g_assert (result_position - result == result_length);
274 	result_position[0] = '\0';
275 
276 	return result;
277 }
278 
279 /**************** Custom printf ***********/
280 
281 typedef struct {
282 	const char *start;
283 	const char *end;
284 	GString *format;
285 	int arg_pos;
286 	int width_pos;
287 	int width_format_index;
288 	int precision_pos;
289 	int precision_format_index;
290 } ConversionInfo;
291 
292 enum {
293 	ARG_TYPE_INVALID,
294 	ARG_TYPE_INT,
295 	ARG_TYPE_LONG,
296 	ARG_TYPE_LONG_LONG,
297 	ARG_TYPE_SIZE,
298 	ARG_TYPE_LONG_DOUBLE,
299 	ARG_TYPE_DOUBLE,
300 	ARG_TYPE_POINTER
301 };
302 
303 typedef int ArgType; /* An int, because custom are < 0 */
304 
305 
306 static const char *
get_position(const char * format,int * i)307 get_position (const char *format, int *i)
308 {
309 	const char *p;
310 
311 	p = format;
312 
313 	if (g_ascii_isdigit (*p)) {
314 		p++;
315 
316 		while (g_ascii_isdigit (*p)) {
317 			p++;
318 		}
319 
320 		if (*p == '$') {
321 			if (i != NULL) {
322 				*i = atoi (format) - 1;
323 			}
324 			return p + 1;
325 		}
326 	}
327 
328 	return format;
329 }
330 
331 static gboolean
is_flag(char c)332 is_flag (char c)
333 {
334 	return strchr ("#0- +'I", c) != NULL;
335 }
336 
337 static gboolean
is_length_modifier(char c)338 is_length_modifier (char c)
339 {
340 	return strchr ("hlLjzt", c) != NULL;
341 }
342 
343 
344 static ArgType
get_arg_type_from_format(EelPrintfHandler * custom_handlers,const char * format,int len)345 get_arg_type_from_format (EelPrintfHandler *custom_handlers,
346 			  const char *format,
347 			  int len)
348 {
349 	int i;
350 	char c;
351 
352 	c = format[len-1];
353 
354 	if (custom_handlers != NULL) {
355 		for (i = 0; custom_handlers[i].character != 0; i++) {
356 			if (custom_handlers[i].character == c) {
357 				return -(i + 1);
358 			}
359 		}
360 	}
361 
362 	switch (c) {
363 	case 'd':
364 	case 'i':
365 	case 'o':
366 	case 'u':
367 	case 'x':
368 	case 'X':
369 		if (g_str_has_prefix (format, "ll")) {
370 			return ARG_TYPE_LONG_LONG;
371 		}
372 		if (g_str_has_prefix (format, "l")) {
373 			return ARG_TYPE_LONG;
374 		}
375 		if (g_str_has_prefix (format, "l")) {
376 			return ARG_TYPE_LONG;
377 		}
378 		if (g_str_has_prefix (format, "z")) {
379 			return ARG_TYPE_SIZE;
380 		}
381 		return ARG_TYPE_INT;
382 	case 'e':
383 	case 'E':
384 	case 'f':
385 	case 'F':
386 	case 'g':
387 	case 'G':
388 	case 'a':
389 	case 'A':
390 		if (g_str_has_prefix (format, "L")) {
391 			return ARG_TYPE_LONG_DOUBLE;
392 		}
393 		return ARG_TYPE_DOUBLE;
394 	case 'c':
395 		return ARG_TYPE_INT;
396 	case 's':
397 	case 'p':
398 	case 'n':
399 		return ARG_TYPE_POINTER;
400         default:
401             break;
402 	}
403 	return ARG_TYPE_INVALID;
404 }
405 
406 static void
skip_argv(va_list * va,ArgType type,EelPrintfHandler * custom_handlers)407 skip_argv (va_list *va,
408 	   ArgType type,
409 	   EelPrintfHandler *custom_handlers)
410 {
411 	if (type < 0) {
412 		custom_handlers[-type - 1].skip (va);
413 		return;
414 	}
415 
416 	switch (type) {
417 	default:
418 	case ARG_TYPE_INVALID:
419 		return;
420 
421 	case ARG_TYPE_INT:
422 		(void) va_arg (*va, int);
423 		break;
424 	case ARG_TYPE_LONG:
425 		(void) va_arg (*va, long int);
426 		break;
427 	case ARG_TYPE_LONG_LONG:
428 		(void) va_arg (*va, long long int);
429 		break;
430 	case ARG_TYPE_SIZE:
431 		(void) va_arg (*va, gsize);
432 		break;
433 	case ARG_TYPE_LONG_DOUBLE:
434 		(void) va_arg (*va, long double);
435 		break;
436 	case ARG_TYPE_DOUBLE:
437 		(void) va_arg (*va, double);
438 		break;
439 	case ARG_TYPE_POINTER:
440 		(void) va_arg (*va, void *);
441 		break;
442 	}
443 }
444 
445 static void
skip_to_arg(va_list * va,ArgType * types,EelPrintfHandler * custom_handlers,int n)446 skip_to_arg (va_list *va,
447 	     ArgType *types,
448 	     EelPrintfHandler *custom_handlers,
449 	     int n)
450 {
451 	int i;
452 	for (i = 0; i < n; i++) {
453 		skip_argv (va, types[i], custom_handlers);
454 	}
455 }
456 
457 char *
eel_strdup_vprintf_with_custom(EelPrintfHandler * custom,const char * format,va_list va_orig)458 eel_strdup_vprintf_with_custom (EelPrintfHandler *custom,
459 				const char *format,
460 				va_list va_orig)
461 {
462 	va_list va;
463 	const char *p;
464 	int num_args, i, j;
465 	ArgType *args;
466 	ArgType type;
467 	ConversionInfo *conversions;
468 	GString *f, *str;
469 	const char *flags, *width, *prec, *mod, *pos;
470 	char *s;
471 
472 	num_args = 0;
473 	for (p = format; *p != 0; p++) {
474 		if (*p == '%') {
475 			p++;
476 			if (*p != '%') {
477 				num_args++;
478 			}
479 		}
480 	}
481 
482 	args = g_new0 (ArgType, num_args * 3 + 1);
483 	conversions = g_new0 (ConversionInfo, num_args);
484 
485 	/* i indexes conversions, j indexes args */
486 	i = 0; j = 0;
487 	p = format;
488 	while (*p != 0) {
489 		if (*p != '%') {
490 			p++;
491 			continue;
492 		}
493 		p++;
494 		if (*p == '%') {
495 			p++;
496 			continue;
497 		}
498 
499 		/* We got a real conversion: */
500 		f = g_string_new ("%");
501 		conversions[i].start = p - 1;
502 
503 		/* First comes the positional arg */
504 
505 		pos = p;
506 		p = get_position (p, NULL);
507 
508 		/* Then flags */
509 		flags = p;
510 		while (is_flag (*p)) {
511 			p++;
512 		}
513 		g_string_append_len (f, flags, p - flags);
514 
515 		/* Field width */
516 
517 		if (*p == '*') {
518 			p++;
519 			p = get_position (p, &j);
520 			args[j] = ARG_TYPE_INT;
521 			conversions[i].width_pos = j++;
522 			conversions[i].width_format_index = f->len;
523 		} else {
524 			conversions[i].width_pos = -1;
525 			conversions[i].width_format_index = -1;
526 			width = p;
527 			while (g_ascii_isdigit (*p)) {
528 				p++;
529 			}
530 			g_string_append_len (f, width, p - width);
531 		}
532 
533 		/* Precision */
534 		conversions[i].precision_pos = -1;
535 		conversions[i].precision_format_index = -1;
536 		if (*p == '.') {
537 			g_string_append_c (f, '.');
538 			p++;
539 
540 			if (*p == '*') {
541 				p++;
542 				p = get_position (p, &j);
543 				args[j] = ARG_TYPE_INT;
544 				conversions[i].precision_pos = j++;
545 				conversions[i].precision_format_index = f->len;
546 			} else {
547 				prec = p;
548 				while (g_ascii_isdigit (*p) || *p == '-') {
549 					p++;
550 				}
551 				g_string_append_len (f, prec, p - prec);
552 			}
553 		}
554 
555 		/* length modifier */
556 
557 		mod = p;
558 
559 		while (is_length_modifier (*p)) {
560 			p++;
561 		}
562 
563 		/* conversion specifier */
564 		if (*p != 0)
565 			p++;
566 
567 		g_string_append_len (f, mod, p - mod);
568 
569 		get_position (pos, &j);
570 		args[j] = get_arg_type_from_format (custom, mod, p - mod);
571 		conversions[i].arg_pos = j++;
572 		conversions[i].format = f;
573 		conversions[i].end = p;
574 
575 		i++;
576 	}
577 
578 	g_assert (i == num_args);
579 
580 	str = g_string_new ("");
581 
582 	p = format;
583 	for (i = 0; i < num_args; i++) {
584 		g_string_append_len (str, p, conversions[i].start - p);
585 		p = conversions[i].end;
586 
587 		if (conversions[i].precision_pos != -1) {
588 			char *val;
589 
590 			G_VA_COPY(va, va_orig);
591 			skip_to_arg (&va, args, custom, conversions[i].precision_pos);
592 			val = g_strdup_vprintf ("%d", va);
593 			va_end (va);
594 
595 			g_string_insert (conversions[i].format,
596 					 conversions[i].precision_format_index,
597 					 val);
598 
599 			g_free (val);
600 		}
601 
602 		if (conversions[i].width_pos != -1) {
603 			char *val;
604 
605 			G_VA_COPY(va, va_orig);
606 			skip_to_arg (&va, args, custom, conversions[i].width_pos);
607 			val = g_strdup_vprintf ("%d", va);
608 			va_end (va);
609 
610 			g_string_insert (conversions[i].format,
611 					 conversions[i].width_format_index,
612 					 val);
613 
614 			g_free (val);
615 		}
616 
617 		G_VA_COPY(va, va_orig);
618 		skip_to_arg (&va, args, custom, conversions[i].arg_pos);
619 		type = args[conversions[i].arg_pos];
620 		if (type < 0) {
621 			s = custom[-type - 1].to_string (conversions[i].format->str, va);
622 			g_string_append (str, s);
623 			g_free (s);
624 		} else{
625 			g_string_append_vprintf (str, conversions[i].format->str, va);
626 		}
627 		va_end (va);
628 
629 		g_string_free (conversions[i].format, TRUE);
630 	}
631 	g_string_append (str, p);
632 
633 	g_free (args);
634 	g_free (conversions);
635 
636 	return g_string_free (str, FALSE);
637 }
638 
639 char *
eel_strdup_printf_with_custom(EelPrintfHandler * handlers,const char * format,...)640 eel_strdup_printf_with_custom (EelPrintfHandler *handlers,
641 			       const char *format,
642 			       ...)
643 {
644 	va_list va;
645 	char *res;
646 
647 	va_start (va, format);
648 	res = eel_strdup_vprintf_with_custom (handlers, format, va);
649 	va_end (va);
650 
651 	return res;
652 }
653 
654 /*********** refcounted strings ****************/
655 
656 G_LOCK_DEFINE_STATIC (unique_ref_strs);
657 static GHashTable *unique_ref_strs = NULL;
658 
659 static eel_ref_str
eel_ref_str_new_internal(const char * string,int start_count)660 eel_ref_str_new_internal (const char *string, int start_count)
661 {
662 	char *res;
663 	volatile gint *count;
664 	gsize len;
665 
666 	len = strlen (string);
667 	res = g_malloc (sizeof (gint) + len + 1);
668 	count = (volatile gint *)res;
669 	*count = start_count;
670 	res += sizeof(gint);
671 	memcpy (res, string, len + 1);
672 	return res;
673 }
674 
675 eel_ref_str
eel_ref_str_new(const char * string)676 eel_ref_str_new (const char *string)
677 {
678 	if (string == NULL) {
679 		return NULL;
680 	}
681 
682 	return eel_ref_str_new_internal (string, 1);
683 }
684 
685 eel_ref_str
eel_ref_str_get_unique(const char * string)686 eel_ref_str_get_unique (const char *string)
687 {
688 	eel_ref_str res;
689 
690 	if (string == NULL) {
691 		return NULL;
692 	}
693 
694 	G_LOCK (unique_ref_strs);
695 	if (unique_ref_strs == NULL) {
696 		unique_ref_strs =
697 			g_hash_table_new (g_str_hash, g_str_equal);
698 	}
699 
700 	res = g_hash_table_lookup (unique_ref_strs, string);
701 	if (res != NULL) {
702 		eel_ref_str_ref (res);
703 	} else {
704 		res = eel_ref_str_new_internal (string, 0x80000001);
705 		g_hash_table_insert (unique_ref_strs, res, res);
706 	}
707 
708 	G_UNLOCK (unique_ref_strs);
709 
710 	return res;
711 }
712 
713 eel_ref_str
eel_ref_str_ref(eel_ref_str str)714 eel_ref_str_ref (eel_ref_str str)
715 {
716 	volatile gint *count;
717 
718 	count = (volatile gint *)((char *)str - sizeof (gint));
719 	g_atomic_int_add (count, 1);
720 
721 	return str;
722 }
723 
724 void
eel_ref_str_unref(eel_ref_str str)725 eel_ref_str_unref (eel_ref_str str)
726 {
727 	volatile gint *count;
728 	gint old_ref;
729 
730 	if (str == NULL)
731 		return;
732 
733 	count = (volatile gint *)((char *)str - sizeof (gint));
734 
735  retry_atomic_decrement:
736 	old_ref = g_atomic_int_get (count);
737 	if (old_ref == 1) {
738 		g_free ((char *)count);
739 	} else if (old_ref == 0x80000001) {
740 		G_LOCK (unique_ref_strs);
741 		/* Need to recheck after taking lock to avoid races with _get_unique() */
742 		if (g_atomic_int_add (count, -1) == 0x80000001) {
743 			g_hash_table_remove (unique_ref_strs, (char *)str);
744 			g_free ((char *)count);
745 		}
746 		G_UNLOCK (unique_ref_strs);
747 	} else if (!g_atomic_int_compare_and_exchange (count,
748 						       old_ref, old_ref - 1)) {
749 		goto retry_atomic_decrement;
750 	}
751 }
752 
753 GList *
eel_strv_to_glist(gchar ** strv)754 eel_strv_to_glist (gchar **strv)
755 {
756     GList *list;
757     gint i;
758 
759     if (strv == NULL) {
760         return NULL;
761     }
762 
763     list = NULL;
764 
765     i = 0;
766 
767     while (strv[i] != NULL) {
768         list = g_list_prepend (list, g_strdup (strv[i]));
769 
770         i++;
771     }
772 
773     list = g_list_reverse (list);
774 
775     return list;
776 }
777 
778 #if !defined (EEL_OMIT_SELF_CHECK)
779 
780 static void
verify_printf(const char * format,...)781 verify_printf (const char *format, ...)
782 {
783 	va_list va;
784 	char *orig, *new;
785 
786 	va_start (va, format);
787 	orig = g_strdup_vprintf (format, va);
788 	va_end (va);
789 
790 	va_start (va, format);
791 	new = eel_strdup_vprintf_with_custom (NULL, format, va);
792 	va_end (va);
793 
794 	EEL_CHECK_STRING_RESULT (new, orig);
795 
796 	g_free (orig);
797 }
798 
799 static char *
custom1_to_string(char * format,va_list va)800 custom1_to_string (char *format, va_list va)
801 {
802 	int i;
803 
804 	i = va_arg (va, int);
805 
806 	return g_strdup_printf ("c1-%d-", i);
807 }
808 
809 static void
custom1_skip(va_list * va)810 custom1_skip (va_list *va)
811 {
812 	(void) va_arg (*va, int);
813 }
814 
815 static char *
custom2_to_string(char * format,va_list va)816 custom2_to_string (char *format, va_list va)
817 {
818 	char *s;
819 
820 	s = va_arg (va, char *);
821 
822 	return g_strdup_printf ("c2-%s-", s);
823 }
824 
825 static void
custom2_skip(va_list * va)826 custom2_skip (va_list *va)
827 {
828 	(void) va_arg (*va, char *);
829 }
830 
831 static EelPrintfHandler handlers[] = {
832 	{ 'N', custom1_to_string, custom1_skip },
833 	{ 'Y', custom2_to_string, custom2_skip },
834 	{ 0 }
835 };
836 
837 static void
verify_custom(const char * orig,const char * format,...)838 verify_custom (const char *orig, const char *format, ...)
839 {
840 	char *new;
841 	va_list va;
842 
843 	va_start (va, format);
844 	new = eel_strdup_vprintf_with_custom (handlers, format, va);
845 	va_end (va);
846 
847 	EEL_CHECK_STRING_RESULT (new, orig);
848 }
849 
850 void
eel_self_check_string(void)851 eel_self_check_string (void)
852 {
853 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores (NULL), NULL);
854 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores (""), "");
855 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_"), "__");
856 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo"), "foo");
857 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar"), "foo__bar");
858 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar_2"), "foo__bar__2");
859 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_foo"), "__foo");
860 	EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_"), "foo__");
861 
862 	EEL_CHECK_STRING_RESULT (eel_str_capitalize (NULL), NULL);
863 	EEL_CHECK_STRING_RESULT (eel_str_capitalize (""), "");
864 	EEL_CHECK_STRING_RESULT (eel_str_capitalize ("foo"), "Foo");
865 	EEL_CHECK_STRING_RESULT (eel_str_capitalize ("Foo"), "Foo");
866 
867 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 0), "foo");
868 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 1), "foo");
869 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 3), "foo");
870 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 4), "foo");
871 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 5), "foo");
872 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 6), "foo");
873 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 7), "foo");
874 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 0), "a_much_longer_foo");
875 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 1), "a_much_longer_foo");
876 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 2), "a_much_longer_foo");
877 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 3), "a_much_longer_foo");
878 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 4), "a_much_longer_foo");
879 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 5), "a...o");
880 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 6), "a...oo");
881 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 7), "a_...oo");
882 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 8), "a_...foo");
883 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 9), "a_m...foo");
884 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 8), "so...ven");
885 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 8), "so...odd");
886 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 9), "som...ven");
887 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 9), "som...odd");
888 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 10), "som...even");
889 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 10), "som..._odd");
890 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 11), "some...even");
891 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 11), "some..._odd");
892 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 12), "some..._even");
893 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 12), "some...g_odd");
894 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 13), "somet..._even");
895 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
896 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 14), "something_even");
897 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
898 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("ääääääääää", 5), "ä...ä");
899 	EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("あぃいぅうぇえぉ", 7), "あぃ...えぉ");
900 
901 	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after (NULL, "bar"), NULL);
902 	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("", "bar"), "");
903 	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo", "bar"), "foo");
904 	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar", "bar"), "foo ");
905 	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar xxx", "bar"), "foo ");
906 	EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("bar", "bar"), "");
907 
908 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", NULL), NULL);
909 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", "bar"), NULL);
910 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", NULL), "bar");
911 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", ""), "");
912 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", "bar"), "");
913 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", ""), "bar");
914 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("xxx", "x", "foo"), "foofoofoo");
915 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("fff", "f", "foo"), "foofoofoo");
916 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "foo", "f"), "fff");
917 	EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "f", ""), "oooooo");
918 
919 	verify_printf ("%.*s", 2, "foo");
920 	verify_printf ("%*.*s", 2, 4, "foo");
921 	verify_printf ("before %5$*1$.*2$s between %6$*3$.*4$d after",
922 		       4, 5, 6, 7, "foo", G_PI);
923 	verify_custom ("c1-42- c2-foo-","%N %Y", 42 ,"foo");
924 	verify_custom ("c1-42- bar c2-foo-","%N %s %Y", 42, "bar" ,"foo");
925 	verify_custom ("c1-42- bar c2-foo-","%3$N %2$s %1$Y","foo", "bar", 42);
926 
927 }
928 
929 #endif /* !EEL_OMIT_SELF_CHECK */
930