Home | History | Annotate | Line # | Download | only in vi
      1  1.10       rin /*	$NetBSD: vs_refresh.c,v 1.10 2018/04/10 12:44:41 rin Exp $ */
      2   1.1  christos /*-
      3   1.1  christos  * Copyright (c) 1992, 1993, 1994
      4   1.1  christos  *	The Regents of the University of California.  All rights reserved.
      5   1.1  christos  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6   1.1  christos  *	Keith Bostic.  All rights reserved.
      7   1.1  christos  *
      8   1.1  christos  * See the LICENSE file for redistribution information.
      9   1.1  christos  */
     10   1.1  christos 
     11   1.1  christos #include "config.h"
     12   1.1  christos 
     13   1.6  christos #include <sys/cdefs.h>
     14   1.6  christos #if 0
     15   1.1  christos #ifndef lint
     16   1.1  christos static const char sccsid[] = "Id: vs_refresh.c,v 10.50 2001/06/25 15:19:37 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:37 ";
     17   1.1  christos #endif /* not lint */
     18   1.6  christos #else
     19  1.10       rin __RCSID("$NetBSD: vs_refresh.c,v 1.10 2018/04/10 12:44:41 rin Exp $");
     20   1.6  christos #endif
     21   1.1  christos 
     22   1.1  christos #include <sys/types.h>
     23   1.1  christos #include <sys/queue.h>
     24   1.1  christos #include <sys/time.h>
     25   1.1  christos 
     26   1.1  christos #include <bitstring.h>
     27   1.1  christos #include <ctype.h>
     28   1.1  christos #include <limits.h>
     29   1.1  christos #include <stdio.h>
     30   1.1  christos #include <stdlib.h>
     31   1.1  christos #include <string.h>
     32   1.1  christos 
     33   1.1  christos #include "../common/common.h"
     34   1.1  christos #include "vi.h"
     35   1.1  christos 
     36   1.1  christos #define	UPDATE_CURSOR	0x01			/* Update the cursor. */
     37   1.1  christos #define	UPDATE_SCREEN	0x02			/* Flush to screen. */
     38   1.1  christos 
     39   1.1  christos static void	vs_modeline __P((SCR *));
     40   1.1  christos static int	vs_paint __P((SCR *, u_int));
     41   1.1  christos 
     42   1.1  christos /*
     43   1.1  christos  * vs_refresh --
     44   1.1  christos  *	Refresh all screens.
     45   1.1  christos  *
     46   1.1  christos  * PUBLIC: int vs_refresh __P((SCR *, int));
     47   1.1  christos  */
     48   1.1  christos int
     49   1.1  christos vs_refresh(SCR *sp, int forcepaint)
     50   1.1  christos {
     51   1.1  christos 	GS *gp;
     52   1.1  christos 	SCR *tsp;
     53   1.1  christos 	int need_refresh;
     54   1.1  christos 	u_int priv_paint, pub_paint;
     55   1.1  christos 
     56   1.1  christos 	gp = sp->gp;
     57   1.1  christos 
     58   1.1  christos 	/*
     59   1.1  christos 	 * 1: Refresh the screen.
     60   1.1  christos 	 *
     61   1.1  christos 	 * If SC_SCR_REDRAW is set in the current screen, repaint everything
     62   1.1  christos 	 * that we can find, including status lines.
     63   1.1  christos 	 */
     64   1.1  christos 	if (F_ISSET(sp, SC_SCR_REDRAW))
     65   1.3  christos 		TAILQ_FOREACH(tsp, &sp->wp->scrq, q)
     66   1.1  christos 			if (tsp != sp)
     67   1.1  christos 				F_SET(tsp, SC_SCR_REDRAW | SC_STATUS);
     68   1.1  christos 
     69   1.1  christos 	/*
     70   1.1  christos 	 * 2: Related or dirtied screens, or screens with messages.
     71   1.1  christos 	 *
     72   1.1  christos 	 * If related screens share a view into a file, they may have been
     73   1.1  christos 	 * modified as well.  Refresh any screens that aren't exiting that
     74   1.1  christos 	 * have paint or dirty bits set.  Always update their screens, we
     75   1.1  christos 	 * are not likely to get another chance.  Finally, if we refresh any
     76   1.1  christos 	 * screens other than the current one, the cursor will be trashed.
     77   1.1  christos 	 */
     78   1.1  christos 	pub_paint = SC_SCR_REFORMAT | SC_SCR_REDRAW;
     79   1.1  christos 	priv_paint = VIP_CUR_INVALID | VIP_N_REFRESH;
     80   1.1  christos 	if (O_ISSET(sp, O_NUMBER))
     81   1.1  christos 		priv_paint |= VIP_N_RENUMBER;
     82   1.3  christos 	TAILQ_FOREACH(tsp, &sp->wp->scrq, q)
     83   1.1  christos 		if (tsp != sp && !F_ISSET(tsp, SC_EXIT | SC_EXIT_FORCE) &&
     84   1.1  christos 		    (F_ISSET(tsp, pub_paint) ||
     85   1.1  christos 		    F_ISSET(VIP(tsp), priv_paint))) {
     86   1.1  christos 			(void)vs_paint(tsp,
     87   1.1  christos 			    (F_ISSET(VIP(tsp), VIP_CUR_INVALID) ?
     88   1.1  christos 			    UPDATE_CURSOR : 0) | UPDATE_SCREEN);
     89   1.5  christos 			F_SET(VIP(sp), VIP_CUR_INVALID);
     90   1.1  christos 		}
     91   1.1  christos 
     92   1.1  christos 	/*
     93   1.1  christos 	 * 3: Refresh the current screen.
     94   1.1  christos 	 *
     95   1.1  christos 	 * Always refresh the current screen, it may be a cursor movement.
     96   1.1  christos 	 * Also, always do it last -- that way, SC_SCR_REDRAW can be set
     97   1.1  christos 	 * in the current screen only, and the screen won't flash.
     98   1.1  christos 	 */
     99   1.1  christos 	if (vs_paint(sp, UPDATE_CURSOR | (!forcepaint &&
    100   1.1  christos 	    F_ISSET(sp, SC_SCR_VI) && KEYS_WAITING(sp) ? 0 : UPDATE_SCREEN)))
    101   1.1  christos 		return (1);
    102   1.1  christos 
    103   1.1  christos 	/*
    104   1.1  christos 	 * 4: Paint any missing status lines.
    105   1.1  christos 	 *
    106   1.1  christos 	 * XXX
    107   1.1  christos 	 * This is fairly evil.  Status lines are written using the vi message
    108   1.1  christos 	 * mechanism, since we have no idea how long they are.  Since we may be
    109   1.1  christos 	 * painting screens other than the current one, we don't want to make
    110   1.1  christos 	 * the user wait.  We depend heavily on there not being any other lines
    111   1.1  christos 	 * currently waiting to be displayed and the message truncation code in
    112   1.1  christos 	 * the msgq_status routine working.
    113   1.1  christos 	 *
    114   1.1  christos 	 * And, finally, if we updated any status lines, make sure the cursor
    115   1.1  christos 	 * gets back to where it belongs.
    116   1.1  christos 	 */
    117   1.3  christos 	need_refresh = 0;
    118   1.3  christos 	TAILQ_FOREACH(tsp, &sp->wp->scrq, q)
    119   1.1  christos 		if (F_ISSET(tsp, SC_STATUS)) {
    120   1.1  christos 			need_refresh = 1;
    121   1.1  christos 			vs_resolve(tsp, sp, 0);
    122   1.1  christos 		}
    123   1.1  christos 	if (need_refresh)
    124   1.1  christos 		(void)gp->scr_refresh(sp, 0);
    125   1.1  christos 
    126   1.1  christos 	/*
    127   1.1  christos 	 * A side-effect of refreshing the screen is that it's now ready
    128   1.1  christos 	 * for everything else, i.e. messages.
    129   1.1  christos 	 */
    130   1.1  christos 	F_SET(sp, SC_SCR_VI);
    131   1.1  christos 	return (0);
    132   1.1  christos }
    133   1.1  christos 
    134   1.1  christos /*
    135   1.1  christos  * vs_paint --
    136   1.1  christos  *	This is the guts of the vi curses screen code.  The idea is that
    137   1.1  christos  *	the SCR structure passed in contains the new coordinates of the
    138   1.1  christos  *	screen.  What makes this hard is that we don't know how big
    139   1.1  christos  *	characters are, doing input can put the cursor in illegal places,
    140   1.1  christos  *	and we're frantically trying to avoid repainting unless it's
    141   1.1  christos  *	absolutely necessary.  If you change this code, you'd better know
    142   1.1  christos  *	what you're doing.  It's subtle and quick to anger.
    143   1.1  christos  */
    144   1.1  christos static int
    145   1.1  christos vs_paint(SCR *sp, u_int flags)
    146   1.1  christos {
    147   1.1  christos 	GS *gp;
    148   1.1  christos 	SMAP *smp, tmp;
    149   1.1  christos 	VI_PRIVATE *vip;
    150   1.1  christos 	db_recno_t lastline, lcnt;
    151   1.7       rin 	size_t cwtotal, cnt, len, notused, off, y, chlen;
    152   1.2  christos 	int ch = 0, didpaint, isempty, leftright_warp;
    153   1.1  christos 	CHAR_T *p;
    154   1.1  christos 
    155   1.1  christos #define	 LNO	sp->lno			/* Current file line. */
    156   1.1  christos #define	OLNO	vip->olno		/* Remembered file line. */
    157   1.1  christos #define	 CNO	sp->cno			/* Current file column. */
    158   1.1  christos #define	OCNO	vip->ocno		/* Remembered file column. */
    159   1.1  christos #define	SCNO	vip->sc_col		/* Current screen column. */
    160   1.1  christos 
    161   1.1  christos 	gp = sp->gp;
    162   1.1  christos 	vip = VIP(sp);
    163   1.4  christos 	if (vip == NULL)
    164   1.4  christos 		return 0;
    165   1.1  christos 	didpaint = leftright_warp = 0;
    166   1.1  christos 
    167   1.1  christos 	/*
    168   1.1  christos 	 * 5: Reformat the lines.
    169   1.1  christos 	 *
    170   1.1  christos 	 * If the lines themselves have changed (:set list, for example),
    171   1.1  christos 	 * fill in the map from scratch.  Adjust the screen that's being
    172   1.1  christos 	 * displayed if the leftright flag is set.
    173   1.1  christos 	 */
    174   1.1  christos 	if (F_ISSET(sp, SC_SCR_REFORMAT)) {
    175   1.1  christos 		/* Invalidate the line size cache. */
    176   1.1  christos 		VI_SCR_CFLUSH(vip);
    177   1.1  christos 
    178   1.1  christos 		/* Toss vs_line() cached information. */
    179   1.1  christos 		if (F_ISSET(sp, SC_SCR_TOP)) {
    180   1.1  christos 			if (vs_sm_fill(sp, LNO, P_TOP))
    181   1.1  christos 				return (1);
    182   1.1  christos 		}
    183   1.1  christos 		else if (F_ISSET(sp, SC_SCR_CENTER)) {
    184   1.1  christos 			if (vs_sm_fill(sp, LNO, P_MIDDLE))
    185   1.1  christos 				return (1);
    186   1.1  christos 		} else
    187   1.1  christos 			if (vs_sm_fill(sp, OOBLNO, P_TOP))
    188   1.1  christos 				return (1);
    189   1.1  christos 		F_SET(sp, SC_SCR_REDRAW);
    190   1.1  christos 	}
    191   1.1  christos 
    192   1.1  christos 	/*
    193   1.1  christos 	 * 6: Line movement.
    194   1.1  christos 	 *
    195   1.1  christos 	 * Line changes can cause the top line to change as well.  As
    196   1.1  christos 	 * before, if the movement is large, the screen is repainted.
    197   1.1  christos 	 *
    198   1.1  christos 	 * 6a: Small screens.
    199   1.1  christos 	 *
    200   1.1  christos 	 * Users can use the window, w300, w1200 and w9600 options to make
    201   1.1  christos 	 * the screen artificially small.  The behavior of these options
    202   1.1  christos 	 * in the historic vi wasn't all that consistent, and, in fact, it
    203   1.1  christos 	 * was never documented how various screen movements affected the
    204   1.1  christos 	 * screen size.  Generally, one of three things would happen:
    205   1.1  christos 	 *	1: The screen would expand in size, showing the line
    206   1.1  christos 	 *	2: The screen would scroll, showing the line
    207   1.1  christos 	 *	3: The screen would compress to its smallest size and
    208   1.1  christos 	 *		repaint.
    209   1.1  christos 	 * In general, scrolling didn't cause compression (200^D was handled
    210   1.1  christos 	 * the same as ^D), movement to a specific line would (:N where N
    211   1.1  christos 	 * was 1 line below the screen caused a screen compress), and cursor
    212   1.1  christos 	 * movement would scroll if it was 11 lines or less, and compress if
    213   1.1  christos 	 * it was more than 11 lines.  (And, no, I have no idea where the 11
    214   1.1  christos 	 * comes from.)
    215   1.1  christos 	 *
    216   1.1  christos 	 * What we do is try and figure out if the line is less than half of
    217   1.1  christos 	 * a full screen away.  If it is, we expand the screen if there's
    218   1.1  christos 	 * room, and then scroll as necessary.  The alternative is to compress
    219   1.1  christos 	 * and repaint.
    220   1.1  christos 	 *
    221   1.1  christos 	 * !!!
    222   1.1  christos 	 * This code is a special case from beginning to end.  Unfortunately,
    223   1.1  christos 	 * home modems are still slow enough that it's worth having.
    224   1.1  christos 	 *
    225   1.1  christos 	 * XXX
    226   1.1  christos 	 * If the line a really long one, i.e. part of the line is on the
    227   1.1  christos 	 * screen but the column offset is not, we'll end up in the adjust
    228   1.1  christos 	 * code, when we should probably have compressed the screen.
    229   1.1  christos 	 */
    230   1.2  christos 	if (IS_SMALL(sp)) {
    231   1.1  christos 		if (LNO < HMAP->lno) {
    232   1.1  christos 			lcnt = vs_sm_nlines(sp, HMAP, LNO, sp->t_maxrows);
    233   1.1  christos 			if (lcnt <= HALFSCREEN(sp))
    234   1.1  christos 				for (; lcnt && sp->t_rows != sp->t_maxrows;
    235   1.1  christos 				     --lcnt, ++sp->t_rows) {
    236   1.1  christos 					++TMAP;
    237   1.1  christos 					if (vs_sm_1down(sp))
    238   1.1  christos 						return (1);
    239   1.1  christos 				}
    240   1.1  christos 			else
    241   1.1  christos 				goto small_fill;
    242   1.1  christos 		} else if (LNO > TMAP->lno) {
    243   1.1  christos 			lcnt = vs_sm_nlines(sp, TMAP, LNO, sp->t_maxrows);
    244   1.1  christos 			if (lcnt <= HALFSCREEN(sp))
    245   1.1  christos 				for (; lcnt && sp->t_rows != sp->t_maxrows;
    246   1.1  christos 				     --lcnt, ++sp->t_rows) {
    247   1.1  christos 					if (vs_sm_next(sp, TMAP, TMAP + 1))
    248   1.1  christos 						return (1);
    249   1.1  christos 					++TMAP;
    250   1.1  christos 					if (vs_line(sp, TMAP, NULL, NULL))
    251   1.1  christos 						return (1);
    252   1.1  christos 				}
    253   1.1  christos 			else {
    254   1.1  christos small_fill:			(void)gp->scr_move(sp, LASTLINE(sp), 0);
    255   1.1  christos 				(void)gp->scr_clrtoeol(sp);
    256   1.1  christos 				for (; sp->t_rows > sp->t_minrows;
    257   1.1  christos 				    --sp->t_rows, --TMAP) {
    258   1.1  christos 					(void)gp->scr_move(sp, TMAP - HMAP, 0);
    259   1.1  christos 					(void)gp->scr_clrtoeol(sp);
    260   1.1  christos 				}
    261   1.1  christos 				if (vs_sm_fill(sp, LNO, P_FILL))
    262   1.1  christos 					return (1);
    263   1.1  christos 				F_SET(sp, SC_SCR_REDRAW);
    264   1.1  christos 				goto adjust;
    265   1.1  christos 			}
    266   1.1  christos 		}
    267   1.2  christos 	}
    268   1.1  christos 
    269   1.1  christos 	/*
    270   1.1  christos 	 * 6b: Line down, or current screen.
    271   1.1  christos 	 */
    272   1.1  christos 	if (LNO >= HMAP->lno) {
    273   1.1  christos 		/* Current screen. */
    274   1.1  christos 		if (LNO <= TMAP->lno)
    275   1.1  christos 			goto adjust;
    276   1.1  christos 		if (F_ISSET(sp, SC_SCR_TOP))
    277   1.1  christos 			goto top;
    278   1.1  christos 		if (F_ISSET(sp, SC_SCR_CENTER))
    279   1.1  christos 			goto middle;
    280   1.1  christos 
    281   1.1  christos 		/*
    282   1.1  christos 		 * If less than half a screen above the line, scroll down
    283   1.1  christos 		 * until the line is on the screen.
    284   1.1  christos 		 */
    285   1.1  christos 		lcnt = vs_sm_nlines(sp, TMAP, LNO, HALFTEXT(sp));
    286   1.1  christos 		if (lcnt < HALFTEXT(sp)) {
    287   1.1  christos 			while (lcnt--)
    288   1.1  christos 				if (vs_sm_1up(sp))
    289   1.1  christos 					return (1);
    290   1.1  christos 			goto adjust;
    291   1.1  christos 		}
    292   1.1  christos 		goto bottom;
    293   1.1  christos 	}
    294   1.1  christos 
    295   1.1  christos 	/*
    296   1.1  christos 	 * 6c: If not on the current screen, may request center or top.
    297   1.1  christos 	 */
    298   1.1  christos 	if (F_ISSET(sp, SC_SCR_TOP))
    299   1.1  christos 		goto top;
    300   1.1  christos 	if (F_ISSET(sp, SC_SCR_CENTER))
    301   1.1  christos 		goto middle;
    302   1.1  christos 
    303   1.1  christos 	/*
    304   1.1  christos 	 * 6d: Line up.
    305   1.1  christos 	 */
    306   1.1  christos 	lcnt = vs_sm_nlines(sp, HMAP, LNO, HALFTEXT(sp));
    307   1.1  christos 	if (lcnt < HALFTEXT(sp)) {
    308   1.1  christos 		/*
    309   1.1  christos 		 * If less than half a screen below the line, scroll up until
    310   1.1  christos 		 * the line is the first line on the screen.  Special check so
    311   1.1  christos 		 * that if the screen has been emptied, we refill it.
    312   1.1  christos 		 */
    313   1.1  christos 		if (db_exist(sp, HMAP->lno)) {
    314   1.1  christos 			while (lcnt--)
    315   1.1  christos 				if (vs_sm_1down(sp))
    316   1.1  christos 					return (1);
    317   1.1  christos 			goto adjust;
    318   1.1  christos 		}
    319   1.1  christos 
    320   1.1  christos 		/*
    321   1.1  christos 		 * If less than a half screen from the bottom of the file,
    322   1.1  christos 		 * put the last line of the file on the bottom of the screen.
    323   1.1  christos 		 */
    324   1.1  christos bottom:		if (db_last(sp, &lastline))
    325   1.1  christos 			return (1);
    326   1.1  christos 		tmp.lno = LNO;
    327   1.1  christos 		tmp.coff = HMAP->coff;
    328   1.1  christos 		tmp.soff = 1;
    329   1.1  christos 		lcnt = vs_sm_nlines(sp, &tmp, lastline+1, sp->t_rows);
    330   1.1  christos 		if (lcnt < HALFTEXT(sp)) {
    331   1.1  christos 			if (vs_sm_fill(sp, lastline, P_BOTTOM))
    332   1.1  christos 				return (1);
    333   1.1  christos 			F_SET(sp, SC_SCR_REDRAW);
    334   1.1  christos 			goto adjust;
    335   1.1  christos 		}
    336   1.1  christos 		/* It's not close, just put the line in the middle. */
    337   1.1  christos 		goto middle;
    338   1.1  christos 	}
    339   1.1  christos 
    340   1.1  christos 	/*
    341   1.1  christos 	 * If less than half a screen from the top of the file, put the first
    342   1.1  christos 	 * line of the file at the top of the screen.  Otherwise, put the line
    343   1.1  christos 	 * in the middle of the screen.
    344   1.1  christos 	 */
    345   1.1  christos 	tmp.lno = 1;
    346   1.1  christos 	tmp.coff = HMAP->coff;
    347   1.1  christos 	tmp.soff = 1;
    348   1.1  christos 	lcnt = vs_sm_nlines(sp, &tmp, LNO, HALFTEXT(sp));
    349   1.1  christos 	if (lcnt < HALFTEXT(sp)) {
    350   1.1  christos 		if (vs_sm_fill(sp, 1, P_TOP))
    351   1.1  christos 			return (1);
    352   1.1  christos 	} else
    353   1.1  christos middle:		if (vs_sm_fill(sp, LNO, P_MIDDLE))
    354   1.1  christos 			return (1);
    355   1.1  christos 	if (0) {
    356   1.1  christos top:		if (vs_sm_fill(sp, LNO, P_TOP))
    357   1.1  christos 			return (1);
    358   1.1  christos 	}
    359   1.1  christos 	F_SET(sp, SC_SCR_REDRAW);
    360   1.1  christos 
    361   1.1  christos 	/*
    362   1.1  christos 	 * At this point we know part of the line is on the screen.  Since
    363   1.1  christos 	 * scrolling is done using logical lines, not physical, all of the
    364   1.1  christos 	 * line may not be on the screen.  While that's not necessarily bad,
    365   1.1  christos 	 * if the part the cursor is on isn't there, we're going to lose.
    366   1.1  christos 	 * This can be tricky; if the line covers the entire screen, lno
    367   1.1  christos 	 * may be the same as both ends of the map, that's why we test BOTH
    368   1.1  christos 	 * the top and the bottom of the map.  This isn't a problem for
    369   1.1  christos 	 * left-right scrolling, the cursor movement code handles the problem.
    370   1.1  christos 	 *
    371   1.1  christos 	 * There's a performance issue here if editing *really* long lines.
    372   1.1  christos 	 * This gets to the right spot by scrolling, and, in a binary, by
    373   1.1  christos 	 * scrolling hundreds of lines.  If the adjustment looks like it's
    374   1.1  christos 	 * going to be a serious problem, refill the screen and repaint.
    375   1.1  christos 	 */
    376   1.1  christos adjust:	if (!O_ISSET(sp, O_LEFTRIGHT) &&
    377   1.1  christos 	    (LNO == HMAP->lno || LNO == TMAP->lno)) {
    378   1.1  christos 		cnt = vs_screens(sp, LNO, &CNO);
    379   1.2  christos 		if (LNO == HMAP->lno && cnt < HMAP->soff) {
    380   1.1  christos 			if ((HMAP->soff - cnt) > HALFTEXT(sp)) {
    381   1.1  christos 				HMAP->soff = cnt;
    382   1.1  christos 				vs_sm_fill(sp, OOBLNO, P_TOP);
    383   1.1  christos 				F_SET(sp, SC_SCR_REDRAW);
    384   1.1  christos 			} else
    385   1.1  christos 				while (cnt < HMAP->soff)
    386   1.1  christos 					if (vs_sm_1down(sp))
    387   1.1  christos 						return (1);
    388   1.2  christos 		}
    389   1.2  christos 		if (LNO == TMAP->lno && cnt > TMAP->soff) {
    390   1.1  christos 			if ((cnt - TMAP->soff) > HALFTEXT(sp)) {
    391   1.1  christos 				TMAP->soff = cnt;
    392   1.1  christos 				vs_sm_fill(sp, OOBLNO, P_BOTTOM);
    393   1.1  christos 				F_SET(sp, SC_SCR_REDRAW);
    394   1.1  christos 			} else
    395   1.1  christos 				while (cnt > TMAP->soff)
    396   1.1  christos 					if (vs_sm_1up(sp))
    397   1.1  christos 						return (1);
    398   1.2  christos 		}
    399   1.1  christos 	}
    400   1.1  christos 
    401   1.1  christos 	/*
    402   1.1  christos 	 * If the screen needs to be repainted, skip cursor optimization.
    403   1.1  christos 	 * However, in the code above we skipped leftright scrolling on
    404   1.1  christos 	 * the grounds that the cursor code would handle it.  Make sure
    405   1.1  christos 	 * the right screen is up.
    406   1.1  christos 	 */
    407   1.1  christos 	if (F_ISSET(sp, SC_SCR_REDRAW)) {
    408   1.1  christos 		if (O_ISSET(sp, O_LEFTRIGHT))
    409   1.1  christos 			goto slow;
    410   1.1  christos 		goto paint;
    411   1.1  christos 	}
    412   1.1  christos 
    413   1.1  christos 	/*
    414   1.1  christos 	 * 7: Cursor movements (current screen only).
    415   1.1  christos 	 */
    416   1.1  christos 	if (!LF_ISSET(UPDATE_CURSOR))
    417   1.1  christos 		goto number;
    418   1.1  christos 
    419   1.1  christos 	/*
    420   1.1  christos 	 * Decide cursor position.  If the line has changed, the cursor has
    421   1.1  christos 	 * moved over a tab, or don't know where the cursor was, reparse the
    422   1.1  christos 	 * line.  Otherwise, we've just moved over fixed-width characters,
    423   1.1  christos 	 * and can calculate the left/right scrolling and cursor movement
    424   1.1  christos 	 * without reparsing the line.  Note that we don't know which (if any)
    425   1.1  christos 	 * of the characters between the old and new cursor positions changed.
    426   1.1  christos 	 *
    427   1.1  christos 	 * XXX
    428   1.1  christos 	 * With some work, it should be possible to handle tabs quickly, at
    429   1.1  christos 	 * least in obvious situations, like moving right and encountering
    430   1.1  christos 	 * a tab, without reparsing the whole line.
    431   1.1  christos 	 *
    432   1.1  christos 	 * If the line we're working with has changed, reread it..
    433   1.1  christos 	 */
    434   1.1  christos 	if (F_ISSET(vip, VIP_CUR_INVALID) || LNO != OLNO)
    435   1.1  christos 		goto slow;
    436   1.1  christos 
    437   1.1  christos 	/* Otherwise, if nothing's changed, ignore the cursor. */
    438   1.1  christos 	if (CNO == OCNO)
    439   1.1  christos 		goto fast;
    440   1.1  christos 
    441   1.1  christos 	/*
    442   1.1  christos 	 * Get the current line.  If this fails, we either have an empty
    443   1.1  christos 	 * file and can just repaint, or there's a real problem.  This
    444   1.1  christos 	 * isn't a performance issue because there aren't any ways to get
    445   1.1  christos 	 * here repeatedly.
    446   1.1  christos 	 */
    447   1.1  christos 	if (db_eget(sp, LNO, &p, &len, &isempty)) {
    448   1.1  christos 		if (isempty)
    449   1.1  christos 			goto slow;
    450   1.1  christos 		return (1);
    451   1.1  christos 	}
    452   1.1  christos 
    453   1.1  christos #ifdef DEBUG
    454   1.1  christos 	/* Sanity checking. */
    455   1.1  christos 	if (CNO >= len && len != 0) {
    456   1.1  christos 		msgq(sp, M_ERR, "Error: %s/%d: cno (%u) >= len (%u)",
    457   1.1  christos 		     tail(__FILE__), __LINE__, CNO, len);
    458   1.1  christos 		return (1);
    459   1.1  christos 	}
    460   1.1  christos #endif
    461   1.1  christos 	/*
    462   1.1  christos 	 * The basic scheme here is to look at the characters in between
    463   1.1  christos 	 * the old and new positions and decide how big they are on the
    464   1.1  christos 	 * screen, and therefore, how many screen positions to move.
    465   1.1  christos 	 */
    466   1.1  christos 	if (CNO < OCNO) {
    467   1.1  christos 		/*
    468   1.1  christos 		 * 7a: Cursor moved left.
    469   1.1  christos 		 *
    470   1.7       rin 		 * The old cursor position can be past EOL if, for example,
    471   1.7       rin 		 * we just deleted the rest of the line.  In this case, since
    472   1.7       rin 		 * we don't know the width of the characters we traversed, we
    473   1.7       rin 		 * have to do it slowly.
    474   1.1  christos 		 */
    475   1.1  christos 		if (OCNO >= len)
    476   1.1  christos 			goto slow;
    477   1.1  christos 
    478   1.1  christos 		/*
    479   1.7       rin 		 * cwtotal acts as new value for SCNO.  Set cwtotal to the
    480   1.7       rin 		 * first char for content on CNO byte, for ease handling of
    481   1.7       rin 		 * wide characters.
    482   1.7       rin 		 *
    483   1.7       rin 		 * If the character we're stepping on lies across a screen
    484   1.7       rin 		 * boundary, we have no hope to speed it up.  Do it slowly.
    485   1.7       rin 		 */
    486   1.7       rin 		p += OCNO;
    487   1.8       rin 		ch = (UCHAR_T)*p;
    488   1.8       rin 		if (INTISWIDE(ch))
    489   1.7       rin 			cwtotal = SCNO;
    490   1.7       rin 		else {
    491   1.7       rin 			if (ch == '\t' || (chlen = KEY_LEN(sp, ch)) > SCNO + 1)
    492   1.7       rin 				goto slow;
    493   1.7       rin 			cwtotal = SCNO + 1 - chlen;
    494   1.7       rin 		}
    495   1.7       rin 		cnt = OCNO - CNO;
    496   1.7       rin 
    497   1.7       rin 		/*
    498   1.1  christos 		 * Quick sanity check -- it's hard to figure out exactly when
    499   1.1  christos 		 * we cross a screen boundary as we do in the cursor right
    500   1.1  christos 		 * movement.  If cnt is so large that we're going to cross the
    501   1.1  christos 		 * boundary no matter what, stop now.
    502   1.1  christos 		 */
    503   1.1  christos 		if (SCNO + 1 + MAX_CHARACTER_COLUMNS < cnt)
    504   1.1  christos 			goto slow;
    505   1.1  christos 
    506   1.1  christos 		/*
    507   1.1  christos 		 * Count up the widths of the characters.  If it's a tab
    508   1.7       rin 		 * character, go do it the slow way.
    509   1.1  christos 		 */
    510   1.7       rin 		while (cnt--) {
    511   1.7       rin 			if ((ch = (UCHAR_T)*--p) == '\t'
    512   1.7       rin 			    || (chlen = KEY_COL(sp, ch)) > cwtotal)
    513   1.1  christos 				goto slow;
    514   1.7       rin 			cwtotal -= chlen;
    515   1.7       rin 		}
    516   1.1  christos 
    517   1.1  christos 		/*
    518   1.7       rin 		 * If we're moving left, and there's a multi-width char in the
    519   1.7       rin 		 * current position, go to the end of the character.
    520   1.1  christos 		 */
    521   1.7       rin 		if (!INTISWIDE(ch) && (chlen = KEY_LEN(sp, ch)) > 1)
    522   1.7       rin 			cwtotal += chlen - 1;
    523   1.1  christos 
    524   1.1  christos 		/*
    525   1.7       rin 		 * At last, update the screen cursor.
    526   1.1  christos 		 */
    527   1.7       rin 		SCNO = cwtotal;
    528   1.7       rin 	} else {
    529   1.1  christos 		/*
    530   1.7       rin 		 * 7b: Cursor moved right.
    531   1.1  christos 		 */
    532   1.7       rin 		if (OCNO >= len)
    533   1.1  christos 			goto slow;
    534   1.7       rin 
    535   1.1  christos 		/*
    536   1.7       rin 		 * cwtotal acts as new value for SCNO.  Set cwtotal to the
    537   1.7       rin 		 * first char for content on CNO byte, for ease handling
    538   1.7       rin 		 * of wide characters.
    539   1.1  christos 		 */
    540   1.7       rin 		p += OCNO;
    541   1.8       rin 		ch = (UCHAR_T)*p;
    542   1.8       rin 		if (INTISWIDE(ch))
    543   1.7       rin 			cwtotal = SCNO;
    544   1.7       rin 		else
    545   1.7       rin 			cwtotal = SCNO + 1 - KEY_LEN(sp, ch);
    546   1.1  christos 		cnt = CNO - OCNO;
    547   1.1  christos 
    548   1.1  christos 		/*
    549   1.1  christos 		 * Count up the widths of the characters.  If it's a tab
    550   1.7       rin 		 * character, go do it the the slow way.
    551   1.7       rin 		 *
    552   1.7       rin 		 * If a multi-width char seems to occupy the screen boundary,
    553   1.7       rin 		 * that will be pushed to the next line.  Adjust the cursor
    554   1.7       rin 		 * in that case.
    555   1.7       rin 		 *
    556   1.7       rin 		 * If we cross a screen boundary, we can quit.
    557   1.1  christos 		 */
    558   1.7       rin 		while (cnt) {
    559   1.7       rin 			if (ch == '\t')
    560   1.1  christos 				goto slow;
    561   1.7       rin 			cwtotal += KEY_COL(sp, ch);
    562   1.7       rin 			cnt--;
    563   1.8       rin 			ch = (UCHAR_T)*++p;
    564   1.8       rin 			if (INTISWIDE(ch)
    565   1.7       rin 			    && (chlen = CHAR_WIDTH(sp, ch)) > 1
    566   1.7       rin 			    && cwtotal + chlen >= SCREEN_COLS(sp))
    567   1.7       rin 				cwtotal = SCREEN_COLS(sp);
    568   1.7       rin 			if (cwtotal >= SCREEN_COLS(sp))
    569   1.1  christos 				break;
    570   1.1  christos 		}
    571   1.1  christos 
    572   1.1  christos 		/*
    573   1.7       rin 		 * If we are on the tab character, we must do it slowly.
    574   1.7       rin 		 *
    575   1.7       rin 		 * If we're on a multi-width character in the current position,
    576   1.7       rin 		 * go to the end of the character.
    577   1.1  christos 		 */
    578   1.7       rin 		if (ch == '\t')
    579   1.7       rin 			goto slow;
    580   1.7       rin 		if (!INTISWIDE(ch) && (chlen = KEY_LEN(sp, ch)) > 1)
    581   1.7       rin 			cwtotal += chlen - 1;
    582   1.1  christos 
    583   1.1  christos 		/* See screen change comment in section 6a. */
    584   1.7       rin 		if (cwtotal >= SCREEN_COLS(sp))
    585   1.1  christos 			goto slow;
    586   1.7       rin 
    587   1.7       rin 		/*
    588   1.7       rin 		 * At last, update the screen cursor.
    589   1.7       rin 		 */
    590   1.7       rin 		SCNO = cwtotal;
    591   1.1  christos 	}
    592   1.1  christos 
    593   1.1  christos 	/*
    594   1.1  christos 	 * 7c: Fast cursor update.
    595   1.1  christos 	 *
    596   1.1  christos 	 * We have the current column, retrieve the current row.
    597   1.1  christos 	 */
    598   1.1  christos fast:	(void)gp->scr_cursor(sp, &y, &notused);
    599   1.1  christos 	goto done_cursor;
    600   1.1  christos 
    601   1.1  christos 	/*
    602   1.1  christos 	 * 7d: Slow cursor update.
    603   1.1  christos 	 *
    604   1.1  christos 	 * Walk through the map and find the current line.
    605   1.1  christos 	 */
    606   1.9       mrg slow:	for (smp = HMAP; smp->lno != LNO; ++smp)
    607   1.9       mrg 		;
    608   1.1  christos 
    609   1.1  christos 	/*
    610   1.1  christos 	 * 7e: Leftright scrolling adjustment.
    611   1.1  christos 	 *
    612   1.1  christos 	 * If doing left-right scrolling and the cursor movement has changed
    613   1.1  christos 	 * the displayed screen, scroll the screen left or right, unless we're
    614   1.1  christos 	 * updating the info line in which case we just scroll that one line.
    615   1.1  christos 	 * We adjust the offset up or down until we have a window that covers
    616   1.1  christos 	 * the current column, making sure that we adjust differently for the
    617   1.1  christos 	 * first screen as compared to subsequent ones.
    618   1.1  christos 	 */
    619   1.1  christos 	if (O_ISSET(sp, O_LEFTRIGHT)) {
    620   1.1  christos 		/*
    621   1.1  christos 		 * Get the screen column for this character, and correct
    622   1.1  christos 		 * for the number option offset.
    623   1.1  christos 		 */
    624   1.1  christos 		cnt = vs_columns(sp, NULL, LNO, &CNO, NULL);
    625   1.1  christos 		if (O_ISSET(sp, O_NUMBER))
    626   1.1  christos 			cnt -= O_NUMBER_LENGTH;
    627   1.1  christos 
    628   1.1  christos 		/* Adjust the window towards the beginning of the line. */
    629   1.1  christos 		off = smp->coff;
    630   1.1  christos 		if (off >= cnt) {
    631   1.1  christos 			do {
    632   1.1  christos 				if (off >= O_VAL(sp, O_SIDESCROLL))
    633   1.1  christos 					off -= O_VAL(sp, O_SIDESCROLL);
    634   1.1  christos 				else {
    635   1.1  christos 					off = 0;
    636   1.1  christos 					break;
    637   1.1  christos 				}
    638   1.1  christos 			} while (off >= cnt);
    639   1.1  christos 			goto shifted;
    640   1.1  christos 		}
    641   1.1  christos 
    642   1.1  christos 		/* Adjust the window towards the end of the line. */
    643   1.2  christos 		if ((off == 0 && off + SCREEN_COLS(sp) < cnt) ||
    644   1.2  christos 		    (off != 0 && off + sp->cols < cnt)) {
    645   1.1  christos 			do {
    646   1.1  christos 				off += O_VAL(sp, O_SIDESCROLL);
    647   1.1  christos 			} while (off + sp->cols < cnt);
    648   1.1  christos 
    649   1.1  christos shifted:		/* Fill in screen map with the new offset. */
    650   1.1  christos 			if (F_ISSET(sp, SC_TINPUT_INFO))
    651   1.1  christos 				smp->coff = off;
    652   1.1  christos 			else {
    653   1.1  christos 				for (smp = HMAP; smp <= TMAP; ++smp)
    654   1.1  christos 					smp->coff = off;
    655   1.1  christos 				leftright_warp = 1;
    656   1.1  christos 			}
    657   1.1  christos 			goto paint;
    658   1.1  christos 		}
    659   1.1  christos 
    660   1.1  christos 		/*
    661   1.1  christos 		 * We may have jumped here to adjust a leftright screen because
    662   1.1  christos 		 * redraw was set.  If so, we have to paint the entire screen.
    663   1.1  christos 		 */
    664   1.1  christos 		if (F_ISSET(sp, SC_SCR_REDRAW))
    665   1.1  christos 			goto paint;
    666   1.1  christos 	}
    667   1.1  christos 
    668   1.1  christos 	/*
    669   1.1  christos 	 * Update the screen lines for this particular file line until we
    670   1.1  christos 	 * have a new screen cursor position.
    671   1.1  christos 	 */
    672   1.1  christos 	for (y = -1,
    673   1.1  christos 	    vip->sc_smap = NULL; smp <= TMAP && smp->lno == LNO; ++smp) {
    674   1.1  christos 		if (vs_line(sp, smp, &y, &SCNO))
    675   1.1  christos 			return (1);
    676   1.2  christos 		if (y != (size_t)-1) {
    677   1.1  christos 			vip->sc_smap = smp;
    678   1.1  christos 			break;
    679   1.1  christos 		}
    680   1.1  christos 	}
    681   1.1  christos 	goto done_cursor;
    682   1.1  christos 
    683   1.1  christos 	/*
    684   1.1  christos 	 * 8: Repaint the entire screen.
    685   1.1  christos 	 *
    686   1.1  christos 	 * Lost big, do what you have to do.  We flush the cache, since
    687   1.1  christos 	 * SC_SCR_REDRAW gets set when the screen isn't worth fixing, and
    688   1.1  christos 	 * it's simpler to repaint.  So, don't trust anything that we
    689   1.1  christos 	 * think we know about it.
    690   1.1  christos 	 */
    691   1.1  christos paint:	for (smp = HMAP; smp <= TMAP; ++smp)
    692   1.1  christos 		SMAP_FLUSH(smp);
    693   1.1  christos 	for (y = -1, vip->sc_smap = NULL, smp = HMAP; smp <= TMAP; ++smp) {
    694   1.1  christos 		if (vs_line(sp, smp, &y, &SCNO))
    695   1.1  christos 			return (1);
    696   1.2  christos 		if (y != (size_t)-1 && vip->sc_smap == NULL)
    697   1.1  christos 			vip->sc_smap = smp;
    698   1.1  christos 	}
    699   1.1  christos 	/*
    700   1.1  christos 	 * If it's a small screen and we're redrawing, clear the unused lines,
    701   1.1  christos 	 * ex may have overwritten them.
    702   1.1  christos 	 */
    703   1.1  christos 	if (F_ISSET(sp, SC_SCR_REDRAW) && IS_SMALL(sp))
    704   1.1  christos 		for (cnt = sp->t_rows; cnt <= sp->t_maxrows; ++cnt) {
    705   1.1  christos 			(void)gp->scr_move(sp, cnt, 0);
    706   1.1  christos 			(void)gp->scr_clrtoeol(sp);
    707   1.1  christos 		}
    708   1.1  christos 
    709   1.1  christos 	didpaint = 1;
    710   1.1  christos 
    711   1.1  christos done_cursor:
    712   1.1  christos 	/*
    713   1.1  christos 	 * Sanity checking.  When the repainting code messes up, the usual
    714   1.1  christos 	 * result is we don't repaint the cursor and so sc_smap will be
    715   1.1  christos 	 * NULL.  If we're debugging, die, otherwise restart from scratch.
    716   1.1  christos 	 */
    717   1.1  christos #ifdef DEBUG
    718   1.1  christos 	if (vip->sc_smap == NULL) {
    719   1.1  christos 		fprintf(stderr, "smap error\n");
    720   1.1  christos 		sleep(100);
    721   1.1  christos 		abort();
    722   1.1  christos 	}
    723   1.1  christos #else
    724   1.1  christos 	if (vip->sc_smap == NULL) {
    725   1.7       rin 		if (F_ISSET(sp, SC_SCR_REFORMAT))
    726  1.10       rin 			abort(); /* XXX infinite recursion */
    727   1.1  christos 		F_SET(sp, SC_SCR_REFORMAT);
    728   1.1  christos 		return (vs_paint(sp, flags));
    729   1.1  christos 	}
    730   1.1  christos #endif
    731   1.1  christos 
    732   1.1  christos 	/*
    733   1.1  christos 	 * 9: Set the remembered cursor values.
    734   1.1  christos 	 */
    735   1.1  christos 	OCNO = CNO;
    736   1.1  christos 	OLNO = LNO;
    737   1.1  christos 
    738   1.1  christos 	/*
    739   1.1  christos 	 * 10: Repaint the line numbers.
    740   1.1  christos 	 *
    741   1.1  christos 	 * If O_NUMBER is set and the VIP_N_RENUMBER bit is set, and we
    742   1.1  christos 	 * didn't repaint the screen, repaint all of the line numbers,
    743   1.1  christos 	 * they've changed.
    744   1.1  christos 	 */
    745   1.1  christos number:	if (O_ISSET(sp, O_NUMBER) &&
    746   1.1  christos 	    F_ISSET(vip, VIP_N_RENUMBER) && !didpaint && vs_number(sp))
    747   1.1  christos 		return (1);
    748   1.1  christos 
    749   1.1  christos 	/*
    750   1.1  christos 	 * 11: Update the mode line, position the cursor, and flush changes.
    751   1.1  christos 	 *
    752   1.1  christos 	 * If we warped the screen, we have to refresh everything.
    753   1.1  christos 	 */
    754   1.1  christos 	if (leftright_warp)
    755   1.1  christos 		LF_SET(UPDATE_CURSOR | UPDATE_SCREEN);
    756   1.1  christos 
    757   1.1  christos 	if (LF_ISSET(UPDATE_SCREEN) && !IS_ONELINE(sp) &&
    758   1.1  christos 	    !F_ISSET(vip, VIP_S_MODELINE) && !F_ISSET(sp, SC_TINPUT_INFO))
    759   1.1  christos 		vs_modeline(sp);
    760   1.1  christos 
    761   1.1  christos 	if (LF_ISSET(UPDATE_CURSOR)) {
    762   1.1  christos 		(void)gp->scr_move(sp, y, SCNO);
    763   1.1  christos 
    764   1.1  christos 		/*
    765   1.1  christos 		 * XXX
    766   1.1  christos 		 * If the screen shifted, we recalculate the "most favorite"
    767   1.1  christos 		 * cursor position.  Vi won't know that we've warped the
    768   1.1  christos 		 * screen, so it's going to have a wrong idea about where the
    769   1.1  christos 		 * cursor should be.  This is vi's problem, and fixing it here
    770   1.1  christos 		 * is a gross layering violation.
    771   1.1  christos 		 */
    772   1.1  christos 		if (leftright_warp)
    773   1.1  christos 			(void)vs_column(sp, &sp->rcm);
    774   1.1  christos 	}
    775   1.1  christos 
    776   1.1  christos 	if (LF_ISSET(UPDATE_SCREEN))
    777   1.1  christos 		(void)gp->scr_refresh(sp, F_ISSET(vip, VIP_N_EX_PAINT));
    778   1.1  christos 
    779   1.1  christos 	/* 12: Clear the flags that are handled by this routine. */
    780   1.1  christos 	F_CLR(sp, SC_SCR_CENTER | SC_SCR_REDRAW | SC_SCR_REFORMAT | SC_SCR_TOP);
    781   1.1  christos 	F_CLR(vip, VIP_CUR_INVALID |
    782   1.1  christos 	    VIP_N_EX_PAINT | VIP_N_REFRESH | VIP_N_RENUMBER | VIP_S_MODELINE);
    783   1.1  christos 
    784   1.1  christos 	return (0);
    785   1.1  christos 
    786   1.1  christos #undef	 LNO
    787   1.1  christos #undef	OLNO
    788   1.1  christos #undef	 CNO
    789   1.1  christos #undef	OCNO
    790   1.1  christos #undef	SCNO
    791   1.1  christos }
    792   1.1  christos 
    793   1.1  christos /*
    794   1.1  christos  * vs_modeline --
    795   1.1  christos  *	Update the mode line.
    796   1.1  christos  */
    797   1.1  christos static void
    798   1.1  christos vs_modeline(SCR *sp)
    799   1.1  christos {
    800   1.2  christos 	static const char * const modes[] = {
    801   1.1  christos 		"215|Append",			/* SM_APPEND */
    802   1.1  christos 		"216|Change",			/* SM_CHANGE */
    803   1.1  christos 		"217|Command",			/* SM_COMMAND */
    804   1.1  christos 		"218|Insert",			/* SM_INSERT */
    805   1.1  christos 		"219|Replace",			/* SM_REPLACE */
    806   1.1  christos 	};
    807   1.1  christos 	GS *gp;
    808   1.1  christos 	size_t cols, curcol, curlen, endpoint, len, midpoint;
    809   1.2  christos 	const char *t = NULL;
    810   1.1  christos 	int ellipsis;
    811   1.1  christos 	char *p, buf[20];
    812   1.1  christos 
    813   1.1  christos 	gp = sp->gp;
    814   1.1  christos 
    815   1.1  christos 	/*
    816   1.1  christos 	 * We put down the file name, the ruler, the mode and the dirty flag.
    817   1.1  christos 	 * If there's not enough room, there's not enough room, we don't play
    818   1.1  christos 	 * any special games.  We try to put the ruler in the middle and the
    819   1.1  christos 	 * mode and dirty flag at the end.
    820   1.1  christos 	 *
    821   1.1  christos 	 * !!!
    822   1.1  christos 	 * Leave the last character blank, in case it's a really dumb terminal
    823   1.1  christos 	 * with hardware scroll.  Second, don't paint the last character in the
    824   1.1  christos 	 * screen, SunOS 4.1.1 and Ultrix 4.2 curses won't let you.
    825   1.1  christos 	 *
    826   1.1  christos 	 * Move to the last line on the screen.
    827   1.1  christos 	 */
    828   1.1  christos 	(void)gp->scr_move(sp, LASTLINE(sp), 0);
    829   1.1  christos 
    830   1.1  christos 	/* If more than one screen in the display, show the file name. */
    831   1.1  christos 	curlen = 0;
    832   1.1  christos 	if (IS_SPLIT(sp)) {
    833   1.1  christos 		for (p = sp->frp->name; *p != '\0'; ++p);
    834   1.1  christos 		for (ellipsis = 0, cols = sp->cols / 2; --p > sp->frp->name;) {
    835   1.1  christos 			if (*p == '/') {
    836   1.1  christos 				++p;
    837   1.1  christos 				break;
    838   1.1  christos 			}
    839   1.1  christos 			if ((curlen += KEY_LEN(sp, *p)) > cols) {
    840   1.1  christos 				ellipsis = 3;
    841   1.1  christos 				curlen +=
    842   1.1  christos 				    KEY_LEN(sp, '.') * 3 + KEY_LEN(sp, ' ');
    843   1.1  christos 				while (curlen > cols) {
    844   1.1  christos 					++p;
    845   1.1  christos 					curlen -= KEY_LEN(sp, *p);
    846   1.1  christos 				}
    847   1.1  christos 				break;
    848   1.1  christos 			}
    849   1.1  christos 		}
    850   1.1  christos 		if (ellipsis) {
    851   1.1  christos 			while (ellipsis--)
    852   1.1  christos 				(void)gp->scr_addstr(sp,
    853   1.2  christos 				    (const char *)KEY_NAME(sp, '.'),
    854   1.2  christos 				    KEY_LEN(sp, '.'));
    855   1.1  christos 			(void)gp->scr_addstr(sp,
    856   1.2  christos 			    (const char *)KEY_NAME(sp, ' '), KEY_LEN(sp, ' '));
    857   1.1  christos 		}
    858   1.1  christos 		for (; *p != '\0'; ++p)
    859   1.1  christos 			(void)gp->scr_addstr(sp,
    860   1.2  christos 			    (const char *)KEY_NAME(sp, *p), KEY_LEN(sp, *p));
    861   1.1  christos 	}
    862   1.1  christos 
    863   1.1  christos 	/* Clear the rest of the line. */
    864   1.1  christos 	(void)gp->scr_clrtoeol(sp);
    865   1.1  christos 
    866   1.1  christos 	/*
    867   1.1  christos 	 * Display the ruler.  If we're not at the midpoint yet, move there.
    868   1.1  christos 	 * Otherwise, add in two extra spaces.
    869   1.1  christos 	 *
    870   1.1  christos 	 * Adjust the current column for the fact that the editor uses it as
    871   1.1  christos 	 * a zero-based number.
    872   1.1  christos 	 *
    873   1.1  christos 	 * XXX
    874   1.1  christos 	 * Assume that numbers, commas, and spaces only take up a single
    875   1.1  christos 	 * column on the screen.
    876   1.1  christos 	 */
    877   1.1  christos 	cols = sp->cols - 1;
    878   1.1  christos 	if (O_ISSET(sp, O_RULER)) {
    879   1.1  christos 		vs_column(sp, &curcol);
    880   1.1  christos 		len =
    881   1.2  christos 		    snprintf(buf, sizeof(buf), "%lu,%lu",
    882   1.2  christos 			(unsigned long)sp->lno, (unsigned long)curcol + 1);
    883   1.1  christos 
    884   1.1  christos 		midpoint = (cols - ((len + 1) / 2)) / 2;
    885   1.1  christos 		if (curlen < midpoint) {
    886   1.1  christos 			(void)gp->scr_move(sp, LASTLINE(sp), midpoint);
    887   1.1  christos 			curlen += len;
    888   1.1  christos 		} else if (curlen + 2 + len < cols) {
    889   1.1  christos 			(void)gp->scr_addstr(sp, "  ", 2);
    890   1.1  christos 			curlen += 2 + len;
    891   1.1  christos 		}
    892   1.1  christos 		(void)gp->scr_addstr(sp, buf, len);
    893   1.1  christos 	}
    894   1.1  christos 
    895   1.1  christos 	/*
    896   1.1  christos 	 * Display the mode and the modified flag, as close to the end of the
    897   1.1  christos 	 * line as possible, but guaranteeing at least two spaces between the
    898   1.1  christos 	 * ruler and the modified flag.
    899   1.1  christos 	 */
    900   1.1  christos #define	MODESIZE	9
    901   1.1  christos 	endpoint = cols;
    902   1.1  christos 	if (O_ISSET(sp, O_SHOWMODE)) {
    903   1.1  christos 		if (F_ISSET(sp->ep, F_MODIFIED))
    904   1.1  christos 			--endpoint;
    905   1.1  christos 		t = msg_cat(sp, modes[sp->showmode], &len);
    906   1.1  christos 		endpoint -= len;
    907   1.1  christos 	}
    908   1.1  christos 
    909   1.1  christos 	if (endpoint > curlen + 2) {
    910   1.1  christos 		(void)gp->scr_move(sp, LASTLINE(sp), endpoint);
    911   1.1  christos 		if (O_ISSET(sp, O_SHOWMODE)) {
    912   1.1  christos 			if (F_ISSET(sp->ep, F_MODIFIED))
    913   1.1  christos 				(void)gp->scr_addstr(sp,
    914   1.2  christos 				    (const char *)KEY_NAME(sp, '*'),
    915   1.2  christos 				    KEY_LEN(sp, '*'));
    916   1.1  christos 			(void)gp->scr_addstr(sp, t, len);
    917   1.1  christos 		}
    918   1.1  christos 	}
    919   1.1  christos }
    920