1 /* $NetBSD: vs_relative.c,v 1.5 2018/06/03 08:08:37 rin 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.5 2018/06/03 08:08:37 rin 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 > 0) { 167 ch = (UCHAR_T)*p; 168 169 /* singlebyte case */ 170 if (!INTISWIDE(ch)) { 171 chlen = CHLEN(curoff); 172 last = scno; 173 scno += chlen; 174 len--; 175 /* p will be modified in CHLEN() */ 176 TAB_RESET; 177 continue; 178 } 179 180 /* multibyte case */ 181 chlen = WIDE_COL(sp, ch); 182 last = scno; 183 scno += chlen; 184 len--; 185 p++; 186 187 /* 188 * If multi-width char crosses the end-of-screen, 189 * put it on the next line. 190 */ 191 curoff += chlen; 192 if (!leftright && curoff >= sp->cols) { 193 if (curoff == sp->cols) 194 curoff = 0; 195 else { 196 scno -= scno % sp->cols; 197 scno += chlen; 198 curoff = chlen; 199 } 200 } 201 } 202 } else { 203 for (cno = *cnop;; --cno) { 204 ch = (UCHAR_T)*p; 205 206 /* singlebyte case */ 207 if (!INTISWIDE(ch)) { 208 chlen = CHLEN(curoff); 209 last = scno; 210 scno += chlen; 211 /* p will be modified in CHLEN() */ 212 TAB_RESET; 213 if (cno == 0) 214 break; 215 continue; 216 } 217 218 /* multibyte case */ 219 chlen = WIDE_COL(sp, ch); 220 last = scno; 221 scno += chlen; 222 p++; 223 224 /* 225 * If multi-width char crosses the end-of-screen, 226 * put it on the next line. 227 */ 228 curoff += chlen; 229 if (!leftright && curoff >= sp->cols) { 230 if (curoff == sp->cols) 231 curoff = 0; 232 else { 233 scno -= scno % sp->cols; 234 scno += chlen; 235 curoff = chlen; 236 } 237 } 238 239 if (cno == 0) 240 break; 241 } 242 } 243 244 /* Add the trailing '$' if the O_LIST option set. */ 245 if (listset && cnop == NULL) 246 scno += KEY_LEN(sp, '$'); 247 248 /* 249 * The text input screen code needs to know how much additional 250 * room the last two characters required, so that it can handle 251 * tab character displays correctly. 252 */ 253 if (diffp != NULL) 254 *diffp = scno - last; 255 return (scno); 256 } 257 258 /* 259 * vs_rcm -- 260 * Return the physical column from the line that will display a 261 * character closest to the currently most attractive character 262 * position (which is stored as a screen column). 263 * 264 * PUBLIC: size_t vs_rcm __P((SCR *, db_recno_t, int)); 265 */ 266 size_t 267 vs_rcm(SCR *sp, db_recno_t lno, int islast) 268 { 269 size_t len; 270 271 /* Last character is easy, and common. */ 272 if (islast) { 273 if (db_get(sp, lno, 0, NULL, &len) || len == 0) 274 return (0); 275 return (len - 1); 276 } 277 278 /* First character is easy, and common. */ 279 if (sp->rcm == 0) 280 return (0); 281 282 return (vs_colpos(sp, lno, sp->rcm)); 283 } 284 285 /* 286 * vs_colpos -- 287 * Return the physical column from the line that will display a 288 * character closest to the specified screen column. 289 * 290 * PUBLIC: size_t vs_colpos __P((SCR *, db_recno_t, size_t)); 291 */ 292 size_t 293 vs_colpos(SCR *sp, db_recno_t lno, size_t cno) 294 { 295 size_t chlen, curoff, len, llen, off, scno; 296 int ch = 0, leftright, listset; 297 CHAR_T *lp, *p; 298 299 /* Need the line to go any further. */ 300 (void)db_get(sp, lno, 0, &lp, &llen); 301 302 /* Missing or empty lines are easy. */ 303 if (lp == NULL || llen == 0) 304 return (0); 305 306 /* Store away the values of the list and leftright edit options. */ 307 listset = O_ISSET(sp, O_LIST); 308 leftright = O_ISSET(sp, O_LEFTRIGHT); 309 310 /* Discard screen (logical) lines. */ 311 off = cno / sp->cols; 312 cno %= sp->cols; 313 for (scno = 0, p = lp, len = llen; off--;) { 314 while (len && scno < sp->cols) { 315 ch = (UCHAR_T)*p; 316 if (ch == '\t' && !listset) { 317 scno += TAB_OFF(scno); 318 len--; 319 p++; 320 continue; 321 } 322 323 chlen = KEY_COL(sp, ch); 324 if (!INTISWIDE(ch) || scno + chlen < sp->cols) { 325 /* 326 * Singlebyte char can be displayed across 327 * the end-of-screen. 328 * If a multi-width char fits into this line, 329 * put it here. 330 */ 331 scno += chlen; 332 len--; 333 p++; 334 } else if (leftright) { 335 /* 336 * Side-scrolling screen is similar to 337 * singlebyte case. 338 */ 339 scno += chlen; 340 len--; 341 p++; 342 } else { 343 /* 344 * If multi-width char crosses the 345 * end-of-screen, put it on the next line. 346 * 347 * We must adjust ch to the last char of the 348 * line. 349 */ 350 scno = sp->cols; 351 } 352 } 353 354 /* 355 * If reached the end of the physical line, return the last 356 * physical character in the line. 357 */ 358 if (len == 0) 359 return (llen - 1); 360 361 /* 362 * If folding screens (the historic vi screen format), past 363 * the end of the current screen, and the character was a tab, 364 * reset the current screen column to 0. Otherwise, the rest 365 * of the character is displayed in the next screen. 366 */ 367 if (leftright && ch == '\t') 368 scno = 0; 369 else 370 scno -= sp->cols; 371 } 372 373 /* Step through the line until reach the right character or EOL. */ 374 for (curoff = scno; len--;) { 375 chlen = CHLEN(curoff); 376 377 /* 378 * If we've reached the specific character, there are three 379 * cases. 380 * 381 * 1: scno == cno, i.e. the current character ends at the 382 * screen character we care about. 383 * a: off < llen - 1, i.e. not the last character in 384 * the line, return the offset of the next character. 385 * b: else return the offset of the last character. 386 * 2: scno != cno, i.e. this character overruns the character 387 * we care about, return the offset of this character. 388 */ 389 if ((scno += chlen) >= cno) { 390 off = p - lp; 391 return (scno == cno ? 392 (off < llen - 1 ? off : llen - 1) : off - 1); 393 } 394 395 TAB_RESET; 396 } 397 398 /* No such character; return the start of the last character. */ 399 return (llen - 1); 400 } 401