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