119304Speter/*-
219304Speter * Copyright (c) 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: vs_relative.c,v 10.19 2011/12/01 15:22:59 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
1819304Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <limits.h>
2219304Speter#include <stdio.h>
2319304Speter#include <string.h>
2419304Speter
2519304Speter#include "../common/common.h"
2619304Speter#include "vi.h"
2719304Speter
2819304Speter/*
2919304Speter * vs_column --
3019304Speter *	Return the logical column of the cursor in the line.
3119304Speter *
3219304Speter * PUBLIC: int vs_column __P((SCR *, size_t *));
3319304Speter */
3419304Speterint
35254225Spetervs_column(SCR *sp, size_t *colp)
3619304Speter{
3719304Speter	VI_PRIVATE *vip;
3819304Speter
3919304Speter	vip = VIP(sp);
4019304Speter
4119304Speter	*colp = (O_ISSET(sp, O_LEFTRIGHT) ?
4219304Speter	    vip->sc_smap->coff : (vip->sc_smap->soff - 1) * sp->cols) +
4319304Speter	    vip->sc_col - (O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0);
4419304Speter	return (0);
4519304Speter}
4619304Speter
4719304Speter/*
4819304Speter * vs_screens --
4919304Speter *	Return the screens necessary to display the line, or if specified,
5019304Speter *	the physical character column within the line, including space
5119304Speter *	required for the O_NUMBER and O_LIST options.
5219304Speter *
5319304Speter * PUBLIC: size_t vs_screens __P((SCR *, recno_t, size_t *));
5419304Speter */
5519304Spetersize_t
56254225Spetervs_screens(SCR *sp, recno_t lno, size_t *cnop)
5719304Speter{
5819304Speter	size_t cols, screens;
5919304Speter
6019304Speter	/* Left-right screens are simple, it's always 1. */
6119304Speter	if (O_ISSET(sp, O_LEFTRIGHT))
6219304Speter		return (1);
6319304Speter
6419304Speter	/*
6519304Speter	 * Check for a cached value.  We maintain a cache because, if the
6619304Speter	 * line is large, this routine gets called repeatedly.  One other
6719304Speter	 * hack, lots of time the cursor is on column one, which is an easy
6819304Speter	 * one.
6919304Speter	 */
7019304Speter	if (cnop == NULL) {
7119304Speter		if (VIP(sp)->ss_lno == lno)
7219304Speter			return (VIP(sp)->ss_screens);
7319304Speter	} else if (*cnop == 0)
7419304Speter		return (1);
7519304Speter
7619304Speter	/* Figure out how many columns the line/column needs. */
7719304Speter	cols = vs_columns(sp, NULL, lno, cnop, NULL);
7819304Speter
7919304Speter	screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
8019304Speter	if (screens == 0)
8119304Speter		screens = 1;
8219304Speter
8319304Speter	/* Cache the value. */
8419304Speter	if (cnop == NULL) {
8519304Speter		VIP(sp)->ss_lno = lno;
8619304Speter		VIP(sp)->ss_screens = screens;
8719304Speter	}
8819304Speter	return (screens);
8919304Speter}
9019304Speter
9119304Speter/*
9219304Speter * vs_columns --
9319304Speter *	Return the screen columns necessary to display the line, or,
9419304Speter *	if specified, the physical character column within the line.
9519304Speter *
96254225Speter * PUBLIC: size_t vs_columns __P((SCR *, CHAR_T *, recno_t, size_t *, size_t *));
9719304Speter */
9819304Spetersize_t
99254225Spetervs_columns(SCR *sp, CHAR_T *lp, recno_t lno, size_t *cnop, size_t *diffp)
10019304Speter{
101254225Speter	size_t chlen, cno, curoff, last = 0, len, scno;
10219304Speter	int ch, leftright, listset;
103254225Speter	CHAR_T *p;
10419304Speter
10589010Ssheldonh	/*
10689010Ssheldonh	 * Initialize the screen offset.
10789010Ssheldonh	 */
10889010Ssheldonh	scno = 0;
10989010Ssheldonh
11089010Ssheldonh	/* Leading number if O_NUMBER option set. */
11189010Ssheldonh	if (O_ISSET(sp, O_NUMBER))
11289010Ssheldonh		scno += O_NUMBER_LENGTH;
11389010Ssheldonh
11419304Speter	/* Need the line to go any further. */
11519304Speter	if (lp == NULL) {
11619304Speter		(void)db_get(sp, lno, 0, &lp, &len);
11719304Speter		if (len == 0)
11819304Speter			goto done;
11919304Speter	}
12019304Speter
12119304Speter	/* Missing or empty lines are easy. */
12219304Speter	if (lp == NULL) {
12319304Speterdone:		if (diffp != NULL)		/* XXX */
12419304Speter			*diffp = 0;
12589010Ssheldonh		return scno;
12619304Speter	}
12719304Speter
12819304Speter	/* Store away the values of the list and leftright edit options. */
12919304Speter	listset = O_ISSET(sp, O_LIST);
13019304Speter	leftright = O_ISSET(sp, O_LEFTRIGHT);
13119304Speter
13219304Speter	/*
13389010Ssheldonh	 * Initialize the pointer into the buffer and current offset.
13419304Speter	 */
13519304Speter	p = lp;
136254225Speter	curoff = scno;
13719304Speter
13819304Speter	/* Macro to return the display length of any signal character. */
139254225Speter#define	CHLEN(val) (ch = *(UCHAR_T *)p++) == '\t' &&			\
140254225Speter	    !listset ? TAB_OFF(val) : KEY_COL(sp, ch);
14119304Speter
14219304Speter	/*
14319304Speter	 * If folding screens (the historic vi screen format), past the end
14419304Speter	 * of the current screen, and the character was a tab, reset the
14519304Speter	 * current screen column to 0, and the total screen columns to the
14619304Speter	 * last column of the screen.  Otherwise, display the rest of the
14719304Speter	 * character in the next screen.
14819304Speter	 */
14919304Speter#define	TAB_RESET {							\
15019304Speter	curoff += chlen;						\
15119304Speter	if (!leftright && curoff >= sp->cols)				\
15219304Speter		if (ch == '\t') {					\
15319304Speter			curoff = 0;					\
15419304Speter			scno -= scno % sp->cols;			\
15519304Speter		} else							\
15619304Speter			curoff -= sp->cols;				\
15719304Speter}
15819304Speter	if (cnop == NULL)
15919304Speter		while (len--) {
16019304Speter			chlen = CHLEN(curoff);
16119304Speter			last = scno;
16219304Speter			scno += chlen;
16319304Speter			TAB_RESET;
16419304Speter		}
16519304Speter	else
16619304Speter		for (cno = *cnop;; --cno) {
16719304Speter			chlen = CHLEN(curoff);
16819304Speter			last = scno;
16919304Speter			scno += chlen;
17019304Speter			TAB_RESET;
17119304Speter			if (cno == 0)
17219304Speter				break;
17319304Speter		}
17419304Speter
17519304Speter	/* Add the trailing '$' if the O_LIST option set. */
17619304Speter	if (listset && cnop == NULL)
17719304Speter		scno += KEY_LEN(sp, '$');
17819304Speter
17919304Speter	/*
18019304Speter	 * The text input screen code needs to know how much additional
18119304Speter	 * room the last two characters required, so that it can handle
18219304Speter	 * tab character displays correctly.
18319304Speter	 */
18419304Speter	if (diffp != NULL)
18519304Speter		*diffp = scno - last;
18619304Speter	return (scno);
18719304Speter}
18819304Speter
18919304Speter/*
19019304Speter * vs_rcm --
19119304Speter *	Return the physical column from the line that will display a
19219304Speter *	character closest to the currently most attractive character
19319304Speter *	position (which is stored as a screen column).
19419304Speter *
19519304Speter * PUBLIC: size_t vs_rcm __P((SCR *, recno_t, int));
19619304Speter */
19719304Spetersize_t
198254225Spetervs_rcm(SCR *sp, recno_t lno, int islast)
19919304Speter{
20019304Speter	size_t len;
20119304Speter
20219304Speter	/* Last character is easy, and common. */
20319304Speter	if (islast) {
20419304Speter		if (db_get(sp, lno, 0, NULL, &len) || len == 0)
20519304Speter			return (0);
20619304Speter		return (len - 1);
20719304Speter	}
20819304Speter
20919304Speter	/* First character is easy, and common. */
21019304Speter	if (sp->rcm == 0)
21119304Speter		return (0);
21219304Speter
21319304Speter	return (vs_colpos(sp, lno, sp->rcm));
21419304Speter}
21519304Speter
21619304Speter/*
21719304Speter * vs_colpos --
21819304Speter *	Return the physical column from the line that will display a
21919304Speter *	character closest to the specified screen column.
22019304Speter *
22119304Speter * PUBLIC: size_t vs_colpos __P((SCR *, recno_t, size_t));
22219304Speter */
22319304Spetersize_t
224254225Spetervs_colpos(SCR *sp, recno_t lno, size_t cno)
22519304Speter{
22619304Speter	size_t chlen, curoff, len, llen, off, scno;
227254225Speter	int ch = 0, leftright, listset;
228254225Speter	CHAR_T *lp, *p;
22919304Speter
23019304Speter	/* Need the line to go any further. */
23119304Speter	(void)db_get(sp, lno, 0, &lp, &llen);
23219304Speter
23319304Speter	/* Missing or empty lines are easy. */
23419304Speter	if (lp == NULL || llen == 0)
23519304Speter		return (0);
23619304Speter
23719304Speter	/* Store away the values of the list and leftright edit options. */
23819304Speter	listset = O_ISSET(sp, O_LIST);
23919304Speter	leftright = O_ISSET(sp, O_LEFTRIGHT);
24019304Speter
24119304Speter	/* Discard screen (logical) lines. */
24219304Speter	off = cno / sp->cols;
24319304Speter	cno %= sp->cols;
24419304Speter	for (scno = 0, p = lp, len = llen; off--;) {
24519304Speter		for (; len && scno < sp->cols; --len)
24619304Speter			scno += CHLEN(scno);
24719304Speter
24819304Speter		/*
24919304Speter		 * If reached the end of the physical line, return the last
25019304Speter		 * physical character in the line.
25119304Speter		 */
25219304Speter		if (len == 0)
25319304Speter			return (llen - 1);
25419304Speter
25519304Speter		/*
25619304Speter		 * If folding screens (the historic vi screen format), past
25719304Speter		 * the end of the current screen, and the character was a tab,
25819304Speter		 * reset the current screen column to 0.  Otherwise, the rest
25919304Speter		 * of the character is displayed in the next screen.
26019304Speter		 */
26119304Speter		if (leftright && ch == '\t')
26219304Speter			scno = 0;
26319304Speter		else
26419304Speter			scno -= sp->cols;
26519304Speter	}
26619304Speter
26719304Speter	/* Step through the line until reach the right character or EOL. */
26819304Speter	for (curoff = scno; len--;) {
26919304Speter		chlen = CHLEN(curoff);
27019304Speter
27119304Speter		/*
27219304Speter		 * If we've reached the specific character, there are three
27319304Speter		 * cases.
27419304Speter		 *
27519304Speter		 * 1: scno == cno, i.e. the current character ends at the
27619304Speter		 *    screen character we care about.
27719304Speter		 *	a: off < llen - 1, i.e. not the last character in
27819304Speter		 *	   the line, return the offset of the next character.
27919304Speter		 *	b: else return the offset of the last character.
28019304Speter		 * 2: scno != cno, i.e. this character overruns the character
28119304Speter		 *    we care about, return the offset of this character.
28219304Speter		 */
28319304Speter		if ((scno += chlen) >= cno) {
28419304Speter			off = p - lp;
28519304Speter			return (scno == cno ?
28619304Speter			    (off < llen - 1 ? off : llen - 1) : off - 1);
28719304Speter		}
28819304Speter
28919304Speter		TAB_RESET;
29019304Speter	}
29119304Speter
29219304Speter	/* No such character; return the start of the last character. */
29319304Speter	return (llen - 1);
29419304Speter}
295