From 7d0fa9afced7e65468499757607fb3b21a7bf378 Mon Sep 17 00:00:00 2001 From: Patrick Winnertz Date: Tue, 10 Feb 2009 13:54:23 +0100 Subject: [PATCH] fixed canonicalize_pathname() breakage: fixed str_move() function (memmove semantics) again and add shell_(un)escape again This patch reintroduces fix firstly appeared in (and recently broken by mhl revert) > commit e48cb7c89ff3e54de70130a3de2136a9902a023d > Author: Sergei Trofimovich > Date: Fri Jan 30 09:31:28 2009 +0200 > > mhl: added mhl_strmove() function (memmove semantics) ... > Snippet of man strcpy: > DESCRIPTION > The strcpy() function copies the string pointed to by src, including the terminating > null byte ('\0'), to the buffer pointed to by dest. ___The strings may not overlap___, > and the destination string dest must be large enough to receive the copy. > We used strcpy to move data chunk in memory: "./foo" -> "foo", etc. > > This patch introduces mhl_strmove and fixed canonicalize_pathname. Conflicts: src/util.h Signed-off-by: Patrick Winnertz --- src/util.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 35 +++++++++++- 2 files changed, 188 insertions(+), 1 deletion(-) diff --git a/src/util.c b/src/util.c index 27d5e9dc9..1f7150c6a 100644 --- a/src/util.c +++ b/src/util.c @@ -1531,3 +1531,157 @@ Q_ (const char *s) sep = strchr(result, '|'); return (sep != NULL) ? sep + 1 : result; } + +#define shell_escape_toesc(x) \ + (((x)==' ')||((x)=='!')||((x)=='#')||((x)=='$')||((x)=='%')|| \ + ((x)=='(')||((x)==')')||((x)=='\'')||((x)=='&')||((x)=='~')|| \ + ((x)=='{')||((x)=='}')||((x)=='[')||((x)==']')||((x)=='`')|| \ + ((x)=='?')||((x)=='|')||((x)=='<')||((x)=='>')||((x)==';')|| \ + ((x)=='*')||((x)=='\\')||((x)=='"')) + +#define shell_escape_nottoesc(x) \ + (((x)!=0) && (!shell_escape_toesc((x)))) +/** To be compatible with the general posix command lines we have to escape + strings for the command line + + \params in + string for escaping + + \returns + return escaped string (which needs to be freed later) + */ +char* +shell_escape(const char* src) +{ + GString *str; + char *result = NULL; + + if ((src==NULL)||(!(*src))) + return strdup(""); + + str = g_string_new(""); + + /* look for the first char to escape */ + while (1) + { + char c; + /* copy over all chars not to escape */ + while ((c=(*src)) && shell_escape_nottoesc(c)) + { + g_string_append_c(str,c); + src++; + } + + /* at this point we either have an \0 or an char to escape */ + if (!c) { + result = str->str; + g_string_free(str,FALSE); + return result; + } + + g_string_append_c(str,'\\'); + g_string_append_c(str,c); + src++; + } +} + +/** Unescape paths or other strings for e.g the internal cd + shell-unescape within a given buffer (writing to it!) + + \params src + string for unescaping + + \returns + return unescaped string (which needs to be freed) + */ +char* +shell_unescape(const char* text) +{ + GString *str; + char *result = NULL; + + if (!text) + return NULL; + + + /* look for the first \ - that's quick skipover if there's nothing to escape */ + const char* readptr = text; + while ((*readptr) && ((*readptr)!='\\')) readptr++; + if (!(*readptr)) { + result = g_strdup(text); + return result; + } + str = g_string_new_len(text, readptr - text); + + /* if we're here, we're standing on the first '\' */ + char c; + while ((c = *readptr)) + { + if (c=='\\') + { + readptr++; + switch ((c = *readptr)) + { + case '\0': /* end of string! malformed escape string */ + goto out; + + case 'n': g_string_append_c(str,'\n'); break; + case 'r': g_string_append_c(str,'\r'); break; + case 't': g_string_append_c(str,'\t'); break; + + case ' ': + case '\\': + case '#': + case '$': + case '%': + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '<': + case '>': + case '!': + case '*': + case '?': + case '~': + case '`': + case '"': + case ';': + default: + g_string_append_c(str,c); break; + } + } + else /* got a normal character */ + { + g_string_append_c(str,c); + } + readptr++; + } +out: + + result = str->str; + g_string_free(str,FALSE); + return result; +} + +/** Check if char in pointer contain escape'd chars + + \params in + string for checking + + \returns + return TRUE if string contain escaped chars + otherwise return FALSE + */ +gboolean +shell_is_char_escaped ( const char *in ) +{ + if (in == NULL || !*in || in[0] != '\\') + return FALSE; + if (shell_escape_toesc(in[1])) + return TRUE; + return FALSE; +} + diff --git a/src/util.h b/src/util.h index f68156688..a81777662 100644 --- a/src/util.h +++ b/src/util.h @@ -1,7 +1,9 @@ #ifndef MC_UTIL_H -#define MC_UTIL_H +#define MC_UTIL_H96fc77bc3ee1f2ae2ae7c0a14d3bf08975b4cb66 #include +#include +#include /* Returns its argument as a "modifiable" string. This function is * intended to pass strings to legacy libraries that don't know yet @@ -256,4 +258,35 @@ extern int ascii_alpha_to_cntrl (int ch); #undef Q_ const char *Q_ (const char *s); + +gboolean shell_is_char_escaped ( const char * ); +char *shell_unescape( const char * ); +char *shell_escape( const char * ); + +#define str_dup_range(s_start, s_bound) (g_strndup(s_start, s_bound - s_start)) + +/* + * strcpy is unsafe on overlapping memory areas, so define memmove-alike + * string function. + * Have sense only when: + * * dest <= src + * AND + * * dest and str are pointers to one object (as Roland Illig pointed). + * + * We can't use str*cpy funs here: + * http://kerneltrap.org/mailarchive/openbsd-misc/2008/5/27/1951294 + */ +static inline char * str_move(char * dest, const char * src) +{ + size_t n; + + assert (dest<=src); + + n = strlen (src) + 1; /* + '\0' */ + + return memmove (dest, src, n); +} + +#define MC_PTR_FREE(ptr) do { g_free(ptr); (ptr) = NULL; } while (0) + #endif