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