strescape.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. /*
  2. Functions for escaping and unescaping strings
  3. Copyright (C) 2009, 2011, 2013
  4. The 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. }
  65. }
  66. if (strchr (escaped_chars, (int) src[curr_index]))
  67. g_string_append_c (ret, '\\');
  68. g_string_append_c (ret, src[curr_index]);
  69. }
  70. return g_string_free (ret, FALSE);
  71. }
  72. /* --------------------------------------------------------------------------------------------- */
  73. char *
  74. strutils_unescape (const char *src, gsize src_len, const char *unescaped_chars,
  75. gboolean unescape_non_printable)
  76. {
  77. GString *ret;
  78. gsize curr_index;
  79. if (src == NULL)
  80. return NULL;
  81. if (*src == '\0')
  82. return strdup ("");
  83. ret = g_string_sized_new (16);
  84. if (src_len == (gsize) (-1))
  85. src_len = strlen (src);
  86. src_len--;
  87. for (curr_index = 0; curr_index < src_len; curr_index++)
  88. {
  89. if (src[curr_index] != '\\')
  90. {
  91. g_string_append_c (ret, src[curr_index]);
  92. continue;
  93. }
  94. curr_index++;
  95. if (unescaped_chars == ESCAPE_SHELL_CHARS && src[curr_index] == '$')
  96. {
  97. /* special case: \$ is used to disallow variable substitution */
  98. g_string_append_c (ret, '\\');
  99. }
  100. else
  101. {
  102. if (unescape_non_printable)
  103. {
  104. switch (src[curr_index])
  105. {
  106. case 'n':
  107. g_string_append_c (ret, '\n');
  108. continue;
  109. case 't':
  110. g_string_append_c (ret, '\t');
  111. continue;
  112. case 'b':
  113. g_string_append_c (ret, '\b');
  114. continue;
  115. case '0':
  116. g_string_append_c (ret, '\0');
  117. continue;
  118. }
  119. }
  120. if (strchr (unescaped_chars, (int) src[curr_index]) == NULL)
  121. g_string_append_c (ret, '\\');
  122. }
  123. g_string_append_c (ret, src[curr_index]);
  124. }
  125. g_string_append_c (ret, src[curr_index]);
  126. return g_string_free (ret, FALSE);
  127. }
  128. /* --------------------------------------------------------------------------------------------- */
  129. /**
  130. * To be compatible with the general posix command lines we have to escape
  131. * strings for the command line
  132. *
  133. * @param src string for escaping
  134. *
  135. * @return escaped string (which needs to be freed later) or NULL when NULL string is passed.
  136. */
  137. char *
  138. strutils_shell_escape (const char *src)
  139. {
  140. return strutils_escape (src, -1, ESCAPE_SHELL_CHARS, FALSE);
  141. }
  142. /* --------------------------------------------------------------------------------------------- */
  143. char *
  144. strutils_glob_escape (const char *src)
  145. {
  146. return strutils_escape (src, -1, ESCAPE_GLOB_CHARS, TRUE);
  147. }
  148. /* --------------------------------------------------------------------------------------------- */
  149. char *
  150. strutils_regex_escape (const char *src)
  151. {
  152. return strutils_escape (src, -1, ESCAPE_REGEX_CHARS, TRUE);
  153. }
  154. /* --------------------------------------------------------------------------------------------- */
  155. /**
  156. * Unescape paths or other strings for e.g the internal cd
  157. * shell-unescape within a given buffer (writing to it!)
  158. *
  159. * @param text string for unescaping
  160. *
  161. * @return unescaped string (which needs to be freed)
  162. */
  163. char *
  164. strutils_shell_unescape (const char *text)
  165. {
  166. return strutils_unescape (text, -1, ESCAPE_SHELL_CHARS, TRUE);
  167. }
  168. /* --------------------------------------------------------------------------------------------- */
  169. char *
  170. strutils_glob_unescape (const char *text)
  171. {
  172. return strutils_unescape (text, -1, ESCAPE_GLOB_CHARS, TRUE);
  173. }
  174. /* --------------------------------------------------------------------------------------------- */
  175. char *
  176. strutils_regex_unescape (const char *text)
  177. {
  178. return strutils_unescape (text, -1, ESCAPE_REGEX_CHARS, TRUE);
  179. }
  180. /* --------------------------------------------------------------------------------------------- */
  181. /**
  182. * Check if char in pointer contain escape'd chars
  183. *
  184. * @param start string for checking
  185. * @param current pointer to checked character
  186. *
  187. * @return TRUE if string contain escaped chars otherwise return FALSE
  188. */
  189. gboolean
  190. strutils_is_char_escaped (const char *start, const char *current)
  191. {
  192. int num_esc = 0;
  193. if (start == NULL || current == NULL || current <= start)
  194. return FALSE;
  195. current--;
  196. while (current >= start && *current == '\\')
  197. {
  198. num_esc++;
  199. current--;
  200. }
  201. return (gboolean) num_esc % 2;
  202. }
  203. /* --------------------------------------------------------------------------------------------- */