chiark / gitweb /
basic: add new ascii_strcasecmp_n() call
[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_tolower(char x) {
314
315         if (x >= 'A' && x <= 'Z')
316                 return x - 'A' + 'a';
317
318         return x;
319 }
320
321 char *ascii_strlower(char *t) {
322         char *p;
323
324         assert(t);
325
326         for (p = t; *p; p++)
327                 *p = ascii_tolower(*p);
328
329         return t;
330 }
331
332 char *ascii_strlower_n(char *t, size_t n) {
333         size_t i;
334
335         if (n <= 0)
336                 return t;
337
338         for (i = 0; i < n; i++)
339                 t[i] = ascii_tolower(t[i]);
340
341         return t;
342 }
343
344 int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
345
346         for (; n > 0; a++, b++, n--) {
347                 int x, y;
348
349                 x = (int) (uint8_t) ascii_tolower(*a);
350                 y = (int) (uint8_t) ascii_tolower(*b);
351
352                 if (x != y)
353                         return x - y;
354         }
355
356         return 0;
357 }
358
359 bool chars_intersect(const char *a, const char *b) {
360         const char *p;
361
362         /* Returns true if any of the chars in a are in b. */
363         for (p = a; *p; p++)
364                 if (strchr(b, *p))
365                         return true;
366
367         return false;
368 }
369
370 bool string_has_cc(const char *p, const char *ok) {
371         const char *t;
372
373         assert(p);
374
375         /*
376          * Check if a string contains control characters. If 'ok' is
377          * non-NULL it may be a string containing additional CCs to be
378          * considered OK.
379          */
380
381         for (t = p; *t; t++) {
382                 if (ok && strchr(ok, *t))
383                         continue;
384
385                 if (*t > 0 && *t < ' ')
386                         return true;
387
388                 if (*t == 127)
389                         return true;
390         }
391
392         return false;
393 }
394
395 static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
396         size_t x;
397         char *r;
398
399         assert(s);
400         assert(percent <= 100);
401         assert(new_length >= 3);
402
403         if (old_length <= 3 || old_length <= new_length)
404                 return strndup(s, old_length);
405
406         r = new0(char, new_length+1);
407         if (!r)
408                 return NULL;
409
410         x = (new_length * percent) / 100;
411
412         if (x > new_length - 3)
413                 x = new_length - 3;
414
415         memcpy(r, s, x);
416         r[x] = '.';
417         r[x+1] = '.';
418         r[x+2] = '.';
419         memcpy(r + x + 3,
420                s + old_length - (new_length - x - 3),
421                new_length - x - 3);
422
423         return r;
424 }
425
426 char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
427         size_t x;
428         char *e;
429         const char *i, *j;
430         unsigned k, len, len2;
431         int r;
432
433         assert(s);
434         assert(percent <= 100);
435         assert(new_length >= 3);
436
437         /* if no multibyte characters use ascii_ellipsize_mem for speed */
438         if (ascii_is_valid(s))
439                 return ascii_ellipsize_mem(s, old_length, new_length, percent);
440
441         if (old_length <= 3 || old_length <= new_length)
442                 return strndup(s, old_length);
443
444         x = (new_length * percent) / 100;
445
446         if (x > new_length - 3)
447                 x = new_length - 3;
448
449         k = 0;
450         for (i = s; k < x && i < s + old_length; i = utf8_next_char(i)) {
451                 char32_t c;
452
453                 r = utf8_encoded_to_unichar(i, &c);
454                 if (r < 0)
455                         return NULL;
456                 k += unichar_iswide(c) ? 2 : 1;
457         }
458
459         if (k > x) /* last character was wide and went over quota */
460                 x ++;
461
462         for (j = s + old_length; k < new_length && j > i; ) {
463                 char32_t c;
464
465                 j = utf8_prev_char(j);
466                 r = utf8_encoded_to_unichar(j, &c);
467                 if (r < 0)
468                         return NULL;
469                 k += unichar_iswide(c) ? 2 : 1;
470         }
471         assert(i <= j);
472
473         /* we don't actually need to ellipsize */
474         if (i == j)
475                 return memdup(s, old_length + 1);
476
477         /* make space for ellipsis */
478         j = utf8_next_char(j);
479
480         len = i - s;
481         len2 = s + old_length - j;
482         e = new(char, len + 3 + len2 + 1);
483         if (!e)
484                 return NULL;
485
486         /*
487         printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
488                old_length, new_length, x, len, len2, k);
489         */
490
491         memcpy(e, s, len);
492         e[len]   = 0xe2; /* tri-dot ellipsis: â€¦ */
493         e[len + 1] = 0x80;
494         e[len + 2] = 0xa6;
495
496         memcpy(e + len + 3, j, len2 + 1);
497
498         return e;
499 }
500
501 char *ellipsize(const char *s, size_t length, unsigned percent) {
502         return ellipsize_mem(s, strlen(s), length, percent);
503 }
504
505 bool nulstr_contains(const char*nulstr, const char *needle) {
506         const char *i;
507
508         if (!nulstr)
509                 return false;
510
511         NULSTR_FOREACH(i, nulstr)
512                 if (streq(i, needle))
513                         return true;
514
515         return false;
516 }
517
518 char* strshorten(char *s, size_t l) {
519         assert(s);
520
521         if (l < strlen(s))
522                 s[l] = 0;
523
524         return s;
525 }
526
527 char *strreplace(const char *text, const char *old_string, const char *new_string) {
528         const char *f;
529         char *t, *r;
530         size_t l, old_len, new_len;
531
532         assert(text);
533         assert(old_string);
534         assert(new_string);
535
536         old_len = strlen(old_string);
537         new_len = strlen(new_string);
538
539         l = strlen(text);
540         r = new(char, l+1);
541         if (!r)
542                 return NULL;
543
544         f = text;
545         t = r;
546         while (*f) {
547                 char *a;
548                 size_t d, nl;
549
550                 if (!startswith(f, old_string)) {
551                         *(t++) = *(f++);
552                         continue;
553                 }
554
555                 d = t - r;
556                 nl = l - old_len + new_len;
557                 a = realloc(r, nl + 1);
558                 if (!a)
559                         goto oom;
560
561                 l = nl;
562                 r = a;
563                 t = r + d;
564
565                 t = stpcpy(t, new_string);
566                 f += old_len;
567         }
568
569         *t = 0;
570         return r;
571
572 oom:
573         free(r);
574         return NULL;
575 }
576
577 char *strip_tab_ansi(char **ibuf, size_t *_isz) {
578         const char *i, *begin = NULL;
579         enum {
580                 STATE_OTHER,
581                 STATE_ESCAPE,
582                 STATE_BRACKET
583         } state = STATE_OTHER;
584         char *obuf = NULL;
585         size_t osz = 0, isz;
586         FILE *f;
587
588         assert(ibuf);
589         assert(*ibuf);
590
591         /* Strips ANSI color and replaces TABs by 8 spaces */
592
593         isz = _isz ? *_isz : strlen(*ibuf);
594
595         f = open_memstream(&obuf, &osz);
596         if (!f)
597                 return NULL;
598
599         for (i = *ibuf; i < *ibuf + isz + 1; i++) {
600
601                 switch (state) {
602
603                 case STATE_OTHER:
604                         if (i >= *ibuf + isz) /* EOT */
605                                 break;
606                         else if (*i == '\x1B')
607                                 state = STATE_ESCAPE;
608                         else if (*i == '\t')
609                                 fputs("        ", f);
610                         else
611                                 fputc(*i, f);
612                         break;
613
614                 case STATE_ESCAPE:
615                         if (i >= *ibuf + isz) { /* EOT */
616                                 fputc('\x1B', f);
617                                 break;
618                         } else if (*i == '[') {
619                                 state = STATE_BRACKET;
620                                 begin = i + 1;
621                         } else {
622                                 fputc('\x1B', f);
623                                 fputc(*i, f);
624                                 state = STATE_OTHER;
625                         }
626
627                         break;
628
629                 case STATE_BRACKET:
630
631                         if (i >= *ibuf + isz || /* EOT */
632                             (!(*i >= '0' && *i <= '9') && *i != ';' && *i != 'm')) {
633                                 fputc('\x1B', f);
634                                 fputc('[', f);
635                                 state = STATE_OTHER;
636                                 i = begin-1;
637                         } else if (*i == 'm')
638                                 state = STATE_OTHER;
639                         break;
640                 }
641         }
642
643         if (ferror(f)) {
644                 fclose(f);
645                 free(obuf);
646                 return NULL;
647         }
648
649         fclose(f);
650
651         free(*ibuf);
652         *ibuf = obuf;
653
654         if (_isz)
655                 *_isz = osz;
656
657         return obuf;
658 }
659
660 char *strextend(char **x, ...) {
661         va_list ap;
662         size_t f, l;
663         char *r, *p;
664
665         assert(x);
666
667         l = f = *x ? strlen(*x) : 0;
668
669         va_start(ap, x);
670         for (;;) {
671                 const char *t;
672                 size_t n;
673
674                 t = va_arg(ap, const char *);
675                 if (!t)
676                         break;
677
678                 n = strlen(t);
679                 if (n > ((size_t) -1) - l) {
680                         va_end(ap);
681                         return NULL;
682                 }
683
684                 l += n;
685         }
686         va_end(ap);
687
688         r = realloc(*x, l+1);
689         if (!r)
690                 return NULL;
691
692         p = r + f;
693
694         va_start(ap, x);
695         for (;;) {
696                 const char *t;
697
698                 t = va_arg(ap, const char *);
699                 if (!t)
700                         break;
701
702                 p = stpcpy(p, t);
703         }
704         va_end(ap);
705
706         *p = 0;
707         *x = r;
708
709         return r + l;
710 }
711
712 char *strrep(const char *s, unsigned n) {
713         size_t l;
714         char *r, *p;
715         unsigned i;
716
717         assert(s);
718
719         l = strlen(s);
720         p = r = malloc(l * n + 1);
721         if (!r)
722                 return NULL;
723
724         for (i = 0; i < n; i++)
725                 p = stpcpy(p, s);
726
727         *p = 0;
728         return r;
729 }
730
731 int split_pair(const char *s, const char *sep, char **l, char **r) {
732         char *x, *a, *b;
733
734         assert(s);
735         assert(sep);
736         assert(l);
737         assert(r);
738
739         if (isempty(sep))
740                 return -EINVAL;
741
742         x = strstr(s, sep);
743         if (!x)
744                 return -EINVAL;
745
746         a = strndup(s, x - s);
747         if (!a)
748                 return -ENOMEM;
749
750         b = strdup(x + strlen(sep));
751         if (!b) {
752                 free(a);
753                 return -ENOMEM;
754         }
755
756         *l = a;
757         *r = b;
758
759         return 0;
760 }
761
762 int free_and_strdup(char **p, const char *s) {
763         char *t;
764
765         assert(p);
766
767         /* Replaces a string pointer with an strdup()ed new string,
768          * possibly freeing the old one. */
769
770         if (streq_ptr(*p, s))
771                 return 0;
772
773         if (s) {
774                 t = strdup(s);
775                 if (!t)
776                         return -ENOMEM;
777         } else
778                 t = NULL;
779
780         free(*p);
781         *p = t;
782
783         return 1;
784 }
785
786 #pragma GCC push_options
787 #pragma GCC optimize("O0")
788
789 void* memory_erase(void *p, size_t l) {
790         volatile uint8_t* x = (volatile uint8_t*) p;
791
792         /* This basically does what memset() does, but hopefully isn't
793          * optimized away by the compiler. One of those days, when
794          * glibc learns memset_s() we should replace this call by
795          * memset_s(), but until then this has to do. */
796
797         for (; l > 0; l--)
798                 *(x++) = 'x';
799
800         return p;
801 }
802
803 #pragma GCC pop_options
804
805 char* string_erase(char *x) {
806
807         if (!x)
808                 return NULL;
809
810         /* A delicious drop of snake-oil! To be called on memory where
811          * we stored passphrases or so, after we used them. */
812
813         return memory_erase(x, strlen(x));
814 }
815
816 char *string_free_erase(char *s) {
817         return mfree(string_erase(s));
818 }
819
820 bool string_is_safe(const char *p) {
821         const char *t;
822
823         if (!p)
824                 return false;
825
826         for (t = p; *t; t++) {
827                 if (*t > 0 && *t < ' ') /* no control characters */
828                         return false;
829
830                 if (strchr(QUOTES "\\\x7f", *t))
831                         return false;
832         }
833
834         return true;
835 }