119304Speter/*- 219304Speter * Copyright (c) 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: vs_relative.c,v 10.19 2011/12/01 15:22:59 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 1819304Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <limits.h> 2219304Speter#include <stdio.h> 2319304Speter#include <string.h> 2419304Speter 2519304Speter#include "../common/common.h" 2619304Speter#include "vi.h" 2719304Speter 2819304Speter/* 2919304Speter * vs_column -- 3019304Speter * Return the logical column of the cursor in the line. 3119304Speter * 3219304Speter * PUBLIC: int vs_column __P((SCR *, size_t *)); 3319304Speter */ 3419304Speterint 35254225Spetervs_column(SCR *sp, size_t *colp) 3619304Speter{ 3719304Speter VI_PRIVATE *vip; 3819304Speter 3919304Speter vip = VIP(sp); 4019304Speter 4119304Speter *colp = (O_ISSET(sp, O_LEFTRIGHT) ? 4219304Speter vip->sc_smap->coff : (vip->sc_smap->soff - 1) * sp->cols) + 4319304Speter vip->sc_col - (O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0); 4419304Speter return (0); 4519304Speter} 4619304Speter 4719304Speter/* 4819304Speter * vs_screens -- 4919304Speter * Return the screens necessary to display the line, or if specified, 5019304Speter * the physical character column within the line, including space 5119304Speter * required for the O_NUMBER and O_LIST options. 5219304Speter * 5319304Speter * PUBLIC: size_t vs_screens __P((SCR *, recno_t, size_t *)); 5419304Speter */ 5519304Spetersize_t 56254225Spetervs_screens(SCR *sp, recno_t lno, size_t *cnop) 5719304Speter{ 5819304Speter size_t cols, screens; 5919304Speter 6019304Speter /* Left-right screens are simple, it's always 1. */ 6119304Speter if (O_ISSET(sp, O_LEFTRIGHT)) 6219304Speter return (1); 6319304Speter 6419304Speter /* 6519304Speter * Check for a cached value. We maintain a cache because, if the 6619304Speter * line is large, this routine gets called repeatedly. One other 6719304Speter * hack, lots of time the cursor is on column one, which is an easy 6819304Speter * one. 6919304Speter */ 7019304Speter if (cnop == NULL) { 7119304Speter if (VIP(sp)->ss_lno == lno) 7219304Speter return (VIP(sp)->ss_screens); 7319304Speter } else if (*cnop == 0) 7419304Speter return (1); 7519304Speter 7619304Speter /* Figure out how many columns the line/column needs. */ 7719304Speter cols = vs_columns(sp, NULL, lno, cnop, NULL); 7819304Speter 7919304Speter screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0)); 8019304Speter if (screens == 0) 8119304Speter screens = 1; 8219304Speter 8319304Speter /* Cache the value. */ 8419304Speter if (cnop == NULL) { 8519304Speter VIP(sp)->ss_lno = lno; 8619304Speter VIP(sp)->ss_screens = screens; 8719304Speter } 8819304Speter return (screens); 8919304Speter} 9019304Speter 9119304Speter/* 9219304Speter * vs_columns -- 9319304Speter * Return the screen columns necessary to display the line, or, 9419304Speter * if specified, the physical character column within the line. 9519304Speter * 96254225Speter * PUBLIC: size_t vs_columns __P((SCR *, CHAR_T *, recno_t, size_t *, size_t *)); 9719304Speter */ 9819304Spetersize_t 99254225Spetervs_columns(SCR *sp, CHAR_T *lp, recno_t lno, size_t *cnop, size_t *diffp) 10019304Speter{ 101254225Speter size_t chlen, cno, curoff, last = 0, len, scno; 10219304Speter int ch, leftright, listset; 103254225Speter CHAR_T *p; 10419304Speter 10589010Ssheldonh /* 10689010Ssheldonh * Initialize the screen offset. 10789010Ssheldonh */ 10889010Ssheldonh scno = 0; 10989010Ssheldonh 11089010Ssheldonh /* Leading number if O_NUMBER option set. */ 11189010Ssheldonh if (O_ISSET(sp, O_NUMBER)) 11289010Ssheldonh scno += O_NUMBER_LENGTH; 11389010Ssheldonh 11419304Speter /* Need the line to go any further. */ 11519304Speter if (lp == NULL) { 11619304Speter (void)db_get(sp, lno, 0, &lp, &len); 11719304Speter if (len == 0) 11819304Speter goto done; 11919304Speter } 12019304Speter 12119304Speter /* Missing or empty lines are easy. */ 12219304Speter if (lp == NULL) { 12319304Speterdone: if (diffp != NULL) /* XXX */ 12419304Speter *diffp = 0; 12589010Ssheldonh return scno; 12619304Speter } 12719304Speter 12819304Speter /* Store away the values of the list and leftright edit options. */ 12919304Speter listset = O_ISSET(sp, O_LIST); 13019304Speter leftright = O_ISSET(sp, O_LEFTRIGHT); 13119304Speter 13219304Speter /* 13389010Ssheldonh * Initialize the pointer into the buffer and current offset. 13419304Speter */ 13519304Speter p = lp; 136254225Speter curoff = scno; 13719304Speter 13819304Speter /* Macro to return the display length of any signal character. */ 139254225Speter#define CHLEN(val) (ch = *(UCHAR_T *)p++) == '\t' && \ 140254225Speter !listset ? TAB_OFF(val) : KEY_COL(sp, ch); 14119304Speter 14219304Speter /* 14319304Speter * If folding screens (the historic vi screen format), past the end 14419304Speter * of the current screen, and the character was a tab, reset the 14519304Speter * current screen column to 0, and the total screen columns to the 14619304Speter * last column of the screen. Otherwise, display the rest of the 14719304Speter * character in the next screen. 14819304Speter */ 14919304Speter#define TAB_RESET { \ 15019304Speter curoff += chlen; \ 15119304Speter if (!leftright && curoff >= sp->cols) \ 15219304Speter if (ch == '\t') { \ 15319304Speter curoff = 0; \ 15419304Speter scno -= scno % sp->cols; \ 15519304Speter } else \ 15619304Speter curoff -= sp->cols; \ 15719304Speter} 15819304Speter if (cnop == NULL) 15919304Speter while (len--) { 16019304Speter chlen = CHLEN(curoff); 16119304Speter last = scno; 16219304Speter scno += chlen; 16319304Speter TAB_RESET; 16419304Speter } 16519304Speter else 16619304Speter for (cno = *cnop;; --cno) { 16719304Speter chlen = CHLEN(curoff); 16819304Speter last = scno; 16919304Speter scno += chlen; 17019304Speter TAB_RESET; 17119304Speter if (cno == 0) 17219304Speter break; 17319304Speter } 17419304Speter 17519304Speter /* Add the trailing '$' if the O_LIST option set. */ 17619304Speter if (listset && cnop == NULL) 17719304Speter scno += KEY_LEN(sp, '$'); 17819304Speter 17919304Speter /* 18019304Speter * The text input screen code needs to know how much additional 18119304Speter * room the last two characters required, so that it can handle 18219304Speter * tab character displays correctly. 18319304Speter */ 18419304Speter if (diffp != NULL) 18519304Speter *diffp = scno - last; 18619304Speter return (scno); 18719304Speter} 18819304Speter 18919304Speter/* 19019304Speter * vs_rcm -- 19119304Speter * Return the physical column from the line that will display a 19219304Speter * character closest to the currently most attractive character 19319304Speter * position (which is stored as a screen column). 19419304Speter * 19519304Speter * PUBLIC: size_t vs_rcm __P((SCR *, recno_t, int)); 19619304Speter */ 19719304Spetersize_t 198254225Spetervs_rcm(SCR *sp, recno_t lno, int islast) 19919304Speter{ 20019304Speter size_t len; 20119304Speter 20219304Speter /* Last character is easy, and common. */ 20319304Speter if (islast) { 20419304Speter if (db_get(sp, lno, 0, NULL, &len) || len == 0) 20519304Speter return (0); 20619304Speter return (len - 1); 20719304Speter } 20819304Speter 20919304Speter /* First character is easy, and common. */ 21019304Speter if (sp->rcm == 0) 21119304Speter return (0); 21219304Speter 21319304Speter return (vs_colpos(sp, lno, sp->rcm)); 21419304Speter} 21519304Speter 21619304Speter/* 21719304Speter * vs_colpos -- 21819304Speter * Return the physical column from the line that will display a 21919304Speter * character closest to the specified screen column. 22019304Speter * 22119304Speter * PUBLIC: size_t vs_colpos __P((SCR *, recno_t, size_t)); 22219304Speter */ 22319304Spetersize_t 224254225Spetervs_colpos(SCR *sp, recno_t lno, size_t cno) 22519304Speter{ 22619304Speter size_t chlen, curoff, len, llen, off, scno; 227254225Speter int ch = 0, leftright, listset; 228254225Speter CHAR_T *lp, *p; 22919304Speter 23019304Speter /* Need the line to go any further. */ 23119304Speter (void)db_get(sp, lno, 0, &lp, &llen); 23219304Speter 23319304Speter /* Missing or empty lines are easy. */ 23419304Speter if (lp == NULL || llen == 0) 23519304Speter return (0); 23619304Speter 23719304Speter /* Store away the values of the list and leftright edit options. */ 23819304Speter listset = O_ISSET(sp, O_LIST); 23919304Speter leftright = O_ISSET(sp, O_LEFTRIGHT); 24019304Speter 24119304Speter /* Discard screen (logical) lines. */ 24219304Speter off = cno / sp->cols; 24319304Speter cno %= sp->cols; 24419304Speter for (scno = 0, p = lp, len = llen; off--;) { 24519304Speter for (; len && scno < sp->cols; --len) 24619304Speter scno += CHLEN(scno); 24719304Speter 24819304Speter /* 24919304Speter * If reached the end of the physical line, return the last 25019304Speter * physical character in the line. 25119304Speter */ 25219304Speter if (len == 0) 25319304Speter return (llen - 1); 25419304Speter 25519304Speter /* 25619304Speter * If folding screens (the historic vi screen format), past 25719304Speter * the end of the current screen, and the character was a tab, 25819304Speter * reset the current screen column to 0. Otherwise, the rest 25919304Speter * of the character is displayed in the next screen. 26019304Speter */ 26119304Speter if (leftright && ch == '\t') 26219304Speter scno = 0; 26319304Speter else 26419304Speter scno -= sp->cols; 26519304Speter } 26619304Speter 26719304Speter /* Step through the line until reach the right character or EOL. */ 26819304Speter for (curoff = scno; len--;) { 26919304Speter chlen = CHLEN(curoff); 27019304Speter 27119304Speter /* 27219304Speter * If we've reached the specific character, there are three 27319304Speter * cases. 27419304Speter * 27519304Speter * 1: scno == cno, i.e. the current character ends at the 27619304Speter * screen character we care about. 27719304Speter * a: off < llen - 1, i.e. not the last character in 27819304Speter * the line, return the offset of the next character. 27919304Speter * b: else return the offset of the last character. 28019304Speter * 2: scno != cno, i.e. this character overruns the character 28119304Speter * we care about, return the offset of this character. 28219304Speter */ 28319304Speter if ((scno += chlen) >= cno) { 28419304Speter off = p - lp; 28519304Speter return (scno == cno ? 28619304Speter (off < llen - 1 ? off : llen - 1) : off - 1); 28719304Speter } 28819304Speter 28919304Speter TAB_RESET; 29019304Speter } 29119304Speter 29219304Speter /* No such character; return the start of the last character. */ 29319304Speter return (llen - 1); 29419304Speter} 295