119304Speter/*- 219304Speter * Copyright (c) 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: ex_argv.c,v 11.2 2012/10/09 23:00:29 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 18254225Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <ctype.h> 2219304Speter#include <dirent.h> 2319304Speter#include <errno.h> 2419304Speter#include <limits.h> 25254225Speter#include <pwd.h> 2619304Speter#include <stdio.h> 2719304Speter#include <stdlib.h> 2819304Speter#include <string.h> 2919304Speter#include <unistd.h> 3019304Speter 3119304Speter#include "../common/common.h" 3219304Speter 3319304Speterstatic int argv_alloc __P((SCR *, size_t)); 3419304Speterstatic int argv_comp __P((const void *, const void *)); 3519304Speterstatic int argv_fexp __P((SCR *, EXCMD *, 36254225Speter CHAR_T *, size_t, CHAR_T *, size_t *, CHAR_T **, size_t *, int)); 37254225Speterstatic int argv_sexp __P((SCR *, CHAR_T **, size_t *, size_t *)); 38254225Speterstatic int argv_flt_user __P((SCR *, EXCMD *, CHAR_T *, size_t)); 3919304Speter 4019304Speter/* 4119304Speter * argv_init -- 4219304Speter * Build a prototype arguments list. 4319304Speter * 4419304Speter * PUBLIC: int argv_init __P((SCR *, EXCMD *)); 4519304Speter */ 4619304Speterint 47254225Speterargv_init(SCR *sp, EXCMD *excp) 4819304Speter{ 4919304Speter EX_PRIVATE *exp; 5019304Speter 5119304Speter exp = EXP(sp); 5219304Speter exp->argsoff = 0; 5319304Speter argv_alloc(sp, 1); 5419304Speter 5519304Speter excp->argv = exp->args; 5619304Speter excp->argc = exp->argsoff; 5719304Speter return (0); 5819304Speter} 5919304Speter 6019304Speter/* 6119304Speter * argv_exp0 -- 6219304Speter * Append a string to the argument list. 6319304Speter * 64254225Speter * PUBLIC: int argv_exp0 __P((SCR *, EXCMD *, CHAR_T *, size_t)); 6519304Speter */ 6619304Speterint 67254225Speterargv_exp0(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 6819304Speter{ 6919304Speter EX_PRIVATE *exp; 7019304Speter 7119304Speter exp = EXP(sp); 7219304Speter argv_alloc(sp, cmdlen); 73254225Speter MEMCPY(exp->args[exp->argsoff]->bp, cmd, cmdlen); 7419304Speter exp->args[exp->argsoff]->bp[cmdlen] = '\0'; 7519304Speter exp->args[exp->argsoff]->len = cmdlen; 7619304Speter ++exp->argsoff; 7719304Speter excp->argv = exp->args; 7819304Speter excp->argc = exp->argsoff; 7919304Speter return (0); 8019304Speter} 8119304Speter 8219304Speter/* 8319304Speter * argv_exp1 -- 8419304Speter * Do file name expansion on a string, and append it to the 8519304Speter * argument list. 8619304Speter * 87254225Speter * PUBLIC: int argv_exp1 __P((SCR *, EXCMD *, CHAR_T *, size_t, int)); 8819304Speter */ 8919304Speterint 90254225Speterargv_exp1(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen, int is_bang) 9119304Speter{ 9219304Speter EX_PRIVATE *exp; 9319304Speter size_t blen, len; 94254225Speter CHAR_T *p, *t, *bp; 9519304Speter 96254225Speter GET_SPACE_RETW(sp, bp, blen, 512); 9719304Speter 9819304Speter len = 0; 9919304Speter exp = EXP(sp); 10019304Speter if (argv_fexp(sp, excp, cmd, cmdlen, bp, &len, &bp, &blen, is_bang)) { 101254225Speter FREE_SPACEW(sp, bp, blen); 10219304Speter return (1); 10319304Speter } 10419304Speter 10519304Speter /* If it's empty, we're done. */ 10619304Speter if (len != 0) { 10719304Speter for (p = bp, t = bp + len; p < t; ++p) 108254225Speter if (!cmdskip(*p)) 10919304Speter break; 11019304Speter if (p == t) 11119304Speter goto ret; 11219304Speter } else 11319304Speter goto ret; 11419304Speter 11519304Speter (void)argv_exp0(sp, excp, bp, len); 11619304Speter 117254225Speterret: FREE_SPACEW(sp, bp, blen); 11819304Speter return (0); 11919304Speter} 12019304Speter 12119304Speter/* 12219304Speter * argv_exp2 -- 12319304Speter * Do file name and shell expansion on a string, and append it to 12419304Speter * the argument list. 12519304Speter * 126254225Speter * PUBLIC: int argv_exp2 __P((SCR *, EXCMD *, CHAR_T *, size_t)); 12719304Speter */ 12819304Speterint 129254225Speterargv_exp2(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 13019304Speter{ 13119304Speter size_t blen, len, n; 13219304Speter int rval; 133254225Speter CHAR_T *bp, *p; 13419304Speter 135254225Speter GET_SPACE_RETW(sp, bp, blen, 512); 13619304Speter 137254225Speter#define SHELLECHO L("echo ") 138254225Speter#define SHELLOFFSET (SIZE(SHELLECHO) - 1) 139254225Speter MEMCPY(bp, SHELLECHO, SHELLOFFSET); 14019304Speter p = bp + SHELLOFFSET; 14119304Speter len = SHELLOFFSET; 14219304Speter 14319304Speter#if defined(DEBUG) && 0 14419304Speter TRACE(sp, "file_argv: {%.*s}\n", (int)cmdlen, cmd); 14519304Speter#endif 14619304Speter 14719304Speter if (argv_fexp(sp, excp, cmd, cmdlen, p, &len, &bp, &blen, 0)) { 14819304Speter rval = 1; 14919304Speter goto err; 15019304Speter } 15119304Speter 15219304Speter#if defined(DEBUG) && 0 15319304Speter TRACE(sp, "before shell: %d: {%s}\n", len, bp); 15419304Speter#endif 15519304Speter 15619304Speter /* 15719304Speter * Do shell word expansion -- it's very, very hard to figure out what 15819304Speter * magic characters the user's shell expects. Historically, it was a 15919304Speter * union of v7 shell and csh meta characters. We match that practice 16019304Speter * by default, so ":read \%" tries to read a file named '%'. It would 16119304Speter * make more sense to pass any special characters through the shell, 16219304Speter * but then, if your shell was csh, the above example will behave 16319304Speter * differently in nvi than in vi. If you want to get other characters 16419304Speter * passed through to your shell, change the "meta" option. 16519304Speter */ 16619304Speter if (opts_empty(sp, O_SHELL, 1) || opts_empty(sp, O_SHELLMETA, 1)) 16719304Speter n = 0; 16819304Speter else { 16919304Speter p = bp + SHELLOFFSET; 17019304Speter n = len - SHELLOFFSET; 171254225Speter for (; n > 0; --n, ++p) 172254225Speter if (IS_SHELLMETA(sp, *p)) 173254225Speter break; 17419304Speter } 17519304Speter 17619304Speter /* 17719304Speter * If we found a meta character in the string, fork a shell to expand 17819304Speter * it. Unfortunately, this is comparatively slow. Historically, it 17919304Speter * didn't matter much, since users don't enter meta characters as part 18019304Speter * of pathnames that frequently. The addition of filename completion 181254225Speter * broke that assumption because it's easy to use. To increase the 182254225Speter * completion performance, nvi used to have an internal routine to 183254225Speter * handle "filename*". However, the shell special characters does not 184254225Speter * limit to "shellmeta", so such a hack breaks historic practice. 185254225Speter * After it all, we split the completion logic out from here. 18619304Speter */ 18719304Speter switch (n) { 18819304Speter case 0: 18919304Speter p = bp + SHELLOFFSET; 19019304Speter len -= SHELLOFFSET; 19119304Speter rval = argv_exp3(sp, excp, p, len); 19219304Speter break; 19319304Speter default: 19419304Speter if (argv_sexp(sp, &bp, &blen, &len)) { 19519304Speter rval = 1; 19619304Speter goto err; 19719304Speter } 19819304Speter p = bp; 19919304Speter rval = argv_exp3(sp, excp, p, len); 20019304Speter break; 20119304Speter } 20219304Speter 203254225Spetererr: FREE_SPACEW(sp, bp, blen); 20419304Speter return (rval); 20519304Speter} 20619304Speter 20719304Speter/* 20819304Speter * argv_exp3 -- 20919304Speter * Take a string and break it up into an argv, which is appended 21019304Speter * to the argument list. 21119304Speter * 212254225Speter * PUBLIC: int argv_exp3 __P((SCR *, EXCMD *, CHAR_T *, size_t)); 21319304Speter */ 21419304Speterint 215254225Speterargv_exp3(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 21619304Speter{ 21719304Speter EX_PRIVATE *exp; 21819304Speter size_t len; 21919304Speter int ch, off; 220254225Speter CHAR_T *ap, *p; 22119304Speter 22219304Speter for (exp = EXP(sp); cmdlen > 0; ++exp->argsoff) { 22319304Speter /* Skip any leading whitespace. */ 22419304Speter for (; cmdlen > 0; --cmdlen, ++cmd) { 22519304Speter ch = *cmd; 226254225Speter if (!cmdskip(ch)) 22719304Speter break; 22819304Speter } 22919304Speter if (cmdlen == 0) 23019304Speter break; 23119304Speter 23219304Speter /* 23319304Speter * Determine the length of this whitespace delimited 23419304Speter * argument. 23519304Speter * 23619304Speter * QUOTING NOTE: 23719304Speter * 23819304Speter * Skip any character preceded by the user's quoting 23919304Speter * character. 24019304Speter */ 24119304Speter for (ap = cmd, len = 0; cmdlen > 0; ++cmd, --cmdlen, ++len) { 24219304Speter ch = *cmd; 24319304Speter if (IS_ESCAPE(sp, excp, ch) && cmdlen > 1) { 24419304Speter ++cmd; 24519304Speter --cmdlen; 246254225Speter } else if (cmdskip(ch)) 24719304Speter break; 24819304Speter } 24919304Speter 25019304Speter /* 25119304Speter * Copy the argument into place. 25219304Speter * 25319304Speter * QUOTING NOTE: 25419304Speter * 25519304Speter * Lose quote chars. 25619304Speter */ 25719304Speter argv_alloc(sp, len); 25819304Speter off = exp->argsoff; 25919304Speter exp->args[off]->len = len; 26019304Speter for (p = exp->args[off]->bp; len > 0; --len, *p++ = *ap++) 26119304Speter if (IS_ESCAPE(sp, excp, *ap)) 26219304Speter ++ap; 26319304Speter *p = '\0'; 26419304Speter } 26519304Speter excp->argv = exp->args; 26619304Speter excp->argc = exp->argsoff; 26719304Speter 26819304Speter#if defined(DEBUG) && 0 26919304Speter for (cnt = 0; cnt < exp->argsoff; ++cnt) 27019304Speter TRACE(sp, "arg %d: {%s}\n", cnt, exp->argv[cnt]); 27119304Speter#endif 27219304Speter return (0); 27319304Speter} 27419304Speter 27519304Speter/* 276254225Speter * argv_flt_ex -- 277254225Speter * Filter the ex commands with a prefix, and append the results to 278254225Speter * the argument list. 279254225Speter * 280254225Speter * PUBLIC: int argv_flt_ex __P((SCR *, EXCMD *, CHAR_T *, size_t)); 281254225Speter */ 282254225Speterint 283254225Speterargv_flt_ex(SCR *sp, EXCMD *excp, CHAR_T *cmd, size_t cmdlen) 284254225Speter{ 285254225Speter EX_PRIVATE *exp; 286254225Speter EXCMDLIST const *cp; 287254225Speter int off; 288254225Speter size_t len; 289254225Speter 290254225Speter exp = EXP(sp); 291254225Speter 292254225Speter for (off = exp->argsoff, cp = cmds; cp->name != NULL; ++cp) { 293254225Speter len = STRLEN(cp->name); 294254225Speter if (cmdlen > 0 && 295254225Speter (cmdlen > len || MEMCMP(cmd, cp->name, cmdlen))) 296254225Speter continue; 297254225Speter 298254225Speter /* Copy the matched ex command name. */ 299254225Speter argv_alloc(sp, len + 1); 300254225Speter MEMCPY(exp->args[exp->argsoff]->bp, cp->name, len + 1); 301254225Speter exp->args[exp->argsoff]->len = len; 302254225Speter ++exp->argsoff; 303254225Speter excp->argv = exp->args; 304254225Speter excp->argc = exp->argsoff; 305254225Speter } 306254225Speter 307254225Speter return (0); 308254225Speter} 309254225Speter 310254225Speter/* 311254225Speter * argv_flt_user -- 312254225Speter * Filter the ~user list on the system with a prefix, and append 313254225Speter * the results to the argument list. 314254225Speter */ 315254225Speterstatic int 316254225Speterargv_flt_user(SCR *sp, EXCMD *excp, CHAR_T *uname, size_t ulen) 317254225Speter{ 318254225Speter EX_PRIVATE *exp; 319254225Speter struct passwd *pw; 320254225Speter int off; 321254225Speter char *np; 322254225Speter size_t len, nlen; 323254225Speter 324254225Speter exp = EXP(sp); 325254225Speter off = exp->argsoff; 326254225Speter 327254225Speter /* The input must come with a leading '~'. */ 328254225Speter INT2CHAR(sp, uname + 1, ulen - 1, np, nlen); 329254225Speter if ((np = v_strdup(sp, np, nlen)) == NULL) 330254225Speter return (1); 331254225Speter 332254225Speter setpwent(); 333254225Speter while ((pw = getpwent()) != NULL) { 334254225Speter len = strlen(pw->pw_name); 335254225Speter if (nlen > 0 && 336254225Speter (nlen > len || memcmp(np, pw->pw_name, nlen))) 337254225Speter continue; 338254225Speter 339254225Speter /* Copy '~' + the matched user name. */ 340254225Speter CHAR2INT(sp, pw->pw_name, len + 1, uname, ulen); 341254225Speter argv_alloc(sp, ulen + 1); 342254225Speter exp->args[exp->argsoff]->bp[0] = '~'; 343254225Speter MEMCPY(exp->args[exp->argsoff]->bp + 1, uname, ulen); 344254225Speter exp->args[exp->argsoff]->len = ulen; 345254225Speter ++exp->argsoff; 346254225Speter excp->argv = exp->args; 347254225Speter excp->argc = exp->argsoff; 348254225Speter } 349254225Speter endpwent(); 350254225Speter free(np); 351254225Speter 352254225Speter qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 353254225Speter return (0); 354254225Speter} 355254225Speter 356254225Speter/* 35719304Speter * argv_fexp -- 35819304Speter * Do file name and bang command expansion. 35919304Speter */ 36019304Speterstatic int 361254225Speterargv_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) 36219304Speter{ 36319304Speter EX_PRIVATE *exp; 364254225Speter char *t; 36519304Speter size_t blen, len, off, tlen; 366254225Speter CHAR_T *bp; 367254225Speter CHAR_T *wp; 368254225Speter size_t wlen; 36919304Speter 37019304Speter /* Replace file name characters. */ 37119304Speter for (bp = *bpp, blen = *blenp, len = *lenp; cmdlen > 0; --cmdlen, ++cmd) 37219304Speter switch (*cmd) { 37319304Speter case '!': 37419304Speter if (!is_bang) 37519304Speter goto ins_ch; 37619304Speter exp = EXP(sp); 37719304Speter if (exp->lastbcomm == NULL) { 37819304Speter msgq(sp, M_ERR, 37919304Speter "115|No previous command to replace \"!\""); 38019304Speter return (1); 38119304Speter } 382254225Speter len += tlen = STRLEN(exp->lastbcomm); 38319304Speter off = p - bp; 384254225Speter ADD_SPACE_RETW(sp, bp, blen, len); 38519304Speter p = bp + off; 386254225Speter MEMCPY(p, exp->lastbcomm, tlen); 38719304Speter p += tlen; 38819304Speter F_SET(excp, E_MODIFY); 38919304Speter break; 39019304Speter case '%': 39119304Speter if ((t = sp->frp->name) == NULL) { 39219304Speter msgq(sp, M_ERR, 39319304Speter "116|No filename to substitute for %%"); 39419304Speter return (1); 39519304Speter } 39619304Speter tlen = strlen(t); 39719304Speter len += tlen; 39819304Speter off = p - bp; 399254225Speter ADD_SPACE_RETW(sp, bp, blen, len); 40019304Speter p = bp + off; 401254225Speter CHAR2INT(sp, t, tlen, wp, wlen); 402254225Speter MEMCPY(p, wp, wlen); 403254225Speter p += wlen; 40419304Speter F_SET(excp, E_MODIFY); 40519304Speter break; 40619304Speter case '#': 40719304Speter if ((t = sp->alt_name) == NULL) { 40819304Speter msgq(sp, M_ERR, 40919304Speter "117|No filename to substitute for #"); 41019304Speter return (1); 41119304Speter } 41219304Speter len += tlen = strlen(t); 41319304Speter off = p - bp; 414254225Speter ADD_SPACE_RETW(sp, bp, blen, len); 41519304Speter p = bp + off; 416254225Speter CHAR2INT(sp, t, tlen, wp, wlen); 417254225Speter MEMCPY(p, wp, wlen); 418254225Speter p += wlen; 41919304Speter F_SET(excp, E_MODIFY); 42019304Speter break; 42119304Speter case '\\': 42219304Speter /* 42319304Speter * QUOTING NOTE: 42419304Speter * 42519304Speter * Strip any backslashes that protected the file 42619304Speter * expansion characters. 42719304Speter */ 42819304Speter if (cmdlen > 1 && 42919304Speter (cmd[1] == '%' || cmd[1] == '#' || cmd[1] == '!')) { 43019304Speter ++cmd; 43119304Speter --cmdlen; 43219304Speter } 43319304Speter /* FALLTHROUGH */ 43419304Speter default: 43519304Speterins_ch: ++len; 43619304Speter off = p - bp; 437254225Speter ADD_SPACE_RETW(sp, bp, blen, len); 43819304Speter p = bp + off; 43919304Speter *p++ = *cmd; 44019304Speter } 44119304Speter 44219304Speter /* Nul termination. */ 44319304Speter ++len; 44419304Speter off = p - bp; 445254225Speter ADD_SPACE_RETW(sp, bp, blen, len); 44619304Speter p = bp + off; 44719304Speter *p = '\0'; 44819304Speter 44919304Speter /* Return the new string length, buffer, buffer length. */ 45019304Speter *lenp = len - 1; 45119304Speter *bpp = bp; 45219304Speter *blenp = blen; 45319304Speter return (0); 45419304Speter} 45519304Speter 45619304Speter/* 45719304Speter * argv_alloc -- 45819304Speter * Make more space for arguments. 45919304Speter */ 46019304Speterstatic int 461254225Speterargv_alloc(SCR *sp, size_t len) 46219304Speter{ 46319304Speter ARGS *ap; 46419304Speter EX_PRIVATE *exp; 46519304Speter int cnt, off; 46619304Speter 46719304Speter /* 46819304Speter * Allocate room for another argument, always leaving 46919304Speter * enough room for an ARGS structure with a length of 0. 47019304Speter */ 47119304Speter#define INCREMENT 20 47219304Speter exp = EXP(sp); 47319304Speter off = exp->argsoff; 47419304Speter if (exp->argscnt == 0 || off + 2 >= exp->argscnt - 1) { 47519304Speter cnt = exp->argscnt + INCREMENT; 47619304Speter REALLOC(sp, exp->args, ARGS **, cnt * sizeof(ARGS *)); 47719304Speter if (exp->args == NULL) { 47819304Speter (void)argv_free(sp); 47919304Speter goto mem; 48019304Speter } 48119304Speter memset(&exp->args[exp->argscnt], 0, INCREMENT * sizeof(ARGS *)); 48219304Speter exp->argscnt = cnt; 48319304Speter } 48419304Speter 48519304Speter /* First argument. */ 48619304Speter if (exp->args[off] == NULL) { 48719304Speter CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); 48819304Speter if (exp->args[off] == NULL) 48919304Speter goto mem; 49019304Speter } 49119304Speter 49219304Speter /* First argument buffer. */ 49319304Speter ap = exp->args[off]; 49419304Speter ap->len = 0; 49519304Speter if (ap->blen < len + 1) { 49619304Speter ap->blen = len + 1; 49719304Speter REALLOC(sp, ap->bp, CHAR_T *, ap->blen * sizeof(CHAR_T)); 49819304Speter if (ap->bp == NULL) { 49919304Speter ap->bp = NULL; 50019304Speter ap->blen = 0; 50119304Speter F_CLR(ap, A_ALLOCATED); 50219304Spetermem: msgq(sp, M_SYSERR, NULL); 50319304Speter return (1); 50419304Speter } 50519304Speter F_SET(ap, A_ALLOCATED); 50619304Speter } 50719304Speter 50819304Speter /* Second argument. */ 50919304Speter if (exp->args[++off] == NULL) { 51019304Speter CALLOC(sp, exp->args[off], ARGS *, 1, sizeof(ARGS)); 51119304Speter if (exp->args[off] == NULL) 51219304Speter goto mem; 51319304Speter } 51419304Speter /* 0 length serves as end-of-argument marker. */ 51519304Speter exp->args[off]->len = 0; 51619304Speter return (0); 51719304Speter} 51819304Speter 51919304Speter/* 52019304Speter * argv_free -- 52119304Speter * Free up argument structures. 52219304Speter * 52319304Speter * PUBLIC: int argv_free __P((SCR *)); 52419304Speter */ 52519304Speterint 526254225Speterargv_free(SCR *sp) 52719304Speter{ 52819304Speter EX_PRIVATE *exp; 52919304Speter int off; 53019304Speter 53119304Speter exp = EXP(sp); 53219304Speter if (exp->args != NULL) { 53319304Speter for (off = 0; off < exp->argscnt; ++off) { 53419304Speter if (exp->args[off] == NULL) 53519304Speter continue; 53619304Speter if (F_ISSET(exp->args[off], A_ALLOCATED)) 53719304Speter free(exp->args[off]->bp); 53819304Speter free(exp->args[off]); 53919304Speter } 54019304Speter free(exp->args); 54119304Speter } 54219304Speter exp->args = NULL; 54319304Speter exp->argscnt = 0; 54419304Speter exp->argsoff = 0; 54519304Speter return (0); 54619304Speter} 54719304Speter 54819304Speter/* 549254225Speter * argv_flt_path -- 55019304Speter * Find all file names matching the prefix and append them to the 551254225Speter * argument list. 552254225Speter * 553254225Speter * PUBLIC: int argv_flt_path __P((SCR *, EXCMD *, CHAR_T *, size_t)); 55419304Speter */ 555254225Speterint 556254225Speterargv_flt_path(SCR *sp, EXCMD *excp, CHAR_T *path, size_t plen) 55719304Speter{ 55819304Speter struct dirent *dp; 55919304Speter DIR *dirp; 56019304Speter EX_PRIVATE *exp; 56119304Speter int off; 56219304Speter size_t dlen, len, nlen; 563254225Speter CHAR_T *dname; 564254225Speter CHAR_T *p, *np, *n; 565254225Speter char *name, *tp, *epd = NULL; 566254225Speter CHAR_T *wp; 567254225Speter size_t wlen; 56819304Speter 56919304Speter exp = EXP(sp); 57019304Speter 57119304Speter /* Set up the name and length for comparison. */ 572254225Speter if ((path = v_wstrdup(sp, path, plen)) == NULL) 573254225Speter return (1); 574254225Speter if ((p = STRRCHR(path, '/')) == NULL) { 575254225Speter if (*path == '~') { 576254225Speter int rc; 577254225Speter 578254225Speter /* Filter ~user list instead. */ 579254225Speter rc = argv_flt_user(sp, excp, path, plen); 580254225Speter free(path); 581254225Speter return (rc); 582254225Speter } 583254225Speter dname = L("."); 58419304Speter dlen = 0; 585254225Speter np = path; 586254225Speter } else { 58719304Speter if (p == path) { 588254225Speter dname = L("/"); 58919304Speter dlen = 1; 59019304Speter } else { 59119304Speter *p = '\0'; 59219304Speter dname = path; 593254225Speter dlen = p - path; 59419304Speter } 595254225Speter np = p + 1; 59619304Speter } 59719304Speter 598254225Speter INT2CHAR(sp, dname, dlen + 1, tp, nlen); 599254225Speter if ((epd = expanduser(tp)) != NULL) 600254225Speter tp = epd; 601254225Speter if ((dirp = opendir(tp)) == NULL) { 602254225Speter free(epd); 603254225Speter free(path); 60419304Speter return (1); 60519304Speter } 606254225Speter free(epd); 607254225Speter 608254225Speter INT2CHAR(sp, np, STRLEN(np), tp, nlen); 609254225Speter if ((name = v_strdup(sp, tp, nlen)) == NULL) { 610254225Speter free(path); 611254225Speter return (1); 612254225Speter } 613254225Speter 61419304Speter for (off = exp->argsoff; (dp = readdir(dirp)) != NULL;) { 61519304Speter if (nlen == 0) { 61619304Speter if (dp->d_name[0] == '.') 61719304Speter continue; 618254225Speter len = dp->d_namlen; 61919304Speter } else { 620254225Speter len = dp->d_namlen; 62119304Speter if (len < nlen || memcmp(dp->d_name, name, nlen)) 62219304Speter continue; 62319304Speter } 62419304Speter 62519304Speter /* Directory + name + slash + null. */ 626254225Speter CHAR2INT(sp, dp->d_name, len + 1, wp, wlen); 627254225Speter argv_alloc(sp, dlen + wlen + 1); 628254225Speter n = exp->args[exp->argsoff]->bp; 62919304Speter if (dlen != 0) { 630254225Speter MEMCPY(n, dname, dlen); 631254225Speter n += dlen; 63219304Speter if (dlen > 1 || dname[0] != '/') 633254225Speter *n++ = '/'; 634254225Speter exp->args[exp->argsoff]->len = dlen + 1; 63519304Speter } 636254225Speter MEMCPY(n, wp, wlen); 637254225Speter exp->args[exp->argsoff]->len += wlen - 1; 63819304Speter ++exp->argsoff; 63919304Speter excp->argv = exp->args; 64019304Speter excp->argc = exp->argsoff; 64119304Speter } 64219304Speter closedir(dirp); 643254225Speter free(name); 644254225Speter free(path); 64519304Speter 64619304Speter qsort(exp->args + off, exp->argsoff - off, sizeof(ARGS *), argv_comp); 64719304Speter return (0); 64819304Speter} 64919304Speter 65019304Speter/* 65119304Speter * argv_comp -- 65219304Speter * Alphabetic comparison. 65319304Speter */ 65419304Speterstatic int 655254225Speterargv_comp(const void *a, const void *b) 65619304Speter{ 657254225Speter return (STRCMP((*(ARGS **)a)->bp, (*(ARGS **)b)->bp)); 65819304Speter} 65919304Speter 66019304Speter/* 66119304Speter * argv_sexp -- 66219304Speter * Fork a shell, pipe a command through it, and read the output into 66319304Speter * a buffer. 66419304Speter */ 66519304Speterstatic int 666254225Speterargv_sexp(SCR *sp, CHAR_T **bpp, size_t *blenp, size_t *lenp) 66719304Speter{ 66819304Speter enum { SEXP_ERR, SEXP_EXPANSION_ERR, SEXP_OK } rval; 66919304Speter FILE *ifp; 67019304Speter pid_t pid; 67119304Speter size_t blen, len; 67219304Speter int ch, std_output[2]; 673254225Speter CHAR_T *bp, *p; 674254225Speter char *sh, *sh_path; 675254225Speter char *np; 676254225Speter size_t nlen; 67719304Speter 67819304Speter /* Secure means no shell access. */ 67919304Speter if (O_ISSET(sp, O_SECURE)) { 68019304Speter msgq(sp, M_ERR, 68119304Speter"289|Shell expansions not supported when the secure edit option is set"); 68219304Speter return (1); 68319304Speter } 68419304Speter 68519304Speter sh_path = O_STR(sp, O_SHELL); 68619304Speter if ((sh = strrchr(sh_path, '/')) == NULL) 68719304Speter sh = sh_path; 68819304Speter else 68919304Speter ++sh; 69019304Speter 69119304Speter /* Local copies of the buffer variables. */ 69219304Speter bp = *bpp; 69319304Speter blen = *blenp; 69419304Speter 69519304Speter /* 69619304Speter * There are two different processes running through this code, named 69719304Speter * the utility (the shell) and the parent. The utility reads standard 69819304Speter * input and writes standard output and standard error output. The 69919304Speter * parent writes to the utility, reads its standard output and ignores 70019304Speter * its standard error output. Historically, the standard error output 70119304Speter * was discarded by vi, as it produces a lot of noise when file patterns 70219304Speter * don't match. 70319304Speter * 70419304Speter * The parent reads std_output[0], and the utility writes std_output[1]. 70519304Speter */ 70619304Speter ifp = NULL; 70719304Speter std_output[0] = std_output[1] = -1; 70819304Speter if (pipe(std_output) < 0) { 70919304Speter msgq(sp, M_SYSERR, "pipe"); 71019304Speter return (1); 71119304Speter } 71219304Speter if ((ifp = fdopen(std_output[0], "r")) == NULL) { 71319304Speter msgq(sp, M_SYSERR, "fdopen"); 71419304Speter goto err; 71519304Speter } 71619304Speter 71719304Speter /* 71819304Speter * Do the minimal amount of work possible, the shell is going to run 71919304Speter * briefly and then exit. We sincerely hope. 72019304Speter */ 72119304Speter switch (pid = vfork()) { 72219304Speter case -1: /* Error. */ 72319304Speter msgq(sp, M_SYSERR, "vfork"); 72419304Spetererr: if (ifp != NULL) 72519304Speter (void)fclose(ifp); 72619304Speter else if (std_output[0] != -1) 72719304Speter close(std_output[0]); 72819304Speter if (std_output[1] != -1) 72919304Speter close(std_output[0]); 73019304Speter return (1); 73119304Speter case 0: /* Utility. */ 73219304Speter /* Redirect stdout to the write end of the pipe. */ 73319304Speter (void)dup2(std_output[1], STDOUT_FILENO); 73419304Speter 73519304Speter /* Close the utility's file descriptors. */ 73619304Speter (void)close(std_output[0]); 73719304Speter (void)close(std_output[1]); 73819304Speter (void)close(STDERR_FILENO); 73919304Speter 74019304Speter /* 74119304Speter * XXX 74219304Speter * Assume that all shells have -c. 74319304Speter */ 744254225Speter INT2CHAR(sp, bp, STRLEN(bp)+1, np, nlen); 745254225Speter execl(sh_path, sh, "-c", np, (char *)NULL); 74619304Speter msgq_str(sp, M_SYSERR, sh_path, "118|Error: execl: %s"); 74719304Speter _exit(127); 74819304Speter default: /* Parent. */ 74919304Speter /* Close the pipe ends the parent won't use. */ 75019304Speter (void)close(std_output[1]); 75119304Speter break; 75219304Speter } 75319304Speter 75419304Speter /* 75519304Speter * Copy process standard output into a buffer. 75619304Speter * 75719304Speter * !!! 75819304Speter * Historic vi apparently discarded leading \n and \r's from 75919304Speter * the shell output stream. We don't on the grounds that any 76019304Speter * shell that does that is broken. 76119304Speter */ 76219304Speter for (p = bp, len = 0, ch = EOF; 763254225Speter (ch = GETC(ifp)) != EOF; *p++ = ch, blen-=sizeof(CHAR_T), ++len) 76419304Speter if (blen < 5) { 765254225Speter ADD_SPACE_GOTOW(sp, bp, *blenp, *blenp * 2); 76619304Speter p = bp + len; 76719304Speter blen = *blenp - len; 76819304Speter } 76919304Speter 77019304Speter /* Delete the final newline, nul terminate the string. */ 77119304Speter if (p > bp && (p[-1] == '\n' || p[-1] == '\r')) { 77219304Speter --p; 77319304Speter --len; 77419304Speter } 77519304Speter *p = '\0'; 77619304Speter *lenp = len; 77719304Speter *bpp = bp; /* *blenp is already updated. */ 77819304Speter 77919304Speter if (ferror(ifp)) 78019304Speter goto ioerr; 78119304Speter if (fclose(ifp)) { 78219304Speterioerr: msgq_str(sp, M_ERR, sh, "119|I/O error: %s"); 78319304Speteralloc_err: rval = SEXP_ERR; 78419304Speter } else 78519304Speter rval = SEXP_OK; 78619304Speter 78719304Speter /* 78819304Speter * Wait for the process. If the shell process fails (e.g., "echo $q" 78919304Speter * where q wasn't a defined variable) or if the returned string has 79019304Speter * no characters or only blank characters, (e.g., "echo $5"), complain 79119304Speter * that the shell expansion failed. We can't know for certain that's 79219304Speter * the error, but it's a good guess, and it matches historic practice. 79319304Speter * This won't catch "echo foo_$5", but that's not a common error and 79419304Speter * historic vi didn't catch it either. 79519304Speter */ 79619304Speter if (proc_wait(sp, (long)pid, sh, 1, 0)) 79719304Speter rval = SEXP_EXPANSION_ERR; 79819304Speter 79919304Speter for (p = bp; len; ++p, --len) 800254225Speter if (!cmdskip(*p)) 80119304Speter break; 80219304Speter if (len == 0) 80319304Speter rval = SEXP_EXPANSION_ERR; 80419304Speter 80519304Speter if (rval == SEXP_EXPANSION_ERR) 80619304Speter msgq(sp, M_ERR, "304|Shell expansion failed"); 80719304Speter 80819304Speter return (rval == SEXP_OK ? 0 : 1); 80919304Speter} 810254225Speter 811254225Speter/* 812254225Speter * argv_esc -- 813254225Speter * Escape a string into an ex and shell argument. 814254225Speter * 815254225Speter * PUBLIC: CHAR_T *argv_esc __P((SCR *, EXCMD *, CHAR_T *, size_t)); 816254225Speter */ 817254225SpeterCHAR_T * 818254225Speterargv_esc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len) 819254225Speter{ 820254225Speter size_t blen, off; 821254225Speter CHAR_T *bp, *p; 822254225Speter int ch; 823254225Speter 824254225Speter GET_SPACE_GOTOW(sp, bp, blen, len + 1); 825254225Speter 826254225Speter /* 827254225Speter * Leaving the first '~' unescaped causes the user to need a 828254225Speter * "./" prefix to edit a file which really starts with a '~'. 829254225Speter * However, the file completion happens to not work for these 830254225Speter * files without the prefix. 831254225Speter * 832254225Speter * All ex expansion characters, "!%#", are double escaped. 833254225Speter */ 834254225Speter for (p = bp; len > 0; ++str, --len) { 835254225Speter ch = *str; 836254225Speter off = p - bp; 837254225Speter if (blen / sizeof(CHAR_T) - off < 3) { 838254225Speter ADD_SPACE_GOTOW(sp, bp, blen, off + 3); 839254225Speter p = bp + off; 840254225Speter } 841254225Speter if (cmdskip(ch) || ch == '\n' || 842254225Speter IS_ESCAPE(sp, excp, ch)) /* Ex. */ 843254225Speter *p++ = CH_LITERAL; 844254225Speter else switch (ch) { 845254225Speter case '~': /* ~user. */ 846254225Speter if (p != bp) 847254225Speter *p++ = '\\'; 848254225Speter break; 849254225Speter case '+': /* Ex +cmd. */ 850254225Speter if (p == bp) 851254225Speter *p++ = '\\'; 852254225Speter break; 853254225Speter case '!': case '%': case '#': /* Ex exp. */ 854254225Speter *p++ = '\\'; 855254225Speter *p++ = '\\'; 856254225Speter break; 857254225Speter case ',': case '-': case '.': case '/': /* Safe. */ 858254225Speter case ':': case '=': case '@': case '_': 859254225Speter break; 860254225Speter default: /* Unsafe. */ 861254225Speter if (isascii(ch) && !isalnum(ch)) 862254225Speter *p++ = '\\'; 863254225Speter } 864254225Speter *p++ = ch; 865254225Speter } 866254225Speter *p = '\0'; 867254225Speter 868254225Speter return bp; 869254225Speter 870254225Speteralloc_err: 871254225Speter return NULL; 872254225Speter} 873254225Speter 874254225Speter/* 875254225Speter * argv_uesc -- 876254225Speter * Unescape an escaped ex and shell argument. 877254225Speter * 878254225Speter * PUBLIC: CHAR_T *argv_uesc __P((SCR *, EXCMD *, CHAR_T *, size_t)); 879254225Speter */ 880254225SpeterCHAR_T * 881254225Speterargv_uesc(SCR *sp, EXCMD *excp, CHAR_T *str, size_t len) 882254225Speter{ 883254225Speter size_t blen; 884254225Speter CHAR_T *bp, *p; 885254225Speter 886254225Speter GET_SPACE_GOTOW(sp, bp, blen, len + 1); 887254225Speter 888254225Speter for (p = bp; len > 0; ++str, --len) { 889254225Speter if (IS_ESCAPE(sp, excp, *str)) { 890254225Speter if (--len < 1) 891254225Speter break; 892254225Speter ++str; 893254225Speter } else if (*str == '\\') { 894254225Speter if (--len < 1) 895254225Speter break; 896254225Speter ++str; 897254225Speter 898254225Speter /* Check for double escaping. */ 899254225Speter if (*str == '\\' && len > 1) 900254225Speter switch (str[1]) { 901254225Speter case '!': case '%': case '#': 902254225Speter ++str; 903254225Speter --len; 904254225Speter } 905254225Speter } 906254225Speter *p++ = *str; 907254225Speter } 908254225Speter *p = '\0'; 909254225Speter 910254225Speter return bp; 911254225Speter 912254225Speteralloc_err: 913254225Speter return NULL; 914254225Speter} 915