summaryrefslogtreecommitdiffstats
path: root/contrib/nvi/ex/ex_argv.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/nvi/ex/ex_argv.c')
-rw-r--r--contrib/nvi/ex/ex_argv.c464
1 files changed, 311 insertions, 153 deletions
diff --git a/contrib/nvi/ex/ex_argv.c b/contrib/nvi/ex/ex_argv.c
index cc5a201..b3e3edf 100644
--- a/contrib/nvi/ex/ex_argv.c
+++ b/contrib/nvi/ex/ex_argv.c
@@ -10,17 +10,19 @@
#include "config.h"
#ifndef lint
-static const char sccsid[] = "@(#)ex_argv.c 10.26 (Berkeley) 9/20/96";
+static const char sccsid[] = "$Id: ex_argv.c,v 11.2 2012/10/09 23:00:29 zy Exp $";
#endif /* not lint */
#include <sys/types.h>
#include <sys/queue.h>
+#include <sys/time.h>
#include <bitstring.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
+#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -31,9 +33,9 @@ static const char sccsid[] = "@(#)ex_argv.c 10.26 (Berkeley) 9/20/96";
static int argv_alloc __P((SCR *, size_t));
static int argv_comp __P((const void *, const void *));
static int argv_fexp __P((SCR *, EXCMD *,
- char *, size_t, char *, size_t *, char **, size_t *, int));
-static int argv_lexp __P((SCR *, EXCMD *, char *));
-static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
+ CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int));
+static int argv_sexp __P((SCR *, CHAR_T **, size_t *, size_t *));
+static int argv_flt_user __P((SCR *, EXCMD *, CHAR_T *, size_t));
/*
* argv_init --
@@ -42,9 +44,7 @@ static int argv_sexp __P((SCR *, char **, size_t *, size_t *));
* PUBLIC: int argv_init __P((SCR *, EXCMD *));
*/
int
-argv_init(sp, excp)
- SCR *sp;
- EXCMD *excp;
+argv_init(SCR *sp, EXCMD *excp)
{
EX_PRIVATE *exp;
@@ -61,20 +61,16 @@ argv_init(sp, excp)
* argv_exp0 --
* Append a string to the argument list.
*
- * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, char *, size_t));
+ * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, CHAR_T *, size_t));
*/
int
-argv_exp0(sp, excp, cmd, cmdlen)
- SCR *sp;
- EXCMD *excp;
- char *cmd;
- size_t cmdlen;
+argv_exp0(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
{
EX_PRIVATE *exp;
exp = EXP(sp);
argv_alloc(sp, cmdlen);
- memcpy(exp->args[exp->argsoff]->bp, cmd, cmdlen);
+ MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen);
exp->args[exp->argsoff]->bp[cmdlen] = '\0';
exp->args[exp->argsoff]->len = cmdlen;
++exp->argsoff;
@@ -88,33 +84,28 @@ argv_exp0(sp, excp, cmd, cmdlen)
* Do file name expansion on a string, and append it to the
* argument list.
*
- * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, char *, size_t, int));
+ * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, CHAR_T *, size_t, int));
*/
int
-argv_exp1(sp, excp, cmd, cmdlen, is_bang)
- SCR *sp;
- EXCMD *excp;
- char *cmd;
- size_t cmdlen;
- int is_bang;
+argv_exp1(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, int is_bang)
{
EX_PRIVATE *exp;
size_t blen, len;
- char *bp, *p, *t;
+ CHAR_T *p, *t, *bp;
- GET_SPACE_RET(sp, bp, blen, 512);
+ GET_SPACE_RETW(sp, bp, blen, 512);
len = 0;
exp = EXP(sp);
if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) {
- FREE_SPACE(sp, bp, blen);
+ FREE_SPACEW(sp, bp, blen);
return (1);
}
/* If it's empty, we're done. */
if (len != 0) {
for (p = bp, t = bp + len; p < t; ++p)
- if (!isblank(*p))
+ if (!cmdskip(*p))
break;
if (p == t)
goto ret;
@@ -123,7 +114,7 @@ argv_exp1(sp, excp, cmd, cmdlen, is_bang)
(void)argv_exp0(sp, excp, bp, len);
-ret: FREE_SPACE(sp, bp, blen);
+ret: FREE_SPACEW(sp, bp, blen);
return (0);
}
@@ -132,24 +123,20 @@ ret: FREE_SPACE(sp, bp, blen);
* Do file name and shell expansion on a string, and append it to
* the argument list.
*
- * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, char *, size_t));
+ * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, CHAR_T *, size_t));
*/
int
-argv_exp2(sp, excp, cmd, cmdlen)
- SCR *sp;
- EXCMD *excp;
- char *cmd;
- size_t cmdlen;
+argv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
{
size_t blen, len, n;
int rval;
- char *bp, *mp, *p;
+ CHAR_T *bp, *p;
- GET_SPACE_RET(sp, bp, blen, 512);
+ GET_SPACE_RETW(sp, bp, blen, 512);
-#define SHELLECHO "echo "
-#define SHELLOFFSET (sizeof(SHELLECHO) - 1)
- memcpy(bp, SHELLECHO, SHELLOFFSET);
+#define SHELLECHO L("echo ")
+#define SHELLOFFSET (SIZE(SHELLECHO) - 1)
+ MEMCPY(bp, SHELLECHO, SHELLOFFSET);
p = bp + SHELLOFFSET;
len = SHELLOFFSET;
@@ -175,28 +162,15 @@ argv_exp2(sp, excp, cmd, cmdlen)
* but then, if your shell was csh, the above example will behave
* differently in nvi than in vi. If you want to get other characters
* passed through to your shell, change the "meta" option.
- *
- * To avoid a function call per character, we do a first pass through
- * the meta characters looking for characters that aren't expected
- * to be there, and then we can ignore them in the user's argument.
*/
if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1))
n = 0;
else {
- for (p = mp = O_STR(sp, O_SHELLMETA); *p != '\0'; ++p)
- if (isblank(*p) || isalnum(*p))
- break;
p = bp + SHELLOFFSET;
n = len - SHELLOFFSET;
- if (*p != '\0') {
- for (; n > 0; --n, ++p)
- if (strchr(mp, *p) != NULL)
- break;
- } else
- for (; n > 0; --n, ++p)
- if (!isblank(*p) &&
- !isalnum(*p) && strchr(mp, *p) != NULL)
- break;
+ for (; n > 0; --n, ++p)
+ if (IS_SHELLMETA(sp, *p))
+ break;
}
/*
@@ -204,13 +178,11 @@ argv_exp2(sp, excp, cmd, cmdlen)
* it. Unfortunately, this is comparatively slow. Historically, it
* didn't matter much, since users don't enter meta characters as part
* of pathnames that frequently. The addition of filename completion
- * broke that assumption because it's easy to use. As a result, lots
- * folks have complained that the expansion code is too slow. So, we
- * detect filename completion as a special case, and do it internally.
- * Note that this code assumes that the <asterisk> character is the
- * match-anything meta character. That feels safe -- if anyone writes
- * a shell that doesn't follow that convention, I'd suggest giving them
- * a festive hot-lead enema.
+ * broke that assumption because it's easy to use. To increase the
+ * completion performance, nvi used to have an internal routine to
+ * handle "filename*". However, the shell special characters does not
+ * limit to "shellmeta", so such a hack breaks historic practice.
+ * After it all, we split the completion logic out from here.
*/
switch (n) {
case 0:
@@ -218,13 +190,6 @@ argv_exp2(sp, excp, cmd, cmdlen)
len -= SHELLOFFSET;
rval = argv_exp3(sp, excp, p, len);
break;
- case 1:
- if (*p == '*') {
- *p = '\0';
- rval = argv_lexp(sp, excp, bp + SHELLOFFSET);
- break;
- }
- /* FALLTHROUGH */
default:
if (argv_sexp(sp, &bp, &blen, &len)) {
rval = 1;
@@ -235,7 +200,7 @@ argv_exp2(sp, excp, cmd, cmdlen)
break;
}
-err: FREE_SPACE(sp, bp, blen);
+err: FREE_SPACEW(sp, bp, blen);
return (rval);
}
@@ -244,25 +209,21 @@ err: FREE_SPACE(sp, bp, blen);
* Take a string and break it up into an argv, which is appended
* to the argument list.
*
- * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, char *, size_t));
+ * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, CHAR_T *, size_t));
*/
int
-argv_exp3(sp, excp, cmd, cmdlen)
- SCR *sp;
- EXCMD *excp;
- char *cmd;
- size_t cmdlen;
+argv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
{
EX_PRIVATE *exp;
size_t len;
int ch, off;
- char *ap, *p;
+ CHAR_T *ap, *p;
for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) {
/* Skip any leading whitespace. */
for (; cmdlen > 0; --cmdlen, ++cmd) {
ch = *cmd;
- if (!isblank(ch))
+ if (!cmdskip(ch))
break;
}
if (cmdlen == 0)
@@ -282,7 +243,7 @@ argv_exp3(sp, excp, cmd, cmdlen)
if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) {
++cmd;
--cmdlen;
- } else if (isblank(ch))
+ } else if (cmdskip(ch))
break;
}
@@ -312,20 +273,99 @@ argv_exp3(sp, excp, cmd, cmdlen)
}
/*
+ * argv_flt_ex --
+ * Filter the ex commands with a prefix, and append the results to
+ * the argument list.
+ *
+ * PUBLIC: int argv_flt_ex __P((SCR *, EXCMD *, CHAR_T *, size_t));
+ */
+int
+argv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen)
+{
+ EX_PRIVATE *exp;
+ EXCMDLIST const *cp;
+ int off;
+ size_t len;
+
+ exp = EXP(sp);
+
+ for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) {
+ len = STRLEN(cp->name);
+ if (cmdlen > 0 &&
+ (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen)))
+ continue;
+
+ /* Copy the matched ex command name. */
+ argv_alloc(sp, len + 1);
+ MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1);
+ exp->args[exp->argsoff]->len = len;
+ ++exp->argsoff;
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ }
+
+ return (0);
+}
+
+/*
+ * argv_flt_user --
+ * Filter the ~user list on the system with a prefix, and append
+ * the results to the argument list.
+ */
+static int
+argv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen)
+{
+ EX_PRIVATE *exp;
+ struct passwd *pw;
+ int off;
+ char *np;
+ size_t len, nlen;
+
+ exp = EXP(sp);
+ off = exp->argsoff;
+
+ /* The input must come with a leading '~'. */
+ INT2CHAR(sp, uname + 1, ulen - 1, np, nlen);
+ if ((np = v_strdup(sp, np, nlen)) == NULL)
+ return (1);
+
+ setpwent();
+ while ((pw = getpwent()) != NULL) {
+ len = strlen(pw->pw_name);
+ if (nlen > 0 &&
+ (nlen > len || memcmp(np, pw->pw_name, nlen)))
+ continue;
+
+ /* Copy '~' + the matched user name. */
+ CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen);
+ argv_alloc(sp, ulen + 1);
+ exp->args[exp->argsoff]->bp[0] = '~';
+ MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen);
+ exp->args[exp->argsoff]->len = ulen;
+ ++exp->argsoff;
+ excp->argv = exp->args;
+ excp->argc = exp->argsoff;
+ }
+ endpwent();
+ free(np);
+
+ qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
+ return (0);
+}
+
+/*
* argv_fexp --
* Do file name and bang command expansion.
*/
static int
-argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
- SCR *sp;
- EXCMD *excp;
- char *cmd, *p, **bpp;
- size_t cmdlen, *lenp, *blenp;
- int is_bang;
+argv_fexp(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, CHAR_T *p, size_t *lenp, CHAR_T **bpp, size_t *blenp, int is_bang)
{
EX_PRIVATE *exp;
- char *bp, *t;
+ char *t;
size_t blen, len, off, tlen;
+ CHAR_T *bp;
+ CHAR_T *wp;
+ size_t wlen;
/* Replace file name characters. */
for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd)
@@ -339,11 +379,11 @@ argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
"115|No previous command to replace \"!\"");
return (1);
}
- len += tlen = strlen(exp->lastbcomm);
+ len += tlen = STRLEN(exp->lastbcomm);
off = p - bp;
- ADD_SPACE_RET(sp, bp, blen, len);
+ ADD_SPACE_RETW(sp, bp, blen, len);
p = bp + off;
- memcpy(p, exp->lastbcomm, tlen);
+ MEMCPY(p, exp->lastbcomm, tlen);
p += tlen;
F_SET(excp, E_MODIFY);
break;
@@ -356,10 +396,11 @@ argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
tlen = strlen(t);
len += tlen;
off = p - bp;
- ADD_SPACE_RET(sp, bp, blen, len);
+ ADD_SPACE_RETW(sp, bp, blen, len);
p = bp + off;
- memcpy(p, t, tlen);
- p += tlen;
+ CHAR2INT(sp, t, tlen, wp, wlen);
+ MEMCPY(p, wp, wlen);
+ p += wlen;
F_SET(excp, E_MODIFY);
break;
case '#':
@@ -370,10 +411,11 @@ argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
}
len += tlen = strlen(t);
off = p - bp;
- ADD_SPACE_RET(sp, bp, blen, len);
+ ADD_SPACE_RETW(sp, bp, blen, len);
p = bp + off;
- memcpy(p, t, tlen);
- p += tlen;
+ CHAR2INT(sp, t, tlen, wp, wlen);
+ MEMCPY(p, wp, wlen);
+ p += wlen;
F_SET(excp, E_MODIFY);
break;
case '\\':
@@ -392,7 +434,7 @@ argv_fexp(sp, excp, cmd, cmdlen, p, lenp, bpp, blenp, is_bang)
default:
ins_ch: ++len;
off = p - bp;
- ADD_SPACE_RET(sp, bp, blen, len);
+ ADD_SPACE_RETW(sp, bp, blen, len);
p = bp + off;
*p++ = *cmd;
}
@@ -400,7 +442,7 @@ ins_ch: ++len;
/* Nul termination. */
++len;
off = p - bp;
- ADD_SPACE_RET(sp, bp, blen, len);
+ ADD_SPACE_RETW(sp, bp, blen, len);
p = bp + off;
*p = '\0';
@@ -416,9 +458,7 @@ ins_ch: ++len;
* Make more space for arguments.
*/
static int
-argv_alloc(sp, len)
- SCR *sp;
- size_t len;
+argv_alloc(SCR *sp, size_t len)
{
ARGS *ap;
EX_PRIVATE *exp;
@@ -483,8 +523,7 @@ mem: msgq(sp, M_SYSERR, NULL);
* PUBLIC: int argv_free __P((SCR *));
*/
int
-argv_free(sp)
- SCR *sp;
+argv_free(SCR *sp)
{
EX_PRIVATE *exp;
int off;
@@ -507,89 +546,103 @@ argv_free(sp)
}
/*
- * argv_lexp --
+ * argv_flt_path --
* Find all file names matching the prefix and append them to the
- * buffer.
+ * argument list.
+ *
+ * PUBLIC: int argv_flt_path __P((SCR *, EXCMD *, CHAR_T *, size_t));
*/
-static int
-argv_lexp(sp, excp, path)
- SCR *sp;
- EXCMD *excp;
- char *path;
+int
+argv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen)
{
struct dirent *dp;
DIR *dirp;
EX_PRIVATE *exp;
int off;
size_t dlen, len, nlen;
- char *dname, *name, *p;
+ CHAR_T *dname;
+ CHAR_T *p, *np, *n;
+ char *name, *tp, *epd = NULL;
+ CHAR_T *wp;
+ size_t wlen;
exp = EXP(sp);
/* Set up the name and length for comparison. */
- if ((p = strrchr(path, '/')) == NULL) {
- dname = ".";
+ if ((path = v_wstrdup(sp, path, plen)) == NULL)
+ return (1);
+ if ((p = STRRCHR(path, '/')) == NULL) {
+ if (*path == '~') {
+ int rc;
+
+ /* Filter ~user list instead. */
+ rc = argv_flt_user(sp, excp, path, plen);
+ free(path);
+ return (rc);
+ }
+ dname = L(".");
dlen = 0;
- name = path;
- } else {
+ np = path;
+ } else {
if (p == path) {
- dname = "/";
+ dname = L("/");
dlen = 1;
} else {
*p = '\0';
dname = path;
- dlen = strlen(path);
+ dlen = p - path;
}
- name = p + 1;
+ np = p + 1;
}
- nlen = strlen(name);
- /*
- * XXX
- * We don't use the d_namlen field, it's not portable enough; we
- * assume that d_name is nul terminated, instead.
- */
- if ((dirp = opendir(dname)) == NULL) {
- msgq_str(sp, M_SYSERR, dname, "%s");
+ INT2CHAR(sp, dname, dlen + 1, tp, nlen);
+ if ((epd = expanduser(tp)) != NULL)
+ tp = epd;
+ if ((dirp = opendir(tp)) == NULL) {
+ free(epd);
+ free(path);
return (1);
}
+ free(epd);
+
+ INT2CHAR(sp, np, STRLEN(np), tp, nlen);
+ if ((name = v_strdup(sp, tp, nlen)) == NULL) {
+ free(path);
+ return (1);
+ }
+
for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) {
if (nlen == 0) {
if (dp->d_name[0] == '.')
continue;
- len = strlen(dp->d_name);
+ len = dp->d_namlen;
} else {
- len = strlen(dp->d_name);
+ len = dp->d_namlen;
if (len < nlen || memcmp(dp->d_name, name, nlen))
continue;
}
/* Directory + name + slash + null. */
- argv_alloc(sp, dlen + len + 2);
- p = exp->args[exp->argsoff]->bp;
+ CHAR2INT(sp, dp->d_name, len + 1, wp, wlen);
+ argv_alloc(sp, dlen + wlen + 1);
+ n = exp->args[exp->argsoff]->bp;
if (dlen != 0) {
- memcpy(p, dname, dlen);
- p += dlen;
+ MEMCPY(n, dname, dlen);
+ n += dlen;
if (dlen > 1 || dname[0] != '/')
- *p++ = '/';
+ *n++ = '/';
+ exp->args[exp->argsoff]->len = dlen + 1;
}
- memcpy(p, dp->d_name, len + 1);
- exp->args[exp->argsoff]->len = dlen + len + 1;
+ MEMCPY(n, wp, wlen);
+ exp->args[exp->argsoff]->len += wlen - 1;
++exp->argsoff;
excp->argv = exp->args;
excp->argc = exp->argsoff;
}
closedir(dirp);
+ free(name);
+ free(path);
- if (off == exp->argsoff) {
- /*
- * If we didn't find a match, complain that the expansion
- * failed. We can't know for certain that's the error, but
- * it's a good guess, and it matches historic practice.
- */
- msgq(sp, M_ERR, "304|Shell expansion failed");
- return (1);
- }
qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp);
return (0);
}
@@ -599,10 +652,9 @@ argv_lexp(sp, excp, path)
* Alphabetic comparison.
*/
static int
-argv_comp(a, b)
- const void *a, *b;
+argv_comp(const void *a, const void *b)
{
- return (strcmp((char *)(*(ARGS **)a)->bp, (char *)(*(ARGS **)b)->bp));
+ return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp));
}
/*
@@ -611,17 +663,17 @@ argv_comp(a, b)
* a buffer.
*/
static int
-argv_sexp(sp, bpp, blenp, lenp)
- SCR *sp;
- char **bpp;
- size_t *blenp, *lenp;
+argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp)
{
enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval;
FILE *ifp;
pid_t pid;
size_t blen, len;
int ch, std_output[2];
- char *bp, *p, *sh, *sh_path;
+ CHAR_T *bp, *p;
+ char *sh, *sh_path;
+ char *np;
+ size_t nlen;
/* Secure means no shell access. */
if (O_ISSET(sp, O_SECURE)) {
@@ -689,7 +741,8 @@ err: if (ifp != NULL)
* XXX
* Assume that all shells have -c.
*/
- execl(sh_path, sh, "-c", bp, NULL);
+ INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen);
+ execl(sh_path, sh, "-c", np, (char *)NULL);
msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s");
_exit(127);
default: /* Parent. */
@@ -707,9 +760,9 @@ err: if (ifp != NULL)
* shell that does that is broken.
*/
for (p = bp, len = 0, ch = EOF;
- (ch = getc(ifp)) != EOF; *p++ = ch, --blen, ++len)
+ (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len)
if (blen < 5) {
- ADD_SPACE_GOTO(sp, bp, *blenp, *blenp * 2);
+ ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2);
p = bp + len;
blen = *blenp - len;
}
@@ -744,7 +797,7 @@ alloc_err: rval = SEXP_ERR;
rval = SEXP_EXPANSION_ERR;
for (p = bp; len; ++p, --len)
- if (!isblank(*p))
+ if (!cmdskip(*p))
break;
if (len == 0)
rval = SEXP_EXPANSION_ERR;
@@ -754,3 +807,108 @@ alloc_err: rval = SEXP_ERR;
return (rval == SEXP_OK ? 0 : 1);
}
+
+/*
+ * argv_esc --
+ * Escape a string into an ex and shell argument.
+ *
+ * PUBLIC: CHAR_T *argv_esc __P((SCR *, EXCMD *, CHAR_T *, size_t));
+ */
+CHAR_T *
+argv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
+{
+ size_t blen, off;
+ CHAR_T *bp, *p;
+ int ch;
+
+ GET_SPACE_GOTOW(sp, bp, blen, len + 1);
+
+ /*
+ * Leaving the first '~' unescaped causes the user to need a
+ * "./" prefix to edit a file which really starts with a '~'.
+ * However, the file completion happens to not work for these
+ * files without the prefix.
+ *
+ * All ex expansion characters, "!%#", are double escaped.
+ */
+ for (p = bp; len > 0; ++str, --len) {
+ ch = *str;
+ off = p - bp;
+ if (blen / sizeof(CHAR_T) - off < 3) {
+ ADD_SPACE_GOTOW(sp, bp, blen, off + 3);
+ p = bp + off;
+ }
+ if (cmdskip(ch) || ch == '\n' ||
+ IS_ESCAPE(sp, excp, ch)) /* Ex. */
+ *p++ = CH_LITERAL;
+ else switch (ch) {
+ case '~': /* ~user. */
+ if (p != bp)
+ *p++ = '\\';
+ break;
+ case '+': /* Ex +cmd. */
+ if (p == bp)
+ *p++ = '\\';
+ break;
+ case '!': case '%': case '#': /* Ex exp. */
+ *p++ = '\\';
+ *p++ = '\\';
+ break;
+ case ',': case '-': case '.': case '/': /* Safe. */
+ case ':': case '=': case '@': case '_':
+ break;
+ default: /* Unsafe. */
+ if (isascii(ch) && !isalnum(ch))
+ *p++ = '\\';
+ }
+ *p++ = ch;
+ }
+ *p = '\0';
+
+ return bp;
+
+alloc_err:
+ return NULL;
+}
+
+/*
+ * argv_uesc --
+ * Unescape an escaped ex and shell argument.
+ *
+ * PUBLIC: CHAR_T *argv_uesc __P((SCR *, EXCMD *, CHAR_T *, size_t));
+ */
+CHAR_T *
+argv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len)
+{
+ size_t blen;
+ CHAR_T *bp, *p;
+
+ GET_SPACE_GOTOW(sp, bp, blen, len + 1);
+
+ for (p = bp; len > 0; ++str, --len) {
+ if (IS_ESCAPE(sp, excp, *str)) {
+ if (--len < 1)
+ break;
+ ++str;
+ } else if (*str == '\\') {
+ if (--len < 1)
+ break;
+ ++str;
+
+ /* Check for double escaping. */
+ if (*str == '\\' && len > 1)
+ switch (str[1]) {
+ case '!': case '%': case '#':
+ ++str;
+ --len;
+ }
+ }
+ *p++ = *str;
+ }
+ *p = '\0';
+
+ return bp;
+
+alloc_err:
+ return NULL;
+}
OpenPOWER on IntegriCloud