From b5ed9841bc60b54b537a120da3ba953d2edc1c25 Mon Sep 17 00:00:00 2001 From: Tamas TEVESZ Date: Tue, 12 Oct 2010 18:38:45 +0200 Subject: [PATCH] WINGs: Add wshellquote() Transforms its argument so that the result is safe to pass to the shell as an argument. With very heavy inspirations from NetBSD's shquote(3), http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/gen/shquote.c?annotate http://man.netbsd.org/cgi-bin/man-cgi?shquote Signed-off-by: Tamas TEVESZ --- WINGs/WINGs/WUtil.h | 6 +++++ WINGs/string.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/WINGs/WINGs/WUtil.h b/WINGs/WINGs/WUtil.h index b78cc85a..f3131c51 100644 --- a/WINGs/WINGs/WUtil.h +++ b/WINGs/WINGs/WUtil.h @@ -243,6 +243,12 @@ void wtokenfree(char **tokens, int count); char* wtrimspace(const char *s); +/* transform `s' so that the result is safe to pass to the shell as an argument. + * returns a newly allocated string. + * with very heavy inspirations from NetBSD's shquote(3). + */ +char *wshellquote(const char *s); + /* ---[ WINGs/wmisc.c ]--------------------------------------------------- */ WMRange wmkrange(int start, int count); diff --git a/WINGs/string.c b/WINGs/string.c index d604fffa..c51a0f14 100644 --- a/WINGs/string.c +++ b/WINGs/string.c @@ -352,3 +352,69 @@ wstrlcpy(char *dst, const char *src, size_t siz) return(s - src - 1); /* count does not include NUL */ } #endif /* HAVE_STRLCPY */ + +/* transform `s' so that the result is safe to pass to the shell as an argument. + * returns a newly allocated string. + * with very heavy inspirations from NetBSD's shquote(3). + */ +char *wshellquote(const char *s) +{ + char *p, *r, *last, *ret; + size_t slen; + int needs_quoting; + + if (!s) + return NULL; + + needs_quoting = !*s; /* the empty string does need quoting */ + + /* do not quote if consists only of the following characters */ + for (p = (char *)s; *p && !needs_quoting; p++) { + needs_quoting = !(isalnum(*p) || (*p == '+') || (*p == '/') || + (*p == '.') || (*p == ',') || (*p == '-')); + } + + if (!needs_quoting) + return wstrdup(s); + + for (slen = 0, p = (char *)s; *p; p++) /* count space needed (worst case) */ + slen += *p == '\'' ? 4 : 1; /* every single ' becomes ''\' */ + + slen += 2 /* leading + trailing "'" */ + 1 /* NULL */; + + ret = r = wmalloc(slen); + p = (char *)s; + last = p; + + if (*p != '\'') /* if string doesn't already begin with "'" */ + *r++ ='\''; /* start putting it in quotes */ + + while (*p) { + last = p; + if (*p == '\'') { /* turn each ' into ''\' */ + if (p != s) /* except if it's the first ', in which case */ + *r++ = '\''; /* only escape it */ + *r++ = '\\'; + *r++ = '\''; + while (*++p && *p == '\'') { /* keep turning each consecutive 's into \' */ + *r++ = '\\'; + *r++ = '\''; + } + if (*p) /* if more input follows, terminate */ + *r++ = '\''; /* what we have so far */ + } else { + *r++ = *p++; + } + } + + if (*last != '\'') /* if the last one isn't already a ' */ + *r++ = '\''; /* terminate the whole shebang */ + + *r = '\0'; + + return ret; /* technically, we lose (but not leak) a couple of */ + /* bytes (twice the number of consecutive 's in the */ + /* input or so), but since these are relatively rare */ + /* and short-lived strings, not sure if a trip to */ + /* wstrdup+wfree worths the gain. */ +}