strescape.c 7.7 KB

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