1 /* vifm
2 * Copyright (C) 2001 Ken Steen.
3 * Copyright (C) 2011 xaizek.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "str.h"
21
22 #include <ctype.h> /* tolower() isspace() */
23 #include <limits.h> /* INT_MAX INT_MIN LONG_MAX LONG_MIN */
24 #include <stdarg.h> /* va_list va_start() va_copy() va_end() */
25 #include <stddef.h> /* NULL size_t wchar_t */
26 #include <stdio.h> /* snprintf() */
27 #include <stdlib.h> /* free() malloc() mbstowcs() memmove() memset() realloc()
28 strtol() wcstombs() */
29 #include <string.h> /* memcpy() strdup() strncmp() strlen() strcmp() strchr()
30 strrchr() strncpy() strcspn() strspn() */
31 #include <wchar.h> /* wint_t vswprintf() */
32 #include <wctype.h> /* iswprint() iswupper() towlower() towupper() */
33
34 #include "../compat/reallocarray.h"
35 #include "macros.h"
36 #include "test_helpers.h"
37 #include "utf8.h"
38 #include "utils.h"
39
40 static int transform_ascii_str(const char str[], int (*f)(int), char buf[],
41 size_t buf_len);
42 static int transform_wide_str(const char str[], wint_t (*f)(wint_t), char buf[],
43 size_t buf_len);
44 TSTATIC void squash_double_commas(char str[]);
45 static char * ellipsis(const char str[], size_t max_width, const char ell[],
46 int right);
47 static size_t copy_substr(char dst[], size_t dst_len, const char src[],
48 char terminator);
49
50 void
chomp(char str[])51 chomp(char str[])
52 {
53 if(str[0] != '\0')
54 {
55 const size_t last_char_pos = strlen(str) - 1;
56 if(str[last_char_pos] == '\n')
57 {
58 str[last_char_pos] = '\0';
59 }
60 }
61 }
62
63 wchar_t *
to_wide(const char s[])64 to_wide(const char s[])
65 {
66 #ifndef _WIN32
67 wchar_t *result = NULL;
68 size_t len;
69
70 len = mbstowcs(NULL, s, 0);
71 if(len != (size_t)-1)
72 {
73 result = reallocarray(NULL, len + 1, sizeof(wchar_t));
74 if(result != NULL)
75 {
76 (void)mbstowcs(result, s, len + 1);
77 }
78 }
79 return result;
80 #else
81 return utf8_to_utf16(s);
82 #endif
83 }
84
85 wchar_t *
to_wide_force(const char s[])86 to_wide_force(const char s[])
87 {
88 wchar_t *w = to_wide(s);
89 wchar_t *p;
90
91 if(w != NULL)
92 {
93 return w;
94 }
95
96 w = reallocarray(NULL, strlen(s) + 1U, sizeof(*w));
97 if(w == NULL)
98 {
99 return NULL;
100 }
101
102 /* There must be broken multi-byte sequence, do our best to convert string to
103 * something meaningful rather than just failing. */
104
105 p = w;
106 while(*s != '\0')
107 {
108 const wchar_t wc = get_first_wchar(s++);
109 if(iswprint(wc))
110 {
111 *p++ = wc;
112 }
113 }
114 *p = L'\0';
115
116 return w;
117 }
118
119 size_t
wide_len(const char s[])120 wide_len(const char s[])
121 {
122 #ifndef _WIN32
123 return mbstowcs(NULL, s, 0);
124 #else
125 return utf8_widen_len(s);
126 #endif
127 }
128
129 wchar_t *
vifm_wcsdup(const wchar_t ws[])130 vifm_wcsdup(const wchar_t ws[])
131 {
132 const size_t len = wcslen(ws) + 1;
133 wchar_t * const result = reallocarray(NULL, len, sizeof(wchar_t));
134 if(result == NULL)
135 {
136 return NULL;
137 }
138 wcsncpy(result, ws, len);
139 return result;
140 }
141
142 int
starts_with(const char str[],const char prefix[])143 starts_with(const char str[], const char prefix[])
144 {
145 const size_t prefix_len = strlen(prefix);
146 return starts_withn(str, prefix, prefix_len);
147 }
148
149 int
skip_prefix(const char ** str,const char prefix[])150 skip_prefix(const char **str, const char prefix[])
151 {
152 const size_t prefix_len = strlen(prefix);
153 if(starts_withn(*str, prefix, prefix_len))
154 {
155 *str += prefix_len;
156 return 1;
157 }
158 return 0;
159 }
160
161 int
cut_suffix(char str[],const char suffix[])162 cut_suffix(char str[], const char suffix[])
163 {
164 if(ends_with(str, suffix))
165 {
166 str[strlen(str) - strlen(suffix)] = '\0';
167 return 1;
168 }
169 return 0;
170 }
171
172 int
starts_withn(const char str[],const char prefix[],size_t prefix_len)173 starts_withn(const char str[], const char prefix[], size_t prefix_len)
174 {
175 return strncmp(str, prefix, prefix_len) == 0;
176 }
177
178 int
ends_with(const char str[],const char suffix[])179 ends_with(const char str[], const char suffix[])
180 {
181 size_t str_len = strlen(str);
182 size_t suffix_len = strlen(suffix);
183
184 if(str_len < suffix_len)
185 {
186 return 0;
187 }
188 return strcmp(suffix, str + str_len - suffix_len) == 0;
189 }
190
191 int
ends_with_case(const char str[],const char suffix[])192 ends_with_case(const char str[], const char suffix[])
193 {
194 size_t str_len = strlen(str);
195 size_t suffix_len = strlen(suffix);
196 if(str_len < suffix_len)
197 {
198 return 0;
199 }
200
201 return (strcasecmp(suffix, str + str_len - suffix_len) == 0);
202 }
203
204 int
surrounded_with(const char str[],char left,char right)205 surrounded_with(const char str[], char left, char right)
206 {
207 const size_t len = strlen(str);
208 return len > 2 && str[0] == left && str[len - 1] == right;
209 }
210
211 char *
to_multibyte(const wchar_t s[])212 to_multibyte(const wchar_t s[])
213 {
214 #ifndef _WIN32
215 const size_t len = wcstombs(NULL, s, 0) + 1;
216 if(len == 0U)
217 {
218 return NULL;
219 }
220
221 char *const result = malloc(len);
222 if(result == NULL)
223 {
224 return NULL;
225 }
226
227 wcstombs(result, s, len);
228 return result;
229 #else
230 return utf8_from_utf16(s);
231 #endif
232 }
233
234 int
str_to_lower(const char str[],char buf[],size_t buf_len)235 str_to_lower(const char str[], char buf[], size_t buf_len)
236 {
237 if(utf8_stro(str) == 0U)
238 {
239 return transform_ascii_str(str, &tolower, buf, buf_len);
240 }
241 else
242 {
243 return transform_wide_str(str, &towlower, buf, buf_len);
244 }
245 }
246
247 int
str_to_upper(const char str[],char buf[],size_t buf_len)248 str_to_upper(const char str[], char buf[], size_t buf_len)
249 {
250 if(utf8_stro(str) == 0U)
251 {
252 return transform_ascii_str(str, &toupper, buf, buf_len);
253 }
254 else
255 {
256 return transform_wide_str(str, &towupper, buf, buf_len);
257 }
258 }
259
260 /* Transforms characters of the string to while they fit in the buffer by
261 * calling specified function on them. Returns zero on success or non-zero if
262 * output buffer is too small. */
263 static int
transform_ascii_str(const char str[],int (* f)(int),char buf[],size_t buf_len)264 transform_ascii_str(const char str[], int (*f)(int), char buf[], size_t buf_len)
265 {
266 int too_small;
267
268 if(buf_len == 0U)
269 {
270 return 1;
271 }
272
273 while(*str != '\0' && buf_len > 1U)
274 {
275 *buf++ = f(*str++);
276 --buf_len;
277 }
278 /* Check comes before the assignment to allow buf == str. */
279 too_small = (*str != '\0');
280 *buf = '\0';
281 return too_small;
282 }
283
284 /* Transforms characters of the string to while they fit in the buffer by
285 * calling specified function on them. Returns zero on success or non-zero if
286 * output buffer is too small. */
287 static int
transform_wide_str(const char str[],wint_t (* f)(wint_t),char buf[],size_t buf_len)288 transform_wide_str(const char str[], wint_t (*f)(wint_t), char buf[],
289 size_t buf_len)
290 {
291 size_t copied;
292 int error;
293 wchar_t *wstring;
294 wchar_t *p;
295 char *narrow;
296
297 wstring = to_wide(str);
298 if(wstring == NULL)
299 {
300 (void)utf8_strcpy(buf, str, buf_len);
301 return 1;
302 }
303
304 p = wstring;
305 while(*p != L'\0')
306 {
307 *p = f(*p);
308 ++p;
309 }
310
311 narrow = to_multibyte(wstring);
312 copied = utf8_strcpy(buf, narrow, buf_len);
313 error = copied == 0U || narrow[copied - 1U] != '\0';
314
315 free(wstring);
316 free(narrow);
317
318 return error;
319 }
320
321 void
wcstolower(wchar_t str[])322 wcstolower(wchar_t str[])
323 {
324 while(*str != L'\0')
325 {
326 *str = towlower(*str);
327 ++str;
328 }
329 }
330
331 void
break_at(char str[],char c)332 break_at(char str[], char c)
333 {
334 char *const p = strchr(str, c);
335 if(p != NULL)
336 {
337 *p = '\0';
338 }
339 }
340
341 void
break_atr(char str[],char c)342 break_atr(char str[], char c)
343 {
344 char *const p = strrchr(str, c);
345 if(p != NULL)
346 {
347 *p = '\0';
348 }
349 }
350
351 char *
skip_whitespace(const char str[])352 skip_whitespace(const char str[])
353 {
354 while(isspace(*str))
355 {
356 ++str;
357 }
358 return (char *)str;
359 }
360
361 int
char_is_one_of(const char * list,char c)362 char_is_one_of(const char *list, char c)
363 {
364 return c != '\0' && strchr(list, c) != NULL;
365 }
366
367 int
stroscmp(const char * s,const char * t)368 stroscmp(const char *s, const char *t)
369 {
370 #ifndef _WIN32
371 return strcmp(s, t);
372 #else
373 return strcasecmp(s, t);
374 #endif
375 }
376
377 int
strnoscmp(const char * s,const char * t,size_t n)378 strnoscmp(const char *s, const char *t, size_t n)
379 {
380 #ifndef _WIN32
381 return strncmp(s, t, n);
382 #else
383 return strncasecmp(s, t, n);
384 #endif
385 }
386
387 int
strossorter(const void * s,const void * t)388 strossorter(const void *s, const void *t)
389 {
390 return stroscmp(*(const char **)s, *(const char **)t);
391 }
392
393 char *
after_last(const char * str,char c)394 after_last(const char *str, char c)
395 {
396 char *result = strrchr(str, c);
397 result = (result == NULL) ? ((char *)str) : (result + 1);
398 return result;
399 }
400
401 char *
until_first(const char str[],char c)402 until_first(const char str[], char c)
403 {
404 const char *result = strchr(str, c);
405 if(result == NULL)
406 {
407 result = str + strlen(str);
408 }
409 return (char *)result;
410 }
411
412 char *
after_first(const char str[],char c)413 after_first(const char str[], char c)
414 {
415 const char *result = strchr(str, c);
416 result = (result == NULL) ? (str + strlen(str)) : (result + 1);
417 return (char *)result;
418 }
419
420 int
replace_string(char ** str,const char with[])421 replace_string(char **str, const char with[])
422 {
423 if(*str != with)
424 {
425 char *new = strdup(with);
426 if(new == NULL)
427 {
428 return 1;
429 }
430 free(*str);
431 *str = new;
432 }
433 return 0;
434 }
435
436 int
update_string(char ** str,const char to[])437 update_string(char **str, const char to[])
438 {
439 if(to == NULL)
440 {
441 free(*str);
442 *str = NULL;
443 return 0;
444 }
445 return replace_string(str, to);
446 }
447
448 int
put_string(char ** str,char with[])449 put_string(char **str, char with[])
450 {
451 if(with == NULL)
452 {
453 return 1;
454 }
455
456 free(*str);
457 *str = with;
458 return 0;
459 }
460
461 char *
strcatch(char str[],char c)462 strcatch(char str[], char c)
463 {
464 const char buf[2] = { c, '\0' };
465 return strcat(str, buf);
466 }
467
468 int
strprepend(char ** str,size_t * len,const char prefix[])469 strprepend(char **str, size_t *len, const char prefix[])
470 {
471 const size_t prefix_len = strlen(prefix);
472 char *const new = realloc(*str, prefix_len + *len + 1);
473 if(new == NULL)
474 {
475 return 1;
476 }
477
478 if(*len == 0)
479 {
480 new[prefix_len] = '\0';
481 }
482 else
483 {
484 memmove(new + prefix_len, new, *len + 1);
485 }
486 memcpy(new, prefix, prefix_len);
487 *str = new;
488 *len += prefix_len;
489
490 return 0;
491 }
492
493 int
strappendch(char ** str,size_t * len,char c)494 strappendch(char **str, size_t *len, char c)
495 {
496 const char suffix[] = {c, '\0'};
497 return strappend(str, len, suffix);
498 }
499
500 int
strappend(char ** str,size_t * len,const char suffix[])501 strappend(char **str, size_t *len, const char suffix[])
502 {
503 const size_t suffix_len = strlen(suffix);
504 char *const new = realloc(*str, *len + suffix_len + 1);
505 if(new == NULL)
506 {
507 return 1;
508 }
509
510 strcpy(new + *len, suffix);
511 *str = new;
512 *len += suffix_len;
513
514 return 0;
515 }
516
517 int
sstrappendch(char str[],size_t * len,size_t size,char c)518 sstrappendch(char str[], size_t *len, size_t size, char c)
519 {
520 const char suffix[] = {c, '\0'};
521 return sstrappend(str, len, size, suffix);
522 }
523
524 int
sstrappend(char str[],size_t * len,size_t size,const char suffix[])525 sstrappend(char str[], size_t *len, size_t size, const char suffix[])
526 {
527 const size_t free_space = size - *len;
528 const size_t suffix_len = snprintf(str + *len, free_space, "%s", suffix);
529 *len += strlen(str + *len);
530 return suffix_len > free_space - 1;
531 }
532
533 void
stralign(char str[],size_t width,char pad,int left_align)534 stralign(char str[], size_t width, char pad, int left_align)
535 {
536 const size_t len = strlen(str);
537 const int pad_width = width - len;
538
539 if(pad_width <= 0)
540 {
541 return;
542 }
543
544 if(left_align)
545 {
546 memset(str + len, pad, pad_width);
547 str[width] = '\0';
548 }
549 else
550 {
551 memmove(str + pad_width, str, len + 1);
552 memset(str, pad, pad_width);
553 }
554 }
555
556 char *
left_ellipsis(const char str[],size_t max_width,const char ell[])557 left_ellipsis(const char str[], size_t max_width, const char ell[])
558 {
559 return ellipsis(str, max_width, ell, 0);
560 }
561
562 char *
right_ellipsis(const char str[],size_t max_width,const char ell[])563 right_ellipsis(const char str[], size_t max_width, const char ell[])
564 {
565 return ellipsis(str, max_width, ell, 1);
566 }
567
568 /* Ensures that str is of width (in character positions) less than or equal to
569 * max_width and is aligned appropriately putting ellipsis on one of the ends if
570 * needed. Returns newly allocated modified string. */
571 static char *
ellipsis(const char str[],size_t max_width,const char ell[],int right)572 ellipsis(const char str[], size_t max_width, const char ell[], int right)
573 {
574 if(max_width == 0U)
575 {
576 /* No room to print anything. */
577 return strdup("");
578 }
579
580 size_t width = utf8_strsw(str);
581 if(width <= max_width)
582 {
583 /* No need to change the string. */
584 return strdup(str);
585 }
586
587 size_t ell_width = utf8_strsw(ell);
588 if(max_width <= ell_width)
589 {
590 /* Insert as many characters of ellipsis as we can. */
591 const int prefix = (int)utf8_nstrsnlen(ell, max_width);
592 return format_str("%.*s", prefix, ell);
593 }
594
595 if(right)
596 {
597 const int prefix = utf8_nstrsnlen(str, max_width - ell_width);
598 return format_str("%.*s%s", prefix, str, ell);
599 }
600
601 while(width > max_width - ell_width)
602 {
603 width -= utf8_chrsw(str);
604 str += utf8_chrw(str);
605 }
606
607 return format_str("%s%s", ell, str);
608 }
609
610 char *
break_in_two(char str[],size_t max,const char separator[])611 break_in_two(char str[], size_t max, const char separator[])
612 {
613 int i;
614 size_t len, size;
615 char *result;
616 char *break_point = strstr(str, separator);
617 if(break_point == NULL)
618 return str;
619
620 const size_t separator_len = strlen(separator);
621 len = utf8_strsw(str) - separator_len;
622 size = strlen(str);
623 size = MAX(size, max);
624 result = malloc(size*4 + 2);
625
626 copy_str(result, break_point - str + 1, str);
627
628 if(len > max)
629 {
630 const int l = utf8_strsw(result) - (len - max);
631 break_point = str + utf8_strsnlen(str, MAX(l, 0));
632 }
633
634 copy_str(result, break_point - str + 1, str);
635 i = break_point - str;
636 while(max > len)
637 {
638 result[i++] = ' ';
639 max--;
640 }
641 result[i] = '\0';
642
643 if(len > max)
644 break_point = strstr(str, separator);
645 strcat(result, break_point + separator_len);
646
647 free(str);
648 return result;
649 }
650
651 int
vifm_swprintf(wchar_t str[],size_t len,const wchar_t format[],...)652 vifm_swprintf(wchar_t str[], size_t len, const wchar_t format[], ...)
653 {
654 int result;
655 va_list ap;
656
657 va_start(ap, format);
658
659 #ifdef BROKEN_SWPRINTF
660 result = vswprintf(str, format, ap);
661 #else
662 result = vswprintf(str, len, format, ap);
663 #endif
664
665 va_end(ap);
666
667 return result;
668 }
669
670 const char *
extract_part(const char str[],const char separators[],char part_buf[])671 extract_part(const char str[], const char separators[], char part_buf[])
672 {
673 const char *end = NULL;
674 str += strspn(str, separators);
675 if(str[0] != '\0')
676 {
677 end = str + strcspn(str, separators);
678 copy_str(part_buf, end - str + 1, str);
679 if(*end != '\0')
680 {
681 ++end;
682 }
683 }
684 return end;
685 }
686
687 char *
skip_char(const char str[],char c)688 skip_char(const char str[], char c)
689 {
690 while(*str == c)
691 {
692 str++;
693 }
694 return (char *)str;
695 }
696
697 char *
escape_chars(const char string[],const char chars[])698 escape_chars(const char string[], const char chars[])
699 {
700 size_t len;
701 size_t i;
702 char *ret, *dup;
703
704 len = strlen(string);
705
706 dup = ret = malloc(len*2 + 2 + 1);
707
708 for(i = 0; i < len; i++)
709 {
710 if(string[i] == '\\' || char_is_one_of(chars, string[i]))
711 {
712 *dup++ = '\\';
713 }
714 *dup++ = string[i];
715 }
716 *dup = '\0';
717 return ret;
718 }
719
720 void
unescape(char s[],int regexp)721 unescape(char s[], int regexp)
722 {
723 char *p;
724
725 p = s;
726 while(s[0] != '\0')
727 {
728 if(s[0] == '\\' && (!regexp || s[1] == '/'))
729 {
730 ++s;
731 }
732 *p++ = s[0];
733 if(s[0] != '\0')
734 {
735 ++s;
736 }
737 }
738 *p = '\0';
739 }
740
741 int
is_null_or_empty(const char string[])742 is_null_or_empty(const char string[])
743 {
744 return string == NULL || string[0] == '\0';
745 }
746
747 char *
format_str(const char format[],...)748 format_str(const char format[], ...)
749 {
750 va_list ap;
751 va_list aq;
752 size_t len;
753 char *result_buf;
754
755 va_start(ap, format);
756 va_copy(aq, ap);
757
758 len = vsnprintf(NULL, 0, format, ap);
759 va_end(ap);
760
761 if((result_buf = malloc(len + 1)) != NULL)
762 {
763 (void)vsprintf(result_buf, format, aq);
764 }
765 va_end(aq);
766
767 return result_buf;
768 }
769
770 const char *
expand_tabulation(const char line[],size_t max,size_t tab_stops,char buf[])771 expand_tabulation(const char line[], size_t max, size_t tab_stops, char buf[])
772 {
773 size_t col = 0;
774 while(col < max && *line != '\0')
775 {
776 const size_t char_width = utf8_chrw(line);
777 const size_t char_screen_width = wcwidth(get_first_wchar(line));
778 if(char_screen_width != (size_t)-1 && col + char_screen_width > max)
779 {
780 break;
781 }
782
783 if(char_width == 1 && *line == '\t')
784 {
785 const size_t space_count = tab_stops - col%tab_stops;
786
787 memset(buf, ' ', space_count);
788 buf += space_count;
789
790 col += space_count;
791 }
792 else
793 {
794 strncpy(buf, line, char_width);
795 buf += char_width;
796
797 col += (char_screen_width == (size_t)-1) ? 1 : char_screen_width;
798 }
799
800 line += char_width;
801 }
802 *buf = '\0';
803 return line;
804 }
805
806 wchar_t
get_first_wchar(const char str[])807 get_first_wchar(const char str[])
808 {
809 #ifndef _WIN32
810 wchar_t wc[2] = {};
811 return (mbstowcs(wc, str, ARRAY_LEN(wc)) == (size_t)-1)
812 ? (unsigned char)str[0]
813 : wc[0];
814 #else
815 return utf8_first_char(str);
816 #endif
817 }
818
819 char *
extend_string(char str[],const char with[],size_t * len)820 extend_string(char str[], const char with[], size_t *len)
821 {
822 size_t with_len = strlen(with);
823 char *new = realloc(str, *len + with_len + 1);
824 if(new == NULL)
825 {
826 return str;
827 }
828
829 strncpy(new + *len, with, with_len + 1);
830 *len += with_len;
831 return new;
832 }
833
834 int
has_uppercase_letters(const char str[])835 has_uppercase_letters(const char str[])
836 {
837 /* TODO: rewrite this without call to to_wide(), use utf8_char_to_wchar(). */
838 int has_uppercase = 0;
839 wchar_t *const wstring = to_wide(str);
840 if(wstring != NULL)
841 {
842 const wchar_t *p = wstring - 1;
843 while(*++p != L'\0')
844 {
845 if(iswupper(*p))
846 {
847 has_uppercase = 1;
848 break;
849 }
850 }
851 free(wstring);
852 }
853 return has_uppercase;
854 }
855
856 size_t
copy_str(char dst[],size_t dst_len,const char src[])857 copy_str(char dst[], size_t dst_len, const char src[])
858 {
859 /* XXX: shouldn't we return "strlen(src)" instead of "0U"? */
860 return (dst == src) ? 0U : copy_substr(dst, dst_len, src, '\0');
861 }
862
863 /* Copies characters from the string pointed to by src and terminated by the
864 * terminator to piece of memory of size dst_len pointed to by dst. Ensures
865 * that copied string ends with null character. Does nothing for zero
866 * dst_len. Returns number of characters written, including terminating null
867 * character. */
868 static size_t
copy_substr(char dst[],size_t dst_len,const char src[],char terminator)869 copy_substr(char dst[], size_t dst_len, const char src[], char terminator)
870 {
871 char *past_end;
872
873 if(dst_len == 0U)
874 {
875 return 0U;
876 }
877
878 past_end = memccpy(dst, src, terminator, dst_len);
879 if(past_end == NULL)
880 {
881 dst[dst_len - 1] = '\0';
882 return dst_len;
883 }
884 else
885 {
886 past_end[-1] = '\0';
887 return past_end - dst;
888 }
889 }
890
891 int
str_to_int(const char str[])892 str_to_int(const char str[])
893 {
894 const long number = strtol(str, NULL, 10);
895 /* Handle overflow and underflow correctly. */
896 return (number == LONG_MAX)
897 ? INT_MAX
898 : ((number == LONG_MIN) ? INT_MIN : number);
899 }
900
901 int
read_int(const char line[],int * i)902 read_int(const char line[], int *i)
903 {
904 char *endptr;
905 const long l = strtol(line, &endptr, 10);
906
907 *i = (l > INT_MAX) ? INT_MAX : ((l < INT_MIN) ? INT_MIN : l);
908
909 return *line != '\0' && *endptr == '\0';
910 }
911
912 void
replace_char(char str[],char from,char to)913 replace_char(char str[], char from, char to)
914 {
915 while(*str != '\0')
916 {
917 if(*str == from)
918 {
919 *str = to;
920 }
921 ++str;
922 }
923 }
924
925 char *
split_and_get(char str[],char sep,char ** state)926 split_and_get(char str[], char sep, char **state)
927 {
928 char *end;
929
930 if(*state != NULL)
931 {
932 if(**state == '\0')
933 {
934 return NULL;
935 }
936
937 str += strlen(str);
938 *str++ = sep;
939 }
940
941 end = strchr(str, sep);
942 while(end != NULL)
943 {
944 if(end != str)
945 {
946 *end = '\0';
947 break;
948 }
949
950 str = end + 1;
951 end = strchr(str, sep);
952 }
953
954 *state = (end == NULL) ? (str + strlen(str)) : (end + 1);
955 return (*str == '\0') ? NULL : str;
956 }
957
958 char *
split_and_get_dc(char str[],char ** state)959 split_and_get_dc(char str[], char **state)
960 {
961 if(*state == NULL)
962 {
963 /* Do nothing on empty input. */
964 if(str[0] == '\0')
965 {
966 return NULL;
967 }
968 }
969 else
970 {
971 /* Check if we reached end of input. */
972 if(**state == '\0')
973 {
974 return NULL;
975 }
976
977 /* Process next item of the list. */
978 str = *state;
979 }
980
981 while(str != NULL)
982 {
983 char *ptr = strchr(str, ',');
984
985 if(ptr != NULL)
986 {
987 while(ptr != NULL && ptr[1] == ',')
988 {
989 ptr = strchr(ptr + 2, ',');
990 }
991 if(ptr != NULL)
992 {
993 *ptr = '\0';
994 ++ptr;
995 }
996 }
997 if(ptr == NULL)
998 {
999 ptr = str + strlen(str);
1000 }
1001
1002 while(isspace(*str) || *str == ',')
1003 {
1004 ++str;
1005 }
1006
1007 if(str[0] != '\0')
1008 {
1009 squash_double_commas(str);
1010 *state = ptr;
1011 break;
1012 }
1013
1014 str = ptr;
1015 }
1016
1017 if(str == NULL)
1018 {
1019 *state = NULL;
1020 }
1021 return str;
1022 }
1023
1024 /* Squashes two consecutive commas into one in place. */
1025 TSTATIC void
squash_double_commas(char str[])1026 squash_double_commas(char str[])
1027 {
1028 char *p = str;
1029 while(*str != '\0')
1030 {
1031 if(str[0] == ',')
1032 {
1033 if(str[1] == ',')
1034 {
1035 *p++ = *str++;
1036 ++str;
1037 continue;
1038 }
1039 }
1040 *p++ = *str++;
1041 }
1042 *p = '\0';
1043 }
1044
1045 int
count_lines(const char text[],int max_width)1046 count_lines(const char text[], int max_width)
1047 {
1048 const char *start, *end;
1049 int nlines;
1050
1051 nlines = 0;
1052
1053 start = text;
1054 end = text - 1;
1055 while((end = strchr(end + 1, '\n')) != NULL)
1056 {
1057 if(max_width == INT_MAX)
1058 {
1059 ++nlines;
1060 }
1061 else
1062 {
1063 nlines += DIV_ROUND_UP(end - start, max_width);
1064 if(start == end)
1065 {
1066 ++nlines;
1067 }
1068 }
1069
1070 start = end + 1;
1071 }
1072 if(*start == '\0' || max_width == INT_MAX)
1073 {
1074 ++nlines;
1075 }
1076 else
1077 {
1078 const size_t screen_width = utf8_strsw(start);
1079 nlines += DIV_ROUND_UP(screen_width, max_width);
1080 }
1081
1082 if(nlines == 0)
1083 {
1084 nlines = 1;
1085 }
1086
1087 return nlines;
1088 }
1089
1090 size_t
chars_in_str(const char s[],char c)1091 chars_in_str(const char s[], char c)
1092 {
1093 size_t char_count = 0;
1094 while(*s != '\0')
1095 {
1096 if(*s++ == c)
1097 {
1098 ++char_count;
1099 }
1100 }
1101 return char_count;
1102 }
1103
1104 char *
double_char(const char str[],char c)1105 double_char(const char str[], char c)
1106 {
1107 char *doubled = malloc(strlen(str) + chars_in_str(str, c) + 1);
1108 char *p = doubled;
1109 while(*str != '\0')
1110 {
1111 if(*str == c)
1112 {
1113 *p++ = c;
1114 }
1115 *p++ = *str++;
1116 }
1117 *p = '\0';
1118 return doubled;
1119 }
1120
1121 #ifdef _WIN32
1122
1123 char *
strcasestr(const char haystack[],const char needle[])1124 strcasestr(const char haystack[], const char needle[])
1125 {
1126 char haystack_us[strlen(haystack) + 1];
1127 char needle_us[strlen(needle) + 1];
1128 const char *s;
1129
1130 strcpy(haystack_us, haystack);
1131 strcpy(needle_us, needle);
1132
1133 s = strstr(haystack_us, needle_us);
1134 return (s == NULL) ? NULL : ((char *)haystack + (s - haystack_us));
1135 }
1136
1137 #endif
1138
1139 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
1140 /* vim: set cinoptions+=t0 filetype=c : */
1141