Beispiel #1
0
/*
 * exwr --
 *	The guts of the ex write commands.
 */
static int
exwr(SCR *sp, EXCMD *cmdp, enum which cmd)
{
	MARK rm;
	int flags;
	char *name;
	CHAR_T *p = NULL;
	size_t nlen;
	char *n;
	int rc;
	EX_PRIVATE *exp;

	NEEDFILE(sp, cmdp);

	/* All write commands can have an associated '!'. */
	LF_INIT(FS_POSSIBLE);
	if (FL_ISSET(cmdp->iflags, E_C_FORCE))
		LF_SET(FS_FORCE);

	/* Skip any leading whitespace. */
	if (cmdp->argc != 0)
		for (p = cmdp->argv[0]->bp; *p != '\0' && cmdskip(*p); ++p);

	/* If "write !" it's a pipe to a utility. */
	if (cmdp->argc != 0 && cmd == WRITE && *p == '!') {
		/* Secure means no shell access. */
		if (O_ISSET(sp, O_SECURE)) {
			ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
			return (1);
		}

		/* Expand the argument. */
		for (++p; *p && cmdskip(*p); ++p);
		if (*p == '\0') {
			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
			return (1);
		}
		if (argv_exp1(sp, cmdp, p, STRLEN(p), 1))
			return (1);

		/* Set the last bang command */
		exp = EXP(sp);
		free(exp->lastbcomm);
		exp->lastbcomm = v_wstrdup(sp, cmdp->argv[1]->bp,
		    cmdp->argv[1]->len);

		/*
		 * Historically, vi waited after a write filter even if there
		 * wasn't any output from the command.  People complained when
		 * nvi waited only if there was output, wanting the visual cue
		 * that the program hadn't written anything.
		 */
		F_SET(sp, SC_EX_WAIT_YES);

		/*
		 * !!!
		 * Ignore the return cursor position, the cursor doesn't
		 * move.
		 */
		if (ex_filter(sp, cmdp, &cmdp->addr1,
		    &cmdp->addr2, &rm, cmdp->argv[1]->bp, FILTER_WRITE))
			return (1);

		/* Ex terminates with a bang, even if the command fails. */
		if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
			(void)ex_puts(sp, "!\n");

		return (0);
	}

	/* Set the FS_ALL flag if we're writing the entire file. */
	if (cmdp->addr1.lno <= 1 && !db_exist(sp, cmdp->addr2.lno + 1))
		LF_SET(FS_ALL);

	/* If "write >>" it's an append to a file. */
	if (cmdp->argc != 0 && cmd != XIT && p[0] == '>' && p[1] == '>') {
		LF_SET(FS_APPEND);

		/* Skip ">>" and whitespace. */
		for (p += 2; *p && cmdskip(*p); ++p);
	}

	/* If no other arguments, just write the file back. */
	if (cmdp->argc == 0 || *p == '\0')
		return (file_write(sp,
		    &cmdp->addr1, &cmdp->addr2, NULL, flags));

	/* Build an argv so we get an argument count and file expansion. */
	if (argv_exp2(sp, cmdp, p, STRLEN(p)))
		return (1);

	/*
	 *  0 args: impossible.
	 *  1 args: impossible (I hope).
	 *  2 args: read it.
	 * >2 args: object, too many args.
	 *
	 * The 1 args case depends on the argv_sexp() function refusing
	 * to return success without at least one non-blank character.
	 */
	switch (cmdp->argc) {
	case 0:
	case 1:
		abort();
		/* NOTREACHED */
	case 2:
		INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len+1,
			 n, nlen);
		name = v_strdup(sp, n, nlen - 1);

		/*
		 * !!!
		 * Historically, the read and write commands renamed
		 * "unnamed" files, or, if the file had a name, set
		 * the alternate file name.
		 */
		if (F_ISSET(sp->frp, FR_TMPFILE) &&
		    !F_ISSET(sp->frp, FR_EXNAMED)) {
			if ((n = v_strdup(sp, name, nlen - 1)) != NULL) {
				free(sp->frp->name);
				sp->frp->name = n;
			}
			/*
			 * The file has a real name, it's no longer a
			 * temporary, clear the temporary file flags.
			 *
			 * !!!
			 * If we're writing the whole file, FR_NAMECHANGE
			 * will be cleared by the write routine -- this is
			 * historic practice.
			 */
			F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
			F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);

			/* Notify the screen. */
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
		} else
			set_alt_name(sp, name);
		break;
	default:
		INT2CHAR(sp, p, STRLEN(p) + 1, n, nlen);
		ex_emsg(sp, n, EXM_FILECOUNT);
		return (1);
	}

	rc = file_write(sp, &cmdp->addr1, &cmdp->addr2, name, flags);

	free(name);

	return rc;
}
Beispiel #2
0
/*
 * ex_read --	:read [file]
 *		:read [!cmd]
 *	Read from a file or utility.
 *
 * !!!
 * Historical vi wouldn't undo a filter read, for no apparent reason.
 *
 * PUBLIC: int ex_read __P((SCR *, EXCMD *));
 */
int
ex_read(SCR *sp, EXCMD *cmdp)
{
	enum { R_ARG, R_EXPANDARG, R_FILTER } which;
	struct stat sb;
	CHAR_T *arg;
	char *name;
	size_t nlen;
	EX_PRIVATE *exp;
	FILE *fp;
	FREF *frp;
	GS *gp;
	MARK rm;
	db_recno_t nlines;
	size_t arglen;
	int argc, rval;
	char *p;
	char *np;

	gp = sp->gp;

	/*
	 * 0 args: read the current pathname.
	 * 1 args: check for "read !arg".
	 */
	switch (cmdp->argc) {
	case 0:
		which = R_ARG;
		break;
	case 1:
		arg = cmdp->argv[0]->bp;
		arglen = cmdp->argv[0]->len;
		if (*arg == '!') {
			++arg;
			--arglen;
			which = R_FILTER;

			/* Secure means no shell access. */
			if (O_ISSET(sp, O_SECURE)) {
				ex_wemsg(sp, cmdp->cmd->name, EXM_SECURE_F);
				return (1);
			}
		} else
			which = R_EXPANDARG;
		break;
	default:
		abort();
		/* NOTREACHED */
	}

	/* Load a temporary file if no file being edited. */
	if (sp->ep == NULL) {
		if ((frp = file_add(sp, NULL)) == NULL)
			return (1);
		if (file_init(sp, frp, NULL, 0))
			return (1);
	}

	switch (which) {
	case R_FILTER:
		/*
		 * File name and bang expand the user's argument.  If
		 * we don't get an additional argument, it's illegal.
		 */
		argc = cmdp->argc;
		if (argv_exp1(sp, cmdp, arg, arglen, 1))
			return (1);
		if (argc == cmdp->argc) {
			ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
			return (1);
		}
		argc = cmdp->argc - 1;

		/* Set the last bang command. */
		exp = EXP(sp);
		if (exp->lastbcomm != NULL)
			free(exp->lastbcomm);
		if ((exp->lastbcomm =
		    v_wstrdup(sp, cmdp->argv[argc]->bp,
				cmdp->argv[argc]->len)) == NULL) {
			msgq(sp, M_SYSERR, NULL);
			return (1);
		}

		/*
		 * Vi redisplayed the user's argument if it changed, ex
		 * always displayed a !, plus the user's argument if it
		 * changed.
		 */
		if (F_ISSET(sp, SC_VI)) {
			if (F_ISSET(cmdp, E_MODIFY))
				(void)vs_update(sp, "!", cmdp->argv[argc]->bp);
		} else {
			if (F_ISSET(cmdp, E_MODIFY))
				(void)ex_printf(sp,
				    "!%s\n", cmdp->argv[argc]->bp);
			else
				(void)ex_puts(sp, "!\n");
			(void)ex_fflush(sp);
		}

		/*
		 * Historically, filter reads as the first ex command didn't
		 * wait for the user. If SC_SCR_EXWROTE not already set, set
		 * the don't-wait flag.
		 */
		if (!F_ISSET(sp, SC_SCR_EXWROTE))
			F_SET(sp, SC_EX_WAIT_NO);

		/*
		 * Switch into ex canonical mode.  The reason to restore the
		 * original terminal modes for read filters is so that users
		 * can do things like ":r! cat /dev/tty".
		 *
		 * !!!
		 * We do not output an extra <newline>, so that we don't touch
		 * the screen on a normal read.
		 */
		if (F_ISSET(sp, SC_VI)) {
			if (gp->scr_screen(sp, SC_EX)) {
				ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON_F);
				return (1);
			}
			/*
			 * !!!
			 * Historically, the read command doesn't switch to
			 * the alternate X11 xterm screen, if doing a filter
			 * read -- don't set SA_ALTERNATE.
			 */
			F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
		}

		if (ex_filter(sp, cmdp, &cmdp->addr1,
		    NULL, &rm, cmdp->argv[argc]->bp, FILTER_READ))
			return (1);

		/* The filter version of read set the autoprint flag. */
		F_SET(cmdp, E_AUTOPRINT);

		/*
		 * If in vi mode, move to the first nonblank.  Might have
		 * switched into ex mode, so saved the original SC_VI value.
		 */
		sp->lno = rm.lno;
		if (F_ISSET(sp, SC_VI)) {
			sp->cno = 0;
			(void)nonblank(sp, sp->lno, &sp->cno);
		}
		return (0);
	case R_ARG:
		name = sp->frp->name;
		break;
	case R_EXPANDARG:
		if (argv_exp2(sp, cmdp, arg, arglen))
			return (1);
		/*
		 *  0 args: impossible.
		 *  1 args: impossible (I hope).
		 *  2 args: read it.
		 * >2 args: object, too many args.
		 *
		 * The 1 args case depends on the argv_sexp() function refusing
		 * to return success without at least one non-blank character.
		 */
		switch (cmdp->argc) {
		case 0:
		case 1:
			abort();
			/* NOTREACHED */
		case 2:
			INT2CHAR(sp, cmdp->argv[1]->bp, cmdp->argv[1]->len + 1, 
				 name, nlen);
			/*
			 * !!!
			 * Historically, the read and write commands renamed
			 * "unnamed" files, or, if the file had a name, set
			 * the alternate file name.
			 */
			if (F_ISSET(sp->frp, FR_TMPFILE) &&
			    !F_ISSET(sp->frp, FR_EXNAMED)) {
				if ((p = strdup(name)) != NULL) {
					free(sp->frp->name);
					sp->frp->name = p;
				}
				/*
				 * The file has a real name, it's no longer a
				 * temporary, clear the temporary file flags.
				 */
				F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE);
				F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED);

				/* Notify the screen. */
				(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
				name = sp->frp->name;
			} else {
				set_alt_name(sp, name);
				name = sp->alt_name;
			}
			break;
		default:
			ex_wemsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT);
			return (1);
		
		}
		break;
	}

	/*
	 * !!!
	 * Historically, vi did not permit reads from non-regular files, nor
	 * did it distinguish between "read !" and "read!", so there was no
	 * way to "force" it.  We permit reading from named pipes too, since
	 * they didn't exist when the original implementation of vi was done
	 * and they seem a reasonable addition.
	 */
	if ((fp = fopen(name, "r")) == NULL || fstat(fileno(fp), &sb)) {
		msgq_str(sp, M_SYSERR, name, "%s");
		return (1);
	}
	if (!S_ISFIFO(sb.st_mode) && !S_ISREG(sb.st_mode)) {
		(void)fclose(fp);
		msgq(sp, M_ERR,
		    "145|Only regular files and named pipes may be read");
		return (1);
	}

	/* Try and get a lock. */
	if (file_lock(sp, NULL, NULL, fileno(fp), 0) == LOCK_UNAVAIL)
		msgq(sp, M_ERR, "146|%s: read lock was unavailable", name);

	rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0);

	/*
	 * In vi, set the cursor to the first line read in, if anything read
	 * in, otherwise, the address.  (Historic vi set it to the line after
	 * the address regardless, but since that line may not exist we don't
	 * bother.)
	 *
	 * In ex, set the cursor to the last line read in, if anything read in,
	 * otherwise, the address.
	 */
	if (F_ISSET(sp, SC_VI)) {
		sp->lno = cmdp->addr1.lno;
		if (nlines)
			++sp->lno;
	} else
		sp->lno = cmdp->addr1.lno + nlines;
	return (rval);
}
Beispiel #3
0
/*
 * ex_bang -- :[line [,line]] ! command
 *
 * Pass the rest of the line after the ! character to the program named by
 * the O_SHELL option.
 *
 * Historical vi did NOT do shell expansion on the arguments before passing
 * them, only file name expansion.  This means that the O_SHELL program got
 * "$t" as an argument if that is what the user entered.  Also, there's a
 * special expansion done for the bang command.  Any exclamation points in
 * the user's argument are replaced by the last, expanded ! command.
 *
 * There's some fairly amazing slop in this routine to make the different
 * ways of getting here display the right things.  It took a long time to
 * get it right (wrong?), so be careful.
 *
 * PUBLIC: int ex_bang(SCR *, EXCMD *);
 */
int
ex_bang(SCR *sp, EXCMD *cmdp)
{
	enum filtertype ftype;
	ARGS *ap;
	EX_PRIVATE *exp;
	MARK rm;
	recno_t lno;
	int rval;
	const char *msg;
	char *np;
	size_t nlen;

	ap = cmdp->argv[0];
	if (ap->len == 0) {
		ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE);
		return (1);
	}

	/* Set the "last bang command" remembered value. */
	exp = EXP(sp);
	free(exp->lastbcomm);
	if ((exp->lastbcomm = v_wstrdup(sp, ap->bp, ap->len)) == NULL) {
		msgq(sp, M_SYSERR, NULL);
		return (1);
	}

	/*
	 * If the command was modified by the expansion, it was historically
	 * redisplayed.
	 */
	if (F_ISSET(cmdp, E_MODIFY) && !F_ISSET(sp, SC_EX_SILENT)) {
		/*
		 * Display the command if modified.  Historic ex/vi displayed
		 * the command if it was modified due to file name and/or bang
		 * expansion.  If piping lines in vi, it would be immediately
		 * overwritten by any error or line change reporting.
		 */
		if (F_ISSET(sp, SC_VI))
			vs_update(sp, "!", ap->bp);
		else {
			(void)ex_printf(sp, "!"WS"\n", ap->bp);
			(void)ex_fflush(sp);
		}
	}

	/*
	 * If no addresses were specified, run the command.  If there's an
	 * underlying file, it's been modified and autowrite is set, write
	 * the file back.  If the file has been modified, autowrite is not
	 * set and the warn option is set, tell the user about the file.
	 */
	if (cmdp->addrcnt == 0) {
		msg = NULL;
		if (sp->ep != NULL && F_ISSET(sp->ep, F_MODIFIED))
			if (O_ISSET(sp, O_AUTOWRITE)) {
				if (file_aw(sp, FS_ALL))
					return (0);
			} else if (O_ISSET(sp, O_WARN) &&
			    !F_ISSET(sp, SC_EX_SILENT))
				msg = msg_cat(sp,
				    "303|File modified since last write.",
				    NULL);

		/* If we're still in a vi screen, move out explicitly. */
		INT2CHAR(sp, ap->bp, ap->len+1, np, nlen);
		(void)ex_exec_proc(sp,
		    cmdp, np, msg, !F_ISSET(sp, SC_EX | SC_SCR_EXWROTE));
	}

	/*
	 * If addresses were specified, pipe lines from the file through the
	 * command.
	 *
	 * Historically, vi lines were replaced by both the stdout and stderr
	 * lines of the command, but ex lines by only the stdout lines.  This
	 * makes no sense to me, so nvi makes it consistent for both, and
	 * matches vi's historic behavior.
	 */
	else {
		NEEDFILE(sp, cmdp);

		/* Autoprint is set historically, even if the command fails. */
		F_SET(cmdp, E_AUTOPRINT);

		/*
		 * !!!
		 * Historical vi permitted "!!" in an empty file.  When this
		 * happens, we arrive here with two addresses of 1,1 and a
		 * bad attitude.  The simple solution is to turn it into a
		 * FILTER_READ operation, with the exception that stdin isn't
		 * opened for the utility, and the cursor position isn't the
		 * same.  The only historic glitch (I think) is that we don't
		 * put an empty line into the default cut buffer, as historic
		 * vi did.  Imagine, if you can, my disappointment.
		 */
		ftype = FILTER_BANG;
		if (cmdp->addr1.lno == 1 && cmdp->addr2.lno == 1) {
			if (db_last(sp, &lno))
				return (1);
			if (lno == 0) {
				cmdp->addr1.lno = cmdp->addr2.lno = 0;
				ftype = FILTER_RBANG;
			}
		}
		rval = ex_filter(sp, cmdp,
		    &cmdp->addr1, &cmdp->addr2, &rm, ap->bp, ftype);

		/*
		 * If in vi mode, move to the first nonblank.
		 *
		 * !!!
		 * Historic vi wasn't consistent in this area -- if you used
		 * a forward motion it moved to the first nonblank, but if you
		 * did a backward motion it didn't.  And, if you followed a
		 * backward motion with a forward motion, it wouldn't move to
		 * the nonblank for either.  Going to the nonblank generally
		 * seems more useful and consistent, so we do it.
		 */
		sp->lno = rm.lno;
		if (F_ISSET(sp, SC_VI)) {
			sp->cno = 0;
			(void)nonblank(sp, sp->lno, &sp->cno);
		} else
			sp->cno = rm.cno;
	}

	/* Ex terminates with a bang, even if the command fails. */
	if (!F_ISSET(sp, SC_VI) && !F_ISSET(sp, SC_EX_SILENT))
		(void)ex_puts(sp, "!\n");

	/*
	 * XXX
	 * The ! commands never return an error, so that autoprint always
	 * happens in the ex parser.
	 */
	return (0);
}