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