Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
eel-string.c:204:3 | clang-analyzer | Null pointer passed as an argument to a 'nonnull' parameter |
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., 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, 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 *
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 *
73 eel_str_capitalize (const char *string)
74 {
75 char *capitalized;
76
77 if (string == NULL) {
78 return NULL;
79 }
80
81 capitalized = g_strdup (string);
82
83 capitalized[0] = g_ascii_toupper (capitalized[0]);
84
85 return capitalized;
86 }
87
88 /* Note: eel_string_ellipsize_* that use a length in pixels
89 * rather than characters can be found in eel_gdk_extensions.h
90 *
91 * FIXME bugzilla.eazel.com 5089:
92 * we should coordinate the names of eel_string_ellipsize_*
93 * and eel_str_*_truncate so that they match better and reflect
94 * their different behavior.
95 */
96 char *
97 eel_str_middle_truncate (const char *string,
98 guint truncate_length)
99 {
100 char *truncated;
101 guint length;
102 guint num_left_chars;
103 guint num_right_chars;
104
105 const char delimter[] = "...";
106 const guint delimter_length = strlen (delimter);
107 const guint min_truncate_length = delimter_length + 2;
108
109 if (string == NULL) {
110 return NULL;
111 }
112
113 /* It doesnt make sense to truncate strings to less than
114 * the size of the delimiter plus 2 characters (one on each
115 * side)
116 */
117 if (truncate_length < min_truncate_length) {
118 return g_strdup (string);
119 }
120
121 length = g_utf8_strlen (string, -1);
122
123 /* Make sure the string is not already small enough. */
124 if (length <= truncate_length) {
125 return g_strdup (string);
126 }
127
128 /* Find the 'middle' where the truncation will occur. */
129 num_left_chars = (truncate_length - delimter_length) / 2;
130 num_right_chars = truncate_length - num_left_chars - delimter_length;
131
132 truncated = g_new (char, strlen (string) + 1);
133
134 g_utf8_strncpy (truncated, string, num_left_chars);
135 strcat (truncated, delimter);
136 strcat (truncated, g_utf8_offset_to_pointer (string, length - num_right_chars));
137
138 return truncated;
139 }
140
141 char *
142 eel_str_strip_substring_and_after (const char *string,
143 const char *substring)
144 {
145 const char *substring_position;
146
147 g_return_val_if_fail (substring != NULL, g_strdup (string));
148 g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
149
150 if (string == NULL) {
151 return NULL;
152 }
153
154 substring_position = strstr (string, substring);
155 if (substring_position == NULL) {
156 return g_strdup (string);
157 }
158
159 return g_strndup (string,
160 substring_position - string);
161 }
162
163 char *
164 eel_str_replace_substring (const char *string,
165 const char *substring,
166 const char *replacement)
167 {
168 int substring_length, replacement_length, result_length, remaining_length;
169 const char *p, *substring_position;
170 char *result, *result_position;
171
172 g_return_val_if_fail (substring != NULL, g_strdup (string));
173 g_return_val_if_fail (substring[0] != '\0', g_strdup (string));
174
175 if (string == NULL) {
176 return NULL;
177 }
178
179 substring_length = substring ? strlen (substring) : 0;
180 replacement_length = replacement ? strlen (replacement) : 0;
181
182 result_length = strlen (string);
183 for (p = string; ; p = substring_position + substring_length) {
184 substring_position = strstr (p, substring);
185 if (substring_position == NULL) {
186 break;
187 }
188 result_length += replacement_length - substring_length;
189 }
190
191 result = g_malloc (result_length + 1);
192
193 result_position = result;
194 for (p = string; ; p = substring_position + substring_length) {
195 substring_position = strstr (p, substring);
196 if (substring_position == NULL) {
197 remaining_length = strlen (p);
198 memcpy (result_position, p, remaining_length);
199 result_position += remaining_length;
200 break;
201 }
202 memcpy (result_position, p, substring_position - p);
203 result_position += substring_position - p;
204 memcpy (result_position, replacement, replacement_length);
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
205 result_position += replacement_length;
206 }
207 g_assert (result_position - result == result_length);
208 result_position[0] = '\0';
209
210 return result;
211 }
212
213 /**************** Custom printf ***********/
214
215 typedef struct {
216 const char *start;
217 const char *end;
218 GString *format;
219 int arg_pos;
220 int width_pos;
221 int width_format_index;
222 int precision_pos;
223 int precision_format_index;
224 } ConversionInfo;
225
226 enum {
227 ARG_TYPE_INVALID,
228 ARG_TYPE_INT,
229 ARG_TYPE_LONG,
230 ARG_TYPE_LONG_LONG,
231 ARG_TYPE_SIZE,
232 ARG_TYPE_LONG_DOUBLE,
233 ARG_TYPE_DOUBLE,
234 ARG_TYPE_POINTER
235 };
236
237 typedef int ArgType; /* An int, because custom are < 0 */
238
239
240 static const char *
241 get_position (const char *format, int *i)
242 {
243 const char *p;
244
245 p = format;
246
247 if (g_ascii_isdigit (*p)) {
248 p++;
249
250 while (g_ascii_isdigit (*p)) {
251 p++;
252 }
253
254 if (*p == '$') {
255 if (i != NULL) {
256 *i = atoi (format) - 1;
257 }
258 return p + 1;
259 }
260 }
261
262 return format;
263 }
264
265 static gboolean
266 is_flag (char c)
267 {
268 return strchr ("#0- +'I", c) != NULL;
269 }
270
271 static gboolean
272 is_length_modifier (char c)
273 {
274 return strchr ("hlLjzt", c) != NULL;
275 }
276
277
278 static ArgType
279 get_arg_type_from_format (EelPrintfHandler *custom_handlers,
280 const char *format,
281 int len)
282 {
283 int i;
284 char c;
285
286 c = format[len-1];
287
288 if (custom_handlers != NULL) {
289 for (i = 0; custom_handlers[i].character != 0; i++) {
290 if (custom_handlers[i].character == c) {
291 return -(i + 1);
292 }
293 }
294 }
295
296 switch (c) {
297 case 'd':
298 case 'i':
299 case 'o':
300 case 'u':
301 case 'x':
302 case 'X':
303 if (g_str_has_prefix (format, "ll")) {
304 return ARG_TYPE_LONG_LONG;
305 }
306 if (g_str_has_prefix (format, "l")) {
307 return ARG_TYPE_LONG;
308 }
309 if (g_str_has_prefix (format, "l")) {
310 return ARG_TYPE_LONG;
311 }
312 if (g_str_has_prefix (format, "z")) {
313 return ARG_TYPE_SIZE;
314 }
315 return ARG_TYPE_INT;
316 case 'e':
317 case 'E':
318 case 'f':
319 case 'F':
320 case 'g':
321 case 'G':
322 case 'a':
323 case 'A':
324 if (g_str_has_prefix (format, "L")) {
325 return ARG_TYPE_LONG_DOUBLE;
326 }
327 return ARG_TYPE_DOUBLE;
328 case 'c':
329 return ARG_TYPE_INT;
330 case 's':
331 case 'p':
332 case 'n':
333 return ARG_TYPE_POINTER;
334 }
335 return ARG_TYPE_INVALID;
336 }
337
338 static void
339 skip_argv (va_list *va,
340 ArgType type,
341 EelPrintfHandler *custom_handlers)
342 {
343 if (type < 0) {
344 custom_handlers[-type - 1].skip (va);
345 return;
346 }
347
348 switch (type) {
349 default:
350 case ARG_TYPE_INVALID:
351 return;
352
353 case ARG_TYPE_INT:
354 (void) va_arg (*va, int);
355 break;
356 case ARG_TYPE_LONG:
357 (void) va_arg (*va, long int);
358 break;
359 case ARG_TYPE_LONG_LONG:
360 (void) va_arg (*va, long long int);
361 break;
362 case ARG_TYPE_SIZE:
363 (void) va_arg (*va, gsize);
364 break;
365 case ARG_TYPE_LONG_DOUBLE:
366 (void) va_arg (*va, long double);
367 break;
368 case ARG_TYPE_DOUBLE:
369 (void) va_arg (*va, double);
370 break;
371 case ARG_TYPE_POINTER:
372 (void) va_arg (*va, void *);
373 break;
374 }
375 }
376
377 static void
378 skip_to_arg (va_list *va,
379 ArgType *types,
380 EelPrintfHandler *custom_handlers,
381 int n)
382 {
383 int i;
384 for (i = 0; i < n; i++) {
385 skip_argv (va, types[i], custom_handlers);
386 }
387 }
388
389 char *
390 eel_strdup_vprintf_with_custom (EelPrintfHandler *custom,
391 const char *format,
392 va_list va_orig)
393 {
394 va_list va;
395 const char *p;
396 int num_args, i, j;
397 ArgType *args;
398 ArgType type;
399 ConversionInfo *conversions;
400 GString *f, *str;
401 const char *flags, *width, *prec, *mod, *pos;
402 char *s;
403
404 num_args = 0;
405 for (p = format; *p != 0; p++) {
406 if (*p == '%') {
407 p++;
408 if (*p != '%') {
409 num_args++;
410 }
411 }
412 }
413
414 args = g_new0 (ArgType, num_args * 3 + 1);
415 conversions = g_new0 (ConversionInfo, num_args);
416
417 /* i indexes conversions, j indexes args */
418 i = 0; j = 0;
419 p = format;
420 while (*p != 0) {
421 if (*p != '%') {
422 p++;
423 continue;
424 }
425 p++;
426 if (*p == '%') {
427 p++;
428 continue;
429 }
430
431 /* We got a real conversion: */
432 f = g_string_new ("%");
433 conversions[i].start = p - 1;
434
435 /* First comes the positional arg */
436
437 pos = p;
438 p = get_position (p, NULL);
439
440 /* Then flags */
441 flags = p;
442 while (is_flag (*p)) {
443 p++;
444 }
445 g_string_append_len (f, flags, p - flags);
446
447 /* Field width */
448
449 if (*p == '*') {
450 p++;
451 p = get_position (p, &j);
452 args[j] = ARG_TYPE_INT;
453 conversions[i].width_pos = j++;
454 conversions[i].width_format_index = f->len;
455 } else {
456 conversions[i].width_pos = -1;
457 conversions[i].width_format_index = -1;
458 width = p;
459 while (g_ascii_isdigit (*p)) {
460 p++;
461 }
462 g_string_append_len (f, width, p - width);
463 }
464
465 /* Precision */
466 conversions[i].precision_pos = -1;
467 conversions[i].precision_format_index = -1;
468 if (*p == '.') {
469 g_string_append_c (f, '.');
470 p++;
471
472 if (*p == '*') {
473 p++;
474 p = get_position (p, &j);
475 args[j] = ARG_TYPE_INT;
476 conversions[i].precision_pos = j++;
477 conversions[i].precision_format_index = f->len;
478 } else {
479 prec = p;
480 while (g_ascii_isdigit (*p) || *p == '-') {
481 p++;
482 }
483 g_string_append_len (f, prec, p - prec);
484 }
485 }
486
487 /* length modifier */
488
489 mod = p;
490
491 while (is_length_modifier (*p)) {
492 p++;
493 }
494
495 /* conversion specifier */
496 if (*p != 0)
497 p++;
498
499 g_string_append_len (f, mod, p - mod);
500
501 get_position (pos, &j);
502 args[j] = get_arg_type_from_format (custom, mod, p - mod);
503 conversions[i].arg_pos = j++;
504 conversions[i].format = f;
505 conversions[i].end = p;
506
507 i++;
508 }
509
510 g_assert (i == num_args);
511
512 str = g_string_new ("");
513
514 p = format;
515 for (i = 0; i < num_args; i++) {
516 g_string_append_len (str, p, conversions[i].start - p);
517 p = conversions[i].end;
518
519 if (conversions[i].precision_pos != -1) {
520 char *val;
521
522 G_VA_COPY(va, va_orig);
523 skip_to_arg (&va, args, custom, conversions[i].precision_pos);
524 val = g_strdup_vprintf ("%d", va);
525 va_end (va);
526
527 g_string_insert (conversions[i].format,
528 conversions[i].precision_format_index,
529 val);
530
531 g_free (val);
532 }
533
534 if (conversions[i].width_pos != -1) {
535 char *val;
536
537 G_VA_COPY(va, va_orig);
538 skip_to_arg (&va, args, custom, conversions[i].width_pos);
539 val = g_strdup_vprintf ("%d", va);
540 va_end (va);
541
542 g_string_insert (conversions[i].format,
543 conversions[i].width_format_index,
544 val);
545
546 g_free (val);
547 }
548
549 G_VA_COPY(va, va_orig);
550 skip_to_arg (&va, args, custom, conversions[i].arg_pos);
551 type = args[conversions[i].arg_pos];
552 if (type < 0) {
553 s = custom[-type - 1].to_string (conversions[i].format->str, va);
554 g_string_append (str, s);
555 g_free (s);
556 } else{
557 g_string_append_vprintf (str, conversions[i].format->str, va);
558 }
559 va_end (va);
560
561 g_string_free (conversions[i].format, TRUE);
562 }
563 g_string_append (str, p);
564
565 g_free (args);
566 g_free (conversions);
567
568 return g_string_free (str, FALSE);
569 }
570
571 char *
572 eel_strdup_printf_with_custom (EelPrintfHandler *handlers,
573 const char *format,
574 ...)
575 {
576 va_list va;
577 char *res;
578
579 va_start (va, format);
580 res = eel_strdup_vprintf_with_custom (handlers, format, va);
581 va_end (va);
582
583 return res;
584 }
585
586 /*********** refcounted strings ****************/
587
588 G_LOCK_DEFINE_STATIC (unique_ref_strs);
589 static GHashTable *unique_ref_strs = NULL;
590
591 static eel_ref_str
592 eel_ref_str_new_internal (const char *string, int start_count)
593 {
594 char *res;
595 volatile gint *count;
596 gsize len;
597
598 len = strlen (string);
599 res = g_malloc (sizeof (gint) + len + 1);
600 count = (volatile gint *)res;
601 *count = start_count;
602 res += sizeof(gint);
603 memcpy (res, string, len + 1);
604 return res;
605 }
606
607 eel_ref_str
608 eel_ref_str_new (const char *string)
609 {
610 if (string == NULL) {
611 return NULL;
612 }
613
614 return eel_ref_str_new_internal (string, 1);
615 }
616
617 eel_ref_str
618 eel_ref_str_get_unique (const char *string)
619 {
620 eel_ref_str res;
621
622 if (string == NULL) {
623 return NULL;
624 }
625
626 G_LOCK (unique_ref_strs);
627 if (unique_ref_strs == NULL) {
628 unique_ref_strs =
629 g_hash_table_new (g_str_hash, g_str_equal);
630 }
631
632 res = g_hash_table_lookup (unique_ref_strs, string);
633 if (res != NULL) {
634 eel_ref_str_ref (res);
635 } else {
636 res = eel_ref_str_new_internal (string, 0x80000001);
637 g_hash_table_insert (unique_ref_strs, res, res);
638 }
639
640 G_UNLOCK (unique_ref_strs);
641
642 return res;
643 }
644
645 eel_ref_str
646 eel_ref_str_ref (eel_ref_str str)
647 {
648 volatile gint *count;
649
650 count = (volatile gint *)((char *)str - sizeof (gint));
651 g_atomic_int_add (count, 1);
652
653 return str;
654 }
655
656 void
657 eel_ref_str_unref (eel_ref_str str)
658 {
659 volatile gint *count;
660 gint old_ref;
661
662 if (str == NULL)
663 return;
664
665 count = (volatile gint *)((char *)str - sizeof (gint));
666
667 retry_atomic_decrement:
668 old_ref = g_atomic_int_get (count);
669 if (old_ref == 1) {
670 g_free ((char *)count);
671 } else if (old_ref == 0x80000001) {
672 G_LOCK (unique_ref_strs);
673 /* Need to recheck after taking lock to avoid races with _get_unique() */
674 if (g_atomic_int_add (count, -1) == 0x80000001) {
675 g_hash_table_remove (unique_ref_strs, (char *)str);
676 g_free ((char *)count);
677 }
678 G_UNLOCK (unique_ref_strs);
679 } else if (!g_atomic_int_compare_and_exchange (count,
680 old_ref, old_ref - 1)) {
681 goto retry_atomic_decrement;
682 }
683 }
684
685
686 #if !defined (EEL_OMIT_SELF_CHECK)
687
688 static void
689 verify_printf (const char *format, ...)
690 {
691 va_list va;
692 char *orig, *new;
693
694 va_start (va, format);
695 orig = g_strdup_vprintf (format, va);
696 va_end (va);
697
698 va_start (va, format);
699 new = eel_strdup_vprintf_with_custom (NULL, format, va);
700 va_end (va);
701
702 EEL_CHECK_STRING_RESULT (new, orig);
703
704 g_free (orig);
705 }
706
707 static char *
708 custom1_to_string (char *format, va_list va)
709 {
710 int i;
711
712 i = va_arg (va, int);
713
714 return g_strdup_printf ("c1-%d-", i);
715 }
716
717 static void
718 custom1_skip (va_list *va)
719 {
720 (void) va_arg (*va, int);
721 }
722
723 static char *
724 custom2_to_string (char *format, va_list va)
725 {
726 char *s;
727
728 s = va_arg (va, char *);
729
730 return g_strdup_printf ("c2-%s-", s);
731 }
732
733 static void
734 custom2_skip (va_list *va)
735 {
736 (void) va_arg (*va, char *);
737 }
738
739 static EelPrintfHandler handlers[] = {
740 { 'N', custom1_to_string, custom1_skip },
741 { 'Y', custom2_to_string, custom2_skip },
742 { 0 }
743 };
744
745 static void
746 verify_custom (const char *orig, const char *format, ...)
747 {
748 char *new;
749 va_list va;
750
751 va_start (va, format);
752 new = eel_strdup_vprintf_with_custom (handlers, format, va);
753 va_end (va);
754
755 EEL_CHECK_STRING_RESULT (new, orig);
756 }
757
758 void
759 eel_self_check_string (void)
760 {
761 EEL_CHECK_STRING_RESULT (eel_str_double_underscores (NULL), NULL);
762 EEL_CHECK_STRING_RESULT (eel_str_double_underscores (""), "");
763 EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_"), "__");
764 EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo"), "foo");
765 EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar"), "foo__bar");
766 EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_bar_2"), "foo__bar__2");
767 EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("_foo"), "__foo");
768 EEL_CHECK_STRING_RESULT (eel_str_double_underscores ("foo_"), "foo__");
769
770 EEL_CHECK_STRING_RESULT (eel_str_capitalize (NULL), NULL);
771 EEL_CHECK_STRING_RESULT (eel_str_capitalize (""), "");
772 EEL_CHECK_STRING_RESULT (eel_str_capitalize ("foo"), "Foo");
773 EEL_CHECK_STRING_RESULT (eel_str_capitalize ("Foo"), "Foo");
774
775 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 0), "foo");
776 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 1), "foo");
777 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 3), "foo");
778 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 4), "foo");
779 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 5), "foo");
780 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 6), "foo");
781 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("foo", 7), "foo");
782 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 0), "a_much_longer_foo");
783 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 1), "a_much_longer_foo");
784 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 2), "a_much_longer_foo");
785 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 3), "a_much_longer_foo");
786 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 4), "a_much_longer_foo");
787 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 5), "a...o");
788 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 6), "a...oo");
789 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 7), "a_...oo");
790 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 8), "a_...foo");
791 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("a_much_longer_foo", 9), "a_m...foo");
792 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 8), "so...ven");
793 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 8), "so...odd");
794 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 9), "som...ven");
795 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 9), "som...odd");
796 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 10), "som...even");
797 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 10), "som..._odd");
798 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 11), "some...even");
799 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 11), "some..._odd");
800 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 12), "some..._even");
801 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 12), "some...g_odd");
802 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 13), "somet..._even");
803 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
804 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_even", 14), "something_even");
805 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("something_odd", 13), "something_odd");
806 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("ääääääääää", 5), "ä...ä");
807 EEL_CHECK_STRING_RESULT (eel_str_middle_truncate ("あぃいぅうぇえぉ", 7), "あぃ...えぉ");
808
809 EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after (NULL, "bar"), NULL);
810 EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("", "bar"), "");
811 EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo", "bar"), "foo");
812 EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar", "bar"), "foo ");
813 EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("foo bar xxx", "bar"), "foo ");
814 EEL_CHECK_STRING_RESULT (eel_str_strip_substring_and_after ("bar", "bar"), "");
815
816 EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", NULL), NULL);
817 EEL_CHECK_STRING_RESULT (eel_str_replace_substring (NULL, "foo", "bar"), NULL);
818 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", NULL), "bar");
819 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", ""), "");
820 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("", "foo", "bar"), "");
821 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("bar", "foo", ""), "bar");
822 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("xxx", "x", "foo"), "foofoofoo");
823 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("fff", "f", "foo"), "foofoofoo");
824 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "foo", "f"), "fff");
825 EEL_CHECK_STRING_RESULT (eel_str_replace_substring ("foofoofoo", "f", ""), "oooooo");
826
827 verify_printf ("%.*s", 2, "foo");
828 verify_printf ("%*.*s", 2, 4, "foo");
829 verify_printf ("before %5$*1$.*2$s between %6$*3$.*4$d after",
830 4, 5, 6, 7, "foo", G_PI);
831 verify_custom ("c1-42- c2-foo-","%N %Y", 42 ,"foo");
832 verify_custom ("c1-42- bar c2-foo-","%N %s %Y", 42, "bar" ,"foo");
833 verify_custom ("c1-42- bar c2-foo-","%3$N %2$s %1$Y","foo", "bar", 42);
834
835 }
836
837 #endif /* !EEL_OMIT_SELF_CHECK */