xref: /dflybsd-src/contrib/nvi2/vi/vs_relative.c (revision 07bc39c2f4bbca56f12568e06d89da17f2eeb965)
1*e0b8e63eSJohn Marino /*-
2*e0b8e63eSJohn Marino  * Copyright (c) 1993, 1994
3*e0b8e63eSJohn Marino  *	The Regents of the University of California.  All rights reserved.
4*e0b8e63eSJohn Marino  * Copyright (c) 1993, 1994, 1995, 1996
5*e0b8e63eSJohn Marino  *	Keith Bostic.  All rights reserved.
6*e0b8e63eSJohn Marino  *
7*e0b8e63eSJohn Marino  * See the LICENSE file for redistribution information.
8*e0b8e63eSJohn Marino  */
9*e0b8e63eSJohn Marino 
10*e0b8e63eSJohn Marino #include "config.h"
11*e0b8e63eSJohn Marino 
12*e0b8e63eSJohn Marino #include <sys/types.h>
13*e0b8e63eSJohn Marino #include <sys/queue.h>
14*e0b8e63eSJohn Marino #include <sys/time.h>
15*e0b8e63eSJohn Marino 
16*e0b8e63eSJohn Marino #include <bitstring.h>
17*e0b8e63eSJohn Marino #include <limits.h>
18*e0b8e63eSJohn Marino #include <stdio.h>
19*e0b8e63eSJohn Marino #include <string.h>
20*e0b8e63eSJohn Marino 
21*e0b8e63eSJohn Marino #include "../common/common.h"
22*e0b8e63eSJohn Marino #include "vi.h"
23*e0b8e63eSJohn Marino 
24*e0b8e63eSJohn Marino /*
25*e0b8e63eSJohn Marino  * vs_column --
26*e0b8e63eSJohn Marino  *	Return the logical column of the cursor in the line.
27*e0b8e63eSJohn Marino  *
28*e0b8e63eSJohn Marino  * PUBLIC: int vs_column(SCR *, size_t *);
29*e0b8e63eSJohn Marino  */
30*e0b8e63eSJohn Marino int
vs_column(SCR * sp,size_t * colp)31*e0b8e63eSJohn Marino vs_column(SCR *sp, size_t *colp)
32*e0b8e63eSJohn Marino {
33*e0b8e63eSJohn Marino 	VI_PRIVATE *vip;
34*e0b8e63eSJohn Marino 
35*e0b8e63eSJohn Marino 	vip = VIP(sp);
36*e0b8e63eSJohn Marino 
37*e0b8e63eSJohn Marino 	*colp = (O_ISSET(sp, O_LEFTRIGHT) ?
38*e0b8e63eSJohn Marino 	    vip->sc_smap->coff : (vip->sc_smap->soff - 1) * sp->cols) +
39*e0b8e63eSJohn Marino 	    vip->sc_col - (O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0);
40*e0b8e63eSJohn Marino 	return (0);
41*e0b8e63eSJohn Marino }
42*e0b8e63eSJohn Marino 
43*e0b8e63eSJohn Marino /*
44*e0b8e63eSJohn Marino  * vs_screens --
45*e0b8e63eSJohn Marino  *	Return the screens necessary to display the line, or if specified,
46*e0b8e63eSJohn Marino  *	the physical character column within the line, including space
47*e0b8e63eSJohn Marino  *	required for the O_NUMBER and O_LIST options.
48*e0b8e63eSJohn Marino  *
49*e0b8e63eSJohn Marino  * PUBLIC: size_t vs_screens(SCR *, recno_t, size_t *);
50*e0b8e63eSJohn Marino  */
51*e0b8e63eSJohn Marino size_t
vs_screens(SCR * sp,recno_t lno,size_t * cnop)52*e0b8e63eSJohn Marino vs_screens(SCR *sp, recno_t lno, size_t *cnop)
53*e0b8e63eSJohn Marino {
54*e0b8e63eSJohn Marino 	size_t cols, screens;
55*e0b8e63eSJohn Marino 
56*e0b8e63eSJohn Marino 	/* Left-right screens are simple, it's always 1. */
57*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_LEFTRIGHT))
58*e0b8e63eSJohn Marino 		return (1);
59*e0b8e63eSJohn Marino 
60*e0b8e63eSJohn Marino 	/*
61*e0b8e63eSJohn Marino 	 * Check for a cached value.  We maintain a cache because, if the
62*e0b8e63eSJohn Marino 	 * line is large, this routine gets called repeatedly.  One other
63*e0b8e63eSJohn Marino 	 * hack, lots of time the cursor is on column one, which is an easy
64*e0b8e63eSJohn Marino 	 * one.
65*e0b8e63eSJohn Marino 	 */
66*e0b8e63eSJohn Marino 	if (cnop == NULL) {
67*e0b8e63eSJohn Marino 		if (VIP(sp)->ss_lno == lno)
68*e0b8e63eSJohn Marino 			return (VIP(sp)->ss_screens);
69*e0b8e63eSJohn Marino 	} else if (*cnop == 0)
70*e0b8e63eSJohn Marino 		return (1);
71*e0b8e63eSJohn Marino 
72*e0b8e63eSJohn Marino 	/* Figure out how many columns the line/column needs. */
73*e0b8e63eSJohn Marino 	cols = vs_columns(sp, NULL, lno, cnop, NULL);
74*e0b8e63eSJohn Marino 
75*e0b8e63eSJohn Marino 	screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
76*e0b8e63eSJohn Marino 	if (screens == 0)
77*e0b8e63eSJohn Marino 		screens = 1;
78*e0b8e63eSJohn Marino 
79*e0b8e63eSJohn Marino 	/* Cache the value. */
80*e0b8e63eSJohn Marino 	if (cnop == NULL) {
81*e0b8e63eSJohn Marino 		VIP(sp)->ss_lno = lno;
82*e0b8e63eSJohn Marino 		VIP(sp)->ss_screens = screens;
83*e0b8e63eSJohn Marino 	}
84*e0b8e63eSJohn Marino 	return (screens);
85*e0b8e63eSJohn Marino }
86*e0b8e63eSJohn Marino 
87*e0b8e63eSJohn Marino /*
88*e0b8e63eSJohn Marino  * vs_columns --
89*e0b8e63eSJohn Marino  *	Return the screen columns necessary to display the line, or,
90*e0b8e63eSJohn Marino  *	if specified, the physical character column within the line.
91*e0b8e63eSJohn Marino  *
92*e0b8e63eSJohn Marino  * PUBLIC: size_t vs_columns(SCR *, CHAR_T *, recno_t, size_t *, size_t *);
93*e0b8e63eSJohn Marino  */
94*e0b8e63eSJohn Marino size_t
vs_columns(SCR * sp,CHAR_T * lp,recno_t lno,size_t * cnop,size_t * diffp)95*e0b8e63eSJohn Marino vs_columns(SCR *sp, CHAR_T *lp, recno_t lno, size_t *cnop, size_t *diffp)
96*e0b8e63eSJohn Marino {
97*e0b8e63eSJohn Marino 	size_t chlen, cno, curoff, last = 0, len, scno;
98*e0b8e63eSJohn Marino 	int ch, leftright, listset;
99*e0b8e63eSJohn Marino 	CHAR_T *p;
100*e0b8e63eSJohn Marino 
101*e0b8e63eSJohn Marino 	/*
102*e0b8e63eSJohn Marino 	 * Initialize the screen offset.
103*e0b8e63eSJohn Marino 	 */
104*e0b8e63eSJohn Marino 	scno = 0;
105*e0b8e63eSJohn Marino 
106*e0b8e63eSJohn Marino 	/* Leading number if O_NUMBER option set. */
107*e0b8e63eSJohn Marino 	if (O_ISSET(sp, O_NUMBER))
108*e0b8e63eSJohn Marino 		scno += O_NUMBER_LENGTH;
109*e0b8e63eSJohn Marino 
110*e0b8e63eSJohn Marino 	/* Need the line to go any further. */
111*e0b8e63eSJohn Marino 	if (lp == NULL) {
112*e0b8e63eSJohn Marino 		(void)db_get(sp, lno, 0, &lp, &len);
113*e0b8e63eSJohn Marino 		if (len == 0)
114*e0b8e63eSJohn Marino 			goto done;
115*e0b8e63eSJohn Marino 	}
116*e0b8e63eSJohn Marino 
117*e0b8e63eSJohn Marino 	/* Missing or empty lines are easy. */
118*e0b8e63eSJohn Marino 	if (lp == NULL) {
119*e0b8e63eSJohn Marino done:		if (diffp != NULL)		/* XXX */
120*e0b8e63eSJohn Marino 			*diffp = 0;
121*e0b8e63eSJohn Marino 		return scno;
122*e0b8e63eSJohn Marino 	}
123*e0b8e63eSJohn Marino 
124*e0b8e63eSJohn Marino 	/* Store away the values of the list and leftright edit options. */
125*e0b8e63eSJohn Marino 	listset = O_ISSET(sp, O_LIST);
126*e0b8e63eSJohn Marino 	leftright = O_ISSET(sp, O_LEFTRIGHT);
127*e0b8e63eSJohn Marino 
128*e0b8e63eSJohn Marino 	/*
129*e0b8e63eSJohn Marino 	 * Initialize the pointer into the buffer and current offset.
130*e0b8e63eSJohn Marino 	 */
131*e0b8e63eSJohn Marino 	p = lp;
132*e0b8e63eSJohn Marino 	curoff = scno;
133*e0b8e63eSJohn Marino 
134*e0b8e63eSJohn Marino 	/* Macro to return the display length of any signal character. */
135*e0b8e63eSJohn Marino #define	CHLEN(val) (ch = *(UCHAR_T *)p++) == '\t' &&			\
136*e0b8e63eSJohn Marino 	    !listset ? TAB_OFF(val) : KEY_COL(sp, ch);
137*e0b8e63eSJohn Marino 
138*e0b8e63eSJohn Marino 	/*
139*e0b8e63eSJohn Marino 	 * If folding screens (the historic vi screen format), past the end
140*e0b8e63eSJohn Marino 	 * of the current screen, and the character was a tab, reset the
141*e0b8e63eSJohn Marino 	 * current screen column to 0, and the total screen columns to the
142*e0b8e63eSJohn Marino 	 * last column of the screen.  Otherwise, display the rest of the
143*e0b8e63eSJohn Marino 	 * character in the next screen.
144*e0b8e63eSJohn Marino 	 */
145*e0b8e63eSJohn Marino #define	TAB_RESET {							\
146*e0b8e63eSJohn Marino 	curoff += chlen;						\
147*e0b8e63eSJohn Marino 	if (!leftright && curoff >= sp->cols)				\
148*e0b8e63eSJohn Marino 		if (ch == '\t') {					\
149*e0b8e63eSJohn Marino 			curoff = 0;					\
150*e0b8e63eSJohn Marino 			scno -= scno % sp->cols;			\
151*e0b8e63eSJohn Marino 		} else							\
152*e0b8e63eSJohn Marino 			curoff -= sp->cols;				\
153*e0b8e63eSJohn Marino }
154*e0b8e63eSJohn Marino 	if (cnop == NULL)
155*e0b8e63eSJohn Marino 		while (len--) {
156*e0b8e63eSJohn Marino 			chlen = CHLEN(curoff);
157*e0b8e63eSJohn Marino 			last = scno;
158*e0b8e63eSJohn Marino 			scno += chlen;
159*e0b8e63eSJohn Marino 			TAB_RESET;
160*e0b8e63eSJohn Marino 		}
161*e0b8e63eSJohn Marino 	else
162*e0b8e63eSJohn Marino 		for (cno = *cnop;; --cno) {
163*e0b8e63eSJohn Marino 			chlen = CHLEN(curoff);
164*e0b8e63eSJohn Marino 			last = scno;
165*e0b8e63eSJohn Marino 			scno += chlen;
166*e0b8e63eSJohn Marino 			TAB_RESET;
167*e0b8e63eSJohn Marino 			if (cno == 0)
168*e0b8e63eSJohn Marino 				break;
169*e0b8e63eSJohn Marino 		}
170*e0b8e63eSJohn Marino 
171*e0b8e63eSJohn Marino 	/* Add the trailing '$' if the O_LIST option set. */
172*e0b8e63eSJohn Marino 	if (listset && cnop == NULL)
173*e0b8e63eSJohn Marino 		scno += KEY_LEN(sp, '$');
174*e0b8e63eSJohn Marino 
175*e0b8e63eSJohn Marino 	/*
176*e0b8e63eSJohn Marino 	 * The text input screen code needs to know how much additional
177*e0b8e63eSJohn Marino 	 * room the last two characters required, so that it can handle
178*e0b8e63eSJohn Marino 	 * tab character displays correctly.
179*e0b8e63eSJohn Marino 	 */
180*e0b8e63eSJohn Marino 	if (diffp != NULL)
181*e0b8e63eSJohn Marino 		*diffp = scno - last;
182*e0b8e63eSJohn Marino 	return (scno);
183*e0b8e63eSJohn Marino }
184*e0b8e63eSJohn Marino 
185*e0b8e63eSJohn Marino /*
186*e0b8e63eSJohn Marino  * vs_rcm --
187*e0b8e63eSJohn Marino  *	Return the physical column from the line that will display a
188*e0b8e63eSJohn Marino  *	character closest to the currently most attractive character
189*e0b8e63eSJohn Marino  *	position (which is stored as a screen column).
190*e0b8e63eSJohn Marino  *
191*e0b8e63eSJohn Marino  * PUBLIC: size_t vs_rcm(SCR *, recno_t, int);
192*e0b8e63eSJohn Marino  */
193*e0b8e63eSJohn Marino size_t
vs_rcm(SCR * sp,recno_t lno,int islast)194*e0b8e63eSJohn Marino vs_rcm(SCR *sp, recno_t lno, int islast)
195*e0b8e63eSJohn Marino {
196*e0b8e63eSJohn Marino 	size_t len;
197*e0b8e63eSJohn Marino 
198*e0b8e63eSJohn Marino 	/* Last character is easy, and common. */
199*e0b8e63eSJohn Marino 	if (islast) {
200*e0b8e63eSJohn Marino 		if (db_get(sp, lno, 0, NULL, &len) || len == 0)
201*e0b8e63eSJohn Marino 			return (0);
202*e0b8e63eSJohn Marino 		return (len - 1);
203*e0b8e63eSJohn Marino 	}
204*e0b8e63eSJohn Marino 
205*e0b8e63eSJohn Marino 	/* First character is easy, and common. */
206*e0b8e63eSJohn Marino 	if (sp->rcm == 0)
207*e0b8e63eSJohn Marino 		return (0);
208*e0b8e63eSJohn Marino 
209*e0b8e63eSJohn Marino 	return (vs_colpos(sp, lno, sp->rcm));
210*e0b8e63eSJohn Marino }
211*e0b8e63eSJohn Marino 
212*e0b8e63eSJohn Marino /*
213*e0b8e63eSJohn Marino  * vs_colpos --
214*e0b8e63eSJohn Marino  *	Return the physical column from the line that will display a
215*e0b8e63eSJohn Marino  *	character closest to the specified screen column.
216*e0b8e63eSJohn Marino  *
217*e0b8e63eSJohn Marino  * PUBLIC: size_t vs_colpos(SCR *, recno_t, size_t);
218*e0b8e63eSJohn Marino  */
219*e0b8e63eSJohn Marino size_t
vs_colpos(SCR * sp,recno_t lno,size_t cno)220*e0b8e63eSJohn Marino vs_colpos(SCR *sp, recno_t lno, size_t cno)
221*e0b8e63eSJohn Marino {
222*e0b8e63eSJohn Marino 	size_t chlen, curoff, len, llen, off, scno;
223*e0b8e63eSJohn Marino 	int ch = 0, leftright, listset;
224*e0b8e63eSJohn Marino 	CHAR_T *lp, *p;
225*e0b8e63eSJohn Marino 
226*e0b8e63eSJohn Marino 	/* Need the line to go any further. */
227*e0b8e63eSJohn Marino 	(void)db_get(sp, lno, 0, &lp, &llen);
228*e0b8e63eSJohn Marino 
229*e0b8e63eSJohn Marino 	/* Missing or empty lines are easy. */
230*e0b8e63eSJohn Marino 	if (lp == NULL || llen == 0)
231*e0b8e63eSJohn Marino 		return (0);
232*e0b8e63eSJohn Marino 
233*e0b8e63eSJohn Marino 	/* Store away the values of the list and leftright edit options. */
234*e0b8e63eSJohn Marino 	listset = O_ISSET(sp, O_LIST);
235*e0b8e63eSJohn Marino 	leftright = O_ISSET(sp, O_LEFTRIGHT);
236*e0b8e63eSJohn Marino 
237*e0b8e63eSJohn Marino 	/* Discard screen (logical) lines. */
238*e0b8e63eSJohn Marino 	off = cno / sp->cols;
239*e0b8e63eSJohn Marino 	cno %= sp->cols;
240*e0b8e63eSJohn Marino 	for (scno = 0, p = lp, len = llen; off--;) {
241*e0b8e63eSJohn Marino 		for (; len && scno < sp->cols; --len)
242*e0b8e63eSJohn Marino 			scno += CHLEN(scno);
243*e0b8e63eSJohn Marino 
244*e0b8e63eSJohn Marino 		/*
245*e0b8e63eSJohn Marino 		 * If reached the end of the physical line, return the last
246*e0b8e63eSJohn Marino 		 * physical character in the line.
247*e0b8e63eSJohn Marino 		 */
248*e0b8e63eSJohn Marino 		if (len == 0)
249*e0b8e63eSJohn Marino 			return (llen - 1);
250*e0b8e63eSJohn Marino 
251*e0b8e63eSJohn Marino 		/*
252*e0b8e63eSJohn Marino 		 * If folding screens (the historic vi screen format), past
253*e0b8e63eSJohn Marino 		 * the end of the current screen, and the character was a tab,
254*e0b8e63eSJohn Marino 		 * reset the current screen column to 0.  Otherwise, the rest
255*e0b8e63eSJohn Marino 		 * of the character is displayed in the next screen.
256*e0b8e63eSJohn Marino 		 */
257*e0b8e63eSJohn Marino 		if (leftright && ch == '\t')
258*e0b8e63eSJohn Marino 			scno = 0;
259*e0b8e63eSJohn Marino 		else
260*e0b8e63eSJohn Marino 			scno -= sp->cols;
261*e0b8e63eSJohn Marino 	}
262*e0b8e63eSJohn Marino 
263*e0b8e63eSJohn Marino 	/* Step through the line until reach the right character or EOL. */
264*e0b8e63eSJohn Marino 	for (curoff = scno; len--;) {
265*e0b8e63eSJohn Marino 		chlen = CHLEN(curoff);
266*e0b8e63eSJohn Marino 
267*e0b8e63eSJohn Marino 		/*
268*e0b8e63eSJohn Marino 		 * If we've reached the specific character, there are three
269*e0b8e63eSJohn Marino 		 * cases.
270*e0b8e63eSJohn Marino 		 *
271*e0b8e63eSJohn Marino 		 * 1: scno == cno, i.e. the current character ends at the
272*e0b8e63eSJohn Marino 		 *    screen character we care about.
273*e0b8e63eSJohn Marino 		 *	a: off < llen - 1, i.e. not the last character in
274*e0b8e63eSJohn Marino 		 *	   the line, return the offset of the next character.
275*e0b8e63eSJohn Marino 		 *	b: else return the offset of the last character.
276*e0b8e63eSJohn Marino 		 * 2: scno != cno, i.e. this character overruns the character
277*e0b8e63eSJohn Marino 		 *    we care about, return the offset of this character.
278*e0b8e63eSJohn Marino 		 */
279*e0b8e63eSJohn Marino 		if ((scno += chlen) >= cno) {
280*e0b8e63eSJohn Marino 			off = p - lp;
281*e0b8e63eSJohn Marino 			return (scno == cno ?
282*e0b8e63eSJohn Marino 			    (off < llen - 1 ? off : llen - 1) : off - 1);
283*e0b8e63eSJohn Marino 		}
284*e0b8e63eSJohn Marino 
285*e0b8e63eSJohn Marino 		TAB_RESET;
286*e0b8e63eSJohn Marino 	}
287*e0b8e63eSJohn Marino 
288*e0b8e63eSJohn Marino 	/* No such character; return the start of the last character. */
289*e0b8e63eSJohn Marino 	return (llen - 1);
290*e0b8e63eSJohn Marino }
291