1e0b8e63eSJohn Marino /*- 2e0b8e63eSJohn Marino * Copyright (c) 1993, 1994 3e0b8e63eSJohn Marino * The Regents of the University of California. All rights reserved. 4e0b8e63eSJohn Marino * Copyright (c) 1993, 1994, 1995, 1996 5e0b8e63eSJohn Marino * Keith Bostic. All rights reserved. 6e0b8e63eSJohn Marino * 7e0b8e63eSJohn Marino * See the LICENSE file for redistribution information. 8e0b8e63eSJohn Marino */ 9e0b8e63eSJohn Marino 10e0b8e63eSJohn Marino #include "config.h" 11e0b8e63eSJohn Marino 12e0b8e63eSJohn Marino #include <sys/types.h> 13e0b8e63eSJohn Marino #include <sys/queue.h> 14e0b8e63eSJohn Marino #include <sys/time.h> 15e0b8e63eSJohn Marino 16e0b8e63eSJohn Marino #include <bitstring.h> 17e0b8e63eSJohn Marino #include <ctype.h> 18e0b8e63eSJohn Marino #include <dirent.h> 19e0b8e63eSJohn Marino #include <errno.h> 20e0b8e63eSJohn Marino #include <limits.h> 21e0b8e63eSJohn Marino #include <pwd.h> 22e0b8e63eSJohn Marino #include <stdio.h> 23e0b8e63eSJohn Marino #include <stdlib.h> 24e0b8e63eSJohn Marino #include <string.h> 25e0b8e63eSJohn Marino #include <unistd.h> 26e0b8e63eSJohn Marino 27e0b8e63eSJohn Marino #include "../common/common.h" 28e0b8e63eSJohn Marino 29e0b8e63eSJohn Marino static int argv_alloc(SCR *, size_t); 30e0b8e63eSJohn Marino static int argv_comp(const void *, const void *); 31e0b8e63eSJohn Marino static int argv_fexp(SCR *, EXCMD *, 32e0b8e63eSJohn Marino CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int); 33e0b8e63eSJohn Marino static int argv_sexp(SCR *, CHAR_T **, size_t *, size_t *); 34e0b8e63eSJohn Marino static int argv_flt_user(SCR *, EXCMD *, CHAR_T *, size_t); 35e0b8e63eSJohn Marino 36e0b8e63eSJohn Marino /* 37e0b8e63eSJohn Marino * argv_init -- 38e0b8e63eSJohn Marino * Build a prototype arguments list. 39e0b8e63eSJohn Marino * 40e0b8e63eSJohn Marino * PUBLIC: int argv_init(SCR *, EXCMD *); 41e0b8e63eSJohn Marino */ 42e0b8e63eSJohn Marino int 43e0b8e63eSJohn Marino argv_init(SCR *sp, EXCMD *excp) 44e0b8e63eSJohn Marino { 45e0b8e63eSJohn Marino EX_PRIVATE *exp; 46e0b8e63eSJohn Marino 47e0b8e63eSJohn Marino exp = EXP(sp); 48e0b8e63eSJohn Marino exp->argsoff = 0; 49e0b8e63eSJohn Marino argv_alloc(sp, 1); 50e0b8e63eSJohn Marino 51e0b8e63eSJohn Marino excp->argv = exp->args; 52e0b8e63eSJohn Marino excp->argc = exp->argsoff; 53e0b8e63eSJohn Marino return (0); 54e0b8e63eSJohn Marino } 55e0b8e63eSJohn Marino 56e0b8e63eSJohn Marino /* 57e0b8e63eSJohn Marino * argv_exp0 -- 58e0b8e63eSJohn Marino * Append a string to the argument list. 59e0b8e63eSJohn Marino * 60e0b8e63eSJohn Marino * PUBLIC: int argv_exp0(SCR *, EXCMD *, CHAR_T *, size_t); 61e0b8e63eSJohn Marino */ 62e0b8e63eSJohn Marino int 63e0b8e63eSJohn Marino argv_exp0(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 64e0b8e63eSJohn Marino { 65e0b8e63eSJohn Marino EX_PRIVATE *exp; 66e0b8e63eSJohn Marino 67e0b8e63eSJohn Marino exp = EXP(sp); 68e0b8e63eSJohn Marino argv_alloc(sp, cmdlen); 69e0b8e63eSJohn Marino MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen); 70e0b8e63eSJohn Marino exp->args[exp->argsoff]->bp[cmdlen] = '\0'; 71e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = cmdlen; 72e0b8e63eSJohn Marino ++exp->argsoff; 73e0b8e63eSJohn Marino excp->argv = exp->args; 74e0b8e63eSJohn Marino excp->argc = exp->argsoff; 75e0b8e63eSJohn Marino return (0); 76e0b8e63eSJohn Marino } 77e0b8e63eSJohn Marino 78e0b8e63eSJohn Marino /* 79e0b8e63eSJohn Marino * argv_exp1 -- 80e0b8e63eSJohn Marino * Do file name expansion on a string, and append it to the 81e0b8e63eSJohn Marino * argument list. 82e0b8e63eSJohn Marino * 83e0b8e63eSJohn Marino * PUBLIC: int argv_exp1(SCR *, EXCMD *, CHAR_T *, size_t, int); 84e0b8e63eSJohn Marino */ 85e0b8e63eSJohn Marino int 86e0b8e63eSJohn Marino argv_exp1(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, int is_bang) 87e0b8e63eSJohn Marino { 88e0b8e63eSJohn Marino EX_PRIVATE *exp; 89e0b8e63eSJohn Marino size_t blen, len; 90e0b8e63eSJohn Marino CHAR_T *p, *t, *bp; 91e0b8e63eSJohn Marino 92e0b8e63eSJohn Marino GET_SPACE_RETW(sp, bp, blen, 512); 93e0b8e63eSJohn Marino 94e0b8e63eSJohn Marino len = 0; 95e0b8e63eSJohn Marino exp = EXP(sp); 96e0b8e63eSJohn Marino if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) { 97e0b8e63eSJohn Marino FREE_SPACEW(sp, bp, blen); 98e0b8e63eSJohn Marino return (1); 99e0b8e63eSJohn Marino } 100e0b8e63eSJohn Marino 101e0b8e63eSJohn Marino /* If it's empty, we're done. */ 102e0b8e63eSJohn Marino if (len != 0) { 103e0b8e63eSJohn Marino for (p = bp, t = bp + len; p < t; ++p) 104e0b8e63eSJohn Marino if (!cmdskip(*p)) 105e0b8e63eSJohn Marino break; 106e0b8e63eSJohn Marino if (p == t) 107e0b8e63eSJohn Marino goto ret; 108e0b8e63eSJohn Marino } else 109e0b8e63eSJohn Marino goto ret; 110e0b8e63eSJohn Marino 111e0b8e63eSJohn Marino (void)argv_exp0(sp, excp, bp, len); 112e0b8e63eSJohn Marino 113e0b8e63eSJohn Marino ret: FREE_SPACEW(sp, bp, blen); 114e0b8e63eSJohn Marino return (0); 115e0b8e63eSJohn Marino } 116e0b8e63eSJohn Marino 117e0b8e63eSJohn Marino /* 118e0b8e63eSJohn Marino * argv_exp2 -- 119e0b8e63eSJohn Marino * Do file name and shell expansion on a string, and append it to 120e0b8e63eSJohn Marino * the argument list. 121e0b8e63eSJohn Marino * 122e0b8e63eSJohn Marino * PUBLIC: int argv_exp2(SCR *, EXCMD *, CHAR_T *, size_t); 123e0b8e63eSJohn Marino */ 124e0b8e63eSJohn Marino int 125e0b8e63eSJohn Marino argv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 126e0b8e63eSJohn Marino { 127e0b8e63eSJohn Marino size_t blen, len, n; 128e0b8e63eSJohn Marino int rval; 129e0b8e63eSJohn Marino CHAR_T *bp, *p; 130e0b8e63eSJohn Marino 131e0b8e63eSJohn Marino GET_SPACE_RETW(sp, bp, blen, 512); 132e0b8e63eSJohn Marino 133e0b8e63eSJohn Marino #define SHELLECHO L("echo ") 134e0b8e63eSJohn Marino #define SHELLOFFSET (SIZE(SHELLECHO) - 1) 135e0b8e63eSJohn Marino MEMCPY(bp, SHELLECHO, SHELLOFFSET); 136e0b8e63eSJohn Marino p = bp + SHELLOFFSET; 137e0b8e63eSJohn Marino len = SHELLOFFSET; 138e0b8e63eSJohn Marino 139e0b8e63eSJohn Marino #if defined(DEBUG) && 0 140e0b8e63eSJohn Marino TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); 141e0b8e63eSJohn Marino #endif 142e0b8e63eSJohn Marino 143e0b8e63eSJohn Marino if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) { 144e0b8e63eSJohn Marino rval = 1; 145e0b8e63eSJohn Marino goto err; 146e0b8e63eSJohn Marino } 147e0b8e63eSJohn Marino 148e0b8e63eSJohn Marino #if defined(DEBUG) && 0 149e0b8e63eSJohn Marino TRACE(sp, "before shell: %d: {%s}\n", len, bp); 150e0b8e63eSJohn Marino #endif 151e0b8e63eSJohn Marino 152e0b8e63eSJohn Marino /* 153e0b8e63eSJohn Marino * Do shell word expansion -- it's very, very hard to figure out what 154e0b8e63eSJohn Marino * magic characters the user's shell expects. Historically, it was a 155e0b8e63eSJohn Marino * union of v7 shell and csh meta characters. We match that practice 156e0b8e63eSJohn Marino * by default, so ":read \%" tries to read a file named '%'. It would 157e0b8e63eSJohn Marino * make more sense to pass any special characters through the shell, 158e0b8e63eSJohn Marino * but then, if your shell was csh, the above example will behave 159e0b8e63eSJohn Marino * differently in nvi than in vi. If you want to get other characters 160e0b8e63eSJohn Marino * passed through to your shell, change the "meta" option. 161e0b8e63eSJohn Marino */ 162e0b8e63eSJohn Marino if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1)) 163e0b8e63eSJohn Marino n = 0; 164e0b8e63eSJohn Marino else { 165e0b8e63eSJohn Marino p = bp + SHELLOFFSET; 166e0b8e63eSJohn Marino n = len - SHELLOFFSET; 167e0b8e63eSJohn Marino for (; n > 0; --n, ++p) 168e0b8e63eSJohn Marino if (IS_SHELLMETA(sp, *p)) 169e0b8e63eSJohn Marino break; 170e0b8e63eSJohn Marino } 171e0b8e63eSJohn Marino 172e0b8e63eSJohn Marino /* 173e0b8e63eSJohn Marino * If we found a meta character in the string, fork a shell to expand 174e0b8e63eSJohn Marino * it. Unfortunately, this is comparatively slow. Historically, it 175e0b8e63eSJohn Marino * didn't matter much, since users don't enter meta characters as part 176e0b8e63eSJohn Marino * of pathnames that frequently. The addition of filename completion 177e0b8e63eSJohn Marino * broke that assumption because it's easy to use. To increase the 178e0b8e63eSJohn Marino * completion performance, nvi used to have an internal routine to 179e0b8e63eSJohn Marino * handle "filename*". However, the shell special characters does not 180e0b8e63eSJohn Marino * limit to "shellmeta", so such a hack breaks historic practice. 181e0b8e63eSJohn Marino * After it all, we split the completion logic out from here. 182e0b8e63eSJohn Marino */ 183e0b8e63eSJohn Marino switch (n) { 184e0b8e63eSJohn Marino case 0: 185e0b8e63eSJohn Marino p = bp + SHELLOFFSET; 186e0b8e63eSJohn Marino len -= SHELLOFFSET; 187e0b8e63eSJohn Marino rval = argv_exp3(sp, excp, p, len); 188e0b8e63eSJohn Marino break; 189e0b8e63eSJohn Marino default: 190e0b8e63eSJohn Marino if (argv_sexp(sp, &bp, &blen, &len)) { 191e0b8e63eSJohn Marino rval = 1; 192e0b8e63eSJohn Marino goto err; 193e0b8e63eSJohn Marino } 194e0b8e63eSJohn Marino p = bp; 195e0b8e63eSJohn Marino rval = argv_exp3(sp, excp, p, len); 196e0b8e63eSJohn Marino break; 197e0b8e63eSJohn Marino } 198e0b8e63eSJohn Marino 199e0b8e63eSJohn Marino err: FREE_SPACEW(sp, bp, blen); 200e0b8e63eSJohn Marino return (rval); 201e0b8e63eSJohn Marino } 202e0b8e63eSJohn Marino 203e0b8e63eSJohn Marino /* 204e0b8e63eSJohn Marino * argv_exp3 -- 205e0b8e63eSJohn Marino * Take a string and break it up into an argv, which is appended 206e0b8e63eSJohn Marino * to the argument list. 207e0b8e63eSJohn Marino * 208e0b8e63eSJohn Marino * PUBLIC: int argv_exp3(SCR *, EXCMD *, CHAR_T *, size_t); 209e0b8e63eSJohn Marino */ 210e0b8e63eSJohn Marino int 211e0b8e63eSJohn Marino argv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 212e0b8e63eSJohn Marino { 213e0b8e63eSJohn Marino EX_PRIVATE *exp; 214e0b8e63eSJohn Marino size_t len; 215e0b8e63eSJohn Marino int ch, off; 216e0b8e63eSJohn Marino CHAR_T *ap, *p; 217e0b8e63eSJohn Marino 218e0b8e63eSJohn Marino for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) { 219e0b8e63eSJohn Marino /* Skip any leading whitespace. */ 220e0b8e63eSJohn Marino for (; cmdlen > 0; --cmdlen, ++cmd) { 221e0b8e63eSJohn Marino ch = *cmd; 222e0b8e63eSJohn Marino if (!cmdskip(ch)) 223e0b8e63eSJohn Marino break; 224e0b8e63eSJohn Marino } 225e0b8e63eSJohn Marino if (cmdlen == 0) 226e0b8e63eSJohn Marino break; 227e0b8e63eSJohn Marino 228e0b8e63eSJohn Marino /* 229e0b8e63eSJohn Marino * Determine the length of this whitespace delimited 230e0b8e63eSJohn Marino * argument. 231e0b8e63eSJohn Marino * 232e0b8e63eSJohn Marino * QUOTING NOTE: 233e0b8e63eSJohn Marino * 234e0b8e63eSJohn Marino * Skip any character preceded by the user's quoting 235e0b8e63eSJohn Marino * character. 236e0b8e63eSJohn Marino */ 237e0b8e63eSJohn Marino for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) { 238e0b8e63eSJohn Marino ch = *cmd; 239e0b8e63eSJohn Marino if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) { 240e0b8e63eSJohn Marino ++cmd; 241e0b8e63eSJohn Marino --cmdlen; 242e0b8e63eSJohn Marino } else if (cmdskip(ch)) 243e0b8e63eSJohn Marino break; 244e0b8e63eSJohn Marino } 245e0b8e63eSJohn Marino 246e0b8e63eSJohn Marino /* 247e0b8e63eSJohn Marino * Copy the argument into place. 248e0b8e63eSJohn Marino * 249e0b8e63eSJohn Marino * QUOTING NOTE: 250e0b8e63eSJohn Marino * 251e0b8e63eSJohn Marino * Lose quote chars. 252e0b8e63eSJohn Marino */ 253e0b8e63eSJohn Marino argv_alloc(sp, len); 254e0b8e63eSJohn Marino off = exp->argsoff; 255e0b8e63eSJohn Marino exp->args[off]->len = len; 256e0b8e63eSJohn Marino for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) 257e0b8e63eSJohn Marino if (IS_ESCAPE(sp, excp, *ap)) 258e0b8e63eSJohn Marino ++ap; 259e0b8e63eSJohn Marino *p = '\0'; 260e0b8e63eSJohn Marino } 261e0b8e63eSJohn Marino excp->argv = exp->args; 262e0b8e63eSJohn Marino excp->argc = exp->argsoff; 263e0b8e63eSJohn Marino 264e0b8e63eSJohn Marino #if defined(DEBUG) && 0 265e0b8e63eSJohn Marino for (cnt = 0; cnt < exp->argsoff; ++cnt) 266e0b8e63eSJohn Marino TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); 267e0b8e63eSJohn Marino #endif 268e0b8e63eSJohn Marino return (0); 269e0b8e63eSJohn Marino } 270e0b8e63eSJohn Marino 271e0b8e63eSJohn Marino /* 272e0b8e63eSJohn Marino * argv_flt_ex -- 273e0b8e63eSJohn Marino * Filter the ex commands with a prefix, and append the results to 274e0b8e63eSJohn Marino * the argument list. 275e0b8e63eSJohn Marino * 276e0b8e63eSJohn Marino * PUBLIC: int argv_flt_ex(SCR *, EXCMD *, CHAR_T *, size_t); 277e0b8e63eSJohn Marino */ 278e0b8e63eSJohn Marino int 279e0b8e63eSJohn Marino argv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 280e0b8e63eSJohn Marino { 281e0b8e63eSJohn Marino EX_PRIVATE *exp; 282e0b8e63eSJohn Marino EXCMDLIST const *cp; 283e0b8e63eSJohn Marino int off; 284e0b8e63eSJohn Marino size_t len; 285e0b8e63eSJohn Marino 286e0b8e63eSJohn Marino exp = EXP(sp); 287e0b8e63eSJohn Marino 288e0b8e63eSJohn Marino for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) { 289e0b8e63eSJohn Marino len = STRLEN(cp->name); 290e0b8e63eSJohn Marino if (cmdlen > 0 && 291e0b8e63eSJohn Marino (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen))) 292e0b8e63eSJohn Marino continue; 293e0b8e63eSJohn Marino 294e0b8e63eSJohn Marino /* Copy the matched ex command name. */ 295e0b8e63eSJohn Marino argv_alloc(sp, len + 1); 296e0b8e63eSJohn Marino MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1); 297e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = len; 298e0b8e63eSJohn Marino ++exp->argsoff; 299e0b8e63eSJohn Marino excp->argv = exp->args; 300e0b8e63eSJohn Marino excp->argc = exp->argsoff; 301e0b8e63eSJohn Marino } 302e0b8e63eSJohn Marino 303e0b8e63eSJohn Marino return (0); 304e0b8e63eSJohn Marino } 305e0b8e63eSJohn Marino 306e0b8e63eSJohn Marino /* 307e0b8e63eSJohn Marino * argv_flt_user -- 308e0b8e63eSJohn Marino * Filter the ~user list on the system with a prefix, and append 309e0b8e63eSJohn Marino * the results to the argument list. 310e0b8e63eSJohn Marino */ 311e0b8e63eSJohn Marino static int 312e0b8e63eSJohn Marino argv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen) 313e0b8e63eSJohn Marino { 314e0b8e63eSJohn Marino EX_PRIVATE *exp; 315e0b8e63eSJohn Marino struct passwd *pw; 316e0b8e63eSJohn Marino int off; 317e0b8e63eSJohn Marino char *np; 318e0b8e63eSJohn Marino size_t len, nlen; 319e0b8e63eSJohn Marino 320e0b8e63eSJohn Marino exp = EXP(sp); 321e0b8e63eSJohn Marino off = exp->argsoff; 322e0b8e63eSJohn Marino 323e0b8e63eSJohn Marino /* The input must come with a leading '~'. */ 324e0b8e63eSJohn Marino INT2CHAR(sp, uname + 1, ulen - 1, np, nlen); 325e0b8e63eSJohn Marino if ((np = v_strdup(sp, np, nlen)) == NULL) 326e0b8e63eSJohn Marino return (1); 327e0b8e63eSJohn Marino 328e0b8e63eSJohn Marino setpwent(); 329e0b8e63eSJohn Marino while ((pw = getpwent()) != NULL) { 330e0b8e63eSJohn Marino len = strlen(pw->pw_name); 331e0b8e63eSJohn Marino if (nlen > 0 && 332e0b8e63eSJohn Marino (nlen > len || memcmp(np, pw->pw_name, nlen))) 333e0b8e63eSJohn Marino continue; 334e0b8e63eSJohn Marino 335e0b8e63eSJohn Marino /* Copy '~' + the matched user name. */ 336e0b8e63eSJohn Marino CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen); 337e0b8e63eSJohn Marino argv_alloc(sp, ulen + 1); 338e0b8e63eSJohn Marino exp->args[exp->argsoff]->bp[0] = '~'; 339e0b8e63eSJohn Marino MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen); 340e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = ulen; 341e0b8e63eSJohn Marino ++exp->argsoff; 342e0b8e63eSJohn Marino excp->argv = exp->args; 343e0b8e63eSJohn Marino excp->argc = exp->argsoff; 344e0b8e63eSJohn Marino } 345e0b8e63eSJohn Marino endpwent(); 346e0b8e63eSJohn Marino free(np); 347e0b8e63eSJohn Marino 348e0b8e63eSJohn Marino qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 349e0b8e63eSJohn Marino return (0); 350e0b8e63eSJohn Marino } 351e0b8e63eSJohn Marino 352e0b8e63eSJohn Marino /* 353e0b8e63eSJohn Marino * argv_fexp -- 354e0b8e63eSJohn Marino * Do file name and bang command expansion. 355e0b8e63eSJohn Marino */ 356e0b8e63eSJohn Marino static int 357e0b8e63eSJohn Marino 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) 358e0b8e63eSJohn Marino { 359e0b8e63eSJohn Marino EX_PRIVATE *exp; 360e0b8e63eSJohn Marino char *t; 361e0b8e63eSJohn Marino size_t blen, len, off, tlen; 362e0b8e63eSJohn Marino CHAR_T *bp; 363e0b8e63eSJohn Marino CHAR_T *wp; 364e0b8e63eSJohn Marino size_t wlen; 365e0b8e63eSJohn Marino 366e0b8e63eSJohn Marino /* Replace file name characters. */ 367e0b8e63eSJohn Marino for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) 368e0b8e63eSJohn Marino switch (*cmd) { 369e0b8e63eSJohn Marino case '!': 370e0b8e63eSJohn Marino if (!is_bang) 371e0b8e63eSJohn Marino goto ins_ch; 372e0b8e63eSJohn Marino exp = EXP(sp); 373e0b8e63eSJohn Marino if (exp->lastbcomm == NULL) { 374e0b8e63eSJohn Marino msgq(sp, M_ERR, 375e0b8e63eSJohn Marino "115|No previous command to replace \"!\""); 376e0b8e63eSJohn Marino return (1); 377e0b8e63eSJohn Marino } 378e0b8e63eSJohn Marino len += tlen = STRLEN(exp->lastbcomm); 379e0b8e63eSJohn Marino off = p - bp; 380e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len); 381e0b8e63eSJohn Marino p = bp + off; 382e0b8e63eSJohn Marino MEMCPY(p, exp->lastbcomm, tlen); 383e0b8e63eSJohn Marino p += tlen; 384e0b8e63eSJohn Marino F_SET(excp, E_MODIFY); 385e0b8e63eSJohn Marino break; 386e0b8e63eSJohn Marino case '%': 387e0b8e63eSJohn Marino if ((t = sp->frp->name) == NULL) { 388e0b8e63eSJohn Marino msgq(sp, M_ERR, 389e0b8e63eSJohn Marino "116|No filename to substitute for %%"); 390e0b8e63eSJohn Marino return (1); 391e0b8e63eSJohn Marino } 392e0b8e63eSJohn Marino tlen = strlen(t); 393e0b8e63eSJohn Marino len += tlen; 394e0b8e63eSJohn Marino off = p - bp; 395e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len); 396e0b8e63eSJohn Marino p = bp + off; 397e0b8e63eSJohn Marino CHAR2INT(sp, t, tlen, wp, wlen); 398e0b8e63eSJohn Marino MEMCPY(p, wp, wlen); 399e0b8e63eSJohn Marino p += wlen; 400e0b8e63eSJohn Marino F_SET(excp, E_MODIFY); 401e0b8e63eSJohn Marino break; 402e0b8e63eSJohn Marino case '#': 403e0b8e63eSJohn Marino if ((t = sp->alt_name) == NULL) { 404e0b8e63eSJohn Marino msgq(sp, M_ERR, 405e0b8e63eSJohn Marino "117|No filename to substitute for #"); 406e0b8e63eSJohn Marino return (1); 407e0b8e63eSJohn Marino } 408e0b8e63eSJohn Marino len += tlen = strlen(t); 409e0b8e63eSJohn Marino off = p - bp; 410e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len); 411e0b8e63eSJohn Marino p = bp + off; 412e0b8e63eSJohn Marino CHAR2INT(sp, t, tlen, wp, wlen); 413e0b8e63eSJohn Marino MEMCPY(p, wp, wlen); 414e0b8e63eSJohn Marino p += wlen; 415e0b8e63eSJohn Marino F_SET(excp, E_MODIFY); 416e0b8e63eSJohn Marino break; 417e0b8e63eSJohn Marino case '\\': 418e0b8e63eSJohn Marino /* 419e0b8e63eSJohn Marino * QUOTING NOTE: 420e0b8e63eSJohn Marino * 421e0b8e63eSJohn Marino * Strip any backslashes that protected the file 422e0b8e63eSJohn Marino * expansion characters. 423e0b8e63eSJohn Marino */ 424e0b8e63eSJohn Marino if (cmdlen > 1 && 425e0b8e63eSJohn Marino (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) { 426e0b8e63eSJohn Marino ++cmd; 427e0b8e63eSJohn Marino --cmdlen; 428e0b8e63eSJohn Marino } 429e0b8e63eSJohn Marino /* FALLTHROUGH */ 430e0b8e63eSJohn Marino default: 431e0b8e63eSJohn Marino ins_ch: ++len; 432e0b8e63eSJohn Marino off = p - bp; 433e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len); 434e0b8e63eSJohn Marino p = bp + off; 435e0b8e63eSJohn Marino *p++ = *cmd; 436e0b8e63eSJohn Marino } 437e0b8e63eSJohn Marino 438e0b8e63eSJohn Marino /* Nul termination. */ 439e0b8e63eSJohn Marino ++len; 440e0b8e63eSJohn Marino off = p - bp; 441e0b8e63eSJohn Marino ADD_SPACE_RETW(sp, bp, blen, len); 442e0b8e63eSJohn Marino p = bp + off; 443e0b8e63eSJohn Marino *p = '\0'; 444e0b8e63eSJohn Marino 445e0b8e63eSJohn Marino /* Return the new string length, buffer, buffer length. */ 446e0b8e63eSJohn Marino *lenp = len - 1; 447e0b8e63eSJohn Marino *bpp = bp; 448e0b8e63eSJohn Marino *blenp = blen; 449e0b8e63eSJohn Marino return (0); 450e0b8e63eSJohn Marino } 451e0b8e63eSJohn Marino 452e0b8e63eSJohn Marino /* 453e0b8e63eSJohn Marino * argv_alloc -- 454e0b8e63eSJohn Marino * Make more space for arguments. 455e0b8e63eSJohn Marino */ 456e0b8e63eSJohn Marino static int 457e0b8e63eSJohn Marino argv_alloc(SCR *sp, size_t len) 458e0b8e63eSJohn Marino { 459e0b8e63eSJohn Marino ARGS *ap; 460e0b8e63eSJohn Marino EX_PRIVATE *exp; 461e0b8e63eSJohn Marino int cnt, off; 462e0b8e63eSJohn Marino 463e0b8e63eSJohn Marino /* 464e0b8e63eSJohn Marino * Allocate room for another argument, always leaving 465e0b8e63eSJohn Marino * enough room for an ARGS structure with a length of 0. 466e0b8e63eSJohn Marino */ 467e0b8e63eSJohn Marino #define INCREMENT 20 468e0b8e63eSJohn Marino exp = EXP(sp); 469e0b8e63eSJohn Marino off = exp->argsoff; 470e0b8e63eSJohn Marino if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { 471e0b8e63eSJohn Marino cnt = exp->argscnt + INCREMENT; 472e0b8e63eSJohn Marino REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *)); 473e0b8e63eSJohn Marino if (exp->args == NULL) { 474e0b8e63eSJohn Marino (void)argv_free(sp); 475e0b8e63eSJohn Marino goto mem; 476e0b8e63eSJohn Marino } 477e0b8e63eSJohn Marino memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *)); 478e0b8e63eSJohn Marino exp->argscnt = cnt; 479e0b8e63eSJohn Marino } 480e0b8e63eSJohn Marino 481e0b8e63eSJohn Marino /* First argument. */ 482e0b8e63eSJohn Marino if (exp->args[off] == NULL) { 483*b1ac2ebbSDaniel Fojt CALLOC(sp, exp->args[off], 1, sizeof(ARGS)); 484e0b8e63eSJohn Marino if (exp->args[off] == NULL) 485e0b8e63eSJohn Marino goto mem; 486e0b8e63eSJohn Marino } 487e0b8e63eSJohn Marino 488e0b8e63eSJohn Marino /* First argument buffer. */ 489e0b8e63eSJohn Marino ap = exp->args[off]; 490e0b8e63eSJohn Marino ap->len = 0; 491e0b8e63eSJohn Marino if (ap->blen < len + 1) { 492e0b8e63eSJohn Marino ap->blen = len + 1; 493e0b8e63eSJohn Marino REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T)); 494e0b8e63eSJohn Marino if (ap->bp == NULL) { 495e0b8e63eSJohn Marino ap->bp = NULL; 496e0b8e63eSJohn Marino ap->blen = 0; 497e0b8e63eSJohn Marino F_CLR(ap, A_ALLOCATED); 498e0b8e63eSJohn Marino mem: msgq(sp, M_SYSERR, NULL); 499e0b8e63eSJohn Marino return (1); 500e0b8e63eSJohn Marino } 501e0b8e63eSJohn Marino F_SET(ap, A_ALLOCATED); 502e0b8e63eSJohn Marino } 503e0b8e63eSJohn Marino 504e0b8e63eSJohn Marino /* Second argument. */ 505e0b8e63eSJohn Marino if (exp->args[++off] == NULL) { 506*b1ac2ebbSDaniel Fojt CALLOC(sp, exp->args[off], 1, sizeof(ARGS)); 507e0b8e63eSJohn Marino if (exp->args[off] == NULL) 508e0b8e63eSJohn Marino goto mem; 509e0b8e63eSJohn Marino } 510e0b8e63eSJohn Marino /* 0 length serves as end-of-argument marker. */ 511e0b8e63eSJohn Marino exp->args[off]->len = 0; 512e0b8e63eSJohn Marino return (0); 513e0b8e63eSJohn Marino } 514e0b8e63eSJohn Marino 515e0b8e63eSJohn Marino /* 516e0b8e63eSJohn Marino * argv_free -- 517e0b8e63eSJohn Marino * Free up argument structures. 518e0b8e63eSJohn Marino * 519e0b8e63eSJohn Marino * PUBLIC: int argv_free(SCR *); 520e0b8e63eSJohn Marino */ 521e0b8e63eSJohn Marino int 522e0b8e63eSJohn Marino argv_free(SCR *sp) 523e0b8e63eSJohn Marino { 524e0b8e63eSJohn Marino EX_PRIVATE *exp; 525e0b8e63eSJohn Marino int off; 526e0b8e63eSJohn Marino 527e0b8e63eSJohn Marino exp = EXP(sp); 528e0b8e63eSJohn Marino if (exp->args != NULL) { 529e0b8e63eSJohn Marino for (off = 0; off < exp->argscnt; ++off) { 530e0b8e63eSJohn Marino if (exp->args[off] == NULL) 531e0b8e63eSJohn Marino continue; 532e0b8e63eSJohn Marino if (F_ISSET(exp->args[off], A_ALLOCATED)) 533e0b8e63eSJohn Marino free(exp->args[off]->bp); 534e0b8e63eSJohn Marino free(exp->args[off]); 535e0b8e63eSJohn Marino } 536e0b8e63eSJohn Marino free(exp->args); 537e0b8e63eSJohn Marino } 538e0b8e63eSJohn Marino exp->args = NULL; 539e0b8e63eSJohn Marino exp->argscnt = 0; 540e0b8e63eSJohn Marino exp->argsoff = 0; 541e0b8e63eSJohn Marino return (0); 542e0b8e63eSJohn Marino } 543e0b8e63eSJohn Marino 544e0b8e63eSJohn Marino /* 545e0b8e63eSJohn Marino * argv_flt_path -- 546e0b8e63eSJohn Marino * Find all file names matching the prefix and append them to the 547e0b8e63eSJohn Marino * argument list. 548e0b8e63eSJohn Marino * 549e0b8e63eSJohn Marino * PUBLIC: int argv_flt_path(SCR *, EXCMD *, CHAR_T *, size_t); 550e0b8e63eSJohn Marino */ 551e0b8e63eSJohn Marino int 552e0b8e63eSJohn Marino argv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen) 553e0b8e63eSJohn Marino { 554e0b8e63eSJohn Marino struct dirent *dp; 555e0b8e63eSJohn Marino DIR *dirp; 556e0b8e63eSJohn Marino EX_PRIVATE *exp; 557e0b8e63eSJohn Marino int off; 558e0b8e63eSJohn Marino size_t dlen, len, nlen; 559e0b8e63eSJohn Marino CHAR_T *dname; 560e0b8e63eSJohn Marino CHAR_T *p, *np, *n; 561e0b8e63eSJohn Marino char *name, *tp, *epd = NULL; 562e0b8e63eSJohn Marino CHAR_T *wp; 563e0b8e63eSJohn Marino size_t wlen; 564e0b8e63eSJohn Marino 565e0b8e63eSJohn Marino exp = EXP(sp); 566e0b8e63eSJohn Marino 567e0b8e63eSJohn Marino /* Set up the name and length for comparison. */ 568e0b8e63eSJohn Marino if ((path = v_wstrdup(sp, path, plen)) == NULL) 569e0b8e63eSJohn Marino return (1); 570e0b8e63eSJohn Marino if ((p = STRRCHR(path, '/')) == NULL) { 571e0b8e63eSJohn Marino if (*path == '~') { 572e0b8e63eSJohn Marino int rc; 573e0b8e63eSJohn Marino 574e0b8e63eSJohn Marino /* Filter ~user list instead. */ 575e0b8e63eSJohn Marino rc = argv_flt_user(sp, excp, path, plen); 576e0b8e63eSJohn Marino free(path); 577e0b8e63eSJohn Marino return (rc); 578e0b8e63eSJohn Marino } 579e0b8e63eSJohn Marino dname = L("."); 580e0b8e63eSJohn Marino dlen = 0; 581e0b8e63eSJohn Marino np = path; 582e0b8e63eSJohn Marino } else { 583e0b8e63eSJohn Marino if (p == path) { 584e0b8e63eSJohn Marino dname = L("/"); 585e0b8e63eSJohn Marino dlen = 1; 586e0b8e63eSJohn Marino } else { 587e0b8e63eSJohn Marino *p = '\0'; 588e0b8e63eSJohn Marino dname = path; 589e0b8e63eSJohn Marino dlen = p - path; 590e0b8e63eSJohn Marino } 591e0b8e63eSJohn Marino np = p + 1; 592e0b8e63eSJohn Marino } 593e0b8e63eSJohn Marino 594e0b8e63eSJohn Marino INT2CHAR(sp, dname, dlen + 1, tp, nlen); 595e0b8e63eSJohn Marino if ((epd = expanduser(tp)) != NULL) 596e0b8e63eSJohn Marino tp = epd; 597e0b8e63eSJohn Marino if ((dirp = opendir(tp)) == NULL) { 598e0b8e63eSJohn Marino free(epd); 599e0b8e63eSJohn Marino free(path); 600e0b8e63eSJohn Marino return (1); 601e0b8e63eSJohn Marino } 602e0b8e63eSJohn Marino free(epd); 603e0b8e63eSJohn Marino 604e0b8e63eSJohn Marino INT2CHAR(sp, np, STRLEN(np), tp, nlen); 605e0b8e63eSJohn Marino if ((name = v_strdup(sp, tp, nlen)) == NULL) { 606e0b8e63eSJohn Marino free(path); 607e0b8e63eSJohn Marino return (1); 608e0b8e63eSJohn Marino } 609e0b8e63eSJohn Marino 610e0b8e63eSJohn Marino for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) { 611e0b8e63eSJohn Marino if (nlen == 0) { 612e0b8e63eSJohn Marino if (dp->d_name[0] == '.') 613e0b8e63eSJohn Marino continue; 614*b1ac2ebbSDaniel Fojt #ifdef HAVE_DIRENT_D_NAMLEN 615e0b8e63eSJohn Marino len = dp->d_namlen; 616*b1ac2ebbSDaniel Fojt #else 617*b1ac2ebbSDaniel Fojt len = strlen(dp->d_name); 618*b1ac2ebbSDaniel Fojt #endif 619e0b8e63eSJohn Marino } else { 620*b1ac2ebbSDaniel Fojt #ifdef HAVE_DIRENT_D_NAMLEN 621e0b8e63eSJohn Marino len = dp->d_namlen; 622*b1ac2ebbSDaniel Fojt #else 623*b1ac2ebbSDaniel Fojt len = strlen(dp->d_name); 624*b1ac2ebbSDaniel Fojt #endif 625e0b8e63eSJohn Marino if (len < nlen || memcmp(dp->d_name, name, nlen)) 626e0b8e63eSJohn Marino continue; 627e0b8e63eSJohn Marino } 628e0b8e63eSJohn Marino 629e0b8e63eSJohn Marino /* Directory + name + slash + null. */ 630e0b8e63eSJohn Marino CHAR2INT(sp, dp->d_name, len + 1, wp, wlen); 631e0b8e63eSJohn Marino argv_alloc(sp, dlen + wlen + 1); 632e0b8e63eSJohn Marino n = exp->args[exp->argsoff]->bp; 633e0b8e63eSJohn Marino if (dlen != 0) { 634e0b8e63eSJohn Marino MEMCPY(n, dname, dlen); 635e0b8e63eSJohn Marino n += dlen; 636e0b8e63eSJohn Marino if (dlen > 1 || dname[0] != '/') 637e0b8e63eSJohn Marino *n++ = '/'; 638e0b8e63eSJohn Marino exp->args[exp->argsoff]->len = dlen + 1; 639e0b8e63eSJohn Marino } 640e0b8e63eSJohn Marino MEMCPY(n, wp, wlen); 641e0b8e63eSJohn Marino exp->args[exp->argsoff]->len += wlen - 1; 642e0b8e63eSJohn Marino ++exp->argsoff; 643e0b8e63eSJohn Marino excp->argv = exp->args; 644e0b8e63eSJohn Marino excp->argc = exp->argsoff; 645e0b8e63eSJohn Marino } 646e0b8e63eSJohn Marino closedir(dirp); 647e0b8e63eSJohn Marino free(name); 648e0b8e63eSJohn Marino free(path); 649e0b8e63eSJohn Marino 650e0b8e63eSJohn Marino qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 651e0b8e63eSJohn Marino return (0); 652e0b8e63eSJohn Marino } 653e0b8e63eSJohn Marino 654e0b8e63eSJohn Marino /* 655e0b8e63eSJohn Marino * argv_comp -- 656e0b8e63eSJohn Marino * Alphabetic comparison. 657e0b8e63eSJohn Marino */ 658e0b8e63eSJohn Marino static int 659e0b8e63eSJohn Marino argv_comp(const void *a, const void *b) 660e0b8e63eSJohn Marino { 661e0b8e63eSJohn Marino return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp)); 662e0b8e63eSJohn Marino } 663e0b8e63eSJohn Marino 664e0b8e63eSJohn Marino /* 665e0b8e63eSJohn Marino * argv_sexp -- 666e0b8e63eSJohn Marino * Fork a shell, pipe a command through it, and read the output into 667e0b8e63eSJohn Marino * a buffer. 668e0b8e63eSJohn Marino */ 669e0b8e63eSJohn Marino static int 670e0b8e63eSJohn Marino argv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp) 671e0b8e63eSJohn Marino { 672e0b8e63eSJohn Marino enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval; 673e0b8e63eSJohn Marino FILE *ifp; 674e0b8e63eSJohn Marino pid_t pid; 675e0b8e63eSJohn Marino size_t blen, len; 676e0b8e63eSJohn Marino int ch, std_output[2]; 677e0b8e63eSJohn Marino CHAR_T *bp, *p; 678e0b8e63eSJohn Marino char *sh, *sh_path; 679e0b8e63eSJohn Marino char *np; 680e0b8e63eSJohn Marino size_t nlen; 681e0b8e63eSJohn Marino 682e0b8e63eSJohn Marino /* Secure means no shell access. */ 683e0b8e63eSJohn Marino if (O_ISSET(sp, O_SECURE)) { 684e0b8e63eSJohn Marino msgq(sp, M_ERR, 685e0b8e63eSJohn Marino "289|Shell expansions not supported when the secure edit option is set"); 686e0b8e63eSJohn Marino return (1); 687e0b8e63eSJohn Marino } 688e0b8e63eSJohn Marino 689e0b8e63eSJohn Marino sh_path = O_STR(sp, O_SHELL); 690e0b8e63eSJohn Marino if ((sh = strrchr(sh_path, '/')) == NULL) 691e0b8e63eSJohn Marino sh = sh_path; 692e0b8e63eSJohn Marino else 693e0b8e63eSJohn Marino ++sh; 694e0b8e63eSJohn Marino 695e0b8e63eSJohn Marino /* Local copies of the buffer variables. */ 696e0b8e63eSJohn Marino bp = *bpp; 697e0b8e63eSJohn Marino blen = *blenp; 698e0b8e63eSJohn Marino 699e0b8e63eSJohn Marino /* 700e0b8e63eSJohn Marino * There are two different processes running through this code, named 701e0b8e63eSJohn Marino * the utility (the shell) and the parent. The utility reads standard 702e0b8e63eSJohn Marino * input and writes standard output and standard error output. The 703e0b8e63eSJohn Marino * parent writes to the utility, reads its standard output and ignores 704e0b8e63eSJohn Marino * its standard error output. Historically, the standard error output 705e0b8e63eSJohn Marino * was discarded by vi, as it produces a lot of noise when file patterns 706e0b8e63eSJohn Marino * don't match. 707e0b8e63eSJohn Marino * 708e0b8e63eSJohn Marino * The parent reads std_output[0], and the utility writes std_output[1]. 709e0b8e63eSJohn Marino */ 710e0b8e63eSJohn Marino ifp = NULL; 711e0b8e63eSJohn Marino std_output[0] = std_output[1] = -1; 712e0b8e63eSJohn Marino if (pipe(std_output) < 0) { 713e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "pipe"); 714e0b8e63eSJohn Marino return (1); 715e0b8e63eSJohn Marino } 716e0b8e63eSJohn Marino if ((ifp = fdopen(std_output[0], "r")) == NULL) { 717e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "fdopen"); 718e0b8e63eSJohn Marino goto err; 719e0b8e63eSJohn Marino } 720e0b8e63eSJohn Marino 721e0b8e63eSJohn Marino /* 722e0b8e63eSJohn Marino * Do the minimal amount of work possible, the shell is going to run 723e0b8e63eSJohn Marino * briefly and then exit. We sincerely hope. 724e0b8e63eSJohn Marino */ 725e0b8e63eSJohn Marino switch (pid = vfork()) { 726e0b8e63eSJohn Marino case -1: /* Error. */ 727e0b8e63eSJohn Marino msgq(sp, M_SYSERR, "vfork"); 728e0b8e63eSJohn Marino err: if (ifp != NULL) 729e0b8e63eSJohn Marino (void)fclose(ifp); 730e0b8e63eSJohn Marino else if (std_output[0] != -1) 731e0b8e63eSJohn Marino close(std_output[0]); 732e0b8e63eSJohn Marino if (std_output[1] != -1) 733e0b8e63eSJohn Marino close(std_output[0]); 734e0b8e63eSJohn Marino return (1); 735e0b8e63eSJohn Marino case 0: /* Utility. */ 736e0b8e63eSJohn Marino /* Redirect stdout to the write end of the pipe. */ 737e0b8e63eSJohn Marino (void)dup2(std_output[1], STDOUT_FILENO); 738e0b8e63eSJohn Marino 739e0b8e63eSJohn Marino /* Close the utility's file descriptors. */ 740e0b8e63eSJohn Marino (void)close(std_output[0]); 741e0b8e63eSJohn Marino (void)close(std_output[1]); 742e0b8e63eSJohn Marino (void)close(STDERR_FILENO); 743e0b8e63eSJohn Marino 744e0b8e63eSJohn Marino /* 745e0b8e63eSJohn Marino * XXX 746e0b8e63eSJohn Marino * Assume that all shells have -c. 747e0b8e63eSJohn Marino */ 748e0b8e63eSJohn Marino INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen); 749e0b8e63eSJohn Marino execl(sh_path, sh, "-c", np, (char *)NULL); 750e0b8e63eSJohn Marino msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s"); 751e0b8e63eSJohn Marino _exit(127); 752e0b8e63eSJohn Marino default: /* Parent. */ 753e0b8e63eSJohn Marino /* Close the pipe ends the parent won't use. */ 754e0b8e63eSJohn Marino (void)close(std_output[1]); 755e0b8e63eSJohn Marino break; 756e0b8e63eSJohn Marino } 757e0b8e63eSJohn Marino 758e0b8e63eSJohn Marino /* 759e0b8e63eSJohn Marino * Copy process standard output into a buffer. 760e0b8e63eSJohn Marino * 761e0b8e63eSJohn Marino * !!! 762e0b8e63eSJohn Marino * Historic vi apparently discarded leading \n and \r's from 763e0b8e63eSJohn Marino * the shell output stream. We don't on the grounds that any 764e0b8e63eSJohn Marino * shell that does that is broken. 765e0b8e63eSJohn Marino */ 766e0b8e63eSJohn Marino for (p = bp, len = 0, ch = EOF; 767e0b8e63eSJohn Marino (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len) 768e0b8e63eSJohn Marino if (blen < 5) { 769e0b8e63eSJohn Marino ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2); 770e0b8e63eSJohn Marino p = bp + len; 771e0b8e63eSJohn Marino blen = *blenp - len; 772e0b8e63eSJohn Marino } 773e0b8e63eSJohn Marino 774e0b8e63eSJohn Marino /* Delete the final newline, nul terminate the string. */ 775e0b8e63eSJohn Marino if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) { 776e0b8e63eSJohn Marino --p; 777e0b8e63eSJohn Marino --len; 778e0b8e63eSJohn Marino } 779e0b8e63eSJohn Marino *p = '\0'; 780e0b8e63eSJohn Marino *lenp = len; 781e0b8e63eSJohn Marino *bpp = bp; /* *blenp is already updated. */ 782e0b8e63eSJohn Marino 783e0b8e63eSJohn Marino if (ferror(ifp)) 784e0b8e63eSJohn Marino goto ioerr; 785e0b8e63eSJohn Marino if (fclose(ifp)) { 786e0b8e63eSJohn Marino ioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s"); 787e0b8e63eSJohn Marino alloc_err: rval = SEXP_ERR; 788e0b8e63eSJohn Marino } else 789e0b8e63eSJohn Marino rval = SEXP_OK; 790e0b8e63eSJohn Marino 791e0b8e63eSJohn Marino /* 792e0b8e63eSJohn Marino * Wait for the process. If the shell process fails (e.g., "echo $q" 793e0b8e63eSJohn Marino * where q wasn't a defined variable) or if the returned string has 794e0b8e63eSJohn Marino * no characters or only blank characters, (e.g., "echo $5"), complain 795e0b8e63eSJohn Marino * that the shell expansion failed. We can't know for certain that's 796e0b8e63eSJohn Marino * the error, but it's a good guess, and it matches historic practice. 797e0b8e63eSJohn Marino * This won't catch "echo foo_$5", but that's not a common error and 798e0b8e63eSJohn Marino * historic vi didn't catch it either. 799e0b8e63eSJohn Marino */ 800e0b8e63eSJohn Marino if (proc_wait(sp, (long)pid, sh, 1, 0)) 801e0b8e63eSJohn Marino rval = SEXP_EXPANSION_ERR; 802e0b8e63eSJohn Marino 803e0b8e63eSJohn Marino for (p = bp; len; ++p, --len) 804e0b8e63eSJohn Marino if (!cmdskip(*p)) 805e0b8e63eSJohn Marino break; 806e0b8e63eSJohn Marino if (len == 0) 807e0b8e63eSJohn Marino rval = SEXP_EXPANSION_ERR; 808e0b8e63eSJohn Marino 809e0b8e63eSJohn Marino if (rval == SEXP_EXPANSION_ERR) 810e0b8e63eSJohn Marino msgq(sp, M_ERR, "304|Shell expansion failed"); 811e0b8e63eSJohn Marino 812e0b8e63eSJohn Marino return (rval == SEXP_OK ? 0 : 1); 813e0b8e63eSJohn Marino } 814e0b8e63eSJohn Marino 815e0b8e63eSJohn Marino /* 816e0b8e63eSJohn Marino * argv_esc -- 817e0b8e63eSJohn Marino * Escape a string into an ex and shell argument. 818e0b8e63eSJohn Marino * 819e0b8e63eSJohn Marino * PUBLIC: CHAR_T *argv_esc(SCR *, EXCMD *, CHAR_T *, size_t); 820e0b8e63eSJohn Marino */ 821e0b8e63eSJohn Marino CHAR_T * 822e0b8e63eSJohn Marino argv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len) 823e0b8e63eSJohn Marino { 824e0b8e63eSJohn Marino size_t blen, off; 825e0b8e63eSJohn Marino CHAR_T *bp, *p; 826e0b8e63eSJohn Marino int ch; 827e0b8e63eSJohn Marino 828e0b8e63eSJohn Marino GET_SPACE_GOTOW(sp, bp, blen, len + 1); 829e0b8e63eSJohn Marino 830e0b8e63eSJohn Marino /* 831e0b8e63eSJohn Marino * Leaving the first '~' unescaped causes the user to need a 832e0b8e63eSJohn Marino * "./" prefix to edit a file which really starts with a '~'. 833e0b8e63eSJohn Marino * However, the file completion happens to not work for these 834e0b8e63eSJohn Marino * files without the prefix. 835e0b8e63eSJohn Marino * 836e0b8e63eSJohn Marino * All ex expansion characters, "!%#", are double escaped. 837e0b8e63eSJohn Marino */ 838e0b8e63eSJohn Marino for (p = bp; len > 0; ++str, --len) { 839e0b8e63eSJohn Marino ch = *str; 840e0b8e63eSJohn Marino off = p - bp; 841e0b8e63eSJohn Marino if (blen / sizeof(CHAR_T) - off < 3) { 842e0b8e63eSJohn Marino ADD_SPACE_GOTOW(sp, bp, blen, off + 3); 843e0b8e63eSJohn Marino p = bp + off; 844e0b8e63eSJohn Marino } 845e0b8e63eSJohn Marino if (cmdskip(ch) || ch == '\n' || 846e0b8e63eSJohn Marino IS_ESCAPE(sp, excp, ch)) /* Ex. */ 847e0b8e63eSJohn Marino *p++ = CH_LITERAL; 848e0b8e63eSJohn Marino else switch (ch) { 849e0b8e63eSJohn Marino case '~': /* ~user. */ 850e0b8e63eSJohn Marino if (p != bp) 851e0b8e63eSJohn Marino *p++ = '\\'; 852e0b8e63eSJohn Marino break; 853e0b8e63eSJohn Marino case '+': /* Ex +cmd. */ 854e0b8e63eSJohn Marino if (p == bp) 855e0b8e63eSJohn Marino *p++ = '\\'; 856e0b8e63eSJohn Marino break; 857e0b8e63eSJohn Marino case '!': case '%': case '#': /* Ex exp. */ 858e0b8e63eSJohn Marino *p++ = '\\'; 859e0b8e63eSJohn Marino *p++ = '\\'; 860e0b8e63eSJohn Marino break; 861e0b8e63eSJohn Marino case ',': case '-': case '.': case '/': /* Safe. */ 862e0b8e63eSJohn Marino case ':': case '=': case '@': case '_': 863e0b8e63eSJohn Marino break; 864e0b8e63eSJohn Marino default: /* Unsafe. */ 865e0b8e63eSJohn Marino if (isascii(ch) && !isalnum(ch)) 866e0b8e63eSJohn Marino *p++ = '\\'; 867e0b8e63eSJohn Marino } 868e0b8e63eSJohn Marino *p++ = ch; 869e0b8e63eSJohn Marino } 870e0b8e63eSJohn Marino *p = '\0'; 871e0b8e63eSJohn Marino 872e0b8e63eSJohn Marino return bp; 873e0b8e63eSJohn Marino 874e0b8e63eSJohn Marino alloc_err: 875e0b8e63eSJohn Marino return NULL; 876e0b8e63eSJohn Marino } 877e0b8e63eSJohn Marino 878e0b8e63eSJohn Marino /* 879e0b8e63eSJohn Marino * argv_uesc -- 880e0b8e63eSJohn Marino * Unescape an escaped ex and shell argument. 881e0b8e63eSJohn Marino * 882e0b8e63eSJohn Marino * PUBLIC: CHAR_T *argv_uesc(SCR *, EXCMD *, CHAR_T *, size_t); 883e0b8e63eSJohn Marino */ 884e0b8e63eSJohn Marino CHAR_T * 885e0b8e63eSJohn Marino argv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len) 886e0b8e63eSJohn Marino { 887e0b8e63eSJohn Marino size_t blen; 888e0b8e63eSJohn Marino CHAR_T *bp, *p; 889e0b8e63eSJohn Marino 890e0b8e63eSJohn Marino GET_SPACE_GOTOW(sp, bp, blen, len + 1); 891e0b8e63eSJohn Marino 892e0b8e63eSJohn Marino for (p = bp; len > 0; ++str, --len) { 893e0b8e63eSJohn Marino if (IS_ESCAPE(sp, excp, *str)) { 894e0b8e63eSJohn Marino if (--len < 1) 895e0b8e63eSJohn Marino break; 896e0b8e63eSJohn Marino ++str; 897e0b8e63eSJohn Marino } else if (*str == '\\') { 898e0b8e63eSJohn Marino if (--len < 1) 899e0b8e63eSJohn Marino break; 900e0b8e63eSJohn Marino ++str; 901e0b8e63eSJohn Marino 902e0b8e63eSJohn Marino /* Check for double escaping. */ 903e0b8e63eSJohn Marino if (*str == '\\' && len > 1) 904e0b8e63eSJohn Marino switch (str[1]) { 905e0b8e63eSJohn Marino case '!': case '%': case '#': 906e0b8e63eSJohn Marino ++str; 907e0b8e63eSJohn Marino --len; 908e0b8e63eSJohn Marino } 909e0b8e63eSJohn Marino } 910e0b8e63eSJohn Marino *p++ = *str; 911e0b8e63eSJohn Marino } 912e0b8e63eSJohn Marino *p = '\0'; 913e0b8e63eSJohn Marino 914e0b8e63eSJohn Marino return bp; 915e0b8e63eSJohn Marino 916e0b8e63eSJohn Marino alloc_err: 917e0b8e63eSJohn Marino return NULL; 918e0b8e63eSJohn Marino } 919