/* vi:ts=4:sw=4
 *
 * VIM - Vi IMproved
 *
 * Code Contributions By:	Bram Moolenaar			mool@oce.nl
 *							Tim Thompson			twitch!tjt
 *							Tony Andrews			onecom!wldrdg!tony 
 *							G. R. (Fred) Walter		watmath!watcgl!grwalter 
 */

/*
 * edit.c: functions for insert mode
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#ifdef JP
#include "jp.h"
#endif
#include "ops.h"	/* for operator */

extern u_char *get_inserted();
static void start_arrow __ARGS((void));
static void stop_arrow __ARGS((void));
static void stop_insert __ARGS((void));
static int echeck_abbr __ARGS((int));

int arrow_used;				/* Normally FALSE, set to TRUE after hitting
							 * cursor key in insert mode. Used by vgetorpeek()
							 * to decide when to call u_sync() */
int restart_edit = 0;		/* call edit when next command finished */
static u_char	*last_insert = NULL;
							/* the text of the previous insert */
static int		last_insert_skip;
							/* number of chars in front of the previous insert */
static int		new_insert_skip;
							/* number of chars in front of the current insert */

	void
edit(count)
	long count;
{
	u_char		 c;
#ifdef JP
	u_char		 k;
#endif
	u_char		 cc;
	u_char		*ptr;
	u_char		*saved_line = NULL;		/* saved line for replace mode */
	linenr_t	 saved_lnum = 0;		/* lnum of saved line */
	u_char		 saved_char = NUL;		/* char replaced by NL */
#ifdef JP
	u_char		 saved_kchar = NUL;
#endif
	linenr_t	 lnum;
	int 		 temp = 0;
	int			 mode;
	int			 nextc = 0;
	int			 lastc = 0;
	colnr_t		 mincol;

	if (restart_edit)
	{
		arrow_used = TRUE;
		restart_edit = 0;
	}
	else
	{
		arrow_used = FALSE;
#ifdef JPFEP
		switch(*p_ji)
		{
			case 'J':
				KanjiInput = TRUE;
				goto modeset;
			case 'A':
				KanjiInput = FALSE;
modeset:
				fep_mode_switch(KanjiInput);
				showmode();
		}
#endif
#ifdef FEPCTRL
		switch(*p_ji)
		{
			case 'J':
				fep_force_on();
				break;
			case 'A':
				fep_force_off();
				break;
		}
#endif
	}

#ifdef DIGRAPHS
	dodigraph(-1);					/* clear digraphs */
#endif

/*
 * Get the current length of the redo buffer, those characters have to be
 * skipped if we want to get to the inserted characters.
 */

	ptr = get_inserted();
	new_insert_skip = strlen((char *)ptr);
	free(ptr);

	old_indent = 0;

	for (;;)
	{
		if (arrow_used)		/* don't repeat insert when arrow key used */
			count = 0;

		set_want_col = TRUE;	/* set Curswant in case of K_DARROW or K_UARROW */
		cursupdate();		/* Figure out where the cursor is based on Curpos. */
		showruler(0);
		setcursor();
		if (nextc)			/* character remaining from CTRL-V */
		{
			c = nextc;
			nextc = 0;
		}
		else
		{
#ifdef JP
			jp_getchar(&c, &k, saved_line);
#else
			c = vgetc();
#endif
#ifdef FEPCTRL
			if (p_fc)
#endif
#if defined(JPFEP) || defined(FEPCTRL)
			if (p_ja && isupper(*p_ji))
			{
				static int	knj_asc = 0;	/* for automatic change of p_ji */
				char		ji;

				ji = *p_ji;
				if (IsKanji(c))
				{
					if (knj_asc > p_ja)
						ji = 'J';
					else
						knj_asc ++;
				}
				else if (c > ' ')
				{
					if (knj_asc < - p_ja)
						ji = 'A';
					else
						knj_asc --;
				}

				if (ji != *p_ji && isupper(*p_ji))
				{
					*p_ji = ji;
					showmode();
				}
			}
#endif
			if (c == Ctrl('C') && got_int)
				got_int = FALSE;

		}
		if (c != Ctrl('D'))			/* remember to detect ^^D and 0^D */
			lastc = c;

/*
 * In replace mode a backspace puts the original text back.
 * We save the current line to be able to do that.
 * If characters are appended to the line, they will be deleted.
 * If we start a new line (with CR) the saved line will be empty, thus
 * the characters will be deleted.
 * If we backspace over the new line, that line will be saved.
 */
		if (State == REPLACE && saved_lnum != Curpos.lnum)
		{
			free(saved_line);
			saved_line = (u_char *)strsave((char *)nr2ptr(Curpos.lnum));
			saved_lnum = Curpos.lnum;
		}

#ifdef DIGRAPHS
		c = dodigraph(c);
#endif /* DIGRAPHS */

		if (c == Ctrl('V'))
		{
			outchar('^');
			AppendToRedobuff("\026");	/* CTRL-V */
			cursupdate();
			setcursor();

#ifdef JP
			get_literal(&nextc, &c, &k);
#else
			c = get_literal(&nextc);
#endif

		/* erase the '^' */
			if ((cc = gcharCurpos()) == NUL || (cc == TAB && !p_list))
				outchar(' ');
			else
				outstrn(transchar(cc));

			if (isidchar(c) || !echeck_abbr(c))
#ifdef JP
				insertchar(c, k);
#else
				insertchar(c);
#endif
			continue;
		}

#ifdef DOSGEN	/* may be used for UNIX */
		if (c == Ctrl(']'))
		{
#ifdef JP
			jp_getchar(&c, &k, saved_line);
#else
			c = vgetc();
#endif
			switch (c)
			{
				case 'h':
					c = K_LARROW;
					break;
				case 'j':
					c = K_DARROW;
					break;
				case 'k':
					c = K_UARROW;
					break;
				case 'l':
					c = K_RARROW;
					break;
				case 'H':
				case 'b':
					c = K_SLARROW;
					break;
				case 'J':
				case Ctrl('F'):
					c = K_SDARROW;
					break;
				case 'K':
				case Ctrl('B'):
					c = K_SUARROW;
					break;
				case 'L':
				case 'w':
					c = K_SRARROW;
					break;
				default:
					beep();
					continue;
			}
		}
#endif

		switch (c)		/* handle character in insert mode */
		{
#ifdef JP
			  case Ctrl('Q'):
#ifdef JPFEP
				fep_mode_switch(KanjiInput = FALSE);
				showmode();
				cursupdate();
				setcursor();
				updateline();
#endif
#ifdef FEPCTRL
				fep_force_off();
#endif
			  	break;
#endif
			  case Ctrl('O'):		/* execute one command */
			    if (echeck_abbr(Ctrl('O') + 0x100))
					break;
			  	count = 0;
				if (State == INSERT)
					restart_edit = 'I';
				else
					restart_edit = 'R';
				goto doESCkey;

			  case ESC: 			/* an escape ends input mode */
			    if (echeck_abbr(ESC + 0x100))
					break;
				/*FALLTHROUGH*/

			  case Ctrl('C'):
doESCkey:
				if (!arrow_used)
				{
					AppendToRedobuff(ESC_STR);

					if (--count > 0)		/* repeat what was typed */
					{
							start_redo_ins();
							continue;
					}
					stop_insert();
				}
				set_want_col = TRUE;

				/*
				 * The cursor should end up on the last inserted character.
				 */
				if (Curpos.col != 0 && (!restart_edit || gcharCurpos() == NUL) && !p_ri)
					decCurpos();
				if (extraspace)			/* did reverse replace in column 0 */
				{
					delchar(FALSE);
					updateline();
					extraspace = FALSE;
				}
				State = NORMAL;
				script_winsize_pp();	/* may need to put :winsize in script */
					/* inchar() may have deleted the "INSERT" message */
				if (Recording)
					showmode();
				else if (p_smd)
					msg("");
				free(saved_line);
				old_indent = 0;
#ifdef JPFEP
				KanjiInput = fep_mode();
#endif
				return;

			  	/*
				 * Insert the previously inserted text.
				 * Last_insert actually is a copy of the redo buffer, so we
				 * first have to remove the command.
				 * For ^@ the trailing ESC will end the insert.
				 */
#if !defined(JPFEP) && (!defined(MSDOS) || !defined(DOSGEN))
			  case K_ZERO:
#endif
			  case Ctrl('A'):
				stuff_inserted(NUL, 1L, (c == Ctrl('A')));
				break;

			  	/*
				 * insert the contents of a register
				 */
			  case Ctrl('R'):
			  	if (!insertbuf(vgetc()))
					beep();
				break;

			  case Ctrl('P'):			/* toggle reverse insert mode */
			  	p_ri = !p_ri;
				showmode();
				break;

				/*
				 * If the cursor is on an indent, ^T/^D insert/delete one
				 * shiftwidth. Otherwise ^T/^D behave like a TAB/backspace.
				 * This isn't completely compatible with
				 * vi, but the difference isn't very noticeable and now you can
				 * mix ^D/backspace and ^T/TAB without thinking about which one
				 * must be used.
				 */
			  case Ctrl('T'):		/* make indent one shiftwidth greater */
			  case Ctrl('D'): 		/* make indent one shiftwidth smaller */
				stop_arrow();
				AppendCharToRedobuff(c);
				if ((lastc == '0' || lastc == '^') && Curpos.col)
				{
					--Curpos.col;
					delchar(FALSE);			/* delete the '^' or '0' */
					if (lastc == '^')
						old_indent = get_indent();	/* remember current indent */

						/* determine offset from first non-blank */
					temp = Curpos.col;
					beginline(TRUE);
					temp -= Curpos.col;
					set_indent(0, TRUE);	/* remove all indent */
				}
				else
				{
						/* determine offset from first non-blank */
					temp = Curpos.col;
					beginline(TRUE);
					temp -= Curpos.col;

					shift_line(c == Ctrl('D'), TRUE);

						/* try to put cursor on same character */
					temp += Curpos.col;
				}
				if (temp <= 0)
					Curpos.col = 0;
				else
					Curpos.col = temp;
				did_ai = FALSE;
				did_si = FALSE;
				can_si = FALSE;
		  		goto redraw;

			  case BS:
			  case DEL:
nextbs:
				mode = 0;
dodel:
				/* can't delete anything in an empty file */
				/* can't backup past first character in buffer */
				/* can't backup past starting point unless 'backspace' > 1 */
				/* can backup to a previous line if 'backspace' == 0 */
				if (bufempty() || (!p_ri &&
						((Curpos.lnum == 1 && Curpos.col <= 0) ||
						(p_bs < 2 && (arrow_used ||
							(Curpos.lnum == Insstart.lnum &&
							Curpos.col <= Insstart.col) ||
							(Curpos.col <= 0 && p_bs == 0))))))
				{
					beep();
					goto redraw;
				}

				stop_arrow();
				if (p_ri)
					incCurpos();
				if (Curpos.col <= 0)		/* delete newline! */
				{
					lnum = Insstart.lnum;
					if (Curpos.lnum == Insstart.lnum || p_ri)
					{
						if (!u_save((linenr_t)(Curpos.lnum - 2), (linenr_t)(Curpos.lnum + 1)))
							goto redraw;
						--Insstart.lnum;
						Insstart.col = 0;
					}
				/* in replace mode, in the line we started replacing, we
														only move the cursor */
					if (State != REPLACE || Curpos.lnum > lnum)
					{
						temp = gcharCurpos();		/* remember current char */
						--Curpos.lnum;
						dojoin(FALSE, TRUE);
						if (temp == NUL && gcharCurpos() != NUL)
#ifdef JP
							if (IsKanji(gcharCurpos()))
								Curpos.col += 2;
							else
#endif
							++Curpos.col;
						if (saved_char)				/* restore what NL replaced */
						{
							State = NORMAL;			/* no replace for this char */
#ifdef JP
							inschar(saved_char, saved_kchar);
													/* but no showmatch */
#else
							inschar(saved_char);	/* but no showmatch */
#endif
							State = REPLACE;
							saved_char = NUL;
							if (!p_ri)
								decCurpos();
						}
						else if (p_ri)				/* in reverse mode */
							saved_lnum = 0;			/* save this line again */
					}
					else
						decCurpos();
					did_ai = FALSE;
				}
				else
				{
					if (p_ri && State != REPLACE)
						decCurpos();
					mincol = 0;
					if (mode == 3 && !p_ri && p_ai)	/* keep indent */
					{
						temp = Curpos.col;
						beginline(TRUE);
						if (Curpos.col < temp)
							mincol = Curpos.col;
						Curpos.col = temp;
					}

					/* delete upto starting point, start of line or previous word */
					do
					{
						if (!p_ri)
							decCurpos();

								/* start of word? */
						if (mode == 1 && !isspace(gcharCurpos()))
						{
							mode = 2;
							temp = isidchar(gcharCurpos());
						}
								/* end of word? */
						else if (mode == 2 && (isspace(cc = gcharCurpos()) || isidchar(cc) != temp))
						{
							if (!p_ri)
								incCurpos();
							else if (State == REPLACE)
								decCurpos();
							break;
						}
						if (State == REPLACE)
						{
							if (saved_line)
							{
								if (extraspace)
								{
#ifdef JP
									char *s, *p;		 /* 1 for extraspace */
									p = nr2ptr(Curpos.lnum) + 1;
									s = (char *)saved_line;
									while(*p && *s)
									{
										p += IsKanji(*p)? 2: 1;
										s += IsKanji(*s)? 2: 1;
									}

									if (*p)
									{
										if (IsKanji(gcharCurpos()))
											delchar(FALSE);
										delchar(FALSE);
									}
									else
									{
										decCurpos();
										delchar(FALSE);
										extraspace = FALSE;

										if (IsKanji(gcharCurpos()))
											delchar(FALSE);
										delchar(FALSE);
										State = INSERT;
										insertchar(*(char *)saved_line,
													*((char *)saved_line + 1));
										State = REPLACE;
									}
#else
									if ((int)strlen(nr2ptr(Curpos.lnum)) - 1 > (int)strlen((char *)saved_line))
										delchar(FALSE);
									else
									{
										decCurpos();
										delchar(FALSE);
										extraspace = FALSE;
										pcharCurpos(*saved_line);
									}
#endif
								}
#ifdef JP
								else
								{
									char *p, *c, *s;

									p = nr2ptr(Curpos.lnum);
									c = p + Curpos.col;
									s = (char *)saved_line;
									while(p < c && *s)
									{
										p += IsKanji(*p) ? 2 : 1;
										s += IsKanji(*s) ? 2 : 1;
									}

									if (*s)
									{
										if (IsKanji(*p))
											delchar(FALSE);
										delchar(FALSE);
										State = INSERT;
										insertchar(*s, *(s + 1));
										State = REPLACE;
										if (!p_ri)
											decCurpos();
									}
									else if (!p_ri)
									{
										if (IsKanji(gcharCurpos()))
											delchar(FALSE);
										delchar(FALSE);
									}
								}
#else
								else if (Curpos.col < strlen((char *)saved_line))
									pcharCurpos(saved_line[Curpos.col]);
								else if (!p_ri)
									delchar(FALSE);
#endif
							}
						}
						else  /* State != REPLACE */
						{
#ifdef JP
							if (IsKanji(gcharCurpos()))
								delchar(FALSE);
#endif
							delchar(FALSE);
							if (p_ri && gcharCurpos() == NUL)
								break;
						}
						if (mode == 0)		/* just a single backspace */
							break;
						if (p_ri && State == REPLACE && incCurpos())
							break;
					} while (p_ri || (Curpos.col > mincol && (Curpos.lnum != Insstart.lnum ||
							Curpos.col != Insstart.col)));
					if (extraspace)
						decCurpos();
				}
				did_si = FALSE;
				can_si = FALSE;
				if (Curpos.col <= 1)
					did_ai = FALSE;
				/*
				 * It's a little strange to put backspaces into the redo
				 * buffer, but it makes auto-indent a lot easier to deal
				 * with.
				 */
#ifdef JPFEP
				if (!KanjiInput)
					AppendCharToRedobuff(c);
				if (vpeekc() == BS && !KanjiInput)
#else
				AppendCharToRedobuff(c);
				if (vpeekc() == BS)
#endif
				{
						c = vgetc();
						goto nextbs;	/* speedup multiple backspaces */
				}
redraw:
				cursupdate();
				updateline();
				break;

			  case Ctrl('W'):		/* delete word before cursor */
			  	mode = 1;
			  	goto dodel;

			  case Ctrl('U'):		/* delete inserted text in current line */
				mode = 3;
			  	goto dodel;

			  case Ctrl('B'):
			  case K_LARROW:
			  	if (oneleft())
					start_arrow();
				else
					beep();
				break;

			  case K_SLARROW:
			  	if (Curpos.lnum > 1 || Curpos.col > 0)
				{
					bck_word(1L, 0);
					start_arrow();
				}
				else
					beep();
				break;

			  case Ctrl('F'):
			  case K_RARROW:
				if (gcharCurpos() != NUL)
				{
					set_want_col = TRUE;
					start_arrow();
#ifdef JP
					if (IsKanji(gcharCurpos()))
						Curpos.col += 2;
					else
#endif
					++Curpos.col;
				}
				else
					beep();
				break;

			  case K_SRARROW:
			  	if (Curpos.lnum < line_count || gcharCurpos() != NUL)
				{
					fwd_word(1L, 0, 0);
					start_arrow();
				}
				else
					beep();
				break;

			  case K_UARROW:
			  	if (oneup(1L))
					start_arrow();
				else
					beep();
				break;

			  case K_SUARROW:
			  	if (onepage(BACKWARD, 1L))
					start_arrow();
				else
					beep();
				break;

			  case Ctrl('N'):
			  case K_DARROW:
			  	if (onedown(1L))
					start_arrow();
				else
					beep();
				break;

			  case K_SDARROW:
			  	if (onepage(FORWARD, 1L))
					start_arrow();
				else
					beep();
				break;

			  case TAB:
			    if (echeck_abbr(TAB + 0x100))
					break;
			  	if (!p_et || (p_ri && State == REPLACE))
					goto normalchar;
										/* expand a tab into spaces */
				stop_arrow();
				did_ai = FALSE;
				did_si = FALSE;
				can_si = FALSE;
				temp = (int)p_ts - Curpos.col % (int)p_ts;
				inschar(' ');			/* delete one char in replace mode */
				while (--temp)
					insstr(" ");		/* insstr does not delete chars */
				AppendToRedobuff("\t");
				goto redraw;

			  case CR:
			  case NL:
			    if (echeck_abbr(c + 0x100))
					break;
				stop_arrow();
				if (State == REPLACE)
				{
#ifdef JP
					char *cp;
					cp = Curpos2ptr();
					saved_char = *cp ++;
					saved_kchar= *cp;
					delchar(FALSE);
					if (IsKanji(saved_char))
						delchar(FALSE);
#else
					saved_char = gcharCurpos();
					delchar(FALSE);
#endif
				}
				AppendToRedobuff(NL_STR);
				if (!Opencmd(FORWARD, TRUE, State == INSERT))
					goto doESCkey;		/* out of memory */
				if (p_ri)
				{
					decCurpos();
					if (State == REPLACE && Curpos.col > 0)
						decCurpos();
				}
				break;

#ifdef JPFEP
			  case K_ZERO: 		/* start/stop kanji mode */
			  case Ctrl('\\'):	/* ^space or ^\ */
				fep_mode_switch(KanjiInput = ! KanjiInput);
				showmode();
				cursupdate();
				setcursor();
				updateline();
				break;

#endif /* JPFEP */
#ifdef FEPCTRL
			  case K_ZERO: 		/* start/stop kanji mode */
			  case Ctrl('\\'):	/* ^space or ^\ */
			  	if (p_fc)
				{
				    if (fep_get_mode())
						fep_force_off();
					else
						fep_force_on();
				}
				break;
#endif /* FEPCTRL */

#ifdef DIGRAPHS
			  case Ctrl('K'):
				outchar('?');
				AppendToRedobuff("\026");	/* CTRL-V */
				setcursor();
			  	c = vgetc();
				outstrn(transchar(c));
				setcursor();
				c = getdigraph(c, vgetc());
				goto normalchar;
#endif /* DIGRAPHS */

			  case Ctrl('Y'):				/* copy from previous line */
				lnum = Curpos.lnum - 1;
				goto copychar;

			  case Ctrl('E'):				/* copy from next line */
				lnum = Curpos.lnum + 1;
copychar:
				if (lnum < 1 || lnum > line_count)
				{
					beep();
					break;
				}

				/* try to advance to the cursor column */
				temp = 0;
				ptr = (u_char *)nr2ptr(lnum);
				while (temp < Cursvcol && *ptr)
#ifdef JP
					if (IsKanji(*ptr))
					{
						temp += 2;
						ptr += 2;
					}
					else
#endif
						temp += chartabsize(*ptr++, temp);

				if (temp > Cursvcol)
#ifdef JP
				{
					--ptr;
					if (IsKanji(*ptr))
						ptr --;
				}
				k = *(ptr + 1);
#else
					--ptr;
#endif
				if ((c = *ptr) == NUL)
				{
					beep();
					break;
				}

				/*FALLTHROUGH*/
			  default:
normalchar:
				if (Curpos.col > 0 && ((can_si && c == '}') || (did_si && c == '{')))
					shift_line(TRUE, TRUE);

				if (isidchar(c) || !echeck_abbr(c))
#ifdef JP
					insertchar(c, k);
#else
					insertchar(c);
#endif
				break;
			}
	}
}

/*
 * Next character is interpreted literally.
 * A one, two or three digit decimal number is interpreted as its byte value.
 * If one or two digits are entered, *nextc is set to the next character.
 */
	int
#ifdef JP
get_literal(nextc, cp, kp)
	int *nextc;
	char *cp, *kp;
#else
get_literal(nextc)
	int *nextc;
#endif
{
	u_char		 cc;
	u_char		 nc;
	int			 oldstate;
	int			 i;
#ifndef JP
	char		 *cp;
#endif

	oldstate = State;
	State = NOMAPPING;		/* next characters not mapped */

	if (got_int)
	{
		*nextc = NUL;
#ifdef JP
		*cp = Ctrl('C');
#endif
		return Ctrl('C');
	}
	cc = 0;
	for (i = 0; i < 3; ++i)
	{
#ifdef JP
		jp_getchar(cp, kp, NULL);
		nc = *cp;
		if (IsKanji(nc))
			break;
#else
		nc = vgetc();
#endif
		if (!isdigit(nc))
			break;
		cc = cc * 10 + nc - '0';
		nc = 0;
	}
	if (i == 0)		/* no number entered */
	{
		cc = nc;
		nc = 0;
		if (cc == K_ZERO)	/* NUL is stored as NL */
			cc = '\n';
	}
#ifdef JP
	else if (IsKanji(cc) && cc & 0x60)
		cc &= 0x7f;
#endif
	else if (cc == 0)		/* NUL is stored as NL */
		cc = '\n';

	State = oldstate;
	*nextc = nc;
	got_int = FALSE;		/* CTRL-C typed after CTRL-V is not an interrupt */
#ifdef JP
	*cp = cc;
	if (IsKanji(nc))
		*nextc = 0;
#endif
	return cc;
}

/*
 * Special characters in this context are those that need processing other
 * than the simple insertion that can be performed here. This includes ESC
 * which terminates the insert, and CR/NL which need special processing to
 * open up a new line. This routine tries to optimize insertions performed by
 * the "redo", "undo" or "put" commands, so it needs to know when it should
 * stop and defer processing to the "normal" mechanism.
 */
#define ISSPECIAL(c)	((c) < ' ' || (c) >= DEL)

	void
#ifdef JP
insertchar(c, k)
	u_char	c, k;
#else
insertchar(c)
	u_char	c;
#endif
{
#ifdef ONEW
	extern int	onew_noredraw;
#endif
	int		haveto_redraw = FALSE;

	stop_arrow();
	/*
	 * If the cursor is past 'textwidth' and we are inserting a non-space,
	 * try to break the line in two or more pieces. If c == NUL then we have
	 * been called to do formatting only. If p_tw == 0 it does nothing.
	 */
#ifdef ONEW
	if (c == NUL || (!onew_noredraw && !isspace(c)))
#else
	if (c == NUL || !isspace(c))
#endif
	{
		while (p_tw && Cursvcol >= p_tw)
		{
			int		startcol;		/* Cursor column at entry */
			int		wantcol;		/* column at textwidth border */
			int		foundcol;		/* column for start of word */
#ifdef JP
			colnr_t	vcol2col();
			int		kborder = FALSE;
#endif

			if ((startcol = Curpos.col) == 0)
				break;
#ifdef JP
			Curpos.col = vcol2col(Curpos.lnum, p_tw);
#else
			coladvance((int)p_tw);			/* find column of textwidth border */
#endif
			wantcol = Curpos.col;
			Curpos.col = startcol - 1;
			foundcol = 0;

			while (Curpos.col > 0)			/* find position to break at */
			{
#ifdef JP
				/* check the last byte of a multi-byte char. */
				if (IsKanji(gcharCurpos()))
				{
					foundcol = Curpos.col + 1;
					if (Curpos.col < wantcol)
						break;

					Curpos.col -= 2;
					kborder = TRUE;
					continue;
				}
#endif
				if (isspace(gcharCurpos()))
				{
					while (Curpos.col > 0 && isspace(gcharCurpos()))
						--Curpos.col;
					if (Curpos.col == 0)	/* only spaces in front of text */
						break;
					foundcol = Curpos.col + 1;
					if (Curpos.col < wantcol)
						break;
				}
#ifdef JP
				else
				{
					if (kborder)
					{
						foundcol = Curpos.col + 1;
						if (Curpos.col < wantcol)
							break;
						kborder = FALSE;
					}
					--Curpos.col;
				}
#else
				--Curpos.col;
#endif
			}

			if (foundcol == 0)			/* no spaces, cannot break line */
			{
				Curpos.col = startcol;
				break;
			}
			Curpos.col = foundcol;		/* put cursor after pos. to break line */
#ifdef JP	/* KINSOKU syori */
			if (foundcol == startcol)
			{
				if (!c || (IsKanji(c) ? isjppunc(c, k, TRUE) /* close punc. */
									  : isaspunc(c, TRUE)) )
					break;
			}
			else if (foundcol < startcol)	/* for closing symbols */
			{
				char *ptr, n;
				n = *(ptr = Curpos2ptr());

				if (IsKanji(n))
				{
					if (isjppunc(n, *(ptr + 1), TRUE))
						foundcol += 2;
				}
				else if (n && isaspunc(n, TRUE))
					foundcol ++;
				Curpos.col = foundcol;
			}
			if (foundcol > 0)				/* for opening symbols */
			{
				char *ptr;
				ptr = Curpos2ptr() - 1;
				if (IsKanji(*ptr))
				{
					ptr --;
					if (isjppunc(*ptr, *(ptr + 1), FALSE))
						foundcol -= 2;
				}
				else
					if (isaspunc(*ptr, FALSE))
						foundcol --;
				Curpos.col = foundcol;
			}

#endif
			startcol -= foundcol;
			if (!Opencmd(FORWARD, FALSE, FALSE))
				break;
			while (isspace(gcharCurpos()) && startcol)		/* delete blanks */
			{
				delchar(FALSE);
				--startcol;				/* adjust cursor pos. */
			}
			Curpos.col += startcol;
			curs_columns(FALSE);		/* update Cursvcol */
			haveto_redraw = TRUE;
		}
		if (c == NUL)					/* formatting only */
#ifdef JP
		{
			if (haveto_redraw)
				updateScreen(NOT_VALID);
			else
				updateline();
			return;
		}
#else
			return;
#endif
		if (haveto_redraw)
		{
			/*
			 * If the cursor ended up just below the screen we scroll up here
			 * to avoid a redraw of the whole screen in the most common cases.
			 */
 			if (Curpos.lnum == Botline && !emptyrows)
				s_del(0, 1, TRUE);
			updateScreen(CURSUPD);
		}
	}

	did_ai = FALSE;
	did_si = FALSE;
	can_si = FALSE;

	/*
	 * If there's any pending input, grab up to MAX_COLUMNS at once.
	 * This speeds up normal text input considerably.
	 */
#ifdef JPFEP
# ifdef ONEW
	if (!KanjiInput && !p_oh && vpeekc() != NUL && State != REPLACE && !p_ri)
# else
	if (!KanjiInput && vpeekc() != NUL && State != REPLACE && !p_ri)
#endif
#else
	if (vpeekc() != NUL && State != REPLACE && !p_ri)
#endif
	{
		char			p[MAX_COLUMNS + 1];
		int 			i;

		p[0] = c;
		i = 1;
#ifdef JP
		if (IsKanji(c))
			p[i++] = k;
#endif
		while ((c = vpeekc()) != NUL && !ISSPECIAL(c) && i < MAX_COLUMNS &&
					(!p_tw || (Cursvcol += charsize(p[i - 1])) < p_tw) &&
					!(!no_abbr && !isidchar(c) && isidchar(p[i - 1])))
			p[i++] = vgetc();
		p[i] = '\0';
		insstr(p);
		AppendToRedobuff(p);
	}
	else
	{
#ifdef JP
		inschar(c, k);
#ifdef ONEW					/* avoid to put candidates into redo buffer */
		if (!onew_noredraw)
#endif
		{
			AppendCharToRedobuff(c);
			if (IsKanji(c))
				AppendCharToRedobuff(k);
		}
#else
		inschar(c);
		AppendCharToRedobuff(c);
#endif
	}

#ifdef ONEW
	if (!onew_noredraw)
#endif
	updateline();
}

/*
 * start_arrow() is called when an arrow key is used in insert mode.
 * It resembles hitting the <ESC> key.
 */
	static void
start_arrow()
{
	if (!arrow_used)		/* something has been inserted */
	{
		AppendToRedobuff(ESC_STR);
		arrow_used = TRUE;		/* this means we stopped the current insert */
		stop_insert();
	}
}

/*
 * stop_arrow() is called before a change is made in insert mode.
 * If an arrow key has been used, start a new insertion.
 */
	static void
stop_arrow()
{
	if (arrow_used)
	{
		u_saveCurpos();			/* errors are ignored! */
		Insstart = Curpos;		/* new insertion starts here */
		ResetRedobuff();
		AppendToRedobuff("1i");	/* pretend we start an insertion */
		arrow_used = FALSE;
	}
}

/*
 * do a few things to stop inserting
 */
	static void
stop_insert()
{
	stop_redo_ins();

	/*
	 * save the inserted text for later redo with ^@
	 */
	free(last_insert);
	last_insert = get_inserted();
	last_insert_skip = new_insert_skip;

	/*
	 * If we just did an auto-indent, truncate the line, and put
	 * the cursor back.
	 */
	if (did_ai && !arrow_used)
	{
		*nr2ptr(Curpos.lnum) = NUL;
		canincrease(0);
		Curpos.col = 0;
		if (p_list)			/* the deletion is only seen in list mode */
			updateline();
	}
	did_ai = FALSE;
	did_si = FALSE;
	can_si = FALSE;
}

/*
 * oneright oneleft onedown oneup
 *
 * Move one char {right,left,down,up}.	Return TRUE when sucessful, FALSE when
 * we hit a boundary (of a line, or the file).
 */

	int
oneright()
{
	char *ptr;
#ifdef JP
	char c;
#endif

	ptr = Curpos2ptr();
	set_want_col = TRUE;
#ifdef JP
	c = *ptr;
#endif
	if (*ptr++ == NUL || *ptr == NUL)
		return FALSE;

#ifdef JP
	if (IsKanji(c))
	{
		if (* ++ptr == NUL)
			return FALSE;
		Curpos.col += 2;
	}
	else
#endif
	++Curpos.col;
	return TRUE;
}

	int
oneleft()
{
	set_want_col = TRUE;

	if (Curpos.col == 0)
		return FALSE;
	--Curpos.col;
#ifdef JP
	{	char *ptr;
		ptr = pos2ptr(&Curpos);
		if (IsKanji(*ptr))
			--Curpos.col;
	}
#endif
	return TRUE;
}

	void
beginline(flag)
	int			flag;
{
	Curpos.col = 0;
	if (flag)
	{
		register u_char *ptr;

		for (ptr = (u_char *)nr2ptr(Curpos.lnum); isspace(*ptr); ++ptr)
			++Curpos.col;
	}
	set_want_col = TRUE;
}

	int
oneup(n)
	long n;
{
	if (n != 0 && Curpos.lnum == 1)
		return FALSE;
	if (n >= Curpos.lnum)
		Curpos.lnum = 1;
	else
		Curpos.lnum -= n;

	if (operator == NOP)
		cursupdate();				/* make sure Topline is valid */

	/* try to advance to the column we want to be at */
	coladvance(Curswant);
	return TRUE;
}

	int
onedown(n)
	long n;
{
	if (n != 0 && Curpos.lnum == line_count)
		return FALSE;
	Curpos.lnum += n;
	if (Curpos.lnum > line_count)
		Curpos.lnum = line_count;

	if (operator == NOP)
		cursupdate();				/* make sure Topline is valid */

	/* try to advance to the column we want to be at */
	coladvance(Curswant);
	return TRUE;
}

	int
onepage(dir, count)
	int		dir;
	long	count;
{
	linenr_t		lp;
	long			n;

	if (line_count == 1)	/* nothing to do */
		return FALSE;
	for ( ; count > 0; --count)
	{
		if (dir == FORWARD ? (Topline >= line_count - 1) : (Topline == 1))
		{
			beep();
			return FALSE;
		}
		if (dir == FORWARD)
		{
			if (Botline > line_count)				/* at end of file */
				Topline = line_count;
			else if (plines(Botline) >= Rows - 3 ||	/* next line is big */
					Botline - Topline <= 3)		/* just three lines on screen */
				Topline = Botline;
			else
				Topline = Botline - 2;
			Curpos.lnum = Topline;
			if (count != 1)
				comp_Botline();
		}
		else	/* dir == BACKWARDS */
		{
			lp = Topline;
			/*
			 * If the first two lines on the screen are not too big, we keep
			 * them on the screen.
			 */
			if ((n = plines(lp)) > Rows / 2)
				--lp;
			else if (lp < line_count && n + plines(lp + 1) < Rows / 2)
				++lp;
			Curpos.lnum = lp;
			n = 0;
			while (n <= Rows - 1 && lp >= 1)
			{
				n += plines(lp);
				--lp;
			}
			if (n <= Rows - 1)				/* at begin of file */
				Topline = 1;
			else if (lp >= Topline - 2)		/* happens with very long lines */
			{
				--Topline;
				comp_Botline();
				Curpos.lnum = Botline - 1;
			}
			else
				Topline = lp + 2;
		}
	}
	beginline(TRUE);
	updateScreen(VALID);
	return TRUE;
}

	void
stuff_inserted(c, count, no_esc)
	int		c;
	long	count;
	int		no_esc;
{
	u_char		*esc_ptr = NULL;
	u_char		*ptr;

	if (last_insert == NULL)
	{
		emsg("No inserted text yet");
		return;
	}
	if (c)
		stuffcharReadbuff(c);
	if (no_esc && (esc_ptr = (u_char *)strrchr((char *)last_insert, 27)) != NULL)
		*esc_ptr = NUL;		/* remove the ESC */

			/* skip the command */
	ptr = last_insert + last_insert_skip;

	do
		stuffReadbuff((char *)ptr);
	while (--count > 0);

	if (no_esc && esc_ptr)
		*esc_ptr = 27;		/* put the ESC back */
}

/*
 * Check the word in front of the cursor for an abbreviation.
 * Called when the non-id character "c" has been entered.
 * When an abbreviation is recognized it is removed from the text and
 * the replacement string is inserted in typestr, followed by "c".
 */
	static int
echeck_abbr(c)
	int c;
{
	if (p_paste || no_abbr)			/* no abbreviations or in paste mode */
		return FALSE;

	return check_abbr(c, nr2ptr(Curpos.lnum), Curpos.col,
				Curpos.lnum == Insstart.lnum ? Insstart.col : 0);
}


syntax highlighted by Code2HTML, v. 0.9.1