]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/string-util.c
Merge pull request #1759 from davidstrauss/master
[thirdparty/systemd.git] / src / basic / string-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "alloc-util.h"
23 #include "gunicode.h"
24 #include "utf8.h"
25 #include "util.h"
26 #include "string-util.h"
27
28 int strcmp_ptr(const char *a, const char *b) {
29
30 /* Like strcmp(), but tries to make sense of NULL pointers */
31 if (a && b)
32 return strcmp(a, b);
33
34 if (!a && b)
35 return -1;
36
37 if (a && !b)
38 return 1;
39
40 return 0;
41 }
42
43 char* endswith(const char *s, const char *postfix) {
44 size_t sl, pl;
45
46 assert(s);
47 assert(postfix);
48
49 sl = strlen(s);
50 pl = strlen(postfix);
51
52 if (pl == 0)
53 return (char*) s + sl;
54
55 if (sl < pl)
56 return NULL;
57
58 if (memcmp(s + sl - pl, postfix, pl) != 0)
59 return NULL;
60
61 return (char*) s + sl - pl;
62 }
63
64 char* endswith_no_case(const char *s, const char *postfix) {
65 size_t sl, pl;
66
67 assert(s);
68 assert(postfix);
69
70 sl = strlen(s);
71 pl = strlen(postfix);
72
73 if (pl == 0)
74 return (char*) s + sl;
75
76 if (sl < pl)
77 return NULL;
78
79 if (strcasecmp(s + sl - pl, postfix) != 0)
80 return NULL;
81
82 return (char*) s + sl - pl;
83 }
84
85 char* first_word(const char *s, const char *word) {
86 size_t sl, wl;
87 const char *p;
88
89 assert(s);
90 assert(word);
91
92 /* Checks if the string starts with the specified word, either
93 * followed by NUL or by whitespace. Returns a pointer to the
94 * NUL or the first character after the whitespace. */
95
96 sl = strlen(s);
97 wl = strlen(word);
98
99 if (sl < wl)
100 return NULL;
101
102 if (wl == 0)
103 return (char*) s;
104
105 if (memcmp(s, word, wl) != 0)
106 return NULL;
107
108 p = s + wl;
109 if (*p == 0)
110 return (char*) p;
111
112 if (!strchr(WHITESPACE, *p))
113 return NULL;
114
115 p += strspn(p, WHITESPACE);
116 return (char*) p;
117 }
118
119 static size_t strcspn_escaped(const char *s, const char *reject) {
120 bool escaped = false;
121 int n;
122
123 for (n=0; s[n]; n++) {
124 if (escaped)
125 escaped = false;
126 else if (s[n] == '\\')
127 escaped = true;
128 else if (strchr(reject, s[n]))
129 break;
130 }
131
132 /* if s ends in \, return index of previous char */
133 return n - escaped;
134 }
135
136 /* Split a string into words. */
137 const char* split(const char **state, size_t *l, const char *separator, bool quoted) {
138 const char *current;
139
140 current = *state;
141
142 if (!*current) {
143 assert(**state == '\0');
144 return NULL;
145 }
146
147 current += strspn(current, separator);
148 if (!*current) {
149 *state = current;
150 return NULL;
151 }
152
153 if (quoted && strchr("\'\"", *current)) {
154 char quotechars[2] = {*current, '\0'};
155
156 *l = strcspn_escaped(current + 1, quotechars);
157 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
158 (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
159 /* right quote missing or garbage at the end */
160 *state = current;
161 return NULL;
162 }
163 *state = current++ + *l + 2;
164 } else if (quoted) {
165 *l = strcspn_escaped(current, separator);
166 if (current[*l] && !strchr(separator, current[*l])) {
167 /* unfinished escape */
168 *state = current;
169 return NULL;
170 }
171 *state = current + *l;
172 } else {
173 *l = strcspn(current, separator);
174 *state = current + *l;
175 }
176
177 return current;
178 }
179
180 char *strnappend(const char *s, const char *suffix, size_t b) {
181 size_t a;
182 char *r;
183
184 if (!s && !suffix)
185 return strdup("");
186
187 if (!s)
188 return strndup(suffix, b);
189
190 if (!suffix)
191 return strdup(s);
192
193 assert(s);
194 assert(suffix);
195
196 a = strlen(s);
197 if (b > ((size_t) -1) - a)
198 return NULL;
199
200 r = new(char, a+b+1);
201 if (!r)
202 return NULL;
203
204 memcpy(r, s, a);
205 memcpy(r+a, suffix, b);
206 r[a+b] = 0;
207
208 return r;
209 }
210
211 char *strappend(const char *s, const char *suffix) {
212 return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
213 }
214
215 char *strjoin(const char *x, ...) {
216 va_list ap;
217 size_t l;
218 char *r, *p;
219
220 va_start(ap, x);
221
222 if (x) {
223 l = strlen(x);
224
225 for (;;) {
226 const char *t;
227 size_t n;
228
229 t = va_arg(ap, const char *);
230 if (!t)
231 break;
232
233 n = strlen(t);
234 if (n > ((size_t) -1) - l) {
235 va_end(ap);
236 return NULL;
237 }
238
239 l += n;
240 }
241 } else
242 l = 0;
243
244 va_end(ap);
245
246 r = new(char, l+1);
247 if (!r)
248 return NULL;
249
250 if (x) {
251 p = stpcpy(r, x);
252
253 va_start(ap, x);
254
255 for (;;) {
256 const char *t;
257
258 t = va_arg(ap, const char *);
259 if (!t)
260 break;
261
262 p = stpcpy(p, t);
263 }
264
265 va_end(ap);
266 } else
267 r[0] = 0;
268
269 return r;
270 }
271
272 char *strstrip(char *s) {
273 char *e;
274
275 /* Drops trailing whitespace. Modifies the string in
276 * place. Returns pointer to first non-space character */
277
278 s += strspn(s, WHITESPACE);
279
280 for (e = strchr(s, 0); e > s; e --)
281 if (!strchr(WHITESPACE, e[-1]))
282 break;
283
284 *e = 0;
285
286 return s;
287 }
288
289 char *delete_chars(char *s, const char *bad) {
290 char *f, *t;
291
292 /* Drops all whitespace, regardless where in the string */
293
294 for (f = s, t = s; *f; f++) {
295 if (strchr(bad, *f))
296 continue;
297
298 *(t++) = *f;
299 }
300
301 *t = 0;
302
303 return s;
304 }
305
306 char *truncate_nl(char *s) {
307 assert(s);
308
309 s[strcspn(s, NEWLINE)] = 0;
310 return s;
311 }
312
313 char *ascii_strlower(char *t) {
314 char *p;
315
316 assert(t);
317
318 for (p = t; *p; p++)
319 if (*p >= 'A' && *p <= 'Z')
320 *p = *p - 'A' + 'a';
321
322 return t;
323 }
324
325 bool chars_intersect(const char *a, const char *b) {
326 const char *p;
327
328 /* Returns true if any of the chars in a are in b. */
329 for (p = a; *p; p++)
330 if (strchr(b, *p))
331 return true;
332
333 return false;
334 }
335
336 bool string_has_cc(const char *p, const char *ok) {
337 const char *t;
338
339 assert(p);
340
341 /*
342 * Check if a string contains control characters. If 'ok' is
343 * non-NULL it may be a string containing additional CCs to be
344 * considered OK.
345 */
346
347 for (t = p; *t; t++) {
348 if (ok && strchr(ok, *t))
349 continue;
350
351 if (*t > 0 && *t < ' ')
352 return true;
353
354 if (*t == 127)
355 return true;
356 }
357
358 return false;
359 }
360
361 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
362 size_t x;
363 char *r;
364
365 assert(s);
366 assert(percent <= 100);
367 assert(new_length >= 3);
368
369 if (old_length <= 3 || old_length <= new_length)
370 return strndup(s, old_length);
371
372 r = new0(char, new_length+1);
373 if (!r)
374 return NULL;
375
376 x = (new_length * percent) / 100;
377
378 if (x > new_length - 3)
379 x = new_length - 3;
380
381 memcpy(r, s, x);
382 r[x] = '.';
383 r[x+1] = '.';
384 r[x+2] = '.';
385 memcpy(r + x + 3,
386 s + old_length - (new_length - x - 3),
387 new_length - x - 3);
388
389 return r;
390 }
391
392 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
393 size_t x;
394 char *e;
395 const char *i, *j;
396 unsigned k, len, len2;
397
398 assert(s);
399 assert(percent <= 100);
400 assert(new_length >= 3);
401
402 /* if no multibyte characters use ascii_ellipsize_mem for speed */
403 if (ascii_is_valid(s))
404 return ascii_ellipsize_mem(s, old_length, new_length, percent);
405
406 if (old_length <= 3 || old_length <= new_length)
407 return strndup(s, old_length);
408
409 x = (new_length * percent) / 100;
410
411 if (x > new_length - 3)
412 x = new_length - 3;
413
414 k = 0;
415 for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
416 int c;
417
418 c = utf8_encoded_to_unichar(i);
419 if (c < 0)
420 return NULL;
421 k += unichar_iswide(c) ? 2 : 1;
422 }
423
424 if (k > x) /* last character was wide and went over quota */
425 x ++;
426
427 for (j = s + old_length; k < new_length && j > i; ) {
428 int c;
429
430 j = utf8_prev_char(j);
431 c = utf8_encoded_to_unichar(j);
432 if (c < 0)
433 return NULL;
434 k += unichar_iswide(c) ? 2 : 1;
435 }
436 assert(i <= j);
437
438 /* we don't actually need to ellipsize */
439 if (i == j)
440 return memdup(s, old_length + 1);
441
442 /* make space for ellipsis */
443 j = utf8_next_char(j);
444
445 len = i - s;
446 len2 = s + old_length - j;
447 e = new(char, len + 3 + len2 + 1);
448 if (!e)
449 return NULL;
450
451 /*
452 printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
453 old_length, new_length, x, len, len2, k);
454 */
455
456 memcpy(e, s, len);
457 e[len] = 0xe2; /* tri-dot ellipsis: … */
458 e[len + 1] = 0x80;
459 e[len + 2] = 0xa6;
460
461 memcpy(e + len + 3, j, len2 + 1);
462
463 return e;
464 }
465
466 char *ellipsize(const char *s, size_t length, unsigned percent) {
467 return ellipsize_mem(s, strlen(s), length, percent);
468 }
469
470 bool nulstr_contains(const char*nulstr, const char *needle) {
471 const char *i;
472
473 if (!nulstr)
474 return false;
475
476 NULSTR_FOREACH(i, nulstr)
477 if (streq(i, needle))
478 return true;
479
480 return false;
481 }
482
483 char* strshorten(char *s, size_t l) {
484 assert(s);
485
486 if (l < strlen(s))
487 s[l] = 0;
488
489 return s;
490 }
491
492 char *strreplace(const char *text, const char *old_string, const char *new_string) {
493 const char *f;
494 char *t, *r;
495 size_t l, old_len, new_len;
496
497 assert(text);
498 assert(old_string);
499 assert(new_string);
500
501 old_len = strlen(old_string);
502 new_len = strlen(new_string);
503
504 l = strlen(text);
505 r = new(char, l+1);
506 if (!r)
507 return NULL;
508
509 f = text;
510 t = r;
511 while (*f) {
512 char *a;
513 size_t d, nl;
514
515 if (!startswith(f, old_string)) {
516 *(t++) = *(f++);
517 continue;
518 }
519
520 d = t - r;
521 nl = l - old_len + new_len;
522 a = realloc(r, nl + 1);
523 if (!a)
524 goto oom;
525
526 l = nl;
527 r = a;
528 t = r + d;
529
530 t = stpcpy(t, new_string);
531 f += old_len;
532 }
533
534 *t = 0;
535 return r;
536
537 oom:
538 free(r);
539 return NULL;
540 }
541
542 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
543 const char *i, *begin = NULL;
544 enum {
545 STATE_OTHER,
546 STATE_ESCAPE,
547 STATE_BRACKET
548 } state = STATE_OTHER;
549 char *obuf = NULL;
550 size_t osz = 0, isz;
551 FILE *f;
552
553 assert(ibuf);
554 assert(*ibuf);
555
556 /* Strips ANSI color and replaces TABs by 8 spaces */
557
558 isz = _isz ? *_isz : strlen(*ibuf);
559
560 f = open_memstream(&obuf, &osz);
561 if (!f)
562 return NULL;
563
564 for (i = *ibuf; i < *ibuf + isz + 1; i++) {
565
566 switch (state) {
567
568 case STATE_OTHER:
569 if (i >= *ibuf + isz) /* EOT */
570 break;
571 else if (*i == '\x1B')
572 state = STATE_ESCAPE;
573 else if (*i == '\t')
574 fputs(" ", f);
575 else
576 fputc(*i, f);
577 break;
578
579 case STATE_ESCAPE:
580 if (i >= *ibuf + isz) { /* EOT */
581 fputc('\x1B', f);
582 break;
583 } else if (*i == '[') {
584 state = STATE_BRACKET;
585 begin = i + 1;
586 } else {
587 fputc('\x1B', f);
588 fputc(*i, f);
589 state = STATE_OTHER;
590 }
591
592 break;
593
594 case STATE_BRACKET:
595
596 if (i >= *ibuf + isz || /* EOT */
597 (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
598 fputc('\x1B', f);
599 fputc('[', f);
600 state = STATE_OTHER;
601 i = begin-1;
602 } else if (*i == 'm')
603 state = STATE_OTHER;
604 break;
605 }
606 }
607
608 if (ferror(f)) {
609 fclose(f);
610 free(obuf);
611 return NULL;
612 }
613
614 fclose(f);
615
616 free(*ibuf);
617 *ibuf = obuf;
618
619 if (_isz)
620 *_isz = osz;
621
622 return obuf;
623 }
624
625 char *strextend(char **x, ...) {
626 va_list ap;
627 size_t f, l;
628 char *r, *p;
629
630 assert(x);
631
632 l = f = *x ? strlen(*x) : 0;
633
634 va_start(ap, x);
635 for (;;) {
636 const char *t;
637 size_t n;
638
639 t = va_arg(ap, const char *);
640 if (!t)
641 break;
642
643 n = strlen(t);
644 if (n > ((size_t) -1) - l) {
645 va_end(ap);
646 return NULL;
647 }
648
649 l += n;
650 }
651 va_end(ap);
652
653 r = realloc(*x, l+1);
654 if (!r)
655 return NULL;
656
657 p = r + f;
658
659 va_start(ap, x);
660 for (;;) {
661 const char *t;
662
663 t = va_arg(ap, const char *);
664 if (!t)
665 break;
666
667 p = stpcpy(p, t);
668 }
669 va_end(ap);
670
671 *p = 0;
672 *x = r;
673
674 return r + l;
675 }
676
677 char *strrep(const char *s, unsigned n) {
678 size_t l;
679 char *r, *p;
680 unsigned i;
681
682 assert(s);
683
684 l = strlen(s);
685 p = r = malloc(l * n + 1);
686 if (!r)
687 return NULL;
688
689 for (i = 0; i < n; i++)
690 p = stpcpy(p, s);
691
692 *p = 0;
693 return r;
694 }
695
696 int split_pair(const char *s, const char *sep, char **l, char **r) {
697 char *x, *a, *b;
698
699 assert(s);
700 assert(sep);
701 assert(l);
702 assert(r);
703
704 if (isempty(sep))
705 return -EINVAL;
706
707 x = strstr(s, sep);
708 if (!x)
709 return -EINVAL;
710
711 a = strndup(s, x - s);
712 if (!a)
713 return -ENOMEM;
714
715 b = strdup(x + strlen(sep));
716 if (!b) {
717 free(a);
718 return -ENOMEM;
719 }
720
721 *l = a;
722 *r = b;
723
724 return 0;
725 }
726
727 int free_and_strdup(char **p, const char *s) {
728 char *t;
729
730 assert(p);
731
732 /* Replaces a string pointer with an strdup()ed new string,
733 * possibly freeing the old one. */
734
735 if (streq_ptr(*p, s))
736 return 0;
737
738 if (s) {
739 t = strdup(s);
740 if (!t)
741 return -ENOMEM;
742 } else
743 t = NULL;
744
745 free(*p);
746 *p = t;
747
748 return 1;
749 }
750
751 #pragma GCC push_options
752 #pragma GCC optimize("O0")
753
754 void* memory_erase(void *p, size_t l) {
755 volatile uint8_t* x = (volatile uint8_t*) p;
756
757 /* This basically does what memset() does, but hopefully isn't
758 * optimized away by the compiler. One of those days, when
759 * glibc learns memset_s() we should replace this call by
760 * memset_s(), but until then this has to do. */
761
762 for (; l > 0; l--)
763 *(x++) = 'x';
764
765 return p;
766 }
767
768 #pragma GCC pop_options
769
770 char* string_erase(char *x) {
771
772 if (!x)
773 return NULL;
774
775 /* A delicious drop of snake-oil! To be called on memory where
776 * we stored passphrases or so, after we used them. */
777
778 return memory_erase(x, strlen(x));
779 }
780
781 char *string_free_erase(char *s) {
782 return mfree(string_erase(s));
783 }
784
785 bool string_is_safe(const char *p) {
786 const char *t;
787
788 if (!p)
789 return false;
790
791 for (t = p; *t; t++) {
792 if (*t > 0 && *t < ' ') /* no control characters */
793 return false;
794
795 if (strchr(QUOTES "\\\x7f", *t))
796 return false;
797 }
798
799 return true;
800 }