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