LCOV - code coverage report
Current view: top level - basic - escape.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 288 312 92.3 %
Date: 2019-08-22 15:41:25 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <stdlib.h>
       5             : #include <string.h>
       6             : 
       7             : #include "alloc-util.h"
       8             : #include "escape.h"
       9             : #include "hexdecoct.h"
      10             : #include "macro.h"
      11             : #include "utf8.h"
      12             : 
      13     3166591 : int cescape_char(char c, char *buf) {
      14     3166591 :         char *buf_old = buf;
      15             : 
      16             :         /* Needs space for 4 characters in the buffer */
      17             : 
      18     3166591 :         switch (c) {
      19             : 
      20           7 :                 case '\a':
      21           7 :                         *(buf++) = '\\';
      22           7 :                         *(buf++) = 'a';
      23           7 :                         break;
      24           9 :                 case '\b':
      25           9 :                         *(buf++) = '\\';
      26           9 :                         *(buf++) = 'b';
      27           9 :                         break;
      28           7 :                 case '\f':
      29           7 :                         *(buf++) = '\\';
      30           7 :                         *(buf++) = 'f';
      31           7 :                         break;
      32         104 :                 case '\n':
      33         104 :                         *(buf++) = '\\';
      34         104 :                         *(buf++) = 'n';
      35         104 :                         break;
      36           3 :                 case '\r':
      37           3 :                         *(buf++) = '\\';
      38           3 :                         *(buf++) = 'r';
      39           3 :                         break;
      40          26 :                 case '\t':
      41          26 :                         *(buf++) = '\\';
      42          26 :                         *(buf++) = 't';
      43          26 :                         break;
      44           2 :                 case '\v':
      45           2 :                         *(buf++) = '\\';
      46           2 :                         *(buf++) = 'v';
      47           2 :                         break;
      48          15 :                 case '\\':
      49          15 :                         *(buf++) = '\\';
      50          15 :                         *(buf++) = '\\';
      51          15 :                         break;
      52          60 :                 case '"':
      53          60 :                         *(buf++) = '\\';
      54          60 :                         *(buf++) = '"';
      55          60 :                         break;
      56          39 :                 case '\'':
      57          39 :                         *(buf++) = '\\';
      58          39 :                         *(buf++) = '\'';
      59          39 :                         break;
      60             : 
      61     3166319 :                 default:
      62             :                         /* For special chars we prefer octal over
      63             :                          * hexadecimal encoding, simply because glib's
      64             :                          * g_strescape() does the same */
      65     3166319 :                         if ((c < ' ') || (c >= 127)) {
      66        9852 :                                 *(buf++) = '\\';
      67        9852 :                                 *(buf++) = octchar((unsigned char) c >> 6);
      68        9852 :                                 *(buf++) = octchar((unsigned char) c >> 3);
      69        9852 :                                 *(buf++) = octchar((unsigned char) c);
      70             :                         } else
      71     3156467 :                                 *(buf++) = c;
      72     3166319 :                         break;
      73             :         }
      74             : 
      75     3166591 :         return buf - buf_old;
      76             : }
      77             : 
      78          77 : char *cescape_length(const char *s, size_t n) {
      79             :         const char *f;
      80             :         char *r, *t;
      81             : 
      82          77 :         assert(s || n == 0);
      83             : 
      84             :         /* Does C style string escaping. May be reversed with
      85             :          * cunescape(). */
      86             : 
      87          77 :         r = new(char, n*4 + 1);
      88          77 :         if (!r)
      89           0 :                 return NULL;
      90             : 
      91     3147657 :         for (f = s, t = r; f < s + n; f++)
      92     3147580 :                 t += cescape_char(*f, t);
      93             : 
      94          77 :         *t = 0;
      95             : 
      96          77 :         return r;
      97             : }
      98             : 
      99          40 : char *cescape(const char *s) {
     100          40 :         assert(s);
     101             : 
     102          40 :         return cescape_length(s, strlen(s));
     103             : }
     104             : 
     105         121 : int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) {
     106         121 :         int r = 1;
     107             : 
     108         121 :         assert(p);
     109         121 :         assert(ret);
     110             : 
     111             :         /* Unescapes C style. Returns the unescaped character in ret.
     112             :          * Sets *eight_bit to true if the escaped sequence either fits in
     113             :          * one byte in UTF-8 or is a non-unicode literal byte and should
     114             :          * instead be copied directly.
     115             :          */
     116             : 
     117         121 :         if (length != (size_t) -1 && length < 1)
     118           0 :                 return -EINVAL;
     119             : 
     120         121 :         switch (p[0]) {
     121             : 
     122           3 :         case 'a':
     123           3 :                 *ret = '\a';
     124           3 :                 break;
     125           7 :         case 'b':
     126           7 :                 *ret = '\b';
     127           7 :                 break;
     128           3 :         case 'f':
     129           3 :                 *ret = '\f';
     130           3 :                 break;
     131          10 :         case 'n':
     132          10 :                 *ret = '\n';
     133          10 :                 break;
     134           3 :         case 'r':
     135           3 :                 *ret = '\r';
     136           3 :                 break;
     137           5 :         case 't':
     138           5 :                 *ret = '\t';
     139           5 :                 break;
     140           3 :         case 'v':
     141           3 :                 *ret = '\v';
     142           3 :                 break;
     143           6 :         case '\\':
     144           6 :                 *ret = '\\';
     145           6 :                 break;
     146           7 :         case '"':
     147           7 :                 *ret = '"';
     148           7 :                 break;
     149           2 :         case '\'':
     150           2 :                 *ret = '\'';
     151           2 :                 break;
     152             : 
     153           3 :         case 's':
     154             :                 /* This is an extension of the XDG syntax files */
     155           3 :                 *ret = ' ';
     156           3 :                 break;
     157             : 
     158          11 :         case 'x': {
     159             :                 /* hexadecimal encoding */
     160             :                 int a, b;
     161             : 
     162          11 :                 if (length != (size_t) -1 && length < 3)
     163           4 :                         return -EINVAL;
     164             : 
     165           7 :                 a = unhexchar(p[1]);
     166           7 :                 if (a < 0)
     167           2 :                         return -EINVAL;
     168             : 
     169           5 :                 b = unhexchar(p[2]);
     170           5 :                 if (b < 0)
     171           0 :                         return -EINVAL;
     172             : 
     173             :                 /* Don't allow NUL bytes */
     174           5 :                 if (a == 0 && b == 0)
     175           1 :                         return -EINVAL;
     176             : 
     177           4 :                 *ret = (a << 4U) | b;
     178           4 :                 *eight_bit = true;
     179           4 :                 r = 3;
     180           4 :                 break;
     181             :         }
     182             : 
     183           5 :         case 'u': {
     184             :                 /* C++11 style 16bit unicode */
     185             : 
     186             :                 int a[4];
     187             :                 size_t i;
     188             :                 uint32_t c;
     189             : 
     190           5 :                 if (length != (size_t) -1 && length < 5)
     191           1 :                         return -EINVAL;
     192             : 
     193          25 :                 for (i = 0; i < 4; i++) {
     194          20 :                         a[i] = unhexchar(p[1 + i]);
     195          20 :                         if (a[i] < 0)
     196           0 :                                 return a[i];
     197             :                 }
     198             : 
     199           5 :                 c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
     200             : 
     201             :                 /* Don't allow 0 chars */
     202           5 :                 if (c == 0)
     203           1 :                         return -EINVAL;
     204             : 
     205           4 :                 *ret = c;
     206           4 :                 r = 5;
     207           4 :                 break;
     208             :         }
     209             : 
     210           4 :         case 'U': {
     211             :                 /* C++11 style 32bit unicode */
     212             : 
     213             :                 int a[8];
     214             :                 size_t i;
     215             :                 char32_t c;
     216             : 
     217           4 :                 if (length != (size_t) -1 && length < 9)
     218           0 :                         return -EINVAL;
     219             : 
     220          36 :                 for (i = 0; i < 8; i++) {
     221          32 :                         a[i] = unhexchar(p[1 + i]);
     222          32 :                         if (a[i] < 0)
     223           0 :                                 return a[i];
     224             :                 }
     225             : 
     226          12 :                 c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
     227           8 :                     ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] <<  8U) | ((uint32_t) a[6] <<  4U) |  (uint32_t) a[7];
     228             : 
     229             :                 /* Don't allow 0 chars */
     230           4 :                 if (c == 0)
     231           0 :                         return -EINVAL;
     232             : 
     233             :                 /* Don't allow invalid code points */
     234           4 :                 if (!unichar_is_valid(c))
     235           0 :                         return -EINVAL;
     236             : 
     237           4 :                 *ret = c;
     238           4 :                 r = 9;
     239           4 :                 break;
     240             :         }
     241             : 
     242          28 :         case '0':
     243             :         case '1':
     244             :         case '2':
     245             :         case '3':
     246             :         case '4':
     247             :         case '5':
     248             :         case '6':
     249             :         case '7': {
     250             :                 /* octal encoding */
     251             :                 int a, b, c;
     252             :                 char32_t m;
     253             : 
     254          28 :                 if (length != (size_t) -1 && length < 3)
     255           4 :                         return -EINVAL;
     256             : 
     257          24 :                 a = unoctchar(p[0]);
     258          24 :                 if (a < 0)
     259           0 :                         return -EINVAL;
     260             : 
     261          24 :                 b = unoctchar(p[1]);
     262          24 :                 if (b < 0)
     263           0 :                         return -EINVAL;
     264             : 
     265          24 :                 c = unoctchar(p[2]);
     266          24 :                 if (c < 0)
     267           0 :                         return -EINVAL;
     268             : 
     269             :                 /* don't allow NUL bytes */
     270          24 :                 if (a == 0 && b == 0 && c == 0)
     271           2 :                         return -EINVAL;
     272             : 
     273             :                 /* Don't allow bytes above 255 */
     274          22 :                 m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
     275          22 :                 if (m > 255)
     276           0 :                         return -EINVAL;
     277             : 
     278          22 :                 *ret = m;
     279          22 :                 *eight_bit = true;
     280          22 :                 r = 3;
     281          22 :                 break;
     282             :         }
     283             : 
     284          21 :         default:
     285          21 :                 return -EINVAL;
     286             :         }
     287             : 
     288          86 :         return r;
     289             : }
     290             : 
     291          93 : int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
     292             :         char *r, *t;
     293             :         const char *f;
     294             :         size_t pl;
     295             : 
     296          93 :         assert(s);
     297          93 :         assert(ret);
     298             : 
     299             :         /* Undoes C style string escaping, and optionally prefixes it. */
     300             : 
     301          93 :         pl = strlen_ptr(prefix);
     302             : 
     303          93 :         r = new(char, pl+length+1);
     304          93 :         if (!r)
     305           0 :                 return -ENOMEM;
     306             : 
     307          93 :         if (prefix)
     308           0 :                 memcpy(r, prefix, pl);
     309             : 
     310         732 :         for (f = s, t = r + pl; f < s + length; f++) {
     311             :                 size_t remaining;
     312         648 :                 bool eight_bit = false;
     313             :                 char32_t u;
     314             :                 int k;
     315             : 
     316         648 :                 remaining = s + length - f;
     317         648 :                 assert(remaining > 0);
     318             : 
     319         648 :                 if (*f != '\\') {
     320             :                         /* A literal, copy verbatim */
     321         572 :                         *(t++) = *f;
     322         579 :                         continue;
     323             :                 }
     324             : 
     325          76 :                 if (remaining == 1) {
     326           3 :                         if (flags & UNESCAPE_RELAX) {
     327             :                                 /* A trailing backslash, copy verbatim */
     328           1 :                                 *(t++) = *f;
     329           1 :                                 continue;
     330             :                         }
     331             : 
     332           2 :                         free(r);
     333           9 :                         return -EINVAL;
     334             :                 }
     335             : 
     336          73 :                 k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit);
     337          73 :                 if (k < 0) {
     338          13 :                         if (flags & UNESCAPE_RELAX) {
     339             :                                 /* Invalid escape code, let's take it literal then */
     340           6 :                                 *(t++) = '\\';
     341           6 :                                 continue;
     342             :                         }
     343             : 
     344           7 :                         free(r);
     345           7 :                         return k;
     346             :                 }
     347             : 
     348          60 :                 f += k;
     349          60 :                 if (eight_bit)
     350             :                         /* One byte? Set directly as specified */
     351          21 :                         *(t++) = u;
     352             :                 else
     353             :                         /* Otherwise encode as multi-byte UTF-8 */
     354          39 :                         t += utf8_encode_unichar(t, u);
     355             :         }
     356             : 
     357          84 :         *t = 0;
     358             : 
     359          84 :         *ret = r;
     360          84 :         return t - r;
     361             : }
     362             : 
     363          93 : int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
     364          93 :         return cunescape_length_with_prefix(s, length, NULL, flags, ret);
     365             : }
     366             : 
     367          93 : int cunescape(const char *s, UnescapeFlags flags, char **ret) {
     368          93 :         return cunescape_length(s, strlen(s), flags, ret);
     369             : }
     370             : 
     371         121 : char *xescape_full(const char *s, const char *bad, size_t console_width, bool eight_bits) {
     372             :         char *ans, *t, *prev, *prev2;
     373             :         const char *f;
     374             : 
     375             :         /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
     376             :          * reversed with cunescape(). If eight_bits is true, characters >= 127 are let through unchanged.
     377             :          * This corresponds to non-ASCII printable characters in pre-unicode encodings.
     378             :          *
     379             :          * If console_width is reached, output is truncated and "..." is appended. */
     380             : 
     381         121 :         if (console_width == 0)
     382           2 :                 return strdup("");
     383             : 
     384         119 :         ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
     385         119 :         if (!ans)
     386           0 :                 return NULL;
     387             : 
     388         119 :         memset(ans, '_', MIN(strlen(s), console_width) * 4);
     389         119 :         ans[MIN(strlen(s), console_width) * 4] = 0;
     390             : 
     391         119 :         for (f = s, t = prev = prev2 = ans; ; f++) {
     392        1237 :                 char *tmp_t = t;
     393             : 
     394        1237 :                 if (!*f) {
     395          20 :                         *t = 0;
     396          20 :                         return ans;
     397             :                 }
     398             : 
     399        1217 :                 if ((unsigned char) *f < ' ' || (!eight_bits && (unsigned char) *f >= 127) ||
     400         607 :                     *f == '\\' || strchr(bad, *f)) {
     401         837 :                         if ((size_t) (t - ans) + 4 > console_width)
     402          92 :                                 break;
     403             : 
     404         745 :                         *(t++) = '\\';
     405         745 :                         *(t++) = 'x';
     406         745 :                         *(t++) = hexchar(*f >> 4);
     407         745 :                         *(t++) = hexchar(*f);
     408             :                 } else {
     409         380 :                         if ((size_t) (t - ans) + 1 > console_width)
     410           7 :                                 break;
     411             : 
     412         373 :                         *(t++) = *f;
     413             :                 }
     414             : 
     415             :                 /* We might need to go back two cycles to fit three dots, so remember two positions */
     416        1118 :                 prev2 = prev;
     417        1118 :                 prev = tmp_t;
     418             :         }
     419             : 
     420             :         /* We can just write where we want, since chars are one-byte */
     421          99 :         size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
     422             :         size_t off;
     423          99 :         if (console_width - c >= (size_t) (t - ans))
     424          23 :                 off = (size_t) (t - ans);
     425          76 :         else if (console_width - c >= (size_t) (prev - ans))
     426          66 :                 off = (size_t) (prev - ans);
     427          10 :         else if (console_width - c >= (size_t) (prev2 - ans))
     428           9 :                 off = (size_t) (prev2 - ans);
     429             :         else
     430           1 :                 off = console_width - c;
     431          99 :         assert(off <= (size_t) (t - ans));
     432             : 
     433          99 :         memcpy(ans + off, "...", c);
     434          99 :         ans[off + c] = '\0';
     435          99 :         return ans;
     436             : }
     437             : 
     438          10 : char *escape_non_printable_full(const char *str, size_t console_width, bool eight_bit) {
     439          10 :         if (eight_bit)
     440           0 :                 return xescape_full(str, "", console_width, true);
     441             :         else
     442          10 :                 return utf8_escape_non_printable_full(str, console_width);
     443             : }
     444             : 
     445          30 : char *octescape(const char *s, size_t len) {
     446             :         char *r, *t;
     447             :         const char *f;
     448             : 
     449             :         /* Escapes all chars in bad, in addition to \ and " chars,
     450             :          * in \nnn style escaping. */
     451             : 
     452          30 :         r = new(char, len * 4 + 1);
     453          30 :         if (!r)
     454           0 :                 return NULL;
     455             : 
     456         390 :         for (f = s, t = r; f < s + len; f++) {
     457             : 
     458         360 :                 if (*f < ' ' || *f >= 127 || IN_SET(*f, '\\', '"')) {
     459           0 :                         *(t++) = '\\';
     460           0 :                         *(t++) = '0' + (*f >> 6);
     461           0 :                         *(t++) = '0' + ((*f >> 3) & 8);
     462           0 :                         *(t++) = '0' + (*f & 8);
     463             :                 } else
     464         360 :                         *(t++) = *f;
     465             :         }
     466             : 
     467          30 :         *t = 0;
     468             : 
     469          30 :         return r;
     470             : 
     471             : }
     472             : 
     473          26 : static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) {
     474          26 :         assert(bad);
     475             : 
     476         144 :         for (; *s; s++) {
     477         118 :                 if (escape_tab_nl && IN_SET(*s, '\n', '\t')) {
     478           3 :                         *(t++) = '\\';
     479           3 :                         *(t++) = *s == '\n' ? 'n' : 't';
     480           3 :                         continue;
     481             :                 }
     482             : 
     483         115 :                 if (*s == '\\' || strchr(bad, *s))
     484          14 :                         *(t++) = '\\';
     485             : 
     486         115 :                 *(t++) = *s;
     487             :         }
     488             : 
     489          26 :         return t;
     490             : }
     491             : 
     492           8 : char *shell_escape(const char *s, const char *bad) {
     493             :         char *r, *t;
     494             : 
     495           8 :         r = new(char, strlen(s)*2+1);
     496           8 :         if (!r)
     497           0 :                 return NULL;
     498             : 
     499           8 :         t = strcpy_backslash_escaped(r, s, bad, false);
     500           8 :         *t = 0;
     501             : 
     502           8 :         return r;
     503             : }
     504             : 
     505          22 : char* shell_maybe_quote(const char *s, EscapeStyle style) {
     506             :         const char *p;
     507             :         char *r, *t;
     508             : 
     509          22 :         assert(s);
     510             : 
     511             :         /* Encloses a string in quotes if necessary to make it OK as a shell
     512             :          * string. Note that we treat benign UTF-8 characters as needing
     513             :          * escaping too, but that should be OK. */
     514             : 
     515          72 :         for (p = s; *p; p++)
     516          68 :                 if (*p <= ' ' ||
     517          58 :                     *p >= 127 ||
     518          58 :                     strchr(SHELL_NEED_QUOTES, *p))
     519             :                         break;
     520             : 
     521          22 :         if (!*p)
     522           4 :                 return strdup(s);
     523             : 
     524          18 :         r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1);
     525          18 :         if (!r)
     526           0 :                 return NULL;
     527             : 
     528          18 :         t = r;
     529          18 :         if (style == ESCAPE_BACKSLASH)
     530           9 :                 *(t++) = '"';
     531           9 :         else if (style == ESCAPE_POSIX) {
     532           9 :                 *(t++) = '$';
     533           9 :                 *(t++) = '\'';
     534             :         } else
     535           0 :                 assert_not_reached("Bad EscapeStyle");
     536             : 
     537          18 :         t = mempcpy(t, s, p - s);
     538             : 
     539          18 :         if (style == ESCAPE_BACKSLASH)
     540           9 :                 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false);
     541             :         else
     542           9 :                 t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true);
     543             : 
     544          18 :         if (style == ESCAPE_BACKSLASH)
     545           9 :                 *(t++) = '"';
     546             :         else
     547           9 :                 *(t++) = '\'';
     548          18 :         *t = 0;
     549             : 
     550          18 :         return r;
     551             : }

Generated by: LCOV version 1.14