[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.1.1.1 (vendor branch), Sat Sep 7 21:40:26 1996 UTC (27 years, 9 months ago) by downsj
Branch: VIM
CVS Tags: VIM42
Changes since 1.1: +0 -0 lines

Initial import of vim 4.2.

This is meant to replace nvi in the tree.  Vim, in general, works better,
provides more features, and does not suffer from the license problems
being imposed upon nvi.

On the other hand, vim lacks a non-visual ex mode, in addition to open mode.

This includes the GUI (X11) code, but doesn't try to compile it.


/*	$OpenBSD: cmdcmds.c,v 1.1.1.1 1996/09/07 21:40:26 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"

#ifdef USE_TMPNAM
# define mktemp(a)	tmpnam(a)
#endif

extern char		*mktemp __ARGS((char *));

#ifdef OS2
static void check_tmpenv __ARGS((void));
#endif

#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 force));
#endif /* VIMINFO */

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

	c = gchar_cursor();
	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, force)
	linenr_t	start;
	linenr_t	end;
	int			new_ts;
	int			force;
{
	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 || (force && 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
	 */
	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));
	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;
	}
	if (addr_count == 0)				/* :! */
	{
			/* echo the command */
		msg_start();
		msg_outchar(':');
		msg_outchar('!');
		msg_outtrans(prevcmd);
		msg_clr_eos();
		windgoto(msg_row, msg_col);

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

/*
 * 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)
#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 */
	}
#ifdef AUTOCMD
	else
		must_redraw = CLEAR;
#endif
}

/*
 * 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.
 */
	void
do_filter(line1, line2, buff, do_in, do_out)
	linenr_t	line1, line2;
	char_u		*buff;
	int			do_in, do_out;
{
#ifdef USE_TMPNAM
	char_u		itmp[L_tmpnam];		/* use tmpnam() */
	char_u		otmp[L_tmpnam];
#else
	char_u		itmp[TMPNAMELEN];
	char_u		otmp[TMPNAMELEN];
#endif
	linenr_t 	linecount;
	FPOS		cursor_save;

	/*
	 * 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
	 */

#ifndef USE_TMPNAM		 /* tmpnam() will make its own name */
# ifdef OS2
	check_tmpenv();
	expand_env(TMPNAME1, itmp, TMPNAMELEN);
	expand_env(TMPNAME2, otmp, TMPNAMELEN);
# else
	STRCPY(itmp, TMPNAME1);
	STRCPY(otmp, TMPNAME2);
# endif
#endif

	if ((do_in && *mktemp((char *)itmp) == NUL) ||
									 (do_out && *mktemp((char *)otmp) == NUL))
	{
		emsg(e_notmp);
		return;
	}

/*
 * 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, 0, 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;
	}
	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
	 */
		p = vim_strchr(IObuff, '|');
		if (p)
			*p = NUL;
		STRCAT(IObuff, " < ");
		STRCAT(IObuff, itmp);
		p = vim_strchr(buff, '|');
		if (p)
			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;
		}

		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 '] */
		}
		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:
	vim_remove(itmp);
	vim_remove(otmp);
}

#ifdef OS2
/*
 * If $TMP is not defined, construct a sensible default.
 * This is required for TMPNAME1 and TMPNAME2 to work.
 */
	static void
check_tmpenv()
{
	char_u	*envent;

	if (getenv("TMP") == NULL)
	{
		envent = alloc(8);
		if (envent != NULL)
		{
			strcpy(envent, "TMP=C:/");
			putenv(envent);
		}
	}
}
#endif /* OS2 */

#ifdef VIMINFO

static int no_viminfo __ARGS((void));

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

/*
 * 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, force)
	char_u	*file;
	int		want_info;
	int		want_marks;
	int		force;
{
	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;

	do_viminfo(fp, NULL, want_info, want_marks, force);

	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
 * force is TRUE, then the old file is not read in, and only internal info is
 * written to the file. -- webb
 */
	void
write_viminfo(file, force)
	char_u	*file;
	int		force;
{
	FILE	*fp_in = NULL;
	FILE	*fp_out = NULL;
#ifdef USE_TMPNAM
	char_u	tmpname[L_tmpnam];		/* use tmpnam() */
#else
	char_u	tmpname[TMPNAMELEN];
#endif

	if (no_viminfo())
		return;

#ifndef USE_TMPNAM		 /* tmpnam() will make its own name */
# ifdef OS2
	check_tmpenv();
	expand_env(TMPNAME2, tmpname, TMPNAMELEN);
# else
	STRCPY(tmpname, TMPNAME2);
# endif
#endif

	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 (*mktemp((char *)tmpname) != NUL)
			fp_out = fopen((char *)tmpname, WRITEBIN);
	}
	if (file == NULL || fp_out == NULL)
	{
		EMSG2("Can't write viminfo file %s!", file == NULL ? (char_u *)"" :
											  fp_in == NULL ? file : tmpname);
		if (fp_in != NULL)
			fclose(fp_in);
		vim_free(file);
		return;
	}

	do_viminfo(fp_in, fp_out, !force, !force, FALSE);

	fclose(fp_out);			/* errors are ignored !? */
	if (fp_in != NULL)
	{
		fclose(fp_in);
		if (vim_rename(tmpname, file) == -1)
			vim_remove(tmpname);
	}
	vim_free(file);
}

	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, force)
	char_u	*line;
	FILE	*fp;
	int		force;
{
	int		eof;

	prepare_viminfo_history(force ? 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, force);
				break;
			case '/':		/* Search string */
			case '&':		/* Substitute search string */
			case '~':		/* Last search string, followed by '/' or '&' */
				eof = read_viminfo_search_pattern(line, fp, force);
				break;
			case '$':
				eof = read_viminfo_sub_string(line, fp, force);
				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, force);
				break;
#if 0
			case '+':
				/* eg: "+40 /path/dir file", for running vim with no args */
				eof = vim_fgets(line, LSIZE, fp);
				break;
#endif
			default:
				EMSG2("viminfo: Illegal starting char in line %s", line);
				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");
}

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

	msg_outchar('\n');
	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));
}

/*
 * 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);
}