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