LCOV - code coverage report
Current view: top level - basic - string-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 471 556 84.7 %
Date: 2019-08-22 15:41:25 Functions: 33 35 94.3 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <stdarg.h>
       5             : #include <stdint.h>
       6             : #include <stdio.h>
       7             : #include <stdlib.h>
       8             : #include <string.h>
       9             : 
      10             : #include "alloc-util.h"
      11             : #include "escape.h"
      12             : #include "fileio.h"
      13             : #include "gunicode.h"
      14             : #include "locale-util.h"
      15             : #include "macro.h"
      16             : #include "memory-util.h"
      17             : #include "string-util.h"
      18             : #include "terminal-util.h"
      19             : #include "utf8.h"
      20             : #include "util.h"
      21             : 
      22     1216862 : int strcmp_ptr(const char *a, const char *b) {
      23             : 
      24             :         /* Like strcmp(), but tries to make sense of NULL pointers */
      25     1216862 :         if (a && b)
      26     1179727 :                 return strcmp(a, b);
      27             : 
      28       37135 :         if (!a && b)
      29       27808 :                 return -1;
      30             : 
      31        9327 :         if (a && !b)
      32        9090 :                 return 1;
      33             : 
      34         237 :         return 0;
      35             : }
      36             : 
      37     1272217 : char* endswith(const char *s, const char *postfix) {
      38             :         size_t sl, pl;
      39             : 
      40     1272217 :         assert(s);
      41     1272217 :         assert(postfix);
      42             : 
      43     1272217 :         sl = strlen(s);
      44     1272217 :         pl = strlen(postfix);
      45             : 
      46     1272217 :         if (pl == 0)
      47           2 :                 return (char*) s + sl;
      48             : 
      49     1272215 :         if (sl < pl)
      50          93 :                 return NULL;
      51             : 
      52     1272122 :         if (memcmp(s + sl - pl, postfix, pl) != 0)
      53     1243381 :                 return NULL;
      54             : 
      55       28741 :         return (char*) s + sl - pl;
      56             : }
      57             : 
      58        1730 : char* endswith_no_case(const char *s, const char *postfix) {
      59             :         size_t sl, pl;
      60             : 
      61        1730 :         assert(s);
      62        1730 :         assert(postfix);
      63             : 
      64        1730 :         sl = strlen(s);
      65        1730 :         pl = strlen(postfix);
      66             : 
      67        1730 :         if (pl == 0)
      68           2 :                 return (char*) s + sl;
      69             : 
      70        1728 :         if (sl < pl)
      71         126 :                 return NULL;
      72             : 
      73        1602 :         if (strcasecmp(s + sl - pl, postfix) != 0)
      74         954 :                 return NULL;
      75             : 
      76         648 :         return (char*) s + sl - pl;
      77             : }
      78             : 
      79        7228 : char* first_word(const char *s, const char *word) {
      80             :         size_t sl, wl;
      81             :         const char *p;
      82             : 
      83        7228 :         assert(s);
      84        7228 :         assert(word);
      85             : 
      86             :         /* Checks if the string starts with the specified word, either
      87             :          * followed by NUL or by whitespace. Returns a pointer to the
      88             :          * NUL or the first character after the whitespace. */
      89             : 
      90        7228 :         sl = strlen(s);
      91        7228 :         wl = strlen(word);
      92             : 
      93        7228 :         if (sl < wl)
      94         664 :                 return NULL;
      95             : 
      96        6564 :         if (wl == 0)
      97           1 :                 return (char*) s;
      98             : 
      99        6563 :         if (memcmp(s, word, wl) != 0)
     100        6535 :                 return NULL;
     101             : 
     102          28 :         p = s + wl;
     103          28 :         if (*p == 0)
     104           1 :                 return (char*) p;
     105             : 
     106          27 :         if (!strchr(WHITESPACE, *p))
     107           1 :                 return NULL;
     108             : 
     109          26 :         p += strspn(p, WHITESPACE);
     110          26 :         return (char*) p;
     111             : }
     112             : 
     113          32 : static size_t strcspn_escaped(const char *s, const char *reject) {
     114          32 :         bool escaped = false;
     115             :         int n;
     116             : 
     117         336 :         for (n=0; s[n]; n++) {
     118         330 :                 if (escaped)
     119           0 :                         escaped = false;
     120         330 :                 else if (s[n] == '\\')
     121           0 :                         escaped = true;
     122         330 :                 else if (strchr(reject, s[n]))
     123          26 :                         break;
     124             :         }
     125             : 
     126             :         /* if s ends in \, return index of previous char */
     127          32 :         return n - escaped;
     128             : }
     129             : 
     130             : /* Split a string into words. */
     131        2270 : const char* split(const char **state, size_t *l, const char *separator, SplitFlags flags) {
     132             :         const char *current;
     133             : 
     134        2270 :         current = *state;
     135             : 
     136        2270 :         if (!*current) {
     137         596 :                 assert(**state == '\0');
     138         596 :                 return NULL;
     139             :         }
     140             : 
     141        1674 :         current += strspn(current, separator);
     142        1674 :         if (!*current) {
     143          15 :                 *state = current;
     144          15 :                 return NULL;
     145             :         }
     146             : 
     147        1659 :         if (flags & SPLIT_QUOTES && strchr("\'\"", *current)) {
     148          32 :                 char quotechars[2] = {*current, '\0'};
     149             : 
     150          32 :                 *l = strcspn_escaped(current + 1, quotechars);
     151          32 :                 if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
     152          26 :                     (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
     153             :                         /* right quote missing or garbage at the end */
     154          10 :                         if (flags & SPLIT_RELAX) {
     155           8 :                                 *state = current + *l + 1 + (current[*l + 1] != '\0');
     156          10 :                                 return current + 1;
     157             :                         }
     158           2 :                         *state = current;
     159           2 :                         return NULL;
     160             :                 }
     161          22 :                 *state = current++ + *l + 2;
     162        1627 :         } else if (flags & SPLIT_QUOTES) {
     163           0 :                 *l = strcspn_escaped(current, separator);
     164           0 :                 if (current[*l] && !strchr(separator, current[*l]) && !(flags & SPLIT_RELAX)) {
     165             :                         /* unfinished escape */
     166           0 :                         *state = current;
     167           0 :                         return NULL;
     168             :                 }
     169           0 :                 *state = current + *l;
     170             :         } else {
     171        1627 :                 *l = strcspn(current, separator);
     172        1627 :                 *state = current + *l;
     173             :         }
     174             : 
     175        1649 :         return current;
     176             : }
     177             : 
     178         147 : char *strnappend(const char *s, const char *suffix, size_t b) {
     179             :         size_t a;
     180             :         char *r;
     181             : 
     182         147 :         if (!s && !suffix)
     183           0 :                 return strdup("");
     184             : 
     185         147 :         if (!s)
     186          65 :                 return strndup(suffix, b);
     187             : 
     188          82 :         if (!suffix)
     189           0 :                 return strdup(s);
     190             : 
     191          82 :         assert(s);
     192          82 :         assert(suffix);
     193             : 
     194          82 :         a = strlen(s);
     195          82 :         if (b > ((size_t) -1) - a)
     196           0 :                 return NULL;
     197             : 
     198          82 :         r = new(char, a+b+1);
     199          82 :         if (!r)
     200           0 :                 return NULL;
     201             : 
     202          82 :         memcpy(r, s, a);
     203          82 :         memcpy(r+a, suffix, b);
     204          82 :         r[a+b] = 0;
     205             : 
     206          82 :         return r;
     207             : }
     208             : 
     209       23027 : char *strjoin_real(const char *x, ...) {
     210             :         va_list ap;
     211             :         size_t l;
     212             :         char *r, *p;
     213             : 
     214       23027 :         va_start(ap, x);
     215             : 
     216       23027 :         if (x) {
     217       23007 :                 l = strlen(x);
     218             : 
     219       42490 :                 for (;;) {
     220             :                         const char *t;
     221             :                         size_t n;
     222             : 
     223       65497 :                         t = va_arg(ap, const char *);
     224       65497 :                         if (!t)
     225       23007 :                                 break;
     226             : 
     227       42490 :                         n = strlen(t);
     228       42490 :                         if (n > ((size_t) -1) - l) {
     229           0 :                                 va_end(ap);
     230           0 :                                 return NULL;
     231             :                         }
     232             : 
     233       42490 :                         l += n;
     234             :                 }
     235             :         } else
     236          20 :                 l = 0;
     237             : 
     238       23027 :         va_end(ap);
     239             : 
     240       23027 :         r = new(char, l+1);
     241       23027 :         if (!r)
     242           0 :                 return NULL;
     243             : 
     244       23027 :         if (x) {
     245       23007 :                 p = stpcpy(r, x);
     246             : 
     247       23007 :                 va_start(ap, x);
     248             : 
     249       42490 :                 for (;;) {
     250             :                         const char *t;
     251             : 
     252       65497 :                         t = va_arg(ap, const char *);
     253       65497 :                         if (!t)
     254       23007 :                                 break;
     255             : 
     256       42490 :                         p = stpcpy(p, t);
     257             :                 }
     258             : 
     259       23007 :                 va_end(ap);
     260             :         } else
     261          20 :                 r[0] = 0;
     262             : 
     263       23027 :         return r;
     264             : }
     265             : 
     266       21256 : char *strstrip(char *s) {
     267       21256 :         if (!s)
     268           0 :                 return NULL;
     269             : 
     270             :         /* Drops trailing whitespace. Modifies the string in place. Returns pointer to first non-space character */
     271             : 
     272       21256 :         return delete_trailing_chars(skip_leading_chars(s, WHITESPACE), WHITESPACE);
     273             : }
     274             : 
     275           9 : char *delete_chars(char *s, const char *bad) {
     276             :         char *f, *t;
     277             : 
     278             :         /* Drops all specified bad characters, regardless where in the string */
     279             : 
     280           9 :         if (!s)
     281           0 :                 return NULL;
     282             : 
     283           9 :         if (!bad)
     284           0 :                 bad = WHITESPACE;
     285             : 
     286         131 :         for (f = s, t = s; *f; f++) {
     287         122 :                 if (strchr(bad, *f))
     288          67 :                         continue;
     289             : 
     290          55 :                 *(t++) = *f;
     291             :         }
     292             : 
     293           9 :         *t = 0;
     294             : 
     295           9 :         return s;
     296             : }
     297             : 
     298       23882 : char *delete_trailing_chars(char *s, const char *bad) {
     299       23882 :         char *p, *c = s;
     300             : 
     301             :         /* Drops all specified bad characters, at the end of the string */
     302             : 
     303       23882 :         if (!s)
     304           0 :                 return NULL;
     305             : 
     306       23882 :         if (!bad)
     307           0 :                 bad = WHITESPACE;
     308             : 
     309     4746121 :         for (p = s; *p; p++)
     310     4722239 :                 if (!strchr(bad, *p))
     311     4695690 :                         c = p + 1;
     312             : 
     313       23882 :         *c = 0;
     314             : 
     315       23882 :         return s;
     316             : }
     317             : 
     318           2 : char *truncate_nl(char *s) {
     319           2 :         assert(s);
     320             : 
     321           2 :         s[strcspn(s, NEWLINE)] = 0;
     322           2 :         return s;
     323             : }
     324             : 
     325       27632 : char ascii_tolower(char x) {
     326             : 
     327       27632 :         if (x >= 'A' && x <= 'Z')
     328         408 :                 return x - 'A' + 'a';
     329             : 
     330       27224 :         return x;
     331             : }
     332             : 
     333           0 : char ascii_toupper(char x) {
     334             : 
     335           0 :         if (x >= 'a' && x <= 'z')
     336           0 :                 return x - 'a' + 'A';
     337             : 
     338           0 :         return x;
     339             : }
     340             : 
     341          18 : char *ascii_strlower(char *t) {
     342             :         char *p;
     343             : 
     344          18 :         assert(t);
     345             : 
     346         110 :         for (p = t; *p; p++)
     347          92 :                 *p = ascii_tolower(*p);
     348             : 
     349          18 :         return t;
     350             : }
     351             : 
     352           0 : char *ascii_strupper(char *t) {
     353             :         char *p;
     354             : 
     355           0 :         assert(t);
     356             : 
     357           0 :         for (p = t; *p; p++)
     358           0 :                 *p = ascii_toupper(*p);
     359             : 
     360           0 :         return t;
     361             : }
     362             : 
     363        2068 : char *ascii_strlower_n(char *t, size_t n) {
     364             :         size_t i;
     365             : 
     366        2068 :         if (n <= 0)
     367          11 :                 return t;
     368             : 
     369       14260 :         for (i = 0; i < n; i++)
     370       12203 :                 t[i] = ascii_tolower(t[i]);
     371             : 
     372        2057 :         return t;
     373             : }
     374             : 
     375        1406 : int ascii_strcasecmp_n(const char *a, const char *b, size_t n) {
     376             : 
     377        8202 :         for (; n > 0; a++, b++, n--) {
     378             :                 int x, y;
     379             : 
     380        6911 :                 x = (int) (uint8_t) ascii_tolower(*a);
     381        6911 :                 y = (int) (uint8_t) ascii_tolower(*b);
     382             : 
     383        6911 :                 if (x != y)
     384         115 :                         return x - y;
     385             :         }
     386             : 
     387        1291 :         return 0;
     388             : }
     389             : 
     390         285 : int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) {
     391             :         int r;
     392             : 
     393         285 :         r = ascii_strcasecmp_n(a, b, MIN(n, m));
     394         285 :         if (r != 0)
     395          68 :                 return r;
     396             : 
     397         217 :         return CMP(n, m);
     398             : }
     399             : 
     400          11 : bool chars_intersect(const char *a, const char *b) {
     401             :         const char *p;
     402             : 
     403             :         /* Returns true if any of the chars in a are in b. */
     404          56 :         for (p = a; *p; p++)
     405          51 :                 if (strchr(b, *p))
     406           6 :                         return true;
     407             : 
     408           5 :         return false;
     409             : }
     410             : 
     411         100 : bool string_has_cc(const char *p, const char *ok) {
     412             :         const char *t;
     413             : 
     414         100 :         assert(p);
     415             : 
     416             :         /*
     417             :          * Check if a string contains control characters. If 'ok' is
     418             :          * non-NULL it may be a string containing additional CCs to be
     419             :          * considered OK.
     420             :          */
     421             : 
     422         877 :         for (t = p; *t; t++) {
     423         791 :                 if (ok && strchr(ok, *t))
     424          16 :                         continue;
     425             : 
     426         775 :                 if (*t > 0 && *t < ' ')
     427           8 :                         return true;
     428             : 
     429         767 :                 if (*t == 127)
     430           6 :                         return true;
     431             :         }
     432             : 
     433          86 :         return false;
     434             : }
     435             : 
     436         799 : static int write_ellipsis(char *buf, bool unicode) {
     437         799 :         if (unicode || is_locale_utf8()) {
     438         799 :                 buf[0] = 0xe2; /* tri-dot ellipsis: … */
     439         799 :                 buf[1] = 0x80;
     440         799 :                 buf[2] = 0xa6;
     441             :         } else {
     442           0 :                 buf[0] = '.';
     443           0 :                 buf[1] = '.';
     444           0 :                 buf[2] = '.';
     445             :         }
     446             : 
     447         799 :         return 3;
     448             : }
     449             : 
     450        1027 : static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
     451             :         size_t x, need_space, suffix_len;
     452             :         char *t;
     453             : 
     454        1027 :         assert(s);
     455        1027 :         assert(percent <= 100);
     456        1027 :         assert(new_length != (size_t) -1);
     457             : 
     458        1027 :         if (old_length <= new_length)
     459         750 :                 return strndup(s, old_length);
     460             : 
     461             :         /* Special case short ellipsations */
     462         277 :         switch (new_length) {
     463             : 
     464           0 :         case 0:
     465           0 :                 return strdup("");
     466             : 
     467          59 :         case 1:
     468          59 :                 if (is_locale_utf8())
     469          59 :                         return strdup("…");
     470             :                 else
     471           0 :                         return strdup(".");
     472             : 
     473          35 :         case 2:
     474          35 :                 if (!is_locale_utf8())
     475           0 :                         return strdup("..");
     476             : 
     477          35 :                 break;
     478             : 
     479         183 :         default:
     480         183 :                 break;
     481             :         }
     482             : 
     483             :         /* Calculate how much space the ellipsis will take up. If we are in UTF-8 mode we only need space for one
     484             :          * character ("…"), otherwise for three characters ("..."). Note that in both cases we need 3 bytes of storage,
     485             :          * either for the UTF-8 encoded character or for three ASCII characters. */
     486         218 :         need_space = is_locale_utf8() ? 1 : 3;
     487             : 
     488         218 :         t = new(char, new_length+3);
     489         218 :         if (!t)
     490           0 :                 return NULL;
     491             : 
     492         218 :         assert(new_length >= need_space);
     493             : 
     494         218 :         x = ((new_length - need_space) * percent + 50) / 100;
     495         218 :         assert(x <= new_length - need_space);
     496             : 
     497         218 :         memcpy(t, s, x);
     498         218 :         write_ellipsis(t + x, false);
     499         218 :         suffix_len = new_length - x - need_space;
     500         218 :         memcpy(t + x + 3, s + old_length - suffix_len, suffix_len);
     501         218 :         *(t + x + 3 + suffix_len) = '\0';
     502             : 
     503         218 :         return t;
     504             : }
     505             : 
     506        3423 : char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
     507             :         size_t x, k, len, len2;
     508             :         const char *i, *j;
     509             :         char *e;
     510             :         int r;
     511             : 
     512             :         /* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
     513             :          * on screen. This distinction doesn't matter for ASCII strings, but it does matter for non-ASCII UTF-8
     514             :          * strings.
     515             :          *
     516             :          * Ellipsation is done in a locale-dependent way:
     517             :          * 1. If the string passed in is fully ASCII and the current locale is not UTF-8, three dots are used ("...")
     518             :          * 2. Otherwise, a unicode ellipsis is used ("…")
     519             :          *
     520             :          * In other words: you'll get a unicode ellipsis as soon as either the string contains non-ASCII characters or
     521             :          * the current locale is UTF-8.
     522             :          */
     523             : 
     524        3423 :         assert(s);
     525        3423 :         assert(percent <= 100);
     526             : 
     527        3423 :         if (new_length == (size_t) -1)
     528           0 :                 return strndup(s, old_length);
     529             : 
     530        3423 :         if (new_length == 0)
     531         164 :                 return strdup("");
     532             : 
     533             :         /* If no multibyte characters use ascii_ellipsize_mem for speed */
     534        3259 :         if (ascii_is_valid_n(s, old_length))
     535        1027 :                 return ascii_ellipsize_mem(s, old_length, new_length, percent);
     536             : 
     537        2232 :         x = ((new_length - 1) * percent) / 100;
     538        2232 :         assert(x <= new_length - 1);
     539             : 
     540        2232 :         k = 0;
     541       10939 :         for (i = s; i < s + old_length; i = utf8_next_char(i)) {
     542             :                 char32_t c;
     543             :                 int w;
     544             : 
     545        9753 :                 r = utf8_encoded_to_unichar(i, &c);
     546        9753 :                 if (r < 0)
     547           0 :                         return NULL;
     548             : 
     549        9753 :                 w = unichar_iswide(c) ? 2 : 1;
     550        9753 :                 if (k + w <= x)
     551        8707 :                         k += w;
     552             :                 else
     553        1046 :                         break;
     554             :         }
     555             : 
     556        5064 :         for (j = s + old_length; j > i; ) {
     557             :                 char32_t c;
     558             :                 int w;
     559             :                 const char *jj;
     560             : 
     561        3389 :                 jj = utf8_prev_char(j);
     562        3389 :                 r = utf8_encoded_to_unichar(jj, &c);
     563        3389 :                 if (r < 0)
     564           0 :                         return NULL;
     565             : 
     566        3389 :                 w = unichar_iswide(c) ? 2 : 1;
     567        3389 :                 if (k + w <= new_length) {
     568        2832 :                         k += w;
     569        2832 :                         j = jj;
     570             :                 } else
     571         557 :                         break;
     572             :         }
     573        2232 :         assert(i <= j);
     574             : 
     575             :         /* we don't actually need to ellipsize */
     576        2232 :         if (i == j)
     577        1675 :                 return memdup_suffix0(s, old_length);
     578             : 
     579             :         /* make space for ellipsis, if possible */
     580         557 :         if (j < s + old_length)
     581         515 :                 j = utf8_next_char(j);
     582          42 :         else if (i > s)
     583          17 :                 i = utf8_prev_char(i);
     584             : 
     585         557 :         len = i - s;
     586         557 :         len2 = s + old_length - j;
     587         557 :         e = new(char, len + 3 + len2 + 1);
     588         557 :         if (!e)
     589           0 :                 return NULL;
     590             : 
     591             :         /*
     592             :         printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
     593             :                old_length, new_length, x, len, len2, k);
     594             :         */
     595             : 
     596         557 :         memcpy(e, s, len);
     597         557 :         write_ellipsis(e + len, true);
     598         557 :         memcpy(e + len + 3, j, len2);
     599         557 :         *(e + len + 3 + len2) = '\0';
     600             : 
     601         557 :         return e;
     602             : }
     603             : 
     604        1191 : char *cellescape(char *buf, size_t len, const char *s) {
     605             :         /* Escape and ellipsize s into buffer buf of size len. Only non-control ASCII
     606             :          * characters are copied as they are, everything else is escaped. The result
     607             :          * is different then if escaping and ellipsization was performed in two
     608             :          * separate steps, because each sequence is either stored in full or skipped.
     609             :          *
     610             :          * This function should be used for logging about strings which expected to
     611             :          * be plain ASCII in a safe way.
     612             :          *
     613             :          * An ellipsis will be used if s is too long. It was always placed at the
     614             :          * very end.
     615             :          */
     616             : 
     617        1191 :         size_t i = 0, last_char_width[4] = {}, k = 0, j;
     618             : 
     619        1191 :         assert(len > 0); /* at least a terminating NUL */
     620             : 
     621       11799 :         for (;;) {
     622             :                 char four[4];
     623             :                 int w;
     624             : 
     625       12990 :                 if (*s == 0) /* terminating NUL detected? then we are done! */
     626        1155 :                         goto done;
     627             : 
     628       11835 :                 w = cescape_char(*s, four);
     629       11835 :                 if (i + w + 1 > len) /* This character doesn't fit into the buffer anymore? In that case let's
     630             :                                       * ellipsize at the previous location */
     631          36 :                         break;
     632             : 
     633             :                 /* OK, there was space, let's add this escaped character to the buffer */
     634       11799 :                 memcpy(buf + i, four, w);
     635       11799 :                 i += w;
     636             : 
     637             :                 /* And remember its width in the ring buffer */
     638       11799 :                 last_char_width[k] = w;
     639       11799 :                 k = (k + 1) % 4;
     640             : 
     641       11799 :                 s++;
     642             :         }
     643             : 
     644             :         /* Ellipsation is necessary. This means we might need to truncate the string again to make space for 4
     645             :          * characters ideally, but the buffer is shorter than that in the first place take what we can get */
     646          81 :         for (j = 0; j < ELEMENTSOF(last_char_width); j++) {
     647             : 
     648          81 :                 if (i + 4 <= len) /* nice, we reached our space goal */
     649          24 :                         break;
     650             : 
     651          57 :                 k = k == 0 ? 3 : k - 1;
     652          57 :                 if (last_char_width[k] == 0) /* bummer, we reached the beginning of the strings */
     653          12 :                         break;
     654             : 
     655          45 :                 assert(i >= last_char_width[k]);
     656          45 :                 i -= last_char_width[k];
     657             :         }
     658             : 
     659          36 :         if (i + 4 <= len) /* yay, enough space */
     660          24 :                 i += write_ellipsis(buf + i, false);
     661          12 :         else if (i + 3 <= len) { /* only space for ".." */
     662           4 :                 buf[i++] = '.';
     663           4 :                 buf[i++] = '.';
     664           8 :         } else if (i + 2 <= len) /* only space for a single "." */
     665           4 :                 buf[i++] = '.';
     666             :         else
     667           4 :                 assert(i + 1 <= len);
     668             : 
     669           4 :  done:
     670        1191 :         buf[i] = '\0';
     671        1191 :         return buf;
     672             : }
     673             : 
     674           8 : char* strshorten(char *s, size_t l) {
     675           8 :         assert(s);
     676             : 
     677           8 :         if (strnlen(s, l+1) > l)
     678           4 :                 s[l] = 0;
     679             : 
     680           8 :         return s;
     681             : }
     682             : 
     683          68 : char *strreplace(const char *text, const char *old_string, const char *new_string) {
     684          68 :         size_t l, old_len, new_len, allocated = 0;
     685          68 :         char *t, *ret = NULL;
     686             :         const char *f;
     687             : 
     688          68 :         assert(old_string);
     689          68 :         assert(new_string);
     690             : 
     691          68 :         if (!text)
     692           1 :                 return NULL;
     693             : 
     694          67 :         old_len = strlen(old_string);
     695          67 :         new_len = strlen(new_string);
     696             : 
     697          67 :         l = strlen(text);
     698          67 :         if (!GREEDY_REALLOC(ret, allocated, l+1))
     699           0 :                 return NULL;
     700             : 
     701          67 :         f = text;
     702          67 :         t = ret;
     703        1624 :         while (*f) {
     704             :                 size_t d, nl;
     705             : 
     706        1557 :                 if (!startswith(f, old_string)) {
     707        1534 :                         *(t++) = *(f++);
     708        1534 :                         continue;
     709             :                 }
     710             : 
     711          23 :                 d = t - ret;
     712          23 :                 nl = l - old_len + new_len;
     713             : 
     714          23 :                 if (!GREEDY_REALLOC(ret, allocated, nl + 1))
     715           0 :                         return mfree(ret);
     716             : 
     717          23 :                 l = nl;
     718          23 :                 t = ret + d;
     719             : 
     720          23 :                 t = stpcpy(t, new_string);
     721          23 :                 f += old_len;
     722             :         }
     723             : 
     724          67 :         *t = 0;
     725          67 :         return ret;
     726             : }
     727             : 
     728          10 : static void advance_offsets(
     729             :                 ssize_t diff,
     730             :                 size_t offsets[2], /* note: we can't use [static 2] here, since this may be NULL */
     731             :                 size_t shift[static 2],
     732             :                 size_t size) {
     733             : 
     734          10 :         if (!offsets)
     735          10 :                 return;
     736             : 
     737           0 :         assert(shift);
     738             : 
     739           0 :         if ((size_t) diff < offsets[0])
     740           0 :                 shift[0] += size;
     741           0 :         if ((size_t) diff < offsets[1])
     742           0 :                 shift[1] += size;
     743             : }
     744             : 
     745           5 : char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
     746           5 :         const char *i, *begin = NULL;
     747             :         enum {
     748             :                 STATE_OTHER,
     749             :                 STATE_ESCAPE,
     750             :                 STATE_CSI,
     751             :                 STATE_CSO,
     752           5 :         } state = STATE_OTHER;
     753           5 :         char *obuf = NULL;
     754           5 :         size_t osz = 0, isz, shift[2] = {};
     755             :         FILE *f;
     756             : 
     757           5 :         assert(ibuf);
     758           5 :         assert(*ibuf);
     759             : 
     760             :         /* This does three things:
     761             :          *
     762             :          * 1. Replaces TABs by 8 spaces
     763             :          * 2. Strips ANSI color sequences (a subset of CSI), i.e. ESC '[' … 'm' sequences
     764             :          * 3. Strips ANSI operating system sequences (CSO), i.e. ESC ']' … BEL sequences
     765             :          *
     766             :          * Everything else will be left as it is. In particular other ANSI sequences are left as they are, as
     767             :          * are any other special characters. Truncated ANSI sequences are left-as is too. This call is
     768             :          * supposed to suppress the most basic formatting noise, but nothing else.
     769             :          *
     770             :          * Why care for CSO sequences? Well, to undo what terminal_urlify() and friends generate. */
     771             : 
     772           5 :         isz = _isz ? *_isz : strlen(*ibuf);
     773             : 
     774             :         /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we
     775             :          * created f here and it doesn't leave our scope. */
     776           5 :         f = open_memstream_unlocked(&obuf, &osz);
     777           5 :         if (!f)
     778           0 :                 return NULL;
     779             : 
     780         170 :         for (i = *ibuf; i < *ibuf + isz + 1; i++) {
     781             : 
     782         165 :                 switch (state) {
     783             : 
     784         111 :                 case STATE_OTHER:
     785         111 :                         if (i >= *ibuf + isz) /* EOT */
     786           5 :                                 break;
     787         106 :                         else if (*i == '\x1B')
     788          13 :                                 state = STATE_ESCAPE;
     789          93 :                         else if (*i == '\t') {
     790           5 :                                 fputs("        ", f);
     791           5 :                                 advance_offsets(i - *ibuf, highlight, shift, 7);
     792             :                         } else
     793          88 :                                 fputc(*i, f);
     794             : 
     795         106 :                         break;
     796             : 
     797          13 :                 case STATE_ESCAPE:
     798          13 :                         if (i >= *ibuf + isz) { /* EOT */
     799           0 :                                 fputc('\x1B', f);
     800           0 :                                 advance_offsets(i - *ibuf, highlight, shift, 1);
     801           0 :                                 break;
     802          13 :                         } else if (*i == '[') { /* ANSI CSI */
     803          13 :                                 state = STATE_CSI;
     804          13 :                                 begin = i + 1;
     805           0 :                         } else if (*i == ']') { /* ANSI CSO */
     806           0 :                                 state = STATE_CSO;
     807           0 :                                 begin = i + 1;
     808             :                         } else {
     809           0 :                                 fputc('\x1B', f);
     810           0 :                                 fputc(*i, f);
     811           0 :                                 advance_offsets(i - *ibuf, highlight, shift, 1);
     812           0 :                                 state = STATE_OTHER;
     813             :                         }
     814             : 
     815          13 :                         break;
     816             : 
     817          41 :                 case STATE_CSI:
     818             : 
     819          41 :                         if (i >= *ibuf + isz || /* EOT … */
     820          41 :                             !strchr("01234567890;m", *i)) { /* … or invalid chars in sequence */
     821           5 :                                 fputc('\x1B', f);
     822           5 :                                 fputc('[', f);
     823           5 :                                 advance_offsets(i - *ibuf, highlight, shift, 2);
     824           5 :                                 state = STATE_OTHER;
     825           5 :                                 i = begin-1;
     826          36 :                         } else if (*i == 'm')
     827           8 :                                 state = STATE_OTHER;
     828             : 
     829          41 :                         break;
     830             : 
     831           0 :                 case STATE_CSO:
     832             : 
     833           0 :                         if (i >= *ibuf + isz || /* EOT … */
     834           0 :                             (*i != '\a' && (uint8_t) *i < 32U) || (uint8_t) *i > 126U) { /* … or invalid chars in sequence */
     835           0 :                                 fputc('\x1B', f);
     836           0 :                                 fputc(']', f);
     837           0 :                                 advance_offsets(i - *ibuf, highlight, shift, 2);
     838           0 :                                 state = STATE_OTHER;
     839           0 :                                 i = begin-1;
     840           0 :                         } else if (*i == '\a')
     841           0 :                                 state = STATE_OTHER;
     842             : 
     843           0 :                         break;
     844             :                 }
     845         165 :         }
     846             : 
     847           5 :         if (fflush_and_check(f) < 0) {
     848           0 :                 fclose(f);
     849           0 :                 return mfree(obuf);
     850             :         }
     851             : 
     852           5 :         fclose(f);
     853             : 
     854           5 :         free_and_replace(*ibuf, obuf);
     855             : 
     856           5 :         if (_isz)
     857           0 :                 *_isz = osz;
     858             : 
     859           5 :         if (highlight) {
     860           0 :                 highlight[0] += shift[0];
     861           0 :                 highlight[1] += shift[1];
     862             :         }
     863             : 
     864           5 :         return *ibuf;
     865             : }
     866             : 
     867       81231 : char *strextend_with_separator(char **x, const char *separator, ...) {
     868             :         bool need_separator;
     869             :         size_t f, l, l_separator;
     870             :         char *r, *p;
     871             :         va_list ap;
     872             : 
     873       81231 :         assert(x);
     874             : 
     875       81231 :         l = f = strlen_ptr(*x);
     876             : 
     877       81231 :         need_separator = !isempty(*x);
     878       81231 :         l_separator = strlen_ptr(separator);
     879             : 
     880       81231 :         va_start(ap, separator);
     881       81294 :         for (;;) {
     882             :                 const char *t;
     883             :                 size_t n;
     884             : 
     885      162525 :                 t = va_arg(ap, const char *);
     886      162525 :                 if (!t)
     887       81231 :                         break;
     888             : 
     889       81294 :                 n = strlen(t);
     890             : 
     891       81294 :                 if (need_separator)
     892       80312 :                         n += l_separator;
     893             : 
     894       81294 :                 if (n > ((size_t) -1) - l) {
     895           0 :                         va_end(ap);
     896           0 :                         return NULL;
     897             :                 }
     898             : 
     899       81294 :                 l += n;
     900       81294 :                 need_separator = true;
     901             :         }
     902       81231 :         va_end(ap);
     903             : 
     904       81231 :         need_separator = !isempty(*x);
     905             : 
     906       81231 :         r = realloc(*x, l+1);
     907       81231 :         if (!r)
     908           0 :                 return NULL;
     909             : 
     910       81231 :         p = r + f;
     911             : 
     912       81231 :         va_start(ap, separator);
     913       81294 :         for (;;) {
     914             :                 const char *t;
     915             : 
     916      162525 :                 t = va_arg(ap, const char *);
     917      162525 :                 if (!t)
     918       81231 :                         break;
     919             : 
     920       81294 :                 if (need_separator && separator)
     921         144 :                         p = stpcpy(p, separator);
     922             : 
     923       81294 :                 p = stpcpy(p, t);
     924             : 
     925       81294 :                 need_separator = true;
     926             :         }
     927       81231 :         va_end(ap);
     928             : 
     929       81231 :         assert(p == r + l);
     930             : 
     931       81231 :         *p = 0;
     932       81231 :         *x = r;
     933             : 
     934       81231 :         return r + l;
     935             : }
     936             : 
     937         129 : char *strrep(const char *s, unsigned n) {
     938             :         size_t l;
     939             :         char *r, *p;
     940             :         unsigned i;
     941             : 
     942         129 :         assert(s);
     943             : 
     944         129 :         l = strlen(s);
     945         129 :         p = r = malloc(l * n + 1);
     946         129 :         if (!r)
     947           0 :                 return NULL;
     948             : 
     949         620 :         for (i = 0; i < n; i++)
     950         491 :                 p = stpcpy(p, s);
     951             : 
     952         129 :         *p = 0;
     953         129 :         return r;
     954             : }
     955             : 
     956          48 : int split_pair(const char *s, const char *sep, char **l, char **r) {
     957             :         char *x, *a, *b;
     958             : 
     959          48 :         assert(s);
     960          48 :         assert(sep);
     961          48 :         assert(l);
     962          48 :         assert(r);
     963             : 
     964          48 :         if (isempty(sep))
     965           2 :                 return -EINVAL;
     966             : 
     967          46 :         x = strstr(s, sep);
     968          46 :         if (!x)
     969           1 :                 return -EINVAL;
     970             : 
     971          45 :         a = strndup(s, x - s);
     972          45 :         if (!a)
     973           0 :                 return -ENOMEM;
     974             : 
     975          45 :         b = strdup(x + strlen(sep));
     976          45 :         if (!b) {
     977           0 :                 free(a);
     978           0 :                 return -ENOMEM;
     979             :         }
     980             : 
     981          45 :         *l = a;
     982          45 :         *r = b;
     983             : 
     984          45 :         return 0;
     985             : }
     986             : 
     987        5480 : int free_and_strdup(char **p, const char *s) {
     988             :         char *t;
     989             : 
     990        5480 :         assert(p);
     991             : 
     992             :         /* Replaces a string pointer with a strdup()ed new string,
     993             :          * possibly freeing the old one. */
     994             : 
     995        5480 :         if (streq_ptr(*p, s))
     996          98 :                 return 0;
     997             : 
     998        5382 :         if (s) {
     999        5378 :                 t = strdup(s);
    1000        5378 :                 if (!t)
    1001           0 :                         return -ENOMEM;
    1002             :         } else
    1003           4 :                 t = NULL;
    1004             : 
    1005        5382 :         free(*p);
    1006        5382 :         *p = t;
    1007             : 
    1008        5382 :         return 1;
    1009             : }
    1010             : 
    1011         241 : int free_and_strndup(char **p, const char *s, size_t l) {
    1012             :         char *t;
    1013             : 
    1014         241 :         assert(p);
    1015         241 :         assert(s || l == 0);
    1016             : 
    1017             :         /* Replaces a string pointer with a strndup()ed new string,
    1018             :          * freeing the old one. */
    1019             : 
    1020         241 :         if (!*p && !s)
    1021           0 :                 return 0;
    1022             : 
    1023         241 :         if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
    1024          87 :                 return 0;
    1025             : 
    1026         154 :         if (s) {
    1027         153 :                 t = strndup(s, l);
    1028         153 :                 if (!t)
    1029           0 :                         return -ENOMEM;
    1030             :         } else
    1031           1 :                 t = NULL;
    1032             : 
    1033         154 :         free_and_replace(*p, t);
    1034         154 :         return 1;
    1035             : }
    1036             : 
    1037        1493 : bool string_is_safe(const char *p) {
    1038             :         const char *t;
    1039             : 
    1040        1493 :         if (!p)
    1041           0 :                 return false;
    1042             : 
    1043       16077 :         for (t = p; *t; t++) {
    1044       14598 :                 if (*t > 0 && *t < ' ') /* no control characters */
    1045           9 :                         return false;
    1046             : 
    1047       14589 :                 if (strchr(QUOTES "\\\x7f", *t))
    1048           5 :                         return false;
    1049             :         }
    1050             : 
    1051        1479 :         return true;
    1052             : }

Generated by: LCOV version 1.14