root/lib/strutil/strescape.c

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

DEFINITIONS

This source file includes following definitions.
  1. strutils_escape
  2. strutils_unescape
  3. strutils_shell_escape
  4. strutils_glob_escape
  5. strutils_regex_escape
  6. strutils_shell_unescape
  7. strutils_glob_unescape
  8. strutils_regex_unescape
  9. strutils_is_char_escaped

   1 /*
   2    Functions for escaping and unescaping strings
   3 
   4    Copyright (C) 2009-2024
   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 <http://www.gnu.org/licenses/>.
  25  */
  26 
  27 #include <config.h>
  28 
  29 #include "lib/global.h"
  30 #include "lib/strescape.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 strutils_escape (const char *src, gsize src_len, const char *escaped_chars,
     /* [previous][next][first][last][top][bottom][index][help]  */
  56                  gboolean escape_non_printable)
  57 {
  58     GString *ret;
  59     gsize curr_index;
  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     if (src_len == (gsize) (-1))
  70         src_len = strlen (src);
  71 
  72     for (curr_index = 0; curr_index < src_len; curr_index++)
  73     {
  74         if (escape_non_printable)
  75         {
  76             switch (src[curr_index])
  77             {
  78             case '\n':
  79                 g_string_append (ret, "\\n");
  80                 continue;
  81             case '\t':
  82                 g_string_append (ret, "\\t");
  83                 continue;
  84             case '\b':
  85                 g_string_append (ret, "\\b");
  86                 continue;
  87             case '\0':
  88                 g_string_append (ret, "\\0");
  89                 continue;
  90             default:
  91                 break;
  92             }
  93         }
  94 
  95         if (strchr (escaped_chars, (int) src[curr_index]))
  96             g_string_append_c (ret, '\\');
  97 
  98         g_string_append_c (ret, src[curr_index]);
  99     }
 100     return g_string_free (ret, FALSE);
 101 }
 102 
 103 /* --------------------------------------------------------------------------------------------- */
 104 
 105 char *
 106 strutils_unescape (const char *src, gsize src_len, const char *unescaped_chars,
     /* [previous][next][first][last][top][bottom][index][help]  */
 107                    gboolean unescape_non_printable)
 108 {
 109     GString *ret;
 110     gsize curr_index;
 111 
 112     if (src == NULL)
 113         return NULL;
 114 
 115     if (*src == '\0')
 116         return strdup ("");
 117 
 118     ret = g_string_sized_new (16);
 119 
 120     if (src_len == (gsize) (-1))
 121         src_len = strlen (src);
 122     src_len--;
 123 
 124     for (curr_index = 0; curr_index < src_len; curr_index++)
 125     {
 126         if (src[curr_index] != '\\')
 127         {
 128             g_string_append_c (ret, src[curr_index]);
 129             continue;
 130         }
 131 
 132         curr_index++;
 133 
 134         if (unescaped_chars == ESCAPE_SHELL_CHARS && src[curr_index] == '$')
 135         {
 136             /* special case: \$ is used to disallow variable substitution */
 137             g_string_append_c (ret, '\\');
 138         }
 139         else
 140         {
 141             if (unescape_non_printable)
 142             {
 143                 switch (src[curr_index])
 144                 {
 145                 case 'n':
 146                     g_string_append_c (ret, '\n');
 147                     continue;
 148                 case 't':
 149                     g_string_append_c (ret, '\t');
 150                     continue;
 151                 case 'b':
 152                     g_string_append_c (ret, '\b');
 153                     continue;
 154                 case '0':
 155                     g_string_append_c (ret, '\0');
 156                     continue;
 157                 default:
 158                     break;
 159                 }
 160             }
 161 
 162             if (strchr (unescaped_chars, (int) src[curr_index]) == NULL)
 163                 g_string_append_c (ret, '\\');
 164         }
 165 
 166         g_string_append_c (ret, src[curr_index]);
 167     }
 168     g_string_append_c (ret, src[curr_index]);
 169 
 170     return g_string_free (ret, FALSE);
 171 }
 172 
 173 /* --------------------------------------------------------------------------------------------- */
 174 
 175 /**
 176  * To be compatible with the general posix command lines we have to escape
 177  * strings for the command line
 178  *
 179  * @param src string for escaping
 180  *
 181  * @return escaped string (which needs to be freed later) or NULL when NULL string is passed.
 182  */
 183 
 184 char *
 185 strutils_shell_escape (const char *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 186 {
 187     return strutils_escape (src, -1, ESCAPE_SHELL_CHARS, FALSE);
 188 }
 189 
 190 /* --------------------------------------------------------------------------------------------- */
 191 
 192 char *
 193 strutils_glob_escape (const char *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 194 {
 195     return strutils_escape (src, -1, ESCAPE_GLOB_CHARS, TRUE);
 196 }
 197 
 198 /* --------------------------------------------------------------------------------------------- */
 199 
 200 char *
 201 strutils_regex_escape (const char *src)
     /* [previous][next][first][last][top][bottom][index][help]  */
 202 {
 203     return strutils_escape (src, -1, ESCAPE_REGEX_CHARS, TRUE);
 204 }
 205 
 206 /* --------------------------------------------------------------------------------------------- */
 207 
 208 /**
 209  * Unescape paths or other strings for e.g the internal cd
 210  * shell-unescape within a given buffer (writing to it!)
 211  *
 212  * @param text string for unescaping
 213  *
 214  * @return unescaped string (which needs to be freed)
 215  */
 216 
 217 char *
 218 strutils_shell_unescape (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 219 {
 220     return strutils_unescape (text, -1, ESCAPE_SHELL_CHARS, TRUE);
 221 }
 222 
 223 /* --------------------------------------------------------------------------------------------- */
 224 
 225 char *
 226 strutils_glob_unescape (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 227 {
 228     return strutils_unescape (text, -1, ESCAPE_GLOB_CHARS, TRUE);
 229 }
 230 
 231 /* --------------------------------------------------------------------------------------------- */
 232 char *
 233 strutils_regex_unescape (const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 234 {
 235     return strutils_unescape (text, -1, ESCAPE_REGEX_CHARS, TRUE);
 236 }
 237 
 238 /* --------------------------------------------------------------------------------------------- */
 239 
 240 /**
 241  * Check if char in pointer contain escape'd chars
 242  *
 243  * @param start string for checking
 244  * @param current pointer to checked character
 245  *
 246  * @return TRUE if string contain escaped chars otherwise return FALSE
 247  */
 248 
 249 gboolean
 250 strutils_is_char_escaped (const char *start, const char *current)
     /* [previous][next][first][last][top][bottom][index][help]  */
 251 {
 252     int num_esc = 0;
 253 
 254     if (start == NULL || current == NULL || current <= start)
 255         return FALSE;
 256 
 257     current--;
 258     while (current >= start && *current == '\\')
 259     {
 260         num_esc++;
 261         current--;
 262     }
 263     return (gboolean) num_esc % 2;
 264 }
 265 
 266 /* --------------------------------------------------------------------------------------------- */

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