[BACK]Return to cmdcmds.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

File: [local] / src / usr.bin / vim / Attic / cmdcmds.c (download)

Revision 1.5, Mon Oct 14 03:55:07 1996 UTC (27 years, 7 months ago) by downsj
Branch: MAIN
CVS Tags: OPENBSD_2_2_BASE, OPENBSD_2_2, OPENBSD_2_1_BASE, OPENBSD_2_1
Changes since 1.4: +2 -1 lines

Backout a previous change that didn't work, and update to version 4.5.

/*	$OpenBSD: cmdcmds.c,v 1.5 1996/10/14 03:55:07 downsj Exp $	*/
/* vi:set ts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 */

/*
 * cmdcmds.c: functions for command line commands
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "option.h"

static void do_filter __ARGS((linenr_t line1, linenr_t line2,
									char_u *buff, int do_in, int do_out));
#ifdef VIMINFO
static char_u *viminfo_filename __ARGS((char_u 	*));
static void do_viminfo __ARGS((FILE *fp_in, FILE *fp_out, int want_info,
											 int want_marks, int force_read));
static int read_viminfo_up_to_marks __ARGS((char_u *line, FILE *fp,
																int forceit));
#endif /* VIMINFO */

	void
do_ascii()
{
	int		c;
	char	buf1[20];
	char	buf2[20];
	char_u	buf3[3];

	c = gchar_cursor();
	if (c == NUL)
	{
		MSG("empty line");
		return;
	}
	if (c == NL)			/* NUL is stored as NL */
		c = NUL;
	if (isprintchar(c) && (c < ' ' || c > '~'))
	{
		transchar_nonprint(buf3, c);
		sprintf(buf1, "  <%s>", (char *)buf3);
	}
	else
		buf1[0] = NUL;
	if (c >= 0x80)
		sprintf(buf2, "  <M-%s>", transchar(c & 0x7f));
	else
		buf2[0] = NUL;
	sprintf((char *)IObuff, "<%s>%s%s  %d,  Hex %02x,  Octal %03o",
										   transchar(c), buf1, buf2, c, c, c);
	msg(IObuff);
}

/*
 * align text:
 * type = -1  left aligned
 * type = 0   centered
 * type = 1   right aligned
 */
	void
do_align(start, end, width, type)
	linenr_t	start;
	linenr_t	end;
	int			width;
	int			type;
{
	FPOS	pos;
	int		len;
	int		indent = 0;
	int		new_indent = 0;			/* init for GCC */
	char_u	*first;
	char_u	*last;
	int		save;

#ifdef RIGHTLEFT
	if (curwin->w_p_rl)
		type = -type;	/* switch left and right aligning */
#endif

	pos = curwin->w_cursor;
	if (type == -1)		/* left align: width is used for new indent */
	{
		if (width >= 0)
			indent = width;
	}
	else
	{
		/*
		 * if 'textwidth' set, use it
		 * else if 'wrapmargin' set, use it
		 * if invalid value, use 80
		 */
		if (width <= 0)
			width = curbuf->b_p_tw;
		if (width == 0 && curbuf->b_p_wm > 0)
			width = Columns - curbuf->b_p_wm;
		if (width <= 0)
			width = 80;
	}

	if (u_save((linenr_t)(start - 1), (linenr_t)(end + 1)) == FAIL)
		return;
	for (curwin->w_cursor.lnum = start;
						curwin->w_cursor.lnum <= end; ++curwin->w_cursor.lnum)
	{
			/* find the first non-blank character */
		first = skipwhite(ml_get_curline());
			/* find the character after the last non-blank character */
		for (last = first + STRLEN(first);
								last > first && vim_iswhite(last[-1]); --last)
			;
		save = *last;
		*last = NUL;
		len = linetabsize(first);					/* get line length */
		*last = save;
		if (len == 0)								/* skip blank lines */
			continue;
		switch (type)
		{
			case -1:	new_indent = indent;			/* left align */
						break;
			case 0:		new_indent = (width - len) / 2;	/* center */
						break;
			case 1:		new_indent = width - len;		/* right align */
						break;
		}
		if (new_indent < 0)
			new_indent = 0;
		set_indent(new_indent, TRUE);			/* set indent */
	}
	curwin->w_cursor = pos;
	beginline(TRUE);
	updateScreen(NOT_VALID);
}

	void
do_retab(start, end, new_ts, forceit)
	linenr_t	start;
	linenr_t	end;
	int			new_ts;
	int			forceit;
{
	linenr_t	lnum;
	int			got_tab = FALSE;
	long		num_spaces = 0;
	long		num_tabs;
	long		len;
	long		col;
	long		vcol;
	long		start_col = 0;			/* For start of white-space string */
	long		start_vcol = 0;			/* For start of white-space string */
	int			temp;
	long		old_len;
	char_u		*ptr;
	char_u		*new_line = (char_u *)1;	/* init to non-NULL */
	int			did_something = FALSE;
	int			did_undo;				/* called u_save for current line */

	if (new_ts == 0)
		new_ts = curbuf->b_p_ts;
	for (lnum = start; !got_int && lnum <= end; ++lnum)
	{
		ptr = ml_get(lnum);
		col = 0;
		vcol = 0;
		did_undo = FALSE;
		for (;;)
		{
			if (vim_iswhite(ptr[col]))
			{
				if (!got_tab && num_spaces == 0)
				{
					/* First consecutive white-space */
					start_vcol = vcol;
					start_col = col;
				}
				if (ptr[col] == ' ')
					num_spaces++;
				else
					got_tab = TRUE;
			}
			else
			{
				if (got_tab || (forceit && num_spaces > 1))
				{
					/* Retabulate this string of white-space */

					/* len is virtual length of white string */
					len = num_spaces = vcol - start_vcol;
					num_tabs = 0;
					if (!curbuf->b_p_et)
					{
						temp = new_ts - (start_vcol % new_ts);
						if (num_spaces >= temp)
						{
							num_spaces -= temp;
							num_tabs++;
						}
						num_tabs += num_spaces / new_ts;
						num_spaces -= (num_spaces / new_ts) * new_ts;
					}
					if (curbuf->b_p_et || got_tab ||
										(num_spaces + num_tabs < len))
					{
						if (did_undo == FALSE)
						{
							did_undo = TRUE;
							if (u_save((linenr_t)(lnum - 1),
												(linenr_t)(lnum + 1)) == FAIL)
							{
								new_line = NULL;		/* flag out-of-memory */
								break;
							}
						}

						/* len is actual number of white characters used */
						len = num_spaces + num_tabs;
						old_len = STRLEN(ptr);
						new_line = lalloc(old_len - col + start_col + len + 1,
																		TRUE);
						if (new_line == NULL)
							break;
						if (start_col > 0)
							vim_memmove(new_line, ptr, (size_t)start_col);
						vim_memmove(new_line + start_col + len,
									  ptr + col, (size_t)(old_len - col + 1));
						ptr = new_line + start_col;
						for (col = 0; col < len; col++)
							ptr[col] = (col < num_tabs) ? '\t' : ' ';
						ml_replace(lnum, new_line, FALSE);
						did_something = TRUE;
						ptr = new_line;
						col = start_col + len;
					}
				}
				got_tab = FALSE;
				num_spaces = 0;
			}
			if (ptr[col] == NUL)
				break;
			vcol += chartabsize(ptr[col++], (colnr_t)vcol);
		}
		if (new_line == NULL)				/* out of memory */
			break;
		line_breakcheck();
	}
	if (got_int)
		emsg(e_interr);
	if (did_something)
		CHANGED;
	curbuf->b_p_ts = new_ts;
	coladvance(curwin->w_curswant);
}

/*
 * :move command - move lines line1-line2 to line n
 *
 * return FAIL for failure, OK otherwise
 */
	int
do_move(line1, line2, n)
	linenr_t	line1;
	linenr_t	line2;
	linenr_t	n;
{
	char_u		*str;
	linenr_t	l;
	linenr_t	extra;		/* Num lines added before line1 */
	linenr_t	num_lines;	/* Num lines moved */
	linenr_t	last_line;	/* Last line in file after adding new text */
	int			has_mark;

	if (n >= line1 && n < line2)
	{
		EMSG("Move lines into themselves");
		return FAIL;
	}

	num_lines = line2 - line1 + 1;

	/*
	 * First we copy the old text to its new location -- webb
	 * Also copy the flag that ":global" command uses.
	 */
	if (u_save(n, n + 1) == FAIL)
		return FAIL;
	for (extra = 0, l = line1; l <= line2; l++)
	{
		str = strsave(ml_get(l + extra));
		if (str != NULL)
		{
			has_mark = ml_has_mark(l + extra);
			ml_append(n + l - line1, str, (colnr_t)0, FALSE);
			vim_free(str);
			if (has_mark)
				ml_setmarked(n + l - line1 + 1);
			if (n < line1)
				extra++;
		}
	}

	/*
	 * Now we must be careful adjusting our marks so that we don't overlap our
	 * mark_adjust() calls.
	 *
	 * We adjust the marks within the old text so that they refer to the
	 * last lines of the file (temporarily), because we know no other marks
	 * will be set there since these line numbers did not exist until we added
	 * our new lines.
	 *
	 * Then we adjust the marks on lines between the old and new text positions
	 * (either forwards or backwards).
	 *
	 * And Finally we adjust the marks we put at the end of the file back to
	 * their final destination at the new text position -- webb
	 */
	last_line = curbuf->b_ml.ml_line_count;
	mark_adjust(line1, line2, last_line - line2, 0L);
	if (n >= line2)
		mark_adjust(line2 + 1, n, -num_lines, 0L);
	else
		mark_adjust(n + 1, line1 - 1, num_lines, 0L);
	mark_adjust(last_line - num_lines + 1, last_line,
												-(last_line - n - extra), 0L);
	
	/*
	 * Now we delete the original text -- webb
	 */
	if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL)
		return FAIL;

	for (l = line1; l <= line2; l++)
		ml_delete(line1 + extra, TRUE);

	CHANGED;
	if (!global_busy && num_lines > p_report)
		smsg((char_u *)"%ld line%s moved", num_lines, plural(num_lines));

	/*
	 * Leave the cursor on the last of the moved lines.
	 */
	if (n >= line1)
		curwin->w_cursor.lnum = n;
	else
		curwin->w_cursor.lnum = n + (line2 - line1) + 1;

	return OK;
}

/*
 * :copy command - copy lines line1-line2 to line n
 */
	void
do_copy(line1, line2, n)
	linenr_t	line1;
	linenr_t	line2;
	linenr_t	n;
{
	linenr_t		lnum;
	char_u			*p;

	mark_adjust(n + 1, MAXLNUM, line2 - line1 + 1, 0L);

	/*
	 * there are three situations:
	 * 1. destination is above line1
	 * 2. destination is between line1 and line2
	 * 3. destination is below line2
	 *
	 * n = destination (when starting)
	 * curwin->w_cursor.lnum = destination (while copying)
	 * line1 = start of source (while copying)
	 * line2 = end of source (while copying)
	 */
	if (u_save(n, n + 1) == FAIL)
		return;
	curwin->w_cursor.lnum = n;
	lnum = line2 - line1 + 1;
	while (line1 <= line2)
	{
		/* need to use strsave() because the line will be unlocked
			within ml_append */
		p = strsave(ml_get(line1));
		if (p != NULL)
		{
			ml_append(curwin->w_cursor.lnum, p, (colnr_t)0, FALSE);
			vim_free(p);
		}
				/* situation 2: skip already copied lines */
		if (line1 == n)
			line1 = curwin->w_cursor.lnum;
		++line1;
		if (curwin->w_cursor.lnum < line1)
			++line1;
		if (curwin->w_cursor.lnum < line2)
			++line2;
		++curwin->w_cursor.lnum;
	}
	CHANGED;
	msgmore((long)lnum);
}

/*
 * Handle the ":!cmd" command.  Also for ":r !cmd" and ":w !cmd"
 * Bangs in the argument are replaced with the previously entered command.
 * Remember the argument.
 */
	void
do_bang(addr_count, line1, line2, forceit, arg, do_in, do_out)
	int			addr_count;
	linenr_t	line1, line2;
	int			forceit;
	char_u		*arg;
	int			do_in, do_out;
{
	static	char_u	*prevcmd = NULL;		/* the previous command */
	char_u			*newcmd = NULL;			/* the new command */
	int				ins_prevcmd;
	char_u			*t;
	char_u			*p;
	char_u			*trailarg;
	int 			len;
	int				scroll_save = msg_scroll;

	/*
	 * Disallow shell commands from .exrc and .vimrc in current directory for
	 * security reasons.
	 */
	if (secure)
	{
		secure = 2;
		emsg(e_curdir);
		return;
	}

	if (addr_count == 0)				/* :! */
	{
		msg_scroll = FALSE;			/* don't scroll here */
		autowrite_all();
		msg_scroll = scroll_save;
	}

	/*
	 * Try to find an embedded bang, like in :!<cmd> ! [args]
	 * (:!! is indicated by the 'forceit' variable)
	 */
	ins_prevcmd = forceit;
	trailarg = arg;
	do
	{
		len = STRLEN(trailarg) + 1;
		if (newcmd != NULL)
			len += STRLEN(newcmd);
		if (ins_prevcmd)
		{
			if (prevcmd == NULL)
			{
				emsg(e_noprev);
				vim_free(newcmd);
				return;
			}
			len += STRLEN(prevcmd);
		}
		if ((t = alloc(len)) == NULL)
		{
			vim_free(newcmd);
			return;
		}
		*t = NUL;
		if (newcmd != NULL)
			STRCAT(t, newcmd);
		if (ins_prevcmd)
			STRCAT(t, prevcmd);
		p = t + STRLEN(t);
		STRCAT(t, trailarg);
		vim_free(newcmd);
		newcmd = t;

		/*
		 * Scan the rest of the argument for '!', which is replaced by the
		 * previous command.  "\!" is replaced by "!" (this is vi compatible).
		 */
		trailarg = NULL;
		while (*p)
		{
			if (*p == '!')
			{
				if (p > newcmd && p[-1] == '\\')
					vim_memmove(p - 1, p, (size_t)(STRLEN(p) + 1));
				else
				{
					trailarg = p;
					*trailarg++ = NUL;
					ins_prevcmd = TRUE;
					break;
				}
			}
			++p;
		}
	} while (trailarg != NULL);

	vim_free(prevcmd);
	prevcmd = newcmd;

	if (bangredo)			/* put cmd in redo buffer for ! command */
	{
		AppendToRedobuff(prevcmd);
		AppendToRedobuff((char_u *)"\n");
		bangredo = FALSE;
	}
	/*
	 * Add quotes around the command, for shells that need them.
	 */
	if (*p_shq != NUL)
	{
		newcmd = alloc((unsigned)(STRLEN(prevcmd) + 2 * STRLEN(p_shq) + 1));
		if (newcmd == NULL)
			return;
		STRCPY(newcmd, p_shq);
		STRCAT(newcmd, prevcmd);
		STRCAT(newcmd, p_shq);
	}
	if (addr_count == 0)				/* :! */
	{
			/* echo the command */
		msg_start();
		msg_outchar(':');
		msg_outchar('!');
		msg_outtrans(newcmd);
		msg_clr_eos();
		windgoto(msg_row, msg_col);

		do_shell(newcmd); 
	}
	else								/* :range! */
		do_filter(line1, line2, newcmd, do_in, do_out);
	if (newcmd != prevcmd)
		vim_free(newcmd);
}

/*
 * call a shell to execute a command
 */
	void
do_shell(cmd)
	char_u	*cmd;
{
	BUF		*buf;
	int		save_nwr;

	/*
	 * Disallow shell commands from .exrc and .vimrc in current directory for
	 * security reasons.
	 */
	if (secure)
	{
		secure = 2;
		emsg(e_curdir);
		msg_end();
		return;
	}

#ifdef WIN32
	/*
	 * Check if external commands are allowed now.
	 */
	if (can_end_termcap_mode(TRUE) == FALSE)
		return;
#endif

	/*
	 * For autocommands we want to get the output on the current screen, to
	 * avoid having to type return below.
	 */
	msg_outchar('\r');					/* put cursor at start of line */
#ifdef AUTOCMD
	if (!autocmd_busy)
#endif
		stoptermcap();
	msg_outchar('\n');					/* may shift screen one line up */

		/* warning message before calling the shell */
	if (p_warn
#ifdef AUTOCMD
				&& !autocmd_busy
#endif
								   )
		for (buf = firstbuf; buf; buf = buf->b_next)
			if (buf->b_changed)
			{
				MSG_OUTSTR("[No write since last change]\n");
				break;
			}

/* This windgoto is required for when the '\n' resulted in a "delete line 1"
 * command to the terminal. */

	windgoto(msg_row, msg_col);
	cursor_on();
	(void)call_shell(cmd, SHELL_COOKED);
	need_check_timestamps = TRUE;

/*
 * put the message cursor at the end of the screen, avoids wait_return() to
 * overwrite the text that the external command showed
 */
	msg_pos((int)Rows - 1, 0);

#ifdef AUTOCMD
	if (autocmd_busy)
		must_redraw = CLEAR;
	else
#endif
	{
		/*
		 * If K_TI is defined, we assume that we switch screens when
		 * starttermcap() is called. In that case we really want to wait for
		 * "hit return to continue".
		 */
		save_nwr = no_wait_return;
		if (*T_TI != NUL)
			no_wait_return = FALSE;
#ifdef AMIGA
		wait_return(term_console ? -1 : TRUE);		/* see below */
#else
		wait_return(TRUE);
#endif
		no_wait_return = save_nwr;
		starttermcap();		/* start termcap if not done by wait_return() */

		/*
		 * In an Amiga window redrawing is caused by asking the window size.
		 * If we got an interrupt this will not work. The chance that the
		 * window size is wrong is very small, but we need to redraw the
		 * screen.  Don't do this if ':' hit in wait_return().  THIS IS UGLY
		 * but it saves an extra redraw.
		 */
#ifdef AMIGA
		if (skip_redraw)				/* ':' hit in wait_return() */
			must_redraw = CLEAR;
		else if (term_console)
		{
			OUTSTR("\033[0 q"); 		/* get window size */
			if (got_int)
				must_redraw = CLEAR;	/* if got_int is TRUE, redraw needed */
			else
				must_redraw = 0;		/* no extra redraw needed */
		}
#endif /* AMIGA */
	}
}

/*
 * do_filter: filter lines through a command given by the user
 *
 * We use temp files and the call_shell() routine here. This would normally
 * be done using pipes on a UNIX machine, but this is more portable to
 * non-unix machines. The call_shell() routine needs to be able
 * to deal with redirection somehow, and should handle things like looking
 * at the PATH env. variable, and adding reasonable extensions to the
 * command name given by the user. All reasonable versions of call_shell()
 * do this.
 * We use input redirection if do_in is TRUE.
 * We use output redirection if do_out is TRUE.
 */
	static void
do_filter(line1, line2, buff, do_in, do_out)
	linenr_t	line1, line2;
	char_u		*buff;
	int			do_in, do_out;
{
	char_u		*itmp = NULL;
	char_u		*otmp = NULL;
	linenr_t 	linecount;
	FPOS		cursor_save;
#ifdef AUTOCMD
	BUF			*old_curbuf = curbuf;
#endif

	/*
	 * Disallow shell commands from .exrc and .vimrc in current directory for
	 * security reasons.
	 */
	if (secure)
	{
		secure = 2;
		emsg(e_curdir);
		return;
	}
	if (*buff == NUL)		/* no filter command */
		return;

#ifdef WIN32
	/*
	 * Check if external commands are allowed now.
	 */
	if (can_end_termcap_mode(TRUE) == FALSE)
		return;
#endif

	cursor_save = curwin->w_cursor;
	linecount = line2 - line1 + 1;
	curwin->w_cursor.lnum = line1;
	curwin->w_cursor.col = 0;

	/*
	 * 1. Form temp file names
	 * 2. Write the lines to a temp file
	 * 3. Run the filter command on the temp file
	 * 4. Read the output of the command into the buffer
	 * 5. Delete the original lines to be filtered
	 * 6. Remove the temp files
	 */

	if ((do_in && (itmp = vim_tempname('i')) == NULL) ||
							   (do_out && (otmp = vim_tempname('o')) == NULL))
	{
		emsg(e_notmp);
		goto filterend;
	}

/*
 * The writing and reading of temp files will not be shown.
 * Vi also doesn't do this and the messages are not very informative.
 */
	++no_wait_return;			/* don't call wait_return() while busy */
	if (do_in && buf_write(curbuf, itmp, NULL, line1, line2,
										   FALSE, FALSE, FALSE, TRUE) == FAIL)
	{
		msg_outchar('\n');					/* keep message from buf_write() */
		--no_wait_return;
		(void)emsg2(e_notcreate, itmp);		/* will call wait_return */
		goto filterend;
	}
#ifdef AUTOCMD
	if (curbuf != old_curbuf)
		goto filterend;
#endif

	if (!do_out)
		msg_outchar('\n');

#if (defined(UNIX) && !defined(ARCHIE)) || defined(OS2)
/*
 * put braces around the command (for concatenated commands)
 */
 	sprintf((char *)IObuff, "(%s)", (char *)buff);
	if (do_in)
	{
		STRCAT(IObuff, " < ");
		STRCAT(IObuff, itmp);
	}
#else
/*
 * for shells that don't understand braces around commands, at least allow
 * the use of commands in a pipe.
 */
	STRCPY(IObuff, buff);
	if (do_in)
	{
		char_u		*p;
	/*
	 * If there is a pipe, we have to put the '<' in front of it.
	 * Don't do this when 'shellquote' is not empty, otherwise the redirection
	 * would be inside the quotes.
	 */
		p = vim_strchr(IObuff, '|');
		if (p && *p_shq == NUL)
			*p = NUL;
		STRCAT(IObuff, " < ");
		STRCAT(IObuff, itmp);
		p = vim_strchr(buff, '|');
		if (p && *p_shq == NUL)
			STRCAT(IObuff, p);
	}
#endif
	if (do_out)
	{
		char_u *p;

		if ((p = vim_strchr(p_srr, '%')) != NULL && p[1] == 's')
		{
			p = IObuff + STRLEN(IObuff);
			*p++ = ' '; /* not really needed? Not with sh, ksh or bash */
			sprintf((char *)p, (char *)p_srr, (char *)otmp);
		}
		else
			sprintf((char *)IObuff + STRLEN(IObuff), " %s %s",
												 (char *)p_srr, (char *)otmp);
	}

	windgoto((int)Rows - 1, 0);
	cursor_on();

	/*
	 * When not redirecting the output the command can write anything to the
	 * screen. If 'shellredir' is equal to ">", screen may be messed up by
	 * stderr output of external command. Clear the screen later.
	 * If do_in is FALSE, this could be something like ":r !cat", which may
	 * also mess up the screen, clear it later.
	 */
	if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in)
		must_redraw = CLEAR;
	else
		redraw_later(NOT_VALID);

	/*
	 * When call_shell() fails wait_return() is called to give the user a
	 * chance to read the error messages. Otherwise errors are ignored, so you
	 * can see the error messages from the command that appear on stdout; use
	 * 'u' to fix the text
	 * Switch to cooked mode when not redirecting stdin, avoids that something
	 * like ":r !cat" hangs.
	 */
	if (call_shell(IObuff, SHELL_FILTER | SHELL_COOKED) == FAIL)
	{
		must_redraw = CLEAR;
		wait_return(FALSE);
	}
	need_check_timestamps = TRUE;

	if (do_out)
	{
		if (u_save((linenr_t)(line2), (linenr_t)(line2 + 1)) == FAIL)
		{
			goto error;
		}
		if (readfile(otmp, NULL, line2, FALSE, (linenr_t)0, MAXLNUM, TRUE)
																	  == FAIL)
		{
			msg_outchar('\n');
			emsg2(e_notread, otmp);
			goto error;
		}
#ifdef AUTOCMD
		if (curbuf != old_curbuf)
			goto filterend;
#endif

		if (do_in)
		{
			/* put cursor on first filtered line for ":range!cmd" */
			curwin->w_cursor.lnum = line1;
			dellines(linecount, TRUE, TRUE);
			curbuf->b_op_start.lnum -= linecount;		/* adjust '[ */
			curbuf->b_op_end.lnum -= linecount;			/* adjust '] */
			write_lnum_adjust(-linecount);				/* adjust last line
														   for next write */
		}
		else
		{
			/* put cursor on last new line for ":r !cmd" */
			curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
			linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
		}
		beginline(TRUE);				/* cursor on first non-blank */
		--no_wait_return;

		if (linecount > p_report)
		{
			if (do_in)
			{
				sprintf((char *)msg_buf, "%ld lines filtered", (long)linecount);
				if (msg(msg_buf) && !msg_scroll)
					keep_msg = msg_buf;		/* display message after redraw */
			}
			else
				msgmore((long)linecount);
		}
	}
	else
	{
error:
		/* put cursor back in same position for ":w !cmd" */
		curwin->w_cursor = cursor_save;
		--no_wait_return;
		wait_return(FALSE);
	}

filterend:

#ifdef AUTOCMD
	if (curbuf != old_curbuf)
	{
		--no_wait_return;
		EMSG("*Filter* Autocommands must not change current buffer");
	}
#endif
	if (itmp != NULL)
		vim_remove(itmp);
	if (otmp != NULL)
		vim_remove(otmp);
	vim_free(itmp);
	vim_free(otmp);
}

#ifdef VIMINFO

static int no_viminfo __ARGS((void));
static int	viminfo_errcnt;

	static int
no_viminfo()
{
	/* "vim -i NONE" does not read or write a viminfo file */
	return (use_viminfo != NULL && STRCMP(use_viminfo, "NONE") == 0);
}

/*
 * Report an error for reading a viminfo file.
 * Count the number of errors.  When there are more than 10, return TRUE.
 */
	int
viminfo_error(message, line)
	char	*message;
	char_u	*line;
{
	sprintf((char *)IObuff, "viminfo: %s in line: ", message);
	STRNCAT(IObuff, line, IOSIZE - STRLEN(IObuff));
	emsg(IObuff);
	if (++viminfo_errcnt >= 10)
	{
		EMSG("viminfo: Too many errors, skipping rest of file");
		return TRUE;
	}
	return FALSE;
}

/*
 * read_viminfo() -- Read the viminfo file.  Registers etc. which are already
 * set are not over-written unless force is TRUE. -- webb
 */
	int
read_viminfo(file, want_info, want_marks, forceit)
	char_u	*file;
	int		want_info;
	int		want_marks;
	int		forceit;
{
	FILE	*fp;

	if (no_viminfo())
		return FAIL;

	file = viminfo_filename(file);			/* may set to default if NULL */
	if ((fp = fopen((char *)file, READBIN)) == NULL)
		return FAIL;

	viminfo_errcnt = 0;
	do_viminfo(fp, NULL, want_info, want_marks, forceit);

	fclose(fp);

	return OK;
}

/*
 * write_viminfo() -- Write the viminfo file.  The old one is read in first so
 * that effectively a merge of current info and old info is done.  This allows
 * multiple vims to run simultaneously, without losing any marks etc.  If
 * forceit is TRUE, then the old file is not read in, and only internal info is
 * written to the file. -- webb
 */
	void
write_viminfo(file, forceit)
	char_u	*file;
	int		forceit;
{
	FILE	*fp_in = NULL;
	FILE	*fp_out = NULL;
	char_u	*tempname = NULL;

	if (no_viminfo())
		return;

	file = viminfo_filename(file);		/* may set to default if NULL */
	file = strsave(file);				/* make a copy, don't want NameBuff */
	if (file != NULL)
	{
		fp_in = fopen((char *)file, READBIN);
		if (fp_in == NULL)
			fp_out = fopen((char *)file, WRITEBIN);
		else if ((tempname = vim_tempname('o')) != NULL)
			fp_out = fopen((char *)tempname, WRITEBIN);
	}
	if (file == NULL || fp_out == NULL)
	{
		EMSG2("Can't write viminfo file %s!", file == NULL ? (char_u *)"" :
											  fp_in == NULL ? file : tempname);
		if (fp_in != NULL)
			fclose(fp_in);
		goto end;
	}

	viminfo_errcnt = 0;
	do_viminfo(fp_in, fp_out, !forceit, !forceit, FALSE);

	fclose(fp_out);			/* errors are ignored !? */
	if (fp_in != NULL)
	{
		fclose(fp_in);
		/*
		 * In case of an error, don't overwrite the original viminfo file.
		 */
		if (viminfo_errcnt || vim_rename(tempname, file) == -1)
			vim_remove(tempname);
	}
end:
	vim_free(file);
	vim_free(tempname);
}

	static char_u *
viminfo_filename(file)
	char_u		*file;
{
	if (file == NULL || *file == NUL)
	{
		expand_env(use_viminfo == NULL ? (char_u *)VIMINFO_FILE : use_viminfo,
														  NameBuff, MAXPATHL);
		return NameBuff;
	}
	return file;
}

/*
 * do_viminfo() -- Should only be called from read_viminfo() & write_viminfo().
 */
	static void
do_viminfo(fp_in, fp_out, want_info, want_marks, force_read)
	FILE	*fp_in;
	FILE	*fp_out;
	int		want_info;
	int		want_marks;
	int		force_read;
{
	int		count = 0;
	int		eof = FALSE;
	char_u	*line;

	if ((line = alloc(LSIZE)) == NULL)
		return;

	if (fp_in != NULL)
	{
		if (want_info)
			eof = read_viminfo_up_to_marks(line, fp_in, force_read);
		else
			/* Skip info, find start of marks */
			while (!(eof = vim_fgets(line, LSIZE, fp_in)) && line[0] != '>')
				;
	}
	if (fp_out != NULL)
	{
		/* Write the info: */
		fprintf(fp_out, "# This viminfo file was generated by vim\n");
		fprintf(fp_out, "# You may edit it if you're careful!\n\n");
		write_viminfo_search_pattern(fp_out);
		write_viminfo_sub_string(fp_out);
		write_viminfo_history(fp_out);
		write_viminfo_registers(fp_out);
		write_viminfo_filemarks(fp_out);
		count = write_viminfo_marks(fp_out);
	}
	if (fp_in != NULL && want_marks)
		copy_viminfo_marks(line, fp_in, fp_out, count, eof);
	vim_free(line);
}

/*
 * read_viminfo_up_to_marks() -- Only called from do_viminfo().  Reads in the
 * first part of the viminfo file which contains everything but the marks that
 * are local to a file.  Returns TRUE when end-of-file is reached. -- webb
 */
	static int
read_viminfo_up_to_marks(line, fp, forceit)
	char_u	*line;
	FILE	*fp;
	int		forceit;
{
	int		eof;

	prepare_viminfo_history(forceit ? 9999 : 0);
	eof = vim_fgets(line, LSIZE, fp);
	while (!eof && line[0] != '>')
	{
		switch (line[0])
		{
			case NUL:
			case '\r':
			case '\n':
			case '#':		/* A comment */
				eof = vim_fgets(line, LSIZE, fp);
				break;
			case '"':
				eof = read_viminfo_register(line, fp, forceit);
				break;
			case '/':		/* Search string */
			case '&':		/* Substitute search string */
			case '~':		/* Last search string, followed by '/' or '&' */
				eof = read_viminfo_search_pattern(line, fp, forceit);
				break;
			case '$':
				eof = read_viminfo_sub_string(line, fp, forceit);
				break;
			case ':':
			case '?':
				eof = read_viminfo_history(line, fp);
				break;
			case '\'':
				/* How do we have a file mark when the file is not in the
				 * buffer list?
				 */
				eof = read_viminfo_filemark(line, fp, forceit);
				break;
#if 0
			case '+':
				/* eg: "+40 /path/dir file", for running vim with no args */
				eof = vim_fgets(line, LSIZE, fp);
				break;
#endif
			default:
				if (viminfo_error("Illegal starting char", line))
					eof = TRUE;
				else
					eof = vim_fgets(line, LSIZE, fp);
				break;
		}
	}
	finish_viminfo_history();
	return eof;
}

/*
 * check string read from viminfo file
 * remove '\n' at the end of the line
 * - replace CTRL-V CTRL-V with CTRL-V
 * - replace CTRL-V 'n'    with '\n'
 */
	void
viminfo_readstring(p)
	char_u		*p;
{
	while (*p != NUL && *p != '\n')
	{
		if (*p == Ctrl('V'))
		{
			if (p[1] == 'n')
				p[0] = '\n';
			vim_memmove(p + 1, p + 2, STRLEN(p));
		}
		++p;
	}
	*p = NUL;
}

/*
 * write string to viminfo file
 * - replace CTRL-V with CTRL-V CTRL-V
 * - replace '\n'   with CTRL-V 'n'
 * - add a '\n' at the end
 */
	void
viminfo_writestring(fd, p)
	FILE	*fd;
	char_u	*p;
{
	register int	c;

	while ((c = *p++) != NUL)
	{
		if (c == Ctrl('V') || c == '\n')
		{
			putc(Ctrl('V'), fd);
			if (c == '\n')
				c = 'n';
		}
		putc(c, fd);
	}
	putc('\n', fd);
}
#endif /* VIMINFO */

/*
 * Implementation of ":fixdel", also used by get_stty().
 *  <BS>    resulting <Del>
 *   ^?        ^H
 * not ^?      ^?
 */
	void
do_fixdel()
{
	char_u	*p;

	p = find_termcode((char_u *)"kb");
	add_termcode((char_u *)"kD", p != NULL && *p == 0x7f ?
										 (char_u *)"\010" : (char_u *)"\177");
}

	static void
print_line_no_prefix(lnum, use_number)
	linenr_t	lnum;
	int			use_number;
{
	char_u		numbuf[20];

	if (curwin->w_p_nu || use_number)
	{
		sprintf((char *)numbuf, "%7ld ", (long)lnum);
		set_highlight('n');		/* Highlight line numbers */
		start_highlight();
		msg_outstr(numbuf);
		stop_highlight();
	}
	msg_prt_line(ml_get(lnum));
}

    void
print_line(lnum, use_number)
	linenr_t	lnum;
	int			use_number;
{
	msg_outchar('\n');
	print_line_no_prefix (lnum, use_number);
}

    void
print_line_cr(lnum, use_number)
	linenr_t	lnum;
	int			use_number;
{
	msg_outchar('\r');
	print_line_no_prefix (lnum, use_number);
}

/*
 * Implementation of ":file[!] [fname]".
 */
	void
do_file(arg, forceit)
	char_u	*arg;
	int		forceit;
{
	char_u		*fname, *sfname;
	BUF			*buf;

	if (*arg != NUL)
	{
		/*
		 * The name of the current buffer will be changed.
		 * A new buffer entry needs to be made to hold the old
		 * file name, which will become the alternate file name.
		 */
		fname = curbuf->b_filename;
		sfname = curbuf->b_sfilename;
		curbuf->b_filename = NULL;
		curbuf->b_sfilename = NULL;
		if (setfname(arg, NULL, TRUE) == FAIL)
		{
			curbuf->b_filename = fname;
			curbuf->b_sfilename = sfname;
			return;
		}
		curbuf->b_notedited = TRUE;
		buf = buflist_new(fname, sfname, curwin->w_cursor.lnum, FALSE);
		if (buf != NULL)
			curwin->w_alt_fnum = buf->b_fnum;
		vim_free(fname);
		vim_free(sfname);
	}
	/* print full filename if :cd used */
	fileinfo(did_cd, FALSE, forceit);
}

/*
 * do the Ex mode :insert and :append commands
 */

void
ex_insert (int before, linenr_t whatline)
{
	/* put the cursor somewhere sane if we insert nothing */

	if (whatline > curbuf->b_ml.ml_line_count) {
		curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
	} else {
		curwin->w_cursor.lnum = whatline;
	}

	while (1) {
		char_u *theline;

		if (((theline = getcmdline (' ', 1L)) == 0) ||
			((theline[0] == '.') && (theline[1] == 0))) {
			break;
		}

		if (before) {
			mark_adjust (whatline, MAXLNUM, 1, 0L);
			ml_append (whatline - 1, theline, (colnr_t) 0, FALSE);
			curwin->w_cursor.lnum = whatline;
		} else {
			mark_adjust (whatline + 1, MAXLNUM, 1, 0L);
			ml_append (whatline, theline, (colnr_t) 0, FALSE);
			curwin->w_cursor.lnum = whatline + 1;
		}

		vim_free (theline);
		whatline++;
	}

	CHANGED;
	beginline (MAYBE);
	updateScreen (NOT_VALID);
}

/*
 * do the Ex mode :change command
 */

void
ex_change (linenr_t start, linenr_t end)
{
	while (end >= start) {
		ml_delete (start, FALSE);
		end--;
	}

	ex_insert (TRUE, start);
}

void
ex_z (linenr_t line, char_u *arg)
{
	char_u *x;
	int bigness = curwin->w_height - 3;
	char_u kind;
	int minus = 0;
	linenr_t start, end, curs, i;

	if (arg == 0) { /* is this possible?  I don't remember */
		arg = "";
	}

	if (bigness < 1) {
		bigness = 1;
	}

	x = arg;
	if (*x == '-' || *x == '+' || *x == '=' || *x == '^' || *x == '.') x++;

	if (*x != 0) {
		if (!isdigit (*x)) {
			EMSG ("non-numeric argument to :z");
			return;
		} else {
			bigness = atoi (x);
		}
	}

	kind = *arg;

	switch (kind) {
		case '-':
			start = line - bigness;
			end = line;
			curs = line;
			break;

		case '=':
			start = line - bigness / 2 + 1;
			end = line + bigness / 2 - 1;
			curs = line;
			minus = 1;
			break;

		case '^':
			start = line - bigness * 2;
			end = line - bigness;
			curs = line - bigness;
			break;

		case '.':
			start = line - bigness / 2;
			end = line + bigness / 2;
			curs = end;
			break;

		default:  /* '+' */
			start = line;
			end = line + bigness;
			curs = end;
			break;
	}

	if (start < 1) {
		start = 1;
	}

	if (end > curbuf->b_ml.ml_line_count) {
		end = curbuf->b_ml.ml_line_count;
	}

	if (curs > curbuf->b_ml.ml_line_count) {
		curs = curbuf->b_ml.ml_line_count;
	}

	for (i = start; i <= end; i++) {
		int j;

		if (minus && (i == line)) {
			msg_outchar ('\n');

			for (j = 1; j < Columns; j++) {
				msg_outchar ('-');
			}
		}

		print_line (i, FALSE);

		if (minus && (i == line)) {
			msg_outchar ('\n');

			for (j = 1; j < Columns; j++) {
				msg_outchar ('-');
			}
		}
	}

	curwin->w_cursor.lnum = curs;
}