Manual pages: mcmcdiffmceditmcview

root/lib/strutil/strescape.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. str_escape
  2. str_unescape
  3. str_shell_escape
  4. str_glob_escape
  5. str_regex_escape
  6. str_shell_unescape
  7. str_glob_unescape
  8. str_regex_unescape
  9. str_is_char_escaped

   1 /*
   2    Functions for escaping and unescaping strings
   3 
   4    Copyright (C) 2009-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Slava Zanko <slavazanko@gmail.com>, 2009;
   9    Patrick Winnertz <winnie@debian.org>, 2009
  10 
  11    This file is part of the Midnight Commander.
  12 
  13    The Midnight Commander is free software: you can redistribute it
  14    and/or modify it under the terms of the GNU General Public License as
  15    published by the Free Software Foundation, either version 3 of the License,
  16    or (at your option) any later version.
  17 
  18    The Midnight Commander is distributed in the hope that it will be useful,
  19    but WITHOUT ANY WARRANTY; without even the implied warranty of
  20    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21    GNU General Public License for more details.
  22 
  23    You should have received a copy of the GNU General Public License
  24    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include "lib/global.h"
  30 #include "lib/strutil.h"
  31 
  32 /*** global variables ****************************************************************************/
  33 
  34 /*** file scope macro definitions ****************************************************************/
  35 
  36 /*** file scope type declarations ****************************************************************/
  37 
  38 /*** forward declarations (file scope functions) *************************************************/
  39 
  40 /*** file scope variables ************************************************************************/
  41 
  42 static const char ESCAPE_SHELL_CHARS[] = " !#$%()&{}[]`?|<>;*\\\"'";
  43 static const char ESCAPE_REGEX_CHARS[] = "^!#$%()&{}[]`?|<>;*+.\\";
  44 static const char ESCAPE_GLOB_CHARS[] = "$*\\?";
  45 
  46 /* --------------------------------------------------------------------------------------------- */
  47 /*** file scope functions ************************************************************************/
  48 /* --------------------------------------------------------------------------------------------- */
  49 
  50 /* --------------------------------------------------------------------------------------------- */
  51 /*** public functions ****************************************************************************/
  52 /* --------------------------------------------------------------------------------------------- */
  53 
  54 char *
  55 str_escape (const char *src, const ssize_t src_len, const char *escaped_chars,
     /* [previous][next][first][last][top][bottom][index][help]  */
  56             const gboolean escape_non_printable)
  57 {
  58     GString *ret;
  59 
  60     // do NOT break allocation semantics
  61     if (src == NULL)
  62         return NULL;
  63 
  64     if (*src == '\0')
  65         return strdup ("");
  66 
  67     ret = g_string_new ("");
  68 
  69     const size_t src_len1 = src_len < 0 ? strlen (src) : (size_t) src_len;
  70 
  71     for (size_t curr_index = 0; curr_index < src_len1; curr_index++)
  72     {
  73         if (escape_non_printable)
  74         {
  75             switch (src[curr_index])
  76             {
  77             case '\n':
  78                 g_string_append (ret, "\\n");
  79                 continue;
  80             case '\t':
  81                 g_string_append (ret, "\\t");
  82                 continue;
  83             case '\b':
  84                 g_string_append (ret, "\\b");
  85                 continue;
  86             case '\0':
  87                 g_string_append (ret, "\\0");
  88                 continue;
  89             default:
  90                 break;
  91             }
  92         }
  93 
  94         if (strchr (escaped_chars, (int) src[curr_index]))
  95             g_string_append_c (ret, '\\');
  96 
  97         g_string_append_c (ret, src[curr_index]);
  98     }
  99     return g_string_free (ret, FALSE);
 100 }
 101 
 102 /* --------------------------------------------------------------------------------------------- */
 103 
 104 char *
 105 str_unescape (const char *src, const ssize_t src_len, const char *unescaped_chars,
     /* [previous][next][first][last][top][bottom][index][help]  */
 106               const gboolean unescape_non_printable)
 107 {
 108     GString *ret;
 109     size_t curr_index;
 110 
 111     if (src == NULL)
 112         return NULL;
 113 
 114     if (*src == '\0')
 115         return strdup ("");
 116 
 117     ret = g_string_sized_new (16);
 118 
 119     const size_t src_len1 = src_len < 0 ? strlen (src) : (size_t) src_len;
 120 
 121     for (curr_index = 0; curr_index < src_len1 - 1; curr_index++)
 122     {
 123         if (src[curr_index] != '\\')
 124         {
 125             g_string_append_c (ret, src[curr_index]);
 126             continue;
 127         }
 128 
 129         curr_index++;
 130 
 131         if (unescaped_chars == ESCAPE_SHELL_CHARS && src[curr_index] == '$')
 132         {
 133             // special case: \$ is used to disallow variable substitution
 134             g_string_append_c (ret, '\\');
 135         }
 136         else
 137         {
 138             if (unescape_non_printable)
 139             {
 140                 switch (src[curr_index])
 141                 {
 142                 case 'n':
 143                     g_string_append_c (ret, '\n');
 144                     continue;
 145                 case 't':
 146                     g_string_append_c (ret, '\t');
 147                     continue;
 148                 case 'b':
 149                     g_string_append_c (ret, '\b');
 150                     continue;
 151                 case '0':
 152                     g_string_append_c (ret, '\0');
 153                     continue;
 154                 default:
 155                     break;
 156                 }
 157             }
 158 
 159             if (strchr (unescaped_chars, (int) src[curr_index]) == NULL)
 160                 g_string_append_c (ret, '\\');
 161         }
 162 
 163         g_string_append_c (ret, src[curr_index]);
 164     }
 165     g_string_append_c (ret, src[curr_index]);
 166 
 167     return g_string_free (ret, FALSE);
 168 }
 169 
 170 /* --------------------------------------------------------------------------------------------- */
 171 
 172 /**
 173  * To be compatible with the general posix command lines we have to escape
 174  * strings for the command line
 175  *
 176  * @param src string for escaping
 177  *
 178  * @return escaped string (which needs to be freed later) or NULL when NULL string is passed.
 179  */
 180 
 181 char *
 182 str_shell_escape (const char *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 183 {
 184     return str_escape (src, -1, ESCAPE_SHELL_CHARS, FALSE);
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 
 189 char *
 190 str_glob_escape (const char *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 191 {
 192     return str_escape (src, -1, ESCAPE_GLOB_CHARS, TRUE);
 193 }
 194 
 195 /* --------------------------------------------------------------------------------------------- */
 196 
 197 char *
 198 str_regex_escape (const char *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 199 {
 200     return str_escape (src, -1, ESCAPE_REGEX_CHARS, TRUE);
 201 }
 202 
 203 /* --------------------------------------------------------------------------------------------- */
 204 
 205 /**
 206  * Unescape paths or other strings for e.g the internal cd
 207  * shell-unescape within a given buffer (writing to it!)
 208  *
 209  * @param text string for unescaping
 210  *
 211  * @return unescaped string (which needs to be freed)
 212  */
 213 
 214 char *
 215 str_shell_unescape (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 216 {
 217     return str_unescape (text, -1, ESCAPE_SHELL_CHARS, TRUE);
 218 }
 219 
 220 /* --------------------------------------------------------------------------------------------- */
 221 
 222 char *
 223 str_glob_unescape (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 224 {
 225     return str_unescape (text, -1, ESCAPE_GLOB_CHARS, TRUE);
 226 }
 227 
 228 /* --------------------------------------------------------------------------------------------- */
 229 char *
 230 str_regex_unescape (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 231 {
 232     return str_unescape (text, -1, ESCAPE_REGEX_CHARS, TRUE);
 233 }
 234 
 235 /* --------------------------------------------------------------------------------------------- */
 236 
 237 /**
 238  * Check if char in pointer contain escape'd chars
 239  *
 240  * @param start string for checking
 241  * @param current pointer to checked character
 242  *
 243  * @return TRUE if string contain escaped chars otherwise return FALSE
 244  */
 245 
 246 gboolean
 247 str_is_char_escaped (const char *start, const char *current)
     /* [previous][next][first][last][top][bottom][index][help]  */
 248 {
 249     int num_esc = 0;
 250 
 251     if (start == NULL || current == NULL || current <= start)
 252         return FALSE;
 253 
 254     current--;
 255     while (current >= start && *current == '\\')
 256     {
 257         num_esc++;
 258         current--;
 259     }
 260     return (gboolean) num_esc % 2;
 261 }
 262 
 263 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */