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