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