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