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

/*
 * screen.c: code for displaying on the screen
 */

#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#ifdef JP
#include "jp.h"
#endif

#ifdef JP
static int		kanji;
static char	kcode = NUL, *kin = NULL, *kout = NULL;
#endif
#ifdef JPFEP
static int		uline;
#endif

/*
	Macros handling inverted lines
*/
#define	ScreenAttr(screenp)	((screenp) - Nextscreen + Nextattrib)
#define	ScreenSetAttr(x)	(*x = 0x0)
#define	ScreenClrAttr(x)	(*x = 0x0)
#define		AM_None			0x00
#define		AM_Inv			0x02
#ifdef JPFEP
# define	AM_UL			0x04
#endif /* JPFEP */
#ifdef JP
# define	AM_K1			0x01
#endif /* JP */

/*
 * Switch Attribute
 */
#ifdef JP
# define KANJI_ON	{if (!kanji) { if (kin ) outstr(kin ); kanji = TRUE;  }}
# define KANJI_OFF	{if ( kanji) { if (kout) outstr(kout); kanji = FALSE; }}
#endif
#ifdef JPFEP
# define ULINE_ON	{if (!uline) { KANJI_OFF outstr(T_US); uline = TRUE;  }}
# define ULINE_OFF	{if ( uline) { KANJI_OFF outstr(T_UE); uline = FALSE; }}
#endif
#ifdef JP
# define INVERT_ON	{if (!invert) { KANJI_OFF outstr(T_TI); invert = TRUE;  }}
# define INVERT_OFF	{if ( invert) { KANJI_OFF outstr(T_TP); invert = FALSE; }}
#else
# define INVERT_ON	{if (!invert) { outstr(T_TI); invert = TRUE;  }}
# define INVERT_OFF	{if ( invert) { outstr(T_TP); invert = FALSE; }}
#endif

char *tgoto __PARMS((char *cm, int col, int line));
char *getvptr __PARMS((char *ptr, int vcol));
colnr_t vcol2col __PARMS((linenr_t lnum, int vcol));

static u_char 	*Nextscreen = NULL; 	/* What's to be put on the screen. */
static u_char	*Nextattrib = NULL; 	/* Attributes for Nextscreen.      */
static int		 NumLineSizes = 0;		/* # of active LineSizes */
static linenr_t *LineNumbers = NULL;	/* Pointer to the line for LineSizes */
static u_char 	*LineSizes = NULL;		/* Number of rows the lines occupy */
static u_char 	**LinePointers = NULL;	/* array of pointers into Netscreen */

/*
 * The following variable is set (in cursupdate) to the number of physical
 * lines taken by the line the cursor is on. We use this to avoid extra calls
 * to plines(). The optimized routine updateline()
 * makes sure that the size of the cursor line hasn't changed. If so, lines
 * below the cursor will move up or down and we need to call the routine
 * updateScreen() to examine the entire screen.
 */
static int		Cline_size; 			/* size (in rows) of the cursor line */
static int		Cline_row;				/* starting row of the cursor line */
static int		Leftcol = 0;			/* starting column of the screen */
static FPOS		oldCurpos = {0, 0};		/* last known end of visual part */
static int		oldCurswant = 0;		/* last known value of Curswant */
static int		canopt;					/* TRUE when cursor goto can be optimized */

static int screenline __ARGS((linenr_t, int, int));
static void screenchar __ARGS((u_char *, int, int));
static void screenfill __ARGS((int, int));
static void screenalloc __ARGS((int));
static void screenclear2 __ARGS((void));

/*
 * updateline() - like updateScreen() but only for cursor line
 *
 * This determines whether or not we need to call updateScreen() to examine
 * the entire screen for changes. This occurs if the size of the cursor line
 * (in rows) hasn't changed.
 */
	void
updateline()
{
	int 		row;
	int 		n;

	if (must_redraw)	/* must redraw whole screen */
	{
		updateScreen(VALID);
		return;
	}

	screenalloc(TRUE);		/* allocate screen buffers if size changed */

	if (Nextscreen == NULL || RedrawingDisabled)
		return;

	screenchar(NULL, 0, 0);	/* init cursor position of screenchar() */
	cursor_off();

	row = screenline(Curpos.lnum, Cline_row, (int)Rows - 1);

	cursor_on();

	if (row == Rows)			/* line too long for screen */
		updateScreen(VALID_TO_CURSCHAR);
	else
	{
		n = row - Cline_row;
		if (n != Cline_size)		/* line changed size */
		{
			if (n < Cline_size) 	/* got smaller: delete lines */
					s_del(row, Cline_size - n, FALSE);
			else					/* got bigger: insert lines */
					s_ins(Cline_row + Cline_size, n - Cline_size, FALSE);

			updateScreen(VALID_TO_CURSCHAR);
		}
	}
}

/*
 * updateScreen()
 *
 * Based on the current value of Topline, transfer a screenfull of stuff from
 * Filemem to Nextscreen, and update Botline.
 */

	void
updateScreen(type)
	int 			type;
{
	register int	row;
	register int	endrow;
	linenr_t		lnum;
	linenr_t		lastline = 0; /* only valid if endrow != Rows -1 */
	int				done;		/* if TRUE, we hit the end of the file */
	int				didline;	/* if TRUE, we finished the last line */
	int 			srow = 0;	/* starting row of the current line */
	int 			idx;
	int 			i;
	long 			j;
	static int		postponed_not_valid = FALSE;
	register u_char *screenp;
	register u_char *attribp;

#ifdef JP
	/*
	 * Prepare kanji-shift-in / out character sequence
	 */
	if (kcode != JP_DISP)
	{
		kin = kanjiin(kcode = JP_DISP);
		kout= kanjiout(kcode);
	}
#endif
	screen_msg(FALSE, NULL);

	screenalloc(TRUE);		/* allocate screen buffers if size changed */

	if (Nextscreen == NULL)
		return;

	cmdoffset = 0;			/* after redraw command line has no offset */
	if (must_redraw)
	{
		type = must_redraw;
		must_redraw = 0;
	}
	if (type == CLEAR)		/* first clear screen */
	{
		screenclear();
		type = NOT_VALID;
	}
	if (type == CURSUPD)	/* update cursor and then redraw */
	{
		NumLineSizes = 0;
		cursupdate();		/* will call updateScreen(VALID) */
		return;
	}
	if (NumLineSizes == 0)
		type = NOT_VALID;

 	if (RedrawingDisabled)
	{
		if (type == NOT_VALID)
			postponed_not_valid = TRUE;		/* use NOT_VALID next time */
		return;
	}

	if (postponed_not_valid)
	{
		type = NOT_VALID;
		postponed_not_valid = FALSE;
	}

/* return if there is nothing to do */
	if ((type == VALID && Topline == LineNumbers[0]) ||
			(type == INVERTED && oldCurpos.lnum == Curpos.lnum &&
					oldCurpos.col == Curpos.col && Curswant == oldCurswant))
		return;

	if (type == NOT_VALID)
	{
		redraw_msg = TRUE;
		NumLineSizes = 0;
	}

	idx = 0;
	row = 0;
	lnum = Topline;
	cursor_off();

	/* The number of rows shown is Rows-1. */
	/* The default last row is the status/command line. */
	endrow = Rows - 1;

	if (type == VALID || type == VALID_TO_CURSCHAR)
	{
		/*
		 * We handle two special cases:
		 * 1: we are off the top of the screen by a few lines: scroll down
		 * 2: Topline is below LineNumbers[0]: may scroll up
		 */
		if (Topline < LineNumbers[0])	/* may scroll down */
		{
			j = LineNumbers[0] - Topline;
			if (j < Rows - 3)				/* not too far off */
			{
				lastline = LineNumbers[0] - 1;
				i = plines_m(Topline, lastline);
				if (i < Rows - 3)		/* less than a screen off */
				{
					/*
					 * Try to insert the correct number of lines.
					 * This may fail and the screen may have been cleared.
					 */
					if (s_ins(0, i, FALSE) && NumLineSizes)
					{
						endrow = i;

						if ((NumLineSizes += j) > Rows - 1)
							NumLineSizes = Rows - 1;
						for (idx = NumLineSizes; idx - j >= 0; idx--)
						{
							LineNumbers[idx] = LineNumbers[idx - j];
							LineSizes[idx] = LineSizes[idx - j];
						}
						idx = 0;
					}
				}
				else		/* far off: clearing the screen is faster */
					screenclear();
			}
			else		/* far off: clearing the screen is faster */
				screenclear();
		}
		else							/* may scroll up */
		{
			j = -1;
			for (i = 0; i < NumLineSizes; i++) /* try to find Topline in LineNumbers[] */
			{
				if (LineNumbers[i] == Topline)
				{
					j = i;
					break;
				}
				row += LineSizes[i];
			}
			if (j == -1)	/* Topline is not in LineNumbers */
			{
				row = 0;
				screenclear();   /* far off: clearing the screen is faster */
			}
			else
			{
				/*
				 * Try to delete the correct number of lines.
				 * Topline is at LineNumbers[i].
				 */
				if ((row == 0 || s_del(0, row, FALSE)) && NumLineSizes)
				{
					srow = row;
					row = 0;
					for (;;)
					{
						if (type == VALID_TO_CURSCHAR && lnum == Curpos.lnum)
								break;
						if (row + srow + (int)LineSizes[j] >= Rows - 1)
								break;
						LineSizes[idx] = LineSizes[j];
						LineNumbers[idx] = lnum++;

						row += LineSizes[idx++];
						if ((int)++j >= NumLineSizes)
							break;
					}
					NumLineSizes = idx;
				}
				else
					row = 0;		/* update all lines */
			}
		}
		if (endrow == Rows - 1 && idx == 0) 	/* no scrolling */
				NumLineSizes = 0;
	}

	done = didline = FALSE;
	screenchar(NULL, 0, 0);	/* init cursor position of screenchar() */

	if (Visual.lnum)				/* check if we are updating the inverted part */
	{
		linenr_t	from, to;

	/* find the line numbers that need to be updated */
		if (Curpos.lnum < oldCurpos.lnum)
		{
			from = Curpos.lnum;
			to = oldCurpos.lnum;
		}
		else
		{
			from = oldCurpos.lnum;
			to = Curpos.lnum;
		}
	/* if in block mode and changed column or Curswant: update all lines */
#ifdef JP
		if (Visual_block)
#else
		if (Visual_block && (Curpos.col != oldCurpos.col || Curswant != oldCurswant))
#endif
		{
			if (from > Visual.lnum)
				from = Visual.lnum;
			if (to < Visual.lnum)
				to = Visual.lnum;
		}

		if (from < Topline)
			from = Topline;
		if (to >= Botline)
			to = Botline - 1;

	/* find the minimal part to be updated */
		if (type == INVERTED)
		{
			while (lnum < from)						/* find start */
			{
				row += LineSizes[idx++];
				++lnum;
			}
			srow = row;
			for (j = idx; j < NumLineSizes; ++j)	/* find end */
			{
				if (LineNumbers[j] == to + 1)
				{
					endrow = srow;
					break;
				}
				srow += LineSizes[j];
			}
			oldCurpos = Curpos;
			oldCurswant = Curswant;
		}
	/* if we update the lines between from and to set oldCurpos */
		else if (lnum <= from && (endrow == Rows - 1 || lastline >= to))
		{
			oldCurpos = Curpos;
			oldCurswant = Curswant;
		}
	}

	/*
	 * Update the screen rows from "row" to "endrow".
	 * Start at line "lnum" which is at LineNumbers[idx].
	 */
	for (;;)
	{
			if (lnum > line_count)		/* hit the end of the file */
			{
				done = TRUE;
				break;
			}
			srow = row;
			row = screenline(lnum, srow, endrow);
			if (row > endrow)	/* past end of screen */
			{
				LineSizes[idx] = plines(lnum);	/* we may need the size of that */
				LineNumbers[idx++] = lnum;		/* too long line later on */
				break;
			}

			LineSizes[idx] = row - srow;
			LineNumbers[idx++] = lnum;
			if (++lnum > line_count)
			{
				done = TRUE;
				break;
			}

			if (row == endrow)
			{
				didline = TRUE;
				break;
			}
	}
	if (idx > NumLineSizes)
		NumLineSizes = idx;

	/* Do we have to do off the top of the screen processing ? */
	if (endrow != Rows - 1)
	{
		row = 0;
		for (idx = 0; idx < NumLineSizes && row < (Rows - 1); idx++)
			row += LineSizes[idx];

		if (row < (Rows - 1))
		{
			done = TRUE;
		}
		else if (row > (Rows - 1))		/* Need to blank out the last line */
		{
			lnum = LineNumbers[idx - 1];
			srow = row - LineSizes[idx - 1];
			didline = FALSE;
		}
		else
		{
			lnum = LineNumbers[idx - 1] + 1;
			didline = TRUE;
		}
	}

	emptyrows = 0;
	/*
	 * If we didn't hit the end of the file, and we didn't finish the last
	 * line we were working on, then the line didn't fit.
	 */
	if (!done && !didline)
	{
		if (lnum == Topline)
		{
			/*
			 * Single line that does not fit!
			 * Fill last line with '@' characters.
			 */
			screenp = LinePointers[Rows - 2];
			attribp = ScreenAttr(screenp);
			for (i = 0; i < Columns; ++i)
			{
				if (*screenp != '@' || *attribp)
				{
					*screenp = '@';
					*attribp = AM_None;
					screenchar(screenp, (int)(Rows - 2), i);
				}
				++screenp;
				++attribp;
			}
			Botline = lnum + 1;
		}
		else
		{
			/*
			 * Clear the rest of the screen and mark the unused lines.
			 */
			screenfill(srow, '@');
			Botline = lnum;
		}
	}
	else
	{
		/* make sure the rest of the screen is blank */
		/* put '~'s on rows that aren't part of the file. */
		screenfill(row, '~');
		emptyrows = Rows - row - 1;

		if (done)				/* we hit the end of the file */
			Botline = line_count + 1;
		else
			Botline = lnum;
	}

	if (redraw_msg)
	{
		showmode();
		redraw_msg = FALSE;
	}

	cursor_on();
}

static int		invert;		/* shared by screenline() and screenchar() */

/*
 * Move line "lnum" to the screen.
 * Start at row "startrow", stop when "endrow" is reached.
 * Return the number of last row the line occupies.
 */

	static int
screenline(lnum, startrow, endrow)
		linenr_t		lnum;
		int 			startrow;
		int 			endrow;
{
	register u_char  *screenp;
	register u_char  *attribp;
	register u_char   c;
	register int	col;				/* visual column on screen */
	register int	vcol;				/* visual column for tabs */
	register int	row;
	register u_char *ptr;
	char			extra[16];			/* "%ld" must fit in here */
	char			*p_extra;
	int 			n_extra;
	int				n_spaces = 0;

	int				fromcol, tocol;		/* start/end of inverting */

#ifdef JPFEP
	int				kufrom, kuto;		/* start/end of underilne for KANAKAN */
	int				krfrom, krto;		/* start/end of inverting for KANAKAN */
#endif
	int				noinvcur = FALSE;	/* don't invert the cursor */
	int				temp;
	FPOS			*top, *bot;

	row = startrow;
	col = 0;
	vcol = 0;
	invert = FALSE;
	fromcol = -10;
	tocol = MAXCOL;
#ifdef JPFEP
	uline = FALSE;
#endif
	ptr = (u_char *)nr2ptr(lnum);
	canopt = TRUE;
	if (Visual.lnum)					/* visual active */
	{
		if (ltoreq(Curpos, Visual))		/* Visual is after Curpos */
		{
			top = &Curpos;
			bot = &Visual;
		}
		else							/* Visual is before Curpos */
		{
			top = &Visual;
			bot = &Curpos;
		}
		if (Visual_block)						/* block mode */
		{
			if (lnum >= top->lnum && lnum <= bot->lnum)
			{
				FPOS tpos;

				fromcol = getvcol(top, 2);
				temp = getvcol(bot, 2);
				if (temp < fromcol)
					fromcol = temp;

#ifdef JP
				/* adjust for multibyte char. */
				tpos.lnum = lnum;
				tpos.col = vcol2col(lnum, fromcol);
				if (*pos2ptr(&tpos) != '\t')
					fromcol = getvcol(&tpos, 2);
#endif
				if (Curswant != MAXCOL)
				{
					tocol = getvcol(top, 3);
					temp = getvcol(bot, 3);
					if (temp > tocol)
						tocol = temp;
#ifdef JP
					/* adjust for multibyte char. */
					tpos.col = vcol2col(lnum, tocol);
					if (*pos2ptr(&tpos) != '\t')
						tocol = getvcol(&tpos, 3);
#endif
					++tocol;
				}
			}
		}
		else							/* non-block mode */
		{
			if (lnum > top->lnum && lnum <= bot->lnum)
				fromcol = 0;
			else if (lnum == top->lnum)
				fromcol = getvcol(top, 2);
			if (lnum == bot->lnum)
				tocol = getvcol(bot, 3) + 1;

			if (Visual.col == VISUALLINE)		/* linewise */
			{
				if (fromcol > 0)
					fromcol = 0;
				tocol = VISUALLINE;
			}
		}
			/* if the cursor can't be switched off, don't invert the character
						where the cursor is */
		if ((T_CI == NULL || *T_CI == NUL) && lnum == Curpos.lnum)
			noinvcur = TRUE;

		/* if inverting in this line, can't optimize cursor positioning */
		if (fromcol >= 0)
			canopt = FALSE;
	}
	if (!p_wrap)		/* advance to first character to be displayed */
	{
		while (vcol < Leftcol && *ptr)
#ifdef JP
			if (IsKanji(*ptr))
			{
				vcol += 2;
				ptr  += 2;
			}
			else
#endif
			vcol += chartabsize(*ptr++, vcol);
		if (vcol > Leftcol)
		{
			n_spaces = vcol - Leftcol;	/* begin with some spaces */
			vcol = Leftcol;
		}
	}
#ifdef JPFEP
	if (Kconvlnum == lnum)
	{
		FPOS	tmp;
		tmp.lnum = lnum;
		tmp.col = KconvAltStart;	krfrom = getvcol(&tmp, 2);
		tmp.col = KconvAltEnd;		krto   = getvcol(&tmp, 2);
		tmp.col = KconvStart;		kufrom = getvcol(&tmp, 2);
		tmp.col = KconvEnd;			kuto   = getvcol(&tmp, 2);
		canopt = FALSE;
	}
	else
		krfrom = kufrom = krto = kuto = -10;
#endif /* JPFEP */

	screenp = LinePointers[row];
	attribp = ScreenAttr(screenp);
	if (p_nu)
	{
		sprintf(extra, "%7ld ", (long)lnum);
		p_extra = extra;
		n_extra = 8;
		vcol -= 8;		/* so vcol is 0 when line number has been printed */
	}
	else
	{
		p_extra = NULL;
		n_extra = 0;
	}
#ifdef JP
	kanji = FALSE;
#endif
	for (;;)
	{
		if (!canopt)	/* Visual in this line */
		{
			if (((vcol == fromcol && !(noinvcur && vcol == Cursvcol)) ||
					(noinvcur && vcol >= fromcol &&
#ifdef JP
					(vcol == Cursvcol + 1 || vcol == Cursvcol + 2)
#else
					vcol == Cursvcol + 1
#endif
					)) && vcol < tocol)
				INVERT_ON		/* start inverting */
			else if (invert && (
#ifdef JP
								(vcol == tocol || vcol == tocol + 1)
#else
								vcol == tocol
#endif
								|| (noinvcur && vcol == Cursvcol)))
				INVERT_OFF		/* stop inverting */
#ifdef JPFEP
			if (fromcol < 0)
			{
				/* invert for KANAKAN */
				if (!invert)
				{
					if (((vcol == krfrom && !(noinvcur && vcol == Cursvcol)) ||
						 (noinvcur && vcol == Cursvcol + 1 && vcol >= krfrom)) &&
							vcol < krto)	/* start inverting */

						INVERT_ON
				}
				else if (vcol == krto || (noinvcur && vcol == Cursvcol))
				{							/* stop inverting */
					int ul;

					if ((ul = uline))	ULINE_OFF
					INVERT_OFF
					if (ul) 			ULINE_ON
				}

				/* underline for KANAKAN */
				if (!uline)
				{
					if (vcol == kufrom && vcol < kuto)	/* start underline */
						ULINE_ON
				}
				else if (vcol == kuto)
				{
					int inv;

					if ((inv = invert))	INVERT_OFF;
					ULINE_OFF		/* stop underline */
					if (inv)			INVERT_ON;
				}
			}
#endif
		}

		/* Get the next character to put on the screen. */
		/*
		 * The 'extra' array contains the extra stuff that is inserted to
		 * represent special characters (non-printable stuff).
		 */

		if (n_extra)
		{
			c = (u_char)*p_extra++;
			n_extra--;
		}
		else if (n_spaces)
		{
			c = ' ';
			n_spaces--;
		}
		else
		{
			if ((c = *ptr++) < ' ' || (c > '~' && c <= 0xa0))
			{
				/*
				 * when getting a character from the file, we may have to turn it
				 * into something else on the way to putting it into 'Nextscreen'.
				 */
				if (c == TAB && !p_list)
				{
					/* tab amount depends on current column */
					n_spaces = (int)p_ts - col % (int)p_ts - 1;
					c = ' ';
				}
				else if (c == NUL && p_list)
				{
					p_extra = "";
					n_extra = 1;
					c = '$';
				}
				else if (c != NUL)
				{
					p_extra = (char *)transchar(c);
					n_extra = charsize(c) - 1;
					c = (u_char)*p_extra++;
				}
			}
		}

		if (c == NUL)
		{
#ifdef UNIX
			int ler;			/* Line Erased */
			ler= FALSE;
#endif
			if (invert)
			{
				if (vcol == 0)	/* invert first char of empty line */
				{
					if (*screenp != ' ' || !(*attribp & AM_Inv))
					{
						*screenp = ' ';
						*attribp = AM_Inv;
						screenchar(screenp, row, col);
					}
					++screenp;
					++attribp;
					++col;
				}
 				outstr(T_TP);
 				invert = FALSE;
			}
			/*
			 * could also use clear-to-end-of-line, but it is slower
			 * on an Amiga
			 */
			while (col < Columns)
			{
				if (*screenp != ' ' || *attribp)
				{
					*screenp = ' ';
					*attribp = AM_None;
#ifdef UNIX
					if (!ler)
					{
						ler = TRUE;
					  	windgoto(row, col);
						clear_line();
					}
#else
					screenchar(screenp, row, col);
#endif
				}
				screenp++;
				attribp++;
				col++;
			}
			row++;
#ifdef JP
			KANJI_OFF
#endif /* JP */
			break;
		}
#ifdef JP
		if (col >= (IsKanji(c) ? Columns - 1 : Columns)) /* continuous line */
		{
			int inv;
#ifdef JPFEP
			int ul;
#endif
			KANJI_OFF
			inv = invert;
			INVERT_OFF
#ifdef JPFEP
			ul = uline;
			ULINE_OFF
#endif

			if (col == Columns - 1 && (*screenp != '\\' || *attribp != AM_None))
			{
				*screenp = '\\';
				*attribp = AM_None;
				screenchar(screenp, row, col);
			}

			col = 0;
			if (!p_wrap || ++row == endrow)		/* line got too long for screen */
			{
				++row;
				break;
			}
			screenp = LinePointers[row];
			attribp = ScreenAttr(screenp);
			screen_msg(FALSE, NULL);

			if (inv) INVERT_ON
#ifdef JPFEP
			if (ul)  ULINE_ON
#endif
		}
#else /* JP */
		if (col >= Columns)
		{
			col = 0;
			if (!p_wrap || ++row == endrow)		/* line got too long for screen */
			{
				++row;
				break;
			}
			screenp = LinePointers[row];
			attribp = ScreenAttr(screenp);
			screen_msg(FALSE, NULL);
		}
#endif /* JP */

		/* store the character in Nextscreen */

#ifdef JP
		if (IsKanji(c) && kcode != JP_NONE)
		{	u_char c1, c2;
			int attr;

			c1 = c;
			c2 = *ptr++;
			attr = *attribp;

			if (*screenp != c1 || *(screenp + 1) != c2
				|| !(attr & AM_K1)
				|| (invert ? !(attr & AM_Inv) : (attr & AM_Inv))
#ifdef JPFEP
		  		|| (uline ?  !(attr & AM_UL) :  (attr & AM_UL))
#endif
				)
			{
				if (invert)
				{
					 *attribp++ = AM_Inv | AM_K1;
					 *attribp++ = AM_Inv;
				}
#ifdef JPFEP
				else if (uline)
				{
					 *attribp++ = AM_UL | AM_K1;
					 *attribp++ = AM_UL;
				}
#endif
				else
				{
					 *attribp++ = AM_K1;
					 *attribp++ = AM_None;
				}
				*screenp++ = c1;
				*screenp++ = c2;
				screenchar(screenp - 2, row, col);
				col  += 2;
				vcol += 2;
			}
			else
			{
				attribp += 2;
				screenp += 2;
				vcol    += 2;
				col     += 2;
			}
		}
		else
#endif /* JP */
		{
			int attr;

			attr = *attribp;
			if (*screenp != c
				|| (invert ? !(attr & AM_Inv) : (attr & AM_Inv))
#ifdef JPFEP
				|| (uline ?  !(attr & AM_UL) :  (attr & AM_UL))
#endif
			   )
			{
				*screenp = c;
				if      (invert) *attribp = AM_Inv;
#ifdef JPFEP
				else if (uline)  *attribp = AM_UL;
#endif
				else	*attribp = AM_None;
				screenchar(screenp, row, col);
			}

			screenp ++;
			attribp ++;
			vcol ++;
			col  ++;
		}
	}

#ifdef JP
	KANJI_OFF
#endif
#ifdef JPFEP
	ULINE_OFF
#endif
	INVERT_OFF

	return (row);
}

/*
 * put character '*p' on the screen at position 'row' and 'col'
 */
	static void
screenchar(p, row, col)
		u_char	*p;
		int 	row;
		int 	col;
{
	static int	oldrow, oldcol;		/* old cursor position */
	if (p == NULL)					/* initialize cursor position */
	{
		oldrow = oldcol = -1;
		return;
	}
	if (oldcol != col || oldrow != row)
	{
		/*
		 * If we're on the same row (which happens a lot!), try to
		 * avoid a windgoto().
		 * If we are only a few characters off, output the
		 * characters. That is faster than cursor positioning.
		 * This can't be used when inverting (a part of) the line.
		 */
		if (oldrow == row && oldcol < col)
		{
			register int i;

			i = col - oldcol;
#ifdef JP
			if (i <= 10 && canopt)
			{
				u_char	k1, k2;
				u_char	*ptr;

				ptr = p - i;
				while(ptr < p)
					if (IsKanji(k1 = *(ptr++)))
					{
					    KANJI_ON
						k2 = *(ptr++);
						kanjito(&k1, &k2, kcode);
						outchar(k1);
						outchar(k2);
					}
					else
					{
					    KANJI_OFF
						outchar(k1);
					}
			}
#else /* JP */
			if (i <= 4 && canopt)
				while (i)
				  outchar( *(p - i--) );
#endif /* JP */
			else
			{
#ifdef JP
				KANJI_OFF
#endif
				if (T_CRI && *T_CRI)	/* use tgoto interface! jw */
					outstr(tgoto(T_CRI, 0, i));
				else
					windgoto(row, col);
			}
			
			oldcol = col;
		}
		else
		{
#ifdef JP
			KANJI_OFF
#endif
			windgoto(oldrow = row, oldcol = col);
		}
	}
#ifdef JP
	if (IsKanji(*p))
	{
		u_char k1, k2;
		k1 = *p++;
		k2 = *p;
		KANJI_ON;
		kanjito(&k1, &k2, kcode);
		outchar(k1);
		outchar(k2);
		oldcol++;
	}
	else
	{
		KANJI_OFF
		outchar(*p);
	}
#else
	outchar(*p);
#endif

	oldcol++;
}

/*
 * Fill the screen at 'srow' with character 'c' followed by blanks.
 */
	static void
screenfill(srow, c)
		int 	srow;
		int		c;
{
		register int row;
		register int col;
		register u_char *screenp;
		register u_char *attribp;

		for (row = srow; row < (Rows - 1); ++row)
		{
			screenp = LinePointers[row];
			attribp = ScreenAttr(screenp);

			if (*screenp != c || *attribp)
			{
				*screenp = c;
				*attribp = AM_None;
				screenchar(screenp, row, 0);
			}
			++attribp;
			++screenp;
			for (col = 1; col < Columns; ++col)
			{
				if (*screenp != ' ' || *attribp)
				{
					*screenp = ' ';
					*attribp = AM_None;
					screenchar(screenp, row, col);
				}
				++attribp;
				++screenp;
			}
		}
}

/*
 * compute Botline. Can be called after Topline or Rows changed.
 */
	void
comp_Botline()
{
	linenr_t	lnum;
	int			done = 0;

	for (lnum = Topline; lnum <= line_count; ++lnum)
	{
		if ((done += plines(lnum)) >= Rows)
			break;
	}
	Botline = lnum;		/* Botline is the line that is just below the window */
}

/*
 * prt_line() - print the given line
 * returns the number of characters written.
 */
	int
prt_line(s)
	char		   *s;
{
	register int	si = 0;
#ifdef JP
	char			c, k;
#else
	register char	c;
#endif
	register int	col = 0;

	int 			n_extra = 0;
	int             n_spaces = 0;
	char			*p = NULL;			/* init to make SASC shut up */
	int 			n;

	for (;;)
	{
		if (n_extra)
		{
			--n_extra;
			c = *p++;
		}
		else if (n_spaces)
		{
		    --n_spaces;
			c = ' ';
		}
		else
		{
			c = s[si++];
#ifdef JP
			if (IsKanji(c))
				k = s[si++];
			else
#endif
			if (c == TAB && !p_list)
			{
				/* tab amount depends on current column */
				n_spaces = p_ts - col % p_ts - 1;
				c = ' ';
			}
			else if (c == NUL && p_list)
			{
				p = "";
				n_extra = 1;
				c = '$';
			}
			else if (c != NUL && (n = charsize(c)) > 1)
			{
				n_extra = n - 1;
				p = transchar(c);
				c = *p++;
			}
		}

		if (c == NUL)
			break;

#ifdef JP
		if (IsKanji(c))
		{
			if (k == NUL) break;

			KANJI_ON
			kanjito(&c, &k, kcode);
			outchar(c);
			outchar(k);
			col += 2;
		}
		else
		{
			KANJI_OFF
			outchar(c);
			col++;
		}
#else
		outchar(c);
		col++;
#endif
	}
#ifdef JP
	KANJI_OFF
#endif
	return col;
}

	static void
screenalloc(clear)
	int		clear;
{
	static int		old_Rows = 0;
	static int		old_Columns = 0;
	register int	i;

	/*
	 * Allocation of the sceen buffers is done only when the size changes
	 */
	if ((Nextscreen != NULL && Rows == old_Rows && Columns == old_Columns) || Rows == 0 || Columns == 0)
		return;

	comp_col();			/* recompute columns for shown command and ruler */
	old_Rows = Rows;
	old_Columns = Columns;

	/*
	 * If we're changing the size of the screen, free the old arrays
	 */
	if (Nextscreen != NULL)
		free((char *)Nextscreen);
	if (Nextattrib != NULL)
		free((char *)Nextattrib);
	if (LinePointers != NULL)
		free((char *)LinePointers);
	if (LineNumbers != NULL)
		free((char *) LineNumbers);
	if (LineSizes != NULL)
		free(LineSizes);

	Nextscreen = (u_char *)malloc((size_t) (Rows * Columns));
	Nextattrib = (u_char *)malloc((size_t) (Rows * Columns));
	LineNumbers = (linenr_t *) malloc((size_t) (Rows * sizeof(linenr_t)));
	LineSizes = (u_char *)malloc((size_t) Rows);
	LinePointers = (u_char **)malloc(sizeof(u_char *) * Rows);

	if (Nextscreen == NULL || Nextattrib == NULL 
			|| LineNumbers == NULL || LineSizes == NULL || LinePointers == NULL)
	{
		emsg(e_outofmem);
		if (Nextscreen != NULL)
		{
			free((char *)Nextscreen);
			free((char *)Nextattrib);
		}
		Nextscreen = Nextattrib = NULL;
	}
	else
	{
		for (i = 0; i < Rows; ++i)
				LinePointers[i] = Nextscreen + i * Columns;
	}

	if (clear)
		screenclear2();
}

	void
screenclear()
{
	screenalloc(FALSE);			/* allocate screen buffers if size changed */
	screenclear2();
}

	static void
screenclear2()
{
	if (starting || Nextscreen == NULL)
		return;

	outstr(T_ED);				/* clear the display */

								/* blank out Nextscreen & Nextattrib */
	memset((char *)Nextscreen, ' ', (size_t)(Rows * Columns));
	memset((char *)Nextattrib, NUL, (size_t)(Rows * Columns));

	NumLineSizes = 0;			/* clear screen info */
	redraw_msg = TRUE;			/* refresh cmdline at next screen redraw */
}

	void
cursupdate()
{
	linenr_t		p;
	long 			nlines;
	int 			i;
	int 			temp;

	screenalloc(TRUE);		/* allocate screen buffers if size changed */

	if (Nextscreen == NULL)
		return;

	if (Curpos.lnum > line_count)
		Curpos.lnum = line_count;
	if (bufempty()) 			/* special case - file is empty */
	{
		Topline = 1;
		Curpos.lnum = 1;
		Curpos.col = 0;
#ifdef JPFEP
		LineSizes[0] = 1;
		for (i = 1; i < Rows; i++)
#else
		for (i = 0; i < Rows; i++)
#endif
			LineSizes[i] = 0;
		if (NumLineSizes == 0)		/* don't know about screen contents */
			updateScreen(NOT_VALID);
		NumLineSizes = 1;
	}
	else if (Curpos.lnum < Topline)
	{
		/*
		 * If the cursor is above the top of the screen, scroll the screen to
		 * put it at the top of the screen.
		 * If we weren't very close to begin with, we scroll more, so that
		 * the line is close to the middle.
		 */
		temp = Rows / 2 - 1;
		if (Topline - Curpos.lnum >= temp)		/* not very close */
		{
			p = Curpos.lnum;
			i = plines(p);
			temp += i;
								/* count lines for 1/2 screenheight */
			while (i < Rows && i < temp && p > 1)
				i += plines(--p);
			Topline = p;
			if (i >= Rows)		/* cursor line won't fit, backup one line */
				++Topline;
		}
		else if (p_sj > 1)		/* scroll at least p_sj lines */
		{
			for (i = 0; i < p_sj && Topline > 1; i += plines(--Topline))
				;
		}
		if (Topline > Curpos.lnum)
			Topline = Curpos.lnum;
		updateScreen(VALID);
	}
	else if (Curpos.lnum >= Botline)
	{
			/* number of lines the cursor is below the bottom of the screen */
		nlines = Curpos.lnum - Botline + 1;
		/*
		 * If the cursor is less than a screenheight down
		 * compute the number of lines at the top which have the same or more
		 * rows than the rows of the lines below the bottom
		 */
		if (nlines <= Rows)
		{
				/* get the number or rows to scroll minus the number of
								free '~' rows */
			temp = plines_m(Botline, Curpos.lnum) - emptyrows;
			if (temp <= 0)				/* emptyrows is larger, no need to scroll */
				nlines = 0;
			else if (temp >= Rows)		/* more than a screenfull, don't scroll */
				nlines = temp;
			else
			{
					/* scroll minimal number of lines */
				if (temp < p_sj)
					temp = p_sj;
				for (i = 0, p = Topline; i < temp && p < Botline; ++p)
					i += plines(p);
				if (i >= temp)				/* it's possible to scroll */
					nlines = p - Topline;
				else						/* below Botline, don't scroll */
					nlines = 9999;
			}
		}

		/*
		 * Scroll up if the cursor is off the bottom of the screen a bit.
		 * Otherwise put it at 1/2 of the screen.
		 */
		if (nlines >= Rows / 2 && nlines > p_sj)
		{
			p = Curpos.lnum;
			temp = Rows / 2 + 1;
			nlines = 0;
			i = 0;
			do				/* this loop could win a contest ... */
				i += plines(p);
			while (i < temp && (nlines = 1) != 0 && --p != 0);
			Topline = p + nlines;
		}
		else
			scrollup(nlines);
		updateScreen(VALID);
	}
	else if (NumLineSizes == 0)		/* don't know about screen contents */
		updateScreen(NOT_VALID);
	Cursrow = Curscol = Cursvcol = i = 0;
	for (p = Topline; p != Curpos.lnum; ++p)
		if (RedrawingDisabled)		/* LineSizes[] invalid */
			Cursrow += plines(p);
		else
			Cursrow += LineSizes[i++];

	Cline_row = Cursrow;
	if (!RedrawingDisabled && i > NumLineSizes)
								/* Should only happen with a line that is too */
								/* long to fit on the last screen line. */
		Cline_size = 0;
	else
	{
		if (RedrawingDisabled)      		/* LineSizes[] invalid */
		    Cline_size = plines(Curpos.lnum);
        else
			Cline_size = LineSizes[i];

		curs_columns(!RedrawingDisabled);	/* compute Cursvcol and Curscol */
		if (must_redraw)
			updateScreen(VALID);
	}

	if (set_want_col)
	{
		if (Track)
		{
			if      (Curswant < Cursvcol)
				Curswant = getvcol(&Curpos, 2);
			else if (Curswant > Cursvcol && Curswant != MAXCOL)
				Curswant = getvcol(&Curpos, 3);
			else
				Curswant = Cursvcol;
		}
		else
			Curswant = Cursvcol;
		set_want_col = FALSE;
	}
}

/*
 * compute Curscol and Cursvcol
 */
	void
curs_columns(scroll)
	int scroll;			/* when TRUE, may scroll horizontally */
{
	int diff;

#ifdef JP
	u_char *ptr, c;
	int  col;

	Cursvcol = 0;
	if (p_nu)
		Curscol = 8;
	else
		Curscol = 0;
	Cursrow = Cline_row;

	ptr = (u_char *)nr2ptr(Curpos.lnum);

	col = 0;
	while(col < Curpos.col)
	{
		if (!(c = *ptr)) break;

		if (IsKanji(c))
		{
			if (Curscol >= Columns - 1 && p_wrap)
			{					 /* last column wrapping for multi-byte char. */
				Cursrow ++;
				Curscol = 0;
			}
			ptr += 2;
			col += 2;
			Cursvcol += 2;
			Curscol  += 2;
		}
		else
		{
			int cw;

			ptr ++;
			col ++;
			cw  = chartabsize(c, Curscol);
			Cursvcol += cw;
			Curscol  += cw;
		}

		if (Curscol >= Columns && p_wrap)
		{						 /* long line wrapping, adjust Cursrow */
			Cursrow ++;
			Curscol = 0;
		}
	}

	if (IsKanji(*ptr) && Curscol == Columns - 1)
	{							/* last column kanji */
		Cursrow ++;
		Curscol = 0;
	}

	if ((c = *ptr) == TAB && State == NORMAL && !p_list)
	{
		int cw;

		cw = chartabsize(c, Curscol) - 1;
		Cursvcol += cw;
		Curscol  += cw;
	}

	if (p_wrap)
	{ }
#else
	Cursvcol = getvcol(&Curpos, 1);
	Curscol = Cursvcol;
	if (p_nu)
		Curscol += 8;

	Cursrow = Cline_row;
	if (p_wrap)			/* long line wrapping, adjust Cursrow */
		while (Curscol >= Columns)
		{
			Curscol -= Columns;
			Cursrow++;
		}
#endif
	else if (scroll)	/* no line wrapping, compute Leftcol if scrolling is on */
						/* if scrolling is off, Leftcol is assumed to be 0 */
	{
						/* If Cursor is left of the screen, scroll rightwards */
						/* If Cursor is right of the screen, scroll leftwards */
		if (((diff = Leftcol + (p_nu ? 8 : 0) - Curscol) > 0 ||
					(diff = Curscol - (Leftcol + Columns) + 1) > 0))
		{
			if (p_ss == 0 || diff >= Columns / 2)
				Leftcol = Curscol - Columns / 2;
			else
			{
				if (diff < p_ss)
					diff = p_ss;
				if (Curscol < Leftcol + 8)
					Leftcol -= diff;
				else
					Leftcol += diff;
			}
			if (Leftcol < 0)
				Leftcol = 0;
			must_redraw = NOT_VALID;	/* screen has to be redrawn with new Leftcol */
		}
		Curscol -= Leftcol;
	}
	if (Cursrow > Rows - 2)		/* Cursor past end of screen */
		Cursrow = Rows - 2;		/* happens with line that does not fit on screen */
}

/*
 * get virtual column number of pos
 * type = 1: where the cursor is on this character
 * type = 2: on the first position of this character (TAB)
 * type = 3: on the last position of this character (TAB)
 */
	int
getvcol(pos, type)
	FPOS	*pos;
	int		type;
{
	int				col;
	int				vcol;
	u_char		   *ptr;
	int 			incr;
	u_char			c;

	vcol = 0;
	ptr = (u_char *)nr2ptr(pos->lnum);
	for (col = pos->col; col >= 0; --col)
	{
		c = *ptr++;
#ifdef JP
		if (IsKanji(c))
			ptr++;
#endif
		if (c == NUL)		/* make sure we don't go past the end of the line */
			break;

		/* A tab gets expanded, depending on the current column */
#ifdef JP
		if (IsKanji(c))
			incr = 2;
		else
#endif
		incr = chartabsize(c, vcol);

		if (col <= 0)		/* character at pos.col */
		{
			if (type == 3 || (type == 1 && c == TAB && State == NORMAL && !p_list))
				--incr;
			else
				break;
		}
#ifdef JP
		if (IsKanji(c))
			col--;
#endif
		vcol += incr;
	}
	return vcol;
}

/*
 *	get pointer corresponding to vcol
 */
 	colnr_t
vcol2col(lnum, dvcol)
	linenr_t lnum;
	int dvcol;
{
	char *line, *ptr, c;
	int vcol;

	ptr = line = nr2ptr(lnum);

	vcol = 0;
	while((c = *ptr))
	{
#ifdef JP
		if (IsKanji(c))
		{
			vcol += 2;
			if (vcol > dvcol)
				break;
			ptr += 2;
			continue;
		}
#endif
		vcol += chartabsize(c, vcol);

		if (vcol > dvcol)
			break;

		ptr ++;
	}

	return (colnr_t)(ptr - line);
}

	void
scrolldown(nlines)
	long	nlines;
{
	register long	done = 0;	/* total # of physical lines done */

	/* Scroll up 'nlines' lines. */
	while (nlines--)
	{
		if (Topline == 1)
			break;
		done += plines(--Topline);
	}
	/*
	 * Compute the row number of the last row of the cursor line
	 * and move it onto the screen.
	 */
	Cursrow += done;
	if (p_wrap)
		Cursrow += plines(Curpos.lnum) - 1 - Cursvcol / Columns;
	while (Cursrow >= Rows - 1 && Curpos.lnum > 1)
		Cursrow -= plines(Curpos.lnum--);
}

	void
scrollup(nlines)
	long	nlines;
{
#ifdef NEVER
	register long	done = 0;	/* total # of physical lines done */

	/* Scroll down 'nlines' lines. */
	while (nlines--)
	{
		if (Topline == line_count)
			break;
		done += plines(Topline);
		if (Curpos.lnum == Topline)
			++Curpos.lnum;
		++Topline;
	}
	s_del(0, done, TRUE);
#endif
	Topline += nlines;
	if (Topline > line_count)
		Topline = line_count;
	if (Curpos.lnum < Topline)
		Curpos.lnum = Topline;
}

/*
 * The rest of the routines in this file perform screen manipulations. The
 * given operation is performed physically on the screen. The corresponding
 * change is also made to the internal screen image. In this way, the editor
 * anticipates the effect of editing changes on the appearance of the screen.
 * That way, when we call screenupdate a complete redraw isn't usually
 * necessary. Another advantage is that we can keep adding code to anticipate
 * screen changes, and in the meantime, everything still works.
 */

/*
 * s_ins(row, nlines, invalid) - insert 'nlines' lines at 'row'
 * if 'invalid' is TRUE the LineNumbers[] is invalidated.
 * Returns 0 if the lines are not inserted, 1 for success.
 */
	int
s_ins(row, nlines, invalid)
	int 		row;
	int 		nlines;
	int			invalid;
{
	int 		i;
	int 		j;
	u_char		*temp;

	screenalloc(TRUE);		/* allocate screen buffers if size changed */

	if (Nextscreen == NULL)
		return 0;

	if (invalid)
		NumLineSizes = 0;

	if (nlines > (Rows - 1 - row))
		nlines = Rows - 1 - row;

	if (RedrawingDisabled || nlines <= 0 ||
						((T_CIL == NULL || *T_CIL == NUL) &&
						(T_IL == NULL || *T_IL == NUL) &&
						(T_SR == NULL || *T_SR == NUL || row != 0)))
		return 0;
	
	if (Rows - nlines < 5)	/* only a few lines left: redraw is faster */
	{
		screenclear();		/* will set NumLineSizes to 0 */
		return 0;
	}

	if (Rows != Rows_max)
	{
		windgoto((int)Rows - 1, 0);		/* delete any garbage that may have */
		clear_line();					/* been shifted to the bottom line */
	}
	/*
	 * It "looks" better if we do all the inserts at once
	 */
    if (T_CIL && *T_CIL) 
    {
        windgoto(row, 0);
		if (nlines == 1 && T_IL && *T_IL)
			outstr(T_IL);
		else
			outstr(tgoto(T_CIL, 0, nlines));
    }
    else
    {
        for (i = 0; i < nlines; i++) 
        {
            if (i == 0 || row != 0)
				windgoto(row, 0);
			if (T_IL && *T_IL)
				outstr(T_IL);
			else
				outstr(T_SR);
        }
    }
	windgoto((int)Rows - 1, 0);		/* delete any garbage that may have */
	clear_line();					/* been shifted to the bottom line */
	redraw_msg = TRUE;

	/*
	 * Now shift LinePointers nlines down to reflect the inserted lines.
	 * Clear the inserted lines.
	 */
	for (i = 0; i < nlines; ++i)
	{
		j = Rows - 2 - i;
		temp = LinePointers[j];
		while ((j -= nlines) >= row)
				LinePointers[j + nlines] = LinePointers[j];
		LinePointers[j + nlines] = temp;
		memset((char *)temp, ' ', (size_t)Columns);
	}
	return 1;
}

/*
 * s_del(row, nlines, invalid) - delete 'nlines' lines at 'row'
 * If 'invalid' is TRUE LineNumbers[] is invalidated.
 * Return 1 for success, 0 if the lines are not deleted.
 */
	int
s_del(row, nlines, invalid)
	int 			row;
	int 			nlines;
	int			invalid;
{
	int 			j;
	int 			i;
	u_char		*temp;

	screenalloc(TRUE);		/* allocate screen buffers if size changed */

	if (Nextscreen == NULL)
		return 0;

	if (invalid)
		NumLineSizes = 0;

	if (nlines > (Rows - 1 - row))
		nlines = Rows - 1 - row;

	if (RedrawingDisabled || nlines <= 0 ||
				((T_DL == NULL || *T_DL == NUL) &&
				(T_CDL == NULL || *T_CDL == NUL) &&
				row != 0))
		return 0;

	if (Rows - nlines < 5)	/* only a few lines left: redraw is faster */
	{
		screenclear();		/* will set NumLineSizes to 0 */
		return 0;
	}

	windgoto((int)Rows - 1, 0);		/* delete any garbage that may be */
	clear_line();					/* on the bottom line */
	redraw_msg = TRUE;

	/* delete the lines */
	if (T_CDL && *T_CDL) 
	{
		windgoto(row, 0);
		if (nlines == 1 && T_DL && *T_DL)
			outstr(T_DL);
		else
			outstr(tgoto(T_CDL, 0, nlines));
	} 
	else
	{
		if (row == 0)
		{
			if (Rows != Rows_max)
				windgoto((int)Rows_max - 1, 0);
			for (i = 0; i < nlines; i++) 
				outchar('\n');
		}
		else
		{
			for (i = 0; i < nlines; i++) 
			{
				windgoto(row, 0);
				outstr(T_DL);           /* delete a line */
			}
		}
	}

	/*
	 * Now shift LinePointers nlines up to reflect the deleted lines.
	 * Clear the deleted lines.
	 */
	for (i = 0; i < nlines; ++i)
	{
		j = row + i;
		temp = LinePointers[j];
		while ((j += nlines) < Rows - 1)
				LinePointers[j - nlines] = LinePointers[j];
		LinePointers[j - nlines] = temp;
		memset((char *)temp, ' ', (size_t)Columns);
	}
	return 1;
}

	void
showmode()
{
	if ((p_smd && (State == INSERT || State == REPLACE)) || Recording)
	{
		gotocmdline(TRUE, NUL);
		if (p_smd)
		{
#ifdef ONEW
			if (!KanjiInput && !p_oh && (State == INSERT || State == REPLACE))
			{
				if (p_ri)
					outstrn("[<-");
				else
					outstrn("[");

				if (State == INSERT)
					outstrn("INS");
				else if (State == REPLACE)
					outstrn("REP");

				outstrn("]");
				if (p_ja)
					outstrn(p_ji);
				if (Recording)
					outstrn("....");
			}
#else
			if (State == INSERT || State == REPLACE)
			{
				outstrn("-- ");
				if (p_ri)
					outstrn("REVERSE ");
				if (State == INSERT)
					outstrn("INSERT --");
				else
					outstrn("REPLACE --");
			}
#endif
		}
#ifndef JP
		if (!p_fm && Recording)
			outstrn("recording");
#endif
	}
	showruler(1);
}

/*
 * delete mode message
 */
	void
delmode()
{
	if (Recording)
		msg("recording");
	else
		msg("");
}

/*
 * if ruler option is set: show current cursor position
 * if always is FALSE, only print if position has changed
 */
	void
showruler(always)
	int		always;
{
	static linenr_t	oldlnum = 0;
	static colnr_t	oldcol = 0;
	static int		oldlen = 0;
	int				newlen;
	char			buffer[20];

	if (p_ru && p_fm)
	{
		sprintf(buffer, "%ld,%d", Curpos.lnum, (int)Curpos.col + 1);
		newlen = strlen(buffer);
		if (Curpos.col != Cursvcol)
		{
			sprintf(buffer + newlen, "-%d", Cursvcol + 1);
			newlen = strlen(buffer);
		}
		screen_msg(TRUE, buffer);
		return;
	}

	if (p_ru && (redraw_msg || always || Curpos.lnum != oldlnum || Cursvcol != oldcol))
	{
		windgoto((int)Rows - 1, ru_col);
		/*
		 * Some sprintfs return the lenght, some return a pointer.
		 * To avoid portability problems we use strlen here.
		 */
		sprintf(buffer, "%ld,%d", Curpos.lnum, (int)Curpos.col + 1);
		newlen = strlen(buffer);
		if (Curpos.col != Cursvcol)
		{
			sprintf(buffer + newlen, "-%d", Cursvcol + 1);
			newlen = strlen(buffer);
		}
		outstrn(buffer);
		while (newlen < oldlen)
		{
			outchar(' ');
			--oldlen;
		}
		oldlen = newlen;
		oldlnum = Curpos.lnum;
		oldcol = Cursvcol;
		redraw_msg = FALSE;
	}
}

/*
 * Clear a line. The cursor must be at the first char of the line.
 */
	void
clear_line()
{
	register int i;

	if (T_EL != NULL && *T_EL != NUL)
		outstr(T_EL);
	else
		for (i = 1; i < Columns; ++i)
			outchar(' ');
}


#define MAXMSG	80
	void
screen_msg(rev, msg)
	int rev;
	char *msg;
{
	static int srow, scol, slen = 0;
	static u_char orgscr[MAXMSG + 1], orgatr[MAXMSG + 1];

	u_char *mp, *screenp, *attribp, attr;
	u_char *osp, *oap;

#ifdef JP
	KANJI_OFF
#endif

	if (!LinePointers)
	{
		if (msg)
		{
			if (rev) outstr(T_TI);
			outstr((char *)msg);
			if (rev) outstr(T_TP);
		}
		return;
	}

	/* do not display the same message */
	if (msg && slen)
	{
		screenp = LinePointers[srow] + scol;
		attribp = ScreenAttr(screenp);

		for(mp = (u_char *)msg; mp - (u_char *)msg < slen; mp++, screenp++)
		  if (*mp != *screenp) break;
		if (mp - (u_char *)msg >= slen) return;
	}

	INVERT_OFF
#ifdef JPFEP
	ULINE_OFF
#endif

	/* remove old message */
	if (slen)
	{
		screenp = LinePointers[srow];
		attribp = ScreenAttr(screenp);

		screenp += scol;
		attribp += scol;
		osp = orgscr;
		oap = orgatr;

		windgoto(srow, scol);
		for(; slen > 0; slen--)
		{
			char c1, c2;
			c1 = 
			*screenp++ = *osp++;
			*attribp++ = *oap++;

#ifdef JP
			if (IsKanji(c1))
			{
				slen --;
				c2 = 
				*screenp++ = *osp++;
				*attribp++ = *oap++;
				KANJI_ON
				kanjito(&c1, &c2, kcode);
				outchar(c1);
				outchar(c2);
			}
			else
			{
				KANJI_OFF
				outchar(c1);
			}
#else /* JP */
			outchar(c1);
#endif /* JP */
		}
#ifdef JP
		KANJI_OFF
#endif /* JP */
	}

	/* write new message */
	if (msg)
	{
		u_char *cp, *msgend;

		curs_columns(TRUE);
		srow = Cursrow;
		scol = Curscol;

		slen = 0;
		cp = (u_char *)msg;
		while(*cp && slen < MAXMSG && slen < Columns)
#ifdef JP
			if (IsKanji(*cp))
			{
				cp  += 2;
				slen+= 2;
			}
			else
#endif
			{
				++ cp;
				++ slen;
			}
		msgend = cp;

		if (srow < Rows - (State == CMDLINE ? 2 : 1))
			srow ++;
		else
			srow --;
#ifdef ONEW
		{
			extern int onew_noredraw;

			if (State == CMDLINE && onew_noredraw)
				srow = 0;
		}
#endif

		if (srow < 0)    srow = 0;
		if (srow > Rows) srow = Rows - 1;

		screenp = LinePointers[srow];
		attribp = ScreenAttr(screenp);
		attr = rev ? AM_None : AM_Inv;

		/* search white spaces */
		{
			int fscol, bscol;
			u_char *cp, *sp, *startp;
			fscol = bscol = -1;
											/* search forward */
			startp = screenp;
			if (scol > slen / 2)
				startp += scol - slen / 2;

			for(cp = startp; cp <= screenp + Columns - slen; cp++)
			{
				for(sp = cp; sp - cp != slen ; sp++)
					if (*sp != ' ')
					{
						cp = sp;
						break;
					}
				if (cp != sp)
				{
					fscol = cp - screenp;
					break;
				}
			}
											/* search backward */
			cp = startp + slen - 1;
			if (cp - screenp > Columns)
				cp = screenp + Columns - 1;
		    for(; cp >= screenp + slen; cp--)
			{
				for(sp = cp; cp - sp != slen; sp--)
					if (*sp != ' ')
					{
						cp = sp;
						break;
					}
				if (cp != sp)
				{
					bscol = sp - screenp + 1;
					break;
				}
			}
			if (fscol < 0)
				if (bscol < 0)		/* when no enough space is found */
					if (scol > Columns - slen / 2 - 1)
						scol = Columns - slen;
					else if (scol > slen / 2)
						scol -= slen / 2;
					else
					    scol = 0;
				else				/* when space is found backword */
					scol = bscol;
			else
				if (bscol < 0) 		/* when space is found foreword */
					scol = fscol;
				else
					scol = (scol - bscol -slen < fscol - scol) ? bscol : fscol;
		}

#ifdef JP
		KANJI_OFF

		if (IsKanji(*(screenp + scol)) && !(*(attribp + scol) & AM_K1))
		{
			if (scol > Columns / 2)
				scol--;
			else
				scol++;
		}
#endif /* JP */
		screenp += scol;
		attribp += scol;

		osp = orgscr;
		oap = orgatr;
		for(mp = (u_char *)msg; mp < msgend; mp++)
	 	{
			*osp ++ = *screenp;
			*oap ++ = *attribp;
			*screenp++ = *mp;
			*attribp++ = attr;
		}

		if (rev) outstr(T_TI);
		windgoto(srow, scol);

		for(mp = (u_char *)msg; mp < msgend; mp++)
		{
			u_char c1, c2;
			c1 = *mp;
#ifdef JP
			if (IsKanji(c1))
			{
				c2 = * ++mp;
				KANJI_ON
				kanjito(&c1, &c2, kcode);
				outchar(c1);
				outchar(c2);
			}
			else
			{
				KANJI_OFF
				outchar(c1);
			}
#else /* JP */
			outchar(c1);
#endif /* JP */
		}
#ifdef JP
		KANJI_OFF
		if (IsKanji(*screenp) && !(*attribp & AM_K1))
		{
			*osp++ = *screenp;
			*oap++ = *attribp;
			*screenp = ' ';
			*attribp = attr;
			outchar(' ');
			slen++;
		}
#endif /* JP */
		if (rev) outstr(T_TP);
	}
	flushbuf();
}


syntax highlighted by Code2HTML, v. 0.9.1