Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <stdarg.h>
5 : #include <stdint.h>
6 : #include <stdio.h>
7 : #include <stdlib.h>
8 : #include <string.h>
9 :
10 : #include "alloc-util.h"
11 : #include "escape.h"
12 : #include "fileio.h"
13 : #include "gunicode.h"
14 : #include "locale-util.h"
15 : #include "macro.h"
16 : #include "memory-util.h"
17 : #include "string-util.h"
18 : #include "terminal-util.h"
19 : #include "utf8.h"
20 : #include "util.h"
21 :
22 1216862 : int strcmp_ptr(const char *a, const char *b) {
23 :
24 : /* Like strcmp(), but tries to make sense of NULL pointers */
25 1216862 : if (a && b)
26 1179727 : return strcmp(a, b);
27 :
28 37135 : if (!a && b)
29 27808 : return -1;
30 :
31 9327 : if (a && !b)
32 9090 : return 1;
33 :
34 237 : return 0;
35 : }
36 :
37 1272217 : char* endswith(const char *s, const char *postfix) {
38 : size_t sl, pl;
39 :
40 1272217 : assert(s);
41 1272217 : assert(postfix);
42 :
43 1272217 : sl = strlen(s);
44 1272217 : pl = strlen(postfix);
45 :
46 1272217 : if (pl == 0)
47 2 : return (char*) s + sl;
48 :
49 1272215 : if (sl < pl)
50 93 : return NULL;
51 :
52 1272122 : if (memcmp(s + sl - pl, postfix, pl) != 0)
53 1243381 : return NULL;
54 :
55 28741 : return (char*) s + sl - pl;
56 : }
57 :
58 1730 : char* endswith_no_case(const char *s, const char *postfix) {
59 : size_t sl, pl;
60 :
61 1730 : assert(s);
62 1730 : assert(postfix);
63 :
64 1730 : sl = strlen(s);
65 1730 : pl = strlen(postfix);
66 :
67 1730 : if (pl == 0)
68 2 : return (char*) s + sl;
69 :
70 1728 : if (sl < pl)
71 126 : return NULL;
72 :
73 1602 : if (strcasecmp(s + sl - pl, postfix) != 0)
74 954 : return NULL;
75 :
76 648 : return (char*) s + sl - pl;
77 : }
78 :
79 7228 : char* first_word(const char *s, const char *word) {
80 : size_t sl, wl;
81 : const char *p;
82 :
83 7228 : assert(s);
84 7228 : assert(word);
85 :
86 : /* Checks if the string starts with the specified word, either
87 : * followed by NUL or by whitespace. Returns a pointer to the
88 : * NUL or the first character after the whitespace. */
89 :
90 7228 : sl = strlen(s);
91 7228 : wl = strlen(word);
92 :
93 7228 : if (sl < wl)
94 664 : return NULL;
95 :
96 6564 : if (wl == 0)
97 1 : return (char*) s;
98 :
99 6563 : if (memcmp(s, word, wl) != 0)
100 6535 : return NULL;
101 :
102 28 : p = s + wl;
103 28 : if (*p == 0)
104 1 : return (char*) p;
105 :
106 27 : if (!strchr(WHITESPACE, *p))
107 1 : return NULL;
108 :
109 26 : p += strspn(p, WHITESPACE);
110 26 : return (char*) p;
111 : }
112 :
113 32 : static size_t strcspn_escaped(const char *s, const char *reject) {
114 32 : bool escaped = false;
115 : int n;
116 :
117 336 : for (n=0; s[n]; n++) {
118 330 : if (escaped)
119 0 : escaped = false;
120 330 : else if (s[n] == '\\')
121 0 : escaped = true;
122 330 : else if (strchr(reject, s[n]))
123 26 : break;
124 : }
125 :
126 : /* if s ends in \, return index of previous char */
127 32 : return n - escaped;
128 : }
129 :
130 : /* Split a string into words. */
131 2270 : const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) {
132 : const char *current;
133 :
134 2270 : current = *state;
135 :
136 2270 : if (!*current) {
137 596 : assert(**state == '\0');
138 596 : return NULL;
139 : }
140 :
141 1674 : current += strspn(current, separator);
142 1674 : if (!*current) {
143 15 : *state = current;
144 15 : return NULL;
145 : }
146 :
147 1659 : if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) {
148 32 : char quotechars[2] = {*current, '\0'};
149 :
150 32 : *l = strcspn_escaped(current + 1, quotechars);
151 32 : if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
152 26 : (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
153 : /* right quote missing or garbage at the end */
154 10 : if (flags & SPLIT_RELAX) {
155 8 : *state = current + *l + 1 + (current[*l + 1] != '\0');
156 10 : return current + 1;
157 : }
158 2 : *state = current;
159 2 : return NULL;
160 : }
161 22 : *state = current++ + *l + 2;
162 1627 : } else if (flags & SPLIT_QUOTES) {
163 0 : *l = strcspn_escaped(current, separator);
164 0 : if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
165 : /* unfinished escape */
166 0 : *state = current;
167 0 : return NULL;
168 : }
169 0 : *state = current + *l;
170 : } else {
171 1627 : *l = strcspn(current, separator);
172 1627 : *state = current + *l;
173 : }
174 :
175 1649 : return current;
176 : }
177 :
178 147 : char *strnappend(const char *s, const char *suffix, size_t b) {
179 : size_t a;
180 : char *r;
181 :
182 147 : if (!s && !suffix)
183 0 : return strdup("");
184 :
185 147 : if (!s)
186 65 : return strndup(suffix, b);
187 :
188 82 : if (!suffix)
189 0 : return strdup(s);
190 :
191 82 : assert(s);
192 82 : assert(suffix);
193 :
194 82 : a = strlen(s);
195 82 : if (b > ((size_t) -1) - a)
196 0 : return NULL;
197 :
198 82 : r = new(char, a+b+1);
199 82 : if (!r)
200 0 : return NULL;
201 :
202 82 : memcpy(r, s, a);
203 82 : memcpy(r+a, suffix, b);
204 82 : r[a+b] = 0;
205 :
206 82 : return r;
207 : }
208 :
209 23027 : char *strjoin_real(const char *x, ...) {
210 : va_list ap;
211 : size_t l;
212 : char *r, *p;
213 :
214 23027 : va_start(ap, x);
215 :
216 23027 : if (x) {
217 23007 : l = strlen(x);
218 :
219 42490 : for (;;) {
220 : const char *t;
221 : size_t n;
222 :
223 65497 : t = va_arg(ap, const char *);
224 65497 : if (!t)
225 23007 : break;
226 :
227 42490 : n = strlen(t);
228 42490 : if (n > ((size_t) -1) - l) {
229 0 : va_end(ap);
230 0 : return NULL;
231 : }
232 :
233 42490 : l += n;
234 : }
235 : } else
236 20 : l = 0;
237 :
238 23027 : va_end(ap);
239 :
240 23027 : r = new(char, l+1);
241 23027 : if (!r)
242 0 : return NULL;
243 :
244 23027 : if (x) {
245 23007 : p = stpcpy(r, x);
246 :
247 23007 : va_start(ap, x);
248 :
249 42490 : for (;;) {
250 : const char *t;
251 :
252 65497 : t = va_arg(ap, const char *);
253 65497 : if (!t)
254 23007 : break;
255 :
256 42490 : p = stpcpy(p, t);
257 : }
258 :
259 23007 : va_end(ap);
260 : } else
261 20 : r[0] = 0;
262 :
263 23027 : return r;
264 : }
265 :
266 21256 : char *strstrip(char *s) {
267 21256 : if (!s)
268 0 : return NULL;
269 :
270 : /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */
271 :
272 21256 : return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
273 : }
274 :
275 9 : char *delete_chars(char *s, const char *bad) {
276 : char *f, *t;
277 :
278 : /* Drops all specified bad characters, regardless where in the string */
279 :
280 9 : if (!s)
281 0 : return NULL;
282 :
283 9 : if (!bad)
284 0 : bad = WHITESPACE;
285 :
286 131 : for (f = s, t = s; *f; f++) {
287 122 : if (strchr(bad, *f))
288 67 : continue;
289 :
290 55 : *(t++) = *f;
291 : }
292 :
293 9 : *t = 0;
294 :
295 9 : return s;
296 : }
297 :
298 23882 : char *delete_trailing_chars(char *s, const char *bad) {
299 23882 : char *p, *c = s;
300 :
301 : /* Drops all specified bad characters, at the end of the string */
302 :
303 23882 : if (!s)
304 0 : return NULL;
305 :
306 23882 : if (!bad)
307 0 : bad = WHITESPACE;
308 :
309 4746121 : for (p = s; *p; p++)
310 4722239 : if (!strchr(bad, *p))
311 4695690 : c = p + 1;
312 :
313 23882 : *c = 0;
314 :
315 23882 : return s;
316 : }
317 :
318 2 : char *truncate_nl(char *s) {
319 2 : assert(s);
320 :
321 2 : s[strcspn(s, NEWLINE)] = 0;
322 2 : return s;
323 : }
324 :
325 27632 : char ascii_tolower(char x) {
326 :
327 27632 : if (x >= 'A' && x <= 'Z')
328 408 : return x - 'A' + 'a';
329 :
330 27224 : return x;
331 : }
332 :
333 0 : char ascii_toupper(char x) {
334 :
335 0 : if (x >= 'a' && x <= 'z')
336 0 : return x - 'a' + 'A';
337 :
338 0 : return x;
339 : }
340 :
341 18 : char *ascii_strlower(char *t) {
342 : char *p;
343 :
344 18 : assert(t);
345 :
346 110 : for (p = t; *p; p++)
347 92 : *p = ascii_tolower(*p);
348 :
349 18 : return t;
350 : }
351 :
352 0 : char *ascii_strupper(char *t) {
353 : char *p;
354 :
355 0 : assert(t);
356 :
357 0 : for (p = t; *p; p++)
358 0 : *p = ascii_toupper(*p);
359 :
360 0 : return t;
361 : }
362 :
363 2068 : char *ascii_strlower_n(char *t, size_t n) {
364 : size_t i;
365 :
366 2068 : if (n <= 0)
367 11 : return t;
368 :
369 14260 : for (i = 0; i < n; i++)
370 12203 : t[i] = ascii_tolower(t[i]);
371 :
372 2057 : return t;
373 : }
374 :
375 1406 : int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
376 :
377 8202 : for (; n > 0; a++, b++, n--) {
378 : int x, y;
379 :
380 6911 : x = (int) (uint8_t) ascii_tolower(*a);
381 6911 : y = (int) (uint8_t) ascii_tolower(*b);
382 :
383 6911 : if (x != y)
384 115 : return x - y;
385 : }
386 :
387 1291 : return 0;
388 : }
389 :
390 285 : int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
391 : int r;
392 :
393 285 : r = ascii_strcasecmp_n(a, b, MIN(n, m));
394 285 : if (r != 0)
395 68 : return r;
396 :
397 217 : return CMP(n, m);
398 : }
399 :
400 11 : bool chars_intersect(const char *a, const char *b) {
401 : const char *p;
402 :
403 : /* Returns true if any of the chars in a are in b. */
404 56 : for (p = a; *p; p++)
405 51 : if (strchr(b, *p))
406 6 : return true;
407 :
408 5 : return false;
409 : }
410 :
411 100 : bool string_has_cc(const char *p, const char *ok) {
412 : const char *t;
413 :
414 100 : assert(p);
415 :
416 : /*
417 : * Check if a string contains control characters. If 'ok' is
418 : * non-NULL it may be a string containing additional CCs to be
419 : * considered OK.
420 : */
421 :
422 877 : for (t = p; *t; t++) {
423 791 : if (ok && strchr(ok, *t))
424 16 : continue;
425 :
426 775 : if (*t > 0 && *t < ' ')
427 8 : return true;
428 :
429 767 : if (*t == 127)
430 6 : return true;
431 : }
432 :
433 86 : return false;
434 : }
435 :
436 799 : static int write_ellipsis(char *buf, bool unicode) {
437 799 : if (unicode || is_locale_utf8()) {
438 799 : buf[0] = 0xe2; /* tri-dot ellipsis: … */
439 799 : buf[1] = 0x80;
440 799 : buf[2] = 0xa6;
441 : } else {
442 0 : buf[0] = '.';
443 0 : buf[1] = '.';
444 0 : buf[2] = '.';
445 : }
446 :
447 799 : return 3;
448 : }
449 :
450 1027 : static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
451 : size_t x, need_space, suffix_len;
452 : char *t;
453 :
454 1027 : assert(s);
455 1027 : assert(percent <= 100);
456 1027 : assert(new_length != (size_t) -1);
457 :
458 1027 : if (old_length <= new_length)
459 750 : return strndup(s, old_length);
460 :
461 : /* Special case short ellipsations */
462 277 : switch (new_length) {
463 :
464 0 : case 0:
465 0 : return strdup("");
466 :
467 59 : case 1:
468 59 : if (is_locale_utf8())
469 59 : return strdup("…");
470 : else
471 0 : return strdup(".");
472 :
473 35 : case 2:
474 35 : if (!is_locale_utf8())
475 0 : return strdup("..");
476 :
477 35 : break;
478 :
479 183 : default:
480 183 : break;
481 : }
482 :
483 : /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
484 : * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
485 : * either for the UTF-8 encoded character or for three ASCII characters. */
486 218 : need_space = is_locale_utf8() ? 1 : 3;
487 :
488 218 : t = new(char, new_length+3);
489 218 : if (!t)
490 0 : return NULL;
491 :
492 218 : assert(new_length >= need_space);
493 :
494 218 : x = ((new_length - need_space) * percent + 50) / 100;
495 218 : assert(x <= new_length - need_space);
496 :
497 218 : memcpy(t, s, x);
498 218 : write_ellipsis(t + x, false);
499 218 : suffix_len = new_length - x - need_space;
500 218 : memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
501 218 : *(t + x + 3 + suffix_len) = '\0';
502 :
503 218 : return t;
504 : }
505 :
506 3423 : char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
507 : size_t x, k, len, len2;
508 : const char *i, *j;
509 : char *e;
510 : int r;
511 :
512 : /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
513 : * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
514 : * strings.
515 : *
516 : * Ellipsation is done in a locale-dependent way:
517 : * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
518 : * 2. Otherwise, a unicode ellipsis is used ("…")
519 : *
520 : * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
521 : * the current locale is UTF-8.
522 : */
523 :
524 3423 : assert(s);
525 3423 : assert(percent <= 100);
526 :
527 3423 : if (new_length == (size_t) -1)
528 0 : return strndup(s, old_length);
529 :
530 3423 : if (new_length == 0)
531 164 : return strdup("");
532 :
533 : /* If no multibyte characters use ascii_ellipsize_mem for speed */
534 3259 : if (ascii_is_valid_n(s, old_length))
535 1027 : return ascii_ellipsize_mem(s, old_length, new_length, percent);
536 :
537 2232 : x = ((new_length - 1) * percent) / 100;
538 2232 : assert(x <= new_length - 1);
539 :
540 2232 : k = 0;
541 10939 : for (i = s; i < s + old_length; i = utf8_next_char(i)) {
542 : char32_t c;
543 : int w;
544 :
545 9753 : r = utf8_encoded_to_unichar(i, &c);
546 9753 : if (r < 0)
547 0 : return NULL;
548 :
549 9753 : w = unichar_iswide(c) ? 2 : 1;
550 9753 : if (k + w <= x)
551 8707 : k += w;
552 : else
553 1046 : break;
554 : }
555 :
556 5064 : for (j = s + old_length; j > i; ) {
557 : char32_t c;
558 : int w;
559 : const char *jj;
560 :
561 3389 : jj = utf8_prev_char(j);
562 3389 : r = utf8_encoded_to_unichar(jj, &c);
563 3389 : if (r < 0)
564 0 : return NULL;
565 :
566 3389 : w = unichar_iswide(c) ? 2 : 1;
567 3389 : if (k + w <= new_length) {
568 2832 : k += w;
569 2832 : j = jj;
570 : } else
571 557 : break;
572 : }
573 2232 : assert(i <= j);
574 :
575 : /* we don't actually need to ellipsize */
576 2232 : if (i == j)
577 1675 : return memdup_suffix0(s, old_length);
578 :
579 : /* make space for ellipsis, if possible */
580 557 : if (j < s + old_length)
581 515 : j = utf8_next_char(j);
582 42 : else if (i > s)
583 17 : i = utf8_prev_char(i);
584 :
585 557 : len = i - s;
586 557 : len2 = s + old_length - j;
587 557 : e = new(char, len + 3 + len2 + 1);
588 557 : if (!e)
589 0 : return NULL;
590 :
591 : /*
592 : printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
593 : old_length, new_length, x, len, len2, k);
594 : */
595 :
596 557 : memcpy(e, s, len);
597 557 : write_ellipsis(e + len, true);
598 557 : memcpy(e + len + 3, j, len2);
599 557 : *(e + len + 3 + len2) = '\0';
600 :
601 557 : return e;
602 : }
603 :
604 1191 : char *cellescape(char *buf, size_t len, const char *s) {
605 : /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
606 : * characters are copied as they are, everything else is escaped. The result
607 : * is different then if escaping and ellipsization was performed in two
608 : * separate steps, because each sequence is either stored in full or skipped.
609 : *
610 : * This function should be used for logging about strings which expected to
611 : * be plain ASCII in a safe way.
612 : *
613 : * An ellipsis will be used if s is too long. It was always placed at the
614 : * very end.
615 : */
616 :
617 1191 : size_t i = 0, last_char_width[4] = {}, k = 0, j;
618 :
619 1191 : assert(len > 0); /* at least a terminating NUL */
620 :
621 11799 : for (;;) {
622 : char four[4];
623 : int w;
624 :
625 12990 : if (*s == 0) /* terminating NUL detected? then we are done! */
626 1155 : goto done;
627 :
628 11835 : w = cescape_char(*s, four);
629 11835 : if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
630 : * ellipsize at the previous location */
631 36 : break;
632 :
633 : /* OK, there was space, let's add this escaped character to the buffer */
634 11799 : memcpy(buf + i, four, w);
635 11799 : i += w;
636 :
637 : /* And remember its width in the ring buffer */
638 11799 : last_char_width[k] = w;
639 11799 : k = (k + 1) % 4;
640 :
641 11799 : s++;
642 : }
643 :
644 : /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
645 : * characters ideally, but the buffer is shorter than that in the first place take what we can get */
646 81 : for (j = 0; j < ELEMENTSOF(last_char_width); j++) {
647 :
648 81 : if (i + 4 <= len) /* nice, we reached our space goal */
649 24 : break;
650 :
651 57 : k = k == 0 ? 3 : k - 1;
652 57 : if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
653 12 : break;
654 :
655 45 : assert(i >= last_char_width[k]);
656 45 : i -= last_char_width[k];
657 : }
658 :
659 36 : if (i + 4 <= len) /* yay, enough space */
660 24 : i += write_ellipsis(buf + i, false);
661 12 : else if (i + 3 <= len) { /* only space for ".." */
662 4 : buf[i++] = '.';
663 4 : buf[i++] = '.';
664 8 : } else if (i + 2 <= len) /* only space for a single "." */
665 4 : buf[i++] = '.';
666 : else
667 4 : assert(i + 1 <= len);
668 :
669 4 : done:
670 1191 : buf[i] = '\0';
671 1191 : return buf;
672 : }
673 :
674 8 : char* strshorten(char *s, size_t l) {
675 8 : assert(s);
676 :
677 8 : if (strnlen(s, l+1) > l)
678 4 : s[l] = 0;
679 :
680 8 : return s;
681 : }
682 :
683 68 : char *strreplace(const char *text, const char *old_string, const char *new_string) {
684 68 : size_t l, old_len, new_len, allocated = 0;
685 68 : char *t, *ret = NULL;
686 : const char *f;
687 :
688 68 : assert(old_string);
689 68 : assert(new_string);
690 :
691 68 : if (!text)
692 1 : return NULL;
693 :
694 67 : old_len = strlen(old_string);
695 67 : new_len = strlen(new_string);
696 :
697 67 : l = strlen(text);
698 67 : if (!GREEDY_REALLOC(ret, allocated, l+1))
699 0 : return NULL;
700 :
701 67 : f = text;
702 67 : t = ret;
703 1624 : while (*f) {
704 : size_t d, nl;
705 :
706 1557 : if (!startswith(f, old_string)) {
707 1534 : *(t++) = *(f++);
708 1534 : continue;
709 : }
710 :
711 23 : d = t - ret;
712 23 : nl = l - old_len + new_len;
713 :
714 23 : if (!GREEDY_REALLOC(ret, allocated, nl + 1))
715 0 : return mfree(ret);
716 :
717 23 : l = nl;
718 23 : t = ret + d;
719 :
720 23 : t = stpcpy(t, new_string);
721 23 : f += old_len;
722 : }
723 :
724 67 : *t = 0;
725 67 : return ret;
726 : }
727 :
728 10 : static void advance_offsets(
729 : ssize_t diff,
730 : size_t offsets[2], /* note: we can't use [static 2] here, since this may be NULL */
731 : size_t shift[static 2],
732 : size_t size) {
733 :
734 10 : if (!offsets)
735 10 : return;
736 :
737 0 : assert(shift);
738 :
739 0 : if ((size_t) diff < offsets[0])
740 0 : shift[0] += size;
741 0 : if ((size_t) diff < offsets[1])
742 0 : shift[1] += size;
743 : }
744 :
745 5 : char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
746 5 : const char *i, *begin = NULL;
747 : enum {
748 : STATE_OTHER,
749 : STATE_ESCAPE,
750 : STATE_CSI,
751 : STATE_CSO,
752 5 : } state = STATE_OTHER;
753 5 : char *obuf = NULL;
754 5 : size_t osz = 0, isz, shift[2] = {};
755 : FILE *f;
756 :
757 5 : assert(ibuf);
758 5 : assert(*ibuf);
759 :
760 : /* This does three things:
761 : *
762 : * 1. Replaces TABs by 8 spaces
763 : * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
764 : * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
765 : *
766 : * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
767 : * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
768 : * supposed to suppress the most basic formatting noise, but nothing else.
769 : *
770 : * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */
771 :
772 5 : isz = _isz ? *_isz : strlen(*ibuf);
773 :
774 : /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we
775 : * created f here and it doesn't leave our scope. */
776 5 : f = open_memstream_unlocked(&obuf, &osz);
777 5 : if (!f)
778 0 : return NULL;
779 :
780 170 : for (i = *ibuf; i < *ibuf + isz + 1; i++) {
781 :
782 165 : switch (state) {
783 :
784 111 : case STATE_OTHER:
785 111 : if (i >= *ibuf + isz) /* EOT */
786 5 : break;
787 106 : else if (*i == '\x1B')
788 13 : state = STATE_ESCAPE;
789 93 : else if (*i == '\t') {
790 5 : fputs(" ", f);
791 5 : advance_offsets(i - *ibuf, highlight, shift, 7);
792 : } else
793 88 : fputc(*i, f);
794 :
795 106 : break;
796 :
797 13 : case STATE_ESCAPE:
798 13 : if (i >= *ibuf + isz) { /* EOT */
799 0 : fputc('\x1B', f);
800 0 : advance_offsets(i - *ibuf, highlight, shift, 1);
801 0 : break;
802 13 : } else if (*i == '[') { /* ANSI CSI */
803 13 : state = STATE_CSI;
804 13 : begin = i + 1;
805 0 : } else if (*i == ']') { /* ANSI CSO */
806 0 : state = STATE_CSO;
807 0 : begin = i + 1;
808 : } else {
809 0 : fputc('\x1B', f);
810 0 : fputc(*i, f);
811 0 : advance_offsets(i - *ibuf, highlight, shift, 1);
812 0 : state = STATE_OTHER;
813 : }
814 :
815 13 : break;
816 :
817 41 : case STATE_CSI:
818 :
819 41 : if (i >= *ibuf + isz || /* EOT … */
820 41 : !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
821 5 : fputc('\x1B', f);
822 5 : fputc('[', f);
823 5 : advance_offsets(i - *ibuf, highlight, shift, 2);
824 5 : state = STATE_OTHER;
825 5 : i = begin-1;
826 36 : } else if (*i == 'm')
827 8 : state = STATE_OTHER;
828 :
829 41 : break;
830 :
831 0 : case STATE_CSO:
832 :
833 0 : if (i >= *ibuf + isz || /* EOT … */
834 0 : (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
835 0 : fputc('\x1B', f);
836 0 : fputc(']', f);
837 0 : advance_offsets(i - *ibuf, highlight, shift, 2);
838 0 : state = STATE_OTHER;
839 0 : i = begin-1;
840 0 : } else if (*i == '\a')
841 0 : state = STATE_OTHER;
842 :
843 0 : break;
844 : }
845 165 : }
846 :
847 5 : if (fflush_and_check(f) < 0) {
848 0 : fclose(f);
849 0 : return mfree(obuf);
850 : }
851 :
852 5 : fclose(f);
853 :
854 5 : free_and_replace(*ibuf, obuf);
855 :
856 5 : if (_isz)
857 0 : *_isz = osz;
858 :
859 5 : if (highlight) {
860 0 : highlight[0] += shift[0];
861 0 : highlight[1] += shift[1];
862 : }
863 :
864 5 : return *ibuf;
865 : }
866 :
867 81231 : char *strextend_with_separator(char **x, const char *separator, ...) {
868 : bool need_separator;
869 : size_t f, l, l_separator;
870 : char *r, *p;
871 : va_list ap;
872 :
873 81231 : assert(x);
874 :
875 81231 : l = f = strlen_ptr(*x);
876 :
877 81231 : need_separator = !isempty(*x);
878 81231 : l_separator = strlen_ptr(separator);
879 :
880 81231 : va_start(ap, separator);
881 81294 : for (;;) {
882 : const char *t;
883 : size_t n;
884 :
885 162525 : t = va_arg(ap, const char *);
886 162525 : if (!t)
887 81231 : break;
888 :
889 81294 : n = strlen(t);
890 :
891 81294 : if (need_separator)
892 80312 : n += l_separator;
893 :
894 81294 : if (n > ((size_t) -1) - l) {
895 0 : va_end(ap);
896 0 : return NULL;
897 : }
898 :
899 81294 : l += n;
900 81294 : need_separator = true;
901 : }
902 81231 : va_end(ap);
903 :
904 81231 : need_separator = !isempty(*x);
905 :
906 81231 : r = realloc(*x, l+1);
907 81231 : if (!r)
908 0 : return NULL;
909 :
910 81231 : p = r + f;
911 :
912 81231 : va_start(ap, separator);
913 81294 : for (;;) {
914 : const char *t;
915 :
916 162525 : t = va_arg(ap, const char *);
917 162525 : if (!t)
918 81231 : break;
919 :
920 81294 : if (need_separator && separator)
921 144 : p = stpcpy(p, separator);
922 :
923 81294 : p = stpcpy(p, t);
924 :
925 81294 : need_separator = true;
926 : }
927 81231 : va_end(ap);
928 :
929 81231 : assert(p == r + l);
930 :
931 81231 : *p = 0;
932 81231 : *x = r;
933 :
934 81231 : return r + l;
935 : }
936 :
937 129 : char *strrep(const char *s, unsigned n) {
938 : size_t l;
939 : char *r, *p;
940 : unsigned i;
941 :
942 129 : assert(s);
943 :
944 129 : l = strlen(s);
945 129 : p = r = malloc(l * n + 1);
946 129 : if (!r)
947 0 : return NULL;
948 :
949 620 : for (i = 0; i < n; i++)
950 491 : p = stpcpy(p, s);
951 :
952 129 : *p = 0;
953 129 : return r;
954 : }
955 :
956 48 : int split_pair(const char *s, const char *sep, char **l, char **r) {
957 : char *x, *a, *b;
958 :
959 48 : assert(s);
960 48 : assert(sep);
961 48 : assert(l);
962 48 : assert(r);
963 :
964 48 : if (isempty(sep))
965 2 : return -EINVAL;
966 :
967 46 : x = strstr(s, sep);
968 46 : if (!x)
969 1 : return -EINVAL;
970 :
971 45 : a = strndup(s, x - s);
972 45 : if (!a)
973 0 : return -ENOMEM;
974 :
975 45 : b = strdup(x + strlen(sep));
976 45 : if (!b) {
977 0 : free(a);
978 0 : return -ENOMEM;
979 : }
980 :
981 45 : *l = a;
982 45 : *r = b;
983 :
984 45 : return 0;
985 : }
986 :
987 5480 : int free_and_strdup(char **p, const char *s) {
988 : char *t;
989 :
990 5480 : assert(p);
991 :
992 : /* Replaces a string pointer with a strdup()ed new string,
993 : * possibly freeing the old one. */
994 :
995 5480 : if (streq_ptr(*p, s))
996 98 : return 0;
997 :
998 5382 : if (s) {
999 5378 : t = strdup(s);
1000 5378 : if (!t)
1001 0 : return -ENOMEM;
1002 : } else
1003 4 : t = NULL;
1004 :
1005 5382 : free(*p);
1006 5382 : *p = t;
1007 :
1008 5382 : return 1;
1009 : }
1010 :
1011 241 : int free_and_strndup(char **p, const char *s, size_t l) {
1012 : char *t;
1013 :
1014 241 : assert(p);
1015 241 : assert(s || l == 0);
1016 :
1017 : /* Replaces a string pointer with a strndup()ed new string,
1018 : * freeing the old one. */
1019 :
1020 241 : if (!*p && !s)
1021 0 : return 0;
1022 :
1023 241 : if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
1024 87 : return 0;
1025 :
1026 154 : if (s) {
1027 153 : t = strndup(s, l);
1028 153 : if (!t)
1029 0 : return -ENOMEM;
1030 : } else
1031 1 : t = NULL;
1032 :
1033 154 : free_and_replace(*p, t);
1034 154 : return 1;
1035 : }
1036 :
1037 1493 : bool string_is_safe(const char *p) {
1038 : const char *t;
1039 :
1040 1493 : if (!p)
1041 0 : return false;
1042 :
1043 16077 : for (t = p; *t; t++) {
1044 14598 : if (*t > 0 && *t < ' ') /* no control characters */
1045 9 : return false;
1046 :
1047 14589 : if (strchr(QUOTES "\\\x7f", *t))
1048 5 : return false;
1049 : }
1050 :
1051 1479 : return true;
1052 : }
|