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