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