chiark / gitweb /
utf8.[ch] et al: use char32_t and char16_t instead of int, int32_t, int16_t
[elogind.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 "string-util.h"
25 #include "utf8.h"
26 #include "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         int r;
398
399         assert(s);
400         assert(percent <= 100);
401         assert(new_length >= 3);
402
403         /* if no multibyte characters use ascii_ellipsize_mem for speed */
404         if (ascii_is_valid(s))
405                 return ascii_ellipsize_mem(s, old_length, new_length, percent);
406
407         if (old_length <= 3 || old_length <= new_length)
408                 return strndup(s, old_length);
409
410         x = (new_length * percent) / 100;
411
412         if (x > new_length - 3)
413                 x = new_length - 3;
414
415         k = 0;
416         for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
417                 char32_t c;
418
419                 r = utf8_encoded_to_unichar(i, &c);
420                 if (r < 0)
421                         return NULL;
422                 k += unichar_iswide(c) ? 2 : 1;
423         }
424
425         if (k > x) /* last character was wide and went over quota */
426                 x ++;
427
428         for (j = s + old_length; k < new_length && j > i; ) {
429                 char32_t c;
430
431                 j = utf8_prev_char(j);
432                 r = utf8_encoded_to_unichar(j, &c);
433                 if (r < 0)
434                         return NULL;
435                 k += unichar_iswide(c) ? 2 : 1;
436         }
437         assert(i <= j);
438
439         /* we don't actually need to ellipsize */
440         if (i == j)
441                 return memdup(s, old_length + 1);
442
443         /* make space for ellipsis */
444         j = utf8_next_char(j);
445
446         len = i - s;
447         len2 = s + old_length - j;
448         e = new(char, len + 3 + len2 + 1);
449         if (!e)
450                 return NULL;
451
452         /*
453         printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
454                old_length, new_length, x, len, len2, k);
455         */
456
457         memcpy(e, s, len);
458         e[len]   = 0xe2; /* tri-dot ellipsis: … */
459         e[len + 1] = 0x80;
460         e[len + 2] = 0xa6;
461
462         memcpy(e + len + 3, j, len2 + 1);
463
464         return e;
465 }
466
467 char *ellipsize(const char *s, size_t length, unsigned percent) {
468         return ellipsize_mem(s, strlen(s), length, percent);
469 }
470
471 bool nulstr_contains(const char*nulstr, const char *needle) {
472         const char *i;
473
474         if (!nulstr)
475                 return false;
476
477         NULSTR_FOREACH(i, nulstr)
478                 if (streq(i, needle))
479                         return true;
480
481         return false;
482 }
483
484 char* strshorten(char *s, size_t l) {
485         assert(s);
486
487         if (l < strlen(s))
488                 s[l] = 0;
489
490         return s;
491 }
492
493 char *strreplace(const char *text, const char *old_string, const char *new_string) {
494         const char *f;
495         char *t, *r;
496         size_t l, old_len, new_len;
497
498         assert(text);
499         assert(old_string);
500         assert(new_string);
501
502         old_len = strlen(old_string);
503         new_len = strlen(new_string);
504
505         l = strlen(text);
506         r = new(char, l+1);
507         if (!r)
508                 return NULL;
509
510         f = text;
511         t = r;
512         while (*f) {
513                 char *a;
514                 size_t d, nl;
515
516                 if (!startswith(f, old_string)) {
517                         *(t++) = *(f++);
518                         continue;
519                 }
520
521                 d = t - r;
522                 nl = l - old_len + new_len;
523                 a = realloc(r, nl + 1);
524                 if (!a)
525                         goto oom;
526
527                 l = nl;
528                 r = a;
529                 t = r + d;
530
531                 t = stpcpy(t, new_string);
532                 f += old_len;
533         }
534
535         *t = 0;
536         return r;
537
538 oom:
539         free(r);
540         return NULL;
541 }
542
543 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
544         const char *i, *begin = NULL;
545         enum {
546                 STATE_OTHER,
547                 STATE_ESCAPE,
548                 STATE_BRACKET
549         } state = STATE_OTHER;
550         char *obuf = NULL;
551         size_t osz = 0, isz;
552         FILE *f;
553
554         assert(ibuf);
555         assert(*ibuf);
556
557         /* Strips ANSI color and replaces TABs by 8 spaces */
558
559         isz = _isz ? *_isz : strlen(*ibuf);
560
561         f = open_memstream(&obuf, &osz);
562         if (!f)
563                 return NULL;
564
565         for (i = *ibuf; i < *ibuf + isz + 1; i++) {
566
567                 switch (state) {
568
569                 case STATE_OTHER:
570                         if (i >= *ibuf + isz) /* EOT */
571                                 break;
572                         else if (*i == '\x1B')
573                                 state = STATE_ESCAPE;
574                         else if (*i == '\t')
575                                 fputs("        ", f);
576                         else
577                                 fputc(*i, f);
578                         break;
579
580                 case STATE_ESCAPE:
581                         if (i >= *ibuf + isz) { /* EOT */
582                                 fputc('\x1B', f);
583                                 break;
584                         } else if (*i == '[') {
585                                 state = STATE_BRACKET;
586                                 begin = i + 1;
587                         } else {
588                                 fputc('\x1B', f);
589                                 fputc(*i, f);
590                                 state = STATE_OTHER;
591                         }
592
593                         break;
594
595                 case STATE_BRACKET:
596
597                         if (i >= *ibuf + isz || /* EOT */
598                             (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
599                                 fputc('\x1B', f);
600                                 fputc('[', f);
601                                 state = STATE_OTHER;
602                                 i = begin-1;
603                         } else if (*i == 'm')
604                                 state = STATE_OTHER;
605                         break;
606                 }
607         }
608
609         if (ferror(f)) {
610                 fclose(f);
611                 free(obuf);
612                 return NULL;
613         }
614
615         fclose(f);
616
617         free(*ibuf);
618         *ibuf = obuf;
619
620         if (_isz)
621                 *_isz = osz;
622
623         return obuf;
624 }
625
626 char *strextend(char **x, ...) {
627         va_list ap;
628         size_t f, l;
629         char *r, *p;
630
631         assert(x);
632
633         l = f = *x ? strlen(*x) : 0;
634
635         va_start(ap, x);
636         for (;;) {
637                 const char *t;
638                 size_t n;
639
640                 t = va_arg(ap, const char *);
641                 if (!t)
642                         break;
643
644                 n = strlen(t);
645                 if (n > ((size_t) -1) - l) {
646                         va_end(ap);
647                         return NULL;
648                 }
649
650                 l += n;
651         }
652         va_end(ap);
653
654         r = realloc(*x, l+1);
655         if (!r)
656                 return NULL;
657
658         p = r + f;
659
660         va_start(ap, x);
661         for (;;) {
662                 const char *t;
663
664                 t = va_arg(ap, const char *);
665                 if (!t)
666                         break;
667
668                 p = stpcpy(p, t);
669         }
670         va_end(ap);
671
672         *p = 0;
673         *x = r;
674
675         return r + l;
676 }
677
678 char *strrep(const char *s, unsigned n) {
679         size_t l;
680         char *r, *p;
681         unsigned i;
682
683         assert(s);
684
685         l = strlen(s);
686         p = r = malloc(l * n + 1);
687         if (!r)
688                 return NULL;
689
690         for (i = 0; i < n; i++)
691                 p = stpcpy(p, s);
692
693         *p = 0;
694         return r;
695 }
696
697 int split_pair(const char *s, const char *sep, char **l, char **r) {
698         char *x, *a, *b;
699
700         assert(s);
701         assert(sep);
702         assert(l);
703         assert(r);
704
705         if (isempty(sep))
706                 return -EINVAL;
707
708         x = strstr(s, sep);
709         if (!x)
710                 return -EINVAL;
711
712         a = strndup(s, x - s);
713         if (!a)
714                 return -ENOMEM;
715
716         b = strdup(x + strlen(sep));
717         if (!b) {
718                 free(a);
719                 return -ENOMEM;
720         }
721
722         *l = a;
723         *r = b;
724
725         return 0;
726 }
727
728 int free_and_strdup(char **p, const char *s) {
729         char *t;
730
731         assert(p);
732
733         /* Replaces a string pointer with an strdup()ed new string,
734          * possibly freeing the old one. */
735
736         if (streq_ptr(*p, s))
737                 return 0;
738
739         if (s) {
740                 t = strdup(s);
741                 if (!t)
742                         return -ENOMEM;
743         } else
744                 t = NULL;
745
746         free(*p);
747         *p = t;
748
749         return 1;
750 }
751
752 #pragma GCC push_options
753 #pragma GCC optimize("O0")
754
755 void* memory_erase(void *p, size_t l) {
756         volatile uint8_t* x = (volatile uint8_t*) p;
757
758         /* This basically does what memset() does, but hopefully isn't
759          * optimized away by the compiler. One of those days, when
760          * glibc learns memset_s() we should replace this call by
761          * memset_s(), but until then this has to do. */
762
763         for (; l > 0; l--)
764                 *(x++) = 'x';
765
766         return p;
767 }
768
769 #pragma GCC pop_options
770
771 char* string_erase(char *x) {
772
773         if (!x)
774                 return NULL;
775
776         /* A delicious drop of snake-oil! To be called on memory where
777          * we stored passphrases or so, after we used them. */
778
779         return memory_erase(x, strlen(x));
780 }
781
782 char *string_free_erase(char *s) {
783         return mfree(string_erase(s));
784 }
785
786 bool string_is_safe(const char *p) {
787         const char *t;
788
789         if (!p)
790                 return false;
791
792         for (t = p; *t; t++) {
793                 if (*t > 0 && *t < ' ') /* no control characters */
794                         return false;
795
796                 if (strchr(QUOTES "\\\x7f", *t))
797                         return false;
798         }
799
800         return true;
801 }