mirror of https://github.com/MidnightCommander/mc
267 lines
7.7 KiB
C
267 lines
7.7 KiB
C
/*
|
|
Functions for escaping and unescaping strings
|
|
|
|
Copyright (C) 2009-2024
|
|
Free Software Foundation, Inc.
|
|
|
|
Written by:
|
|
Slava Zanko <slavazanko@gmail.com>, 2009;
|
|
Patrick Winnertz <winnie@debian.org>, 2009
|
|
|
|
This file is part of the Midnight Commander.
|
|
|
|
The Midnight Commander is free software: you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation, either version 3 of the License,
|
|
or (at your option) any later version.
|
|
|
|
The Midnight Commander is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "lib/global.h"
|
|
#include "lib/strutil.h"
|
|
|
|
/*** global variables ****************************************************************************/
|
|
|
|
/*** file scope macro definitions ****************************************************************/
|
|
|
|
/*** file scope type declarations ****************************************************************/
|
|
|
|
/*** forward declarations (file scope functions) *************************************************/
|
|
|
|
/*** file scope variables ************************************************************************/
|
|
|
|
static const char ESCAPE_SHELL_CHARS[] = " !#$%()&{}[]`?|<>;*\\\"'";
|
|
static const char ESCAPE_REGEX_CHARS[] = "^!#$%()&{}[]`?|<>;*+.\\";
|
|
static const char ESCAPE_GLOB_CHARS[] = "$*\\?";
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
/*** file scope functions ************************************************************************/
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
/*** public functions ****************************************************************************/
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
char *
|
|
str_escape (const char *src, gsize src_len, const char *escaped_chars,
|
|
gboolean escape_non_printable)
|
|
{
|
|
GString *ret;
|
|
gsize curr_index;
|
|
/* do NOT break allocation semantics */
|
|
if (src == NULL)
|
|
return NULL;
|
|
|
|
if (*src == '\0')
|
|
return strdup ("");
|
|
|
|
ret = g_string_new ("");
|
|
|
|
if (src_len == (gsize) (-1))
|
|
src_len = strlen (src);
|
|
|
|
for (curr_index = 0; curr_index < src_len; curr_index++)
|
|
{
|
|
if (escape_non_printable)
|
|
{
|
|
switch (src[curr_index])
|
|
{
|
|
case '\n':
|
|
g_string_append (ret, "\\n");
|
|
continue;
|
|
case '\t':
|
|
g_string_append (ret, "\\t");
|
|
continue;
|
|
case '\b':
|
|
g_string_append (ret, "\\b");
|
|
continue;
|
|
case '\0':
|
|
g_string_append (ret, "\\0");
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (strchr (escaped_chars, (int) src[curr_index]))
|
|
g_string_append_c (ret, '\\');
|
|
|
|
g_string_append_c (ret, src[curr_index]);
|
|
}
|
|
return g_string_free (ret, FALSE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
char *
|
|
str_unescape (const char *src, gsize src_len, const char *unescaped_chars,
|
|
gboolean unescape_non_printable)
|
|
{
|
|
GString *ret;
|
|
gsize curr_index;
|
|
|
|
if (src == NULL)
|
|
return NULL;
|
|
|
|
if (*src == '\0')
|
|
return strdup ("");
|
|
|
|
ret = g_string_sized_new (16);
|
|
|
|
if (src_len == (gsize) (-1))
|
|
src_len = strlen (src);
|
|
src_len--;
|
|
|
|
for (curr_index = 0; curr_index < src_len; curr_index++)
|
|
{
|
|
if (src[curr_index] != '\\')
|
|
{
|
|
g_string_append_c (ret, src[curr_index]);
|
|
continue;
|
|
}
|
|
|
|
curr_index++;
|
|
|
|
if (unescaped_chars == ESCAPE_SHELL_CHARS && src[curr_index] == '$')
|
|
{
|
|
/* special case: \$ is used to disallow variable substitution */
|
|
g_string_append_c (ret, '\\');
|
|
}
|
|
else
|
|
{
|
|
if (unescape_non_printable)
|
|
{
|
|
switch (src[curr_index])
|
|
{
|
|
case 'n':
|
|
g_string_append_c (ret, '\n');
|
|
continue;
|
|
case 't':
|
|
g_string_append_c (ret, '\t');
|
|
continue;
|
|
case 'b':
|
|
g_string_append_c (ret, '\b');
|
|
continue;
|
|
case '0':
|
|
g_string_append_c (ret, '\0');
|
|
continue;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (strchr (unescaped_chars, (int) src[curr_index]) == NULL)
|
|
g_string_append_c (ret, '\\');
|
|
}
|
|
|
|
g_string_append_c (ret, src[curr_index]);
|
|
}
|
|
g_string_append_c (ret, src[curr_index]);
|
|
|
|
return g_string_free (ret, FALSE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* To be compatible with the general posix command lines we have to escape
|
|
* strings for the command line
|
|
*
|
|
* @param src string for escaping
|
|
*
|
|
* @return escaped string (which needs to be freed later) or NULL when NULL string is passed.
|
|
*/
|
|
|
|
char *
|
|
str_shell_escape (const char *src)
|
|
{
|
|
return str_escape (src, -1, ESCAPE_SHELL_CHARS, FALSE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
char *
|
|
str_glob_escape (const char *src)
|
|
{
|
|
return str_escape (src, -1, ESCAPE_GLOB_CHARS, TRUE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
char *
|
|
str_regex_escape (const char *src)
|
|
{
|
|
return str_escape (src, -1, ESCAPE_REGEX_CHARS, TRUE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* Unescape paths or other strings for e.g the internal cd
|
|
* shell-unescape within a given buffer (writing to it!)
|
|
*
|
|
* @param text string for unescaping
|
|
*
|
|
* @return unescaped string (which needs to be freed)
|
|
*/
|
|
|
|
char *
|
|
str_shell_unescape (const char *text)
|
|
{
|
|
return str_unescape (text, -1, ESCAPE_SHELL_CHARS, TRUE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
char *
|
|
str_glob_unescape (const char *text)
|
|
{
|
|
return str_unescape (text, -1, ESCAPE_GLOB_CHARS, TRUE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
char *
|
|
str_regex_unescape (const char *text)
|
|
{
|
|
return str_unescape (text, -1, ESCAPE_REGEX_CHARS, TRUE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* Check if char in pointer contain escape'd chars
|
|
*
|
|
* @param start string for checking
|
|
* @param current pointer to checked character
|
|
*
|
|
* @return TRUE if string contain escaped chars otherwise return FALSE
|
|
*/
|
|
|
|
gboolean
|
|
str_is_char_escaped (const char *start, const char *current)
|
|
{
|
|
int num_esc = 0;
|
|
|
|
if (start == NULL || current == NULL || current <= start)
|
|
return FALSE;
|
|
|
|
current--;
|
|
while (current >= start && *current == '\\')
|
|
{
|
|
num_esc++;
|
|
current--;
|
|
}
|
|
return (gboolean) num_esc % 2;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|