LCOV - code coverage report
Current view: top level - basic - extract-word.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 133 139 95.7 %
Date: 2019-08-23 13:36:53 Functions: 3 3 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 101 120 84.2 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <stdarg.h>
       5                 :            : #include <stdbool.h>
       6                 :            : #include <stddef.h>
       7                 :            : #include <stdint.h>
       8                 :            : #include <stdlib.h>
       9                 :            : #include <string.h>
      10                 :            : #include <syslog.h>
      11                 :            : 
      12                 :            : #include "alloc-util.h"
      13                 :            : #include "escape.h"
      14                 :            : #include "extract-word.h"
      15                 :            : #include "log.h"
      16                 :            : #include "macro.h"
      17                 :            : #include "string-util.h"
      18                 :            : #include "utf8.h"
      19                 :            : 
      20                 :      58856 : int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) {
      21                 :      58856 :         _cleanup_free_ char *s = NULL;
      22                 :      58856 :         size_t allocated = 0, sz = 0;
      23                 :            :         char c;
      24                 :            :         int r;
      25                 :            : 
      26                 :      58856 :         char quote = 0;                 /* 0 or ' or " */
      27                 :      58856 :         bool backslash = false;         /* whether we've just seen a backslash */
      28                 :            : 
      29         [ -  + ]:      58856 :         assert(p);
      30         [ -  + ]:      58856 :         assert(ret);
      31                 :            : 
      32                 :            :         /* Bail early if called after last value or with no input */
      33         [ +  + ]:      58856 :         if (!*p)
      34                 :      10334 :                 goto finish;
      35                 :      48522 :         c = **p;
      36                 :            : 
      37         [ +  + ]:      48522 :         if (!separators)
      38                 :      23574 :                 separators = WHITESPACE;
      39                 :            : 
      40                 :            :         /* Parses the first word of a string, and returns it in
      41                 :            :          * *ret. Removes all quotes in the process. When parsing fails
      42                 :            :          * (because of an uneven number of quotes or similar), leaves
      43                 :            :          * the pointer *p at the first invalid character. */
      44                 :            : 
      45         [ +  + ]:      48522 :         if (flags & EXTRACT_DONT_COALESCE_SEPARATORS)
      46         [ -  + ]:       8784 :                 if (!GREEDY_REALLOC(s, allocated, sz+1))
      47                 :          0 :                         return -ENOMEM;
      48                 :            : 
      49                 :        396 :         for (;; (*p)++, c = **p) {
      50         [ +  + ]:      48918 :                 if (c == 0)
      51                 :         96 :                         goto finish_force_terminate;
      52         [ +  + ]:      48822 :                 else if (strchr(separators, c)) {
      53         [ +  + ]:        420 :                         if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
      54                 :         24 :                                 (*p)++;
      55                 :         24 :                                 goto finish_force_next;
      56                 :            :                         }
      57                 :            :                 } else {
      58                 :            :                         /* We found a non-blank character, so we will always
      59                 :            :                          * want to return a string (even if it is empty),
      60                 :            :                          * allocate it here. */
      61         [ -  + ]:      48402 :                         if (!GREEDY_REALLOC(s, allocated, sz+1))
      62                 :          0 :                                 return -ENOMEM;
      63                 :      48402 :                         break;
      64                 :            :                 }
      65                 :            :         }
      66                 :            : 
      67                 :       1720 :         for (;; (*p)++, c = **p) {
      68         [ +  + ]:      50122 :                 if (backslash) {
      69         [ -  + ]:        364 :                         if (!GREEDY_REALLOC(s, allocated, sz+7))
      70                 :          0 :                                 return -ENOMEM;
      71                 :            : 
      72         [ +  + ]:        364 :                         if (c == 0) {
      73   [ +  +  +  + ]:        108 :                                 if ((flags & EXTRACT_CUNESCAPE_RELAX) &&
      74         [ +  + ]:         24 :                                     (!quote || flags & EXTRACT_RELAX)) {
      75                 :            :                                         /* If we find an unquoted trailing backslash and we're in
      76                 :            :                                          * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the
      77                 :            :                                          * output.
      78                 :            :                                          *
      79                 :            :                                          * Unbalanced quotes will only be allowed in EXTRACT_RELAX
      80                 :            :                                          * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them.
      81                 :            :                                          */
      82                 :         32 :                                         s[sz++] = '\\';
      83                 :         32 :                                         goto finish_force_terminate;
      84                 :            :                                 }
      85         [ +  + ]:         76 :                                 if (flags & EXTRACT_RELAX)
      86                 :         20 :                                         goto finish_force_terminate;
      87                 :         56 :                                 return -EINVAL;
      88                 :            :                         }
      89                 :            : 
      90         [ +  + ]:        256 :                         if (flags & EXTRACT_CUNESCAPE) {
      91                 :        192 :                                 bool eight_bit = false;
      92                 :            :                                 char32_t u;
      93                 :            : 
      94                 :        192 :                                 r = cunescape_one(*p, (size_t) -1, &u, &eight_bit);
      95         [ +  + ]:        192 :                                 if (r < 0) {
      96         [ +  + ]:         88 :                                         if (flags & EXTRACT_CUNESCAPE_RELAX) {
      97                 :         56 :                                                 s[sz++] = '\\';
      98                 :         56 :                                                 s[sz++] = c;
      99                 :            :                                         } else
     100                 :         32 :                                                 return -EINVAL;
     101                 :            :                                 } else {
     102                 :        104 :                                         (*p) += r - 1;
     103                 :            : 
     104         [ +  + ]:        104 :                                         if (eight_bit)
     105                 :         20 :                                                 s[sz++] = u;
     106                 :            :                                         else
     107                 :         84 :                                                 sz += utf8_encode_unichar(s + sz, u);
     108                 :            :                                 }
     109                 :            :                         } else
     110                 :         64 :                                 s[sz++] = c;
     111                 :            : 
     112                 :        224 :                         backslash = false;
     113                 :            : 
     114         [ +  + ]:      49758 :                 } else if (quote) {     /* inside either single or double quotes */
     115                 :       4148 :                         for (;; (*p)++, c = **p) {
     116         [ +  + ]:       4832 :                                 if (c == 0) {
     117         [ +  + ]:        104 :                                         if (flags & EXTRACT_RELAX)
     118                 :         12 :                                                 goto finish_force_terminate;
     119                 :         92 :                                         return -EINVAL;
     120         [ +  + ]:       4728 :                                 } else if (c == quote) {        /* found the end quote */
     121                 :        488 :                                         quote = 0;
     122                 :        488 :                                         break;
     123   [ +  +  +  + ]:       4240 :                                 } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
     124                 :         92 :                                         backslash = true;
     125                 :         92 :                                         break;
     126                 :            :                                 } else {
     127         [ -  + ]:       4148 :                                         if (!GREEDY_REALLOC(s, allocated, sz+2))
     128                 :          0 :                                                 return -ENOMEM;
     129                 :            : 
     130                 :       4148 :                                         s[sz++] = c;
     131                 :            :                                 }
     132                 :            :                         }
     133                 :            : 
     134                 :            :                 } else {
     135                 :     769740 :                         for (;; (*p)++, c = **p) {
     136         [ +  + ]:     818814 :                                 if (c == 0)
     137                 :      10946 :                                         goto finish_force_terminate;
     138   [ +  +  +  +  :     807868 :                                 else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
                   +  + ]
     139                 :        644 :                                         quote = c;
     140                 :        644 :                                         break;
     141   [ +  +  +  + ]:     807224 :                                 } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
     142                 :        272 :                                         backslash = true;
     143                 :        272 :                                         break;
     144         [ +  + ]:     806952 :                                 } else if (strchr(separators, c)) {
     145         [ +  + ]:      37212 :                                         if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) {
     146                 :       7000 :                                                 (*p)++;
     147                 :       7000 :                                                 goto finish_force_next;
     148                 :            :                                         }
     149                 :            :                                         /* Skip additional coalesced separators. */
     150                 :      41088 :                                         for (;; (*p)++, c = **p) {
     151         [ +  + ]:      71300 :                                                 if (c == 0)
     152                 :        176 :                                                         goto finish_force_terminate;
     153         [ +  + ]:      71124 :                                                 if (!strchr(separators, c))
     154                 :      30036 :                                                         break;
     155                 :            :                                         }
     156                 :      30036 :                                         goto finish;
     157                 :            : 
     158                 :            :                                 } else {
     159         [ -  + ]:     769740 :                                         if (!GREEDY_REALLOC(s, allocated, sz+2))
     160                 :          0 :                                                 return -ENOMEM;
     161                 :            : 
     162                 :     769740 :                                         s[sz++] = c;
     163                 :            :                                 }
     164                 :            :                         }
     165                 :            :                 }
     166                 :            :         }
     167                 :            : 
     168                 :      11282 : finish_force_terminate:
     169                 :      11282 :         *p = NULL;
     170                 :      51652 : finish:
     171         [ +  + ]:      51652 :         if (!s) {
     172                 :      10414 :                 *p = NULL;
     173                 :      10414 :                 *ret = NULL;
     174                 :      10414 :                 return 0;
     175                 :            :         }
     176                 :            : 
     177                 :      41238 : finish_force_next:
     178                 :      48262 :         s[sz] = 0;
     179                 :      48262 :         *ret = TAKE_PTR(s);
     180                 :            : 
     181                 :      48262 :         return 1;
     182                 :            : }
     183                 :            : 
     184                 :        564 : int extract_first_word_and_warn(
     185                 :            :                 const char **p,
     186                 :            :                 char **ret,
     187                 :            :                 const char *separators,
     188                 :            :                 ExtractFlags flags,
     189                 :            :                 const char *unit,
     190                 :            :                 const char *filename,
     191                 :            :                 unsigned line,
     192                 :            :                 const char *rvalue) {
     193                 :            : 
     194                 :            :         /* Try to unquote it, if it fails, warn about it and try again
     195                 :            :          * but this time using EXTRACT_CUNESCAPE_RELAX to keep the
     196                 :            :          * backslashes verbatim in invalid escape sequences. */
     197                 :            : 
     198                 :            :         const char *save;
     199                 :            :         int r;
     200                 :            : 
     201                 :        564 :         save = *p;
     202                 :        564 :         r = extract_first_word(p, ret, separators, flags);
     203         [ +  + ]:        564 :         if (r >= 0)
     204                 :        500 :                 return r;
     205                 :            : 
     206   [ +  -  +  - ]:         64 :         if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) {
     207                 :            : 
     208                 :            :                 /* Retry it with EXTRACT_CUNESCAPE_RELAX. */
     209                 :         64 :                 *p = save;
     210                 :         64 :                 r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
     211         [ +  + ]:         64 :                 if (r >= 0) {
     212                 :            :                         /* It worked this time, hence it must have been an invalid escape sequence. */
     213         [ +  - ]:         36 :                         log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
     214                 :         36 :                         return r;
     215                 :            :                 }
     216                 :            : 
     217                 :            :                 /* If it's still EINVAL; then it must be unbalanced quoting, report this. */
     218         [ +  - ]:         28 :                 if (r == -EINVAL)
     219         [ +  - ]:         28 :                         return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue);
     220                 :            :         }
     221                 :            : 
     222                 :            :         /* Can be any error, report it */
     223         [ #  # ]:          0 :         return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue);
     224                 :            : }
     225                 :            : 
     226                 :            : /* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing
     227                 :            :  * an object that undergoes default argument promotion as an argument to va_start).
     228                 :            :  * Let's make sure that ExtractFlags fits into an unsigned int. */
     229                 :            : assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned));
     230                 :            : 
     231                 :        360 : int extract_many_words(const char **p, const char *separators, unsigned flags, ...) {
     232                 :            :         va_list ap;
     233                 :            :         char **l;
     234                 :        360 :         int n = 0, i, c, r;
     235                 :            : 
     236                 :            :         /* Parses a number of words from a string, stripping any
     237                 :            :          * quotes if necessary. */
     238                 :            : 
     239         [ -  + ]:        360 :         assert(p);
     240                 :            : 
     241                 :            :         /* Count how many words are expected */
     242                 :        360 :         va_start(ap, flags);
     243                 :            :         for (;;) {
     244         [ +  + ]:       2396 :                 if (!va_arg(ap, char **))
     245                 :        360 :                         break;
     246                 :       2036 :                 n++;
     247                 :            :         }
     248                 :        360 :         va_end(ap);
     249                 :            : 
     250         [ +  + ]:        360 :         if (n <= 0)
     251                 :          4 :                 return 0;
     252                 :            : 
     253                 :            :         /* Read all words into a temporary array */
     254   [ -  +  -  +  :        356 :         l = newa0(char*, n);
                   -  + ]
     255         [ +  + ]:       1928 :         for (c = 0; c < n; c++) {
     256                 :            : 
     257                 :       1716 :                 r = extract_first_word(p, &l[c], separators, flags);
     258         [ +  + ]:       1716 :                 if (r < 0) {
     259                 :            :                         int j;
     260                 :            : 
     261         [ +  + ]:         16 :                         for (j = 0; j < c; j++)
     262                 :          8 :                                 free(l[j]);
     263                 :            : 
     264                 :          8 :                         return r;
     265                 :            :                 }
     266                 :            : 
     267         [ +  + ]:       1708 :                 if (r == 0)
     268                 :        136 :                         break;
     269                 :            :         }
     270                 :            : 
     271                 :            :         /* If we managed to parse all words, return them in the passed
     272                 :            :          * in parameters */
     273                 :        348 :         va_start(ap, flags);
     274         [ +  + ]:       2336 :         for (i = 0; i < n; i++) {
     275                 :            :                 char **v;
     276                 :            : 
     277                 :       1988 :                 v = va_arg(ap, char **);
     278         [ -  + ]:       1988 :                 assert(v);
     279                 :            : 
     280                 :       1988 :                 *v = l[i];
     281                 :            :         }
     282                 :        348 :         va_end(ap);
     283                 :            : 
     284                 :        348 :         return c;
     285                 :            : }

Generated by: LCOV version 1.14