strescape.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. Functions for escaping and unescaping strings
  3. Copyright (C) 2009-2021
  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/strescape.h"
  23. /*** global variables ****************************************************************************/
  24. /*** file scope macro definitions ****************************************************************/
  25. /*** file scope type declarations ****************************************************************/
  26. /*** file scope variables ************************************************************************/
  27. static const char ESCAPE_SHELL_CHARS[] = " !#$%()&{}[]`?|<>;*\\\"'";
  28. static const char ESCAPE_REGEX_CHARS[] = "^!#$%()&{}[]`?|<>;*+.\\";
  29. static const char ESCAPE_GLOB_CHARS[] = "$*\\?";
  30. /*** file scope functions ************************************************************************/
  31. /*** public functions ****************************************************************************/
  32. char *
  33. strutils_escape (const char *src, gsize src_len, const char *escaped_chars,
  34. gboolean escape_non_printable)
  35. {
  36. GString *ret;
  37. gsize curr_index;
  38. /* do NOT break allocation semantics */
  39. if (src == NULL)
  40. return NULL;
  41. if (*src == '\0')
  42. return strdup ("");
  43. ret = g_string_new ("");
  44. if (src_len == (gsize) (-1))
  45. src_len = strlen (src);
  46. for (curr_index = 0; curr_index < src_len; curr_index++)
  47. {
  48. if (escape_non_printable)
  49. {
  50. switch (src[curr_index])
  51. {
  52. case '\n':
  53. g_string_append (ret, "\\n");
  54. continue;
  55. case '\t':
  56. g_string_append (ret, "\\t");
  57. continue;
  58. case '\b':
  59. g_string_append (ret, "\\b");
  60. continue;
  61. case '\0':
  62. g_string_append (ret, "\\0");
  63. continue;
  64. default:
  65. break;
  66. }
  67. }
  68. if (strchr (escaped_chars, (int) src[curr_index]))
  69. g_string_append_c (ret, '\\');
  70. g_string_append_c (ret, src[curr_index]);
  71. }
  72. return g_string_free (ret, FALSE);
  73. }
  74. /* --------------------------------------------------------------------------------------------- */
  75. char *
  76. strutils_unescape (const char *src, gsize src_len, const char *unescaped_chars,
  77. gboolean unescape_non_printable)
  78. {
  79. GString *ret;
  80. gsize curr_index;
  81. if (src == NULL)
  82. return NULL;
  83. if (*src == '\0')
  84. return strdup ("");
  85. ret = g_string_sized_new (16);
  86. if (src_len == (gsize) (-1))
  87. src_len = strlen (src);
  88. src_len--;
  89. for (curr_index = 0; curr_index < src_len; curr_index++)
  90. {
  91. if (src[curr_index] != '\\')
  92. {
  93. g_string_append_c (ret, src[curr_index]);
  94. continue;
  95. }
  96. curr_index++;
  97. if (unescaped_chars == ESCAPE_SHELL_CHARS && src[curr_index] == '$')
  98. {
  99. /* special case: \$ is used to disallow variable substitution */
  100. g_string_append_c (ret, '\\');
  101. }
  102. else
  103. {
  104. if (unescape_non_printable)
  105. {
  106. switch (src[curr_index])
  107. {
  108. case 'n':
  109. g_string_append_c (ret, '\n');
  110. continue;
  111. case 't':
  112. g_string_append_c (ret, '\t');
  113. continue;
  114. case 'b':
  115. g_string_append_c (ret, '\b');
  116. continue;
  117. case '0':
  118. g_string_append_c (ret, '\0');
  119. continue;
  120. default:
  121. break;
  122. }
  123. }
  124. if (strchr (unescaped_chars, (int) src[curr_index]) == NULL)
  125. g_string_append_c (ret, '\\');
  126. }
  127. g_string_append_c (ret, src[curr_index]);
  128. }
  129. g_string_append_c (ret, src[curr_index]);
  130. return g_string_free (ret, FALSE);
  131. }
  132. /* --------------------------------------------------------------------------------------------- */
  133. /**
  134. * To be compatible with the general posix command lines we have to escape
  135. * strings for the command line
  136. *
  137. * @param src string for escaping
  138. *
  139. * @return escaped string (which needs to be freed later) or NULL when NULL string is passed.
  140. */
  141. char *
  142. strutils_shell_escape (const char *src)
  143. {
  144. return strutils_escape (src, -1, ESCAPE_SHELL_CHARS, FALSE);
  145. }
  146. /* --------------------------------------------------------------------------------------------- */
  147. char *
  148. strutils_glob_escape (const char *src)
  149. {
  150. return strutils_escape (src, -1, ESCAPE_GLOB_CHARS, TRUE);
  151. }
  152. /* --------------------------------------------------------------------------------------------- */
  153. char *
  154. strutils_regex_escape (const char *src)
  155. {
  156. return strutils_escape (src, -1, ESCAPE_REGEX_CHARS, TRUE);
  157. }
  158. /* --------------------------------------------------------------------------------------------- */
  159. /**
  160. * Unescape paths or other strings for e.g the internal cd
  161. * shell-unescape within a given buffer (writing to it!)
  162. *
  163. * @param text string for unescaping
  164. *
  165. * @return unescaped string (which needs to be freed)
  166. */
  167. char *
  168. strutils_shell_unescape (const char *text)
  169. {
  170. return strutils_unescape (text, -1, ESCAPE_SHELL_CHARS, TRUE);
  171. }
  172. /* --------------------------------------------------------------------------------------------- */
  173. char *
  174. strutils_glob_unescape (const char *text)
  175. {
  176. return strutils_unescape (text, -1, ESCAPE_GLOB_CHARS, TRUE);
  177. }
  178. /* --------------------------------------------------------------------------------------------- */
  179. char *
  180. strutils_regex_unescape (const char *text)
  181. {
  182. return strutils_unescape (text, -1, ESCAPE_REGEX_CHARS, TRUE);
  183. }
  184. /* --------------------------------------------------------------------------------------------- */
  185. /**
  186. * Check if char in pointer contain escape'd chars
  187. *
  188. * @param start string for checking
  189. * @param current pointer to checked character
  190. *
  191. * @return TRUE if string contain escaped chars otherwise return FALSE
  192. */
  193. gboolean
  194. strutils_is_char_escaped (const char *start, const char *current)
  195. {
  196. int num_esc = 0;
  197. if (start == NULL || current == NULL || current <= start)
  198. return FALSE;
  199. current--;
  200. while (current >= start && *current == '\\')
  201. {
  202. num_esc++;
  203. current--;
  204. }
  205. return (gboolean) num_esc % 2;
  206. }
  207. /* --------------------------------------------------------------------------------------------- */