diff options
Diffstat (limited to 'contrib/nvi/vi/vs_relative.c')
-rw-r--r-- | contrib/nvi/vi/vs_relative.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/contrib/nvi/vi/vs_relative.c b/contrib/nvi/vi/vs_relative.c new file mode 100644 index 0000000..c92c10c --- /dev/null +++ b/contrib/nvi/vi/vs_relative.c @@ -0,0 +1,305 @@ +/*- + * Copyright (c) 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1993, 1994, 1995, 1996 + * Keith Bostic. All rights reserved. + * + * See the LICENSE file for redistribution information. + */ + +#include "config.h" + +#ifndef lint +static const char sccsid[] = "@(#)vs_relative.c 10.11 (Berkeley) 5/13/96"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/time.h> + +#include <bitstring.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include "../common/common.h" +#include "vi.h" + +/* + * vs_column -- + * Return the logical column of the cursor in the line. + * + * PUBLIC: int vs_column __P((SCR *, size_t *)); + */ +int +vs_column(sp, colp) + SCR *sp; + size_t *colp; +{ + VI_PRIVATE *vip; + + vip = VIP(sp); + + *colp = (O_ISSET(sp, O_LEFTRIGHT) ? + vip->sc_smap->coff : (vip->sc_smap->soff - 1) * sp->cols) + + vip->sc_col - (O_ISSET(sp, O_NUMBER) ? O_NUMBER_LENGTH : 0); + return (0); +} + +/* + * vs_screens -- + * Return the screens necessary to display the line, or if specified, + * the physical character column within the line, including space + * required for the O_NUMBER and O_LIST options. + * + * PUBLIC: size_t vs_screens __P((SCR *, recno_t, size_t *)); + */ +size_t +vs_screens(sp, lno, cnop) + SCR *sp; + recno_t lno; + size_t *cnop; +{ + size_t cols, screens; + + /* Left-right screens are simple, it's always 1. */ + if (O_ISSET(sp, O_LEFTRIGHT)) + return (1); + + /* + * Check for a cached value. We maintain a cache because, if the + * line is large, this routine gets called repeatedly. One other + * hack, lots of time the cursor is on column one, which is an easy + * one. + */ + if (cnop == NULL) { + if (VIP(sp)->ss_lno == lno) + return (VIP(sp)->ss_screens); + } else if (*cnop == 0) + return (1); + + /* Figure out how many columns the line/column needs. */ + cols = vs_columns(sp, NULL, lno, cnop, NULL); + + screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0)); + if (screens == 0) + screens = 1; + + /* Cache the value. */ + if (cnop == NULL) { + VIP(sp)->ss_lno = lno; + VIP(sp)->ss_screens = screens; + } + return (screens); +} + +/* + * vs_columns -- + * Return the screen columns necessary to display the line, or, + * if specified, the physical character column within the line. + * + * PUBLIC: size_t vs_columns __P((SCR *, char *, recno_t, size_t *, size_t *)); + */ +size_t +vs_columns(sp, lp, lno, cnop, diffp) + SCR *sp; + char *lp; + recno_t lno; + size_t *cnop, *diffp; +{ + size_t chlen, cno, curoff, last, len, scno; + int ch, leftright, listset; + char *p; + + /* Need the line to go any further. */ + if (lp == NULL) { + (void)db_get(sp, lno, 0, &lp, &len); + if (len == 0) + goto done; + } + + /* Missing or empty lines are easy. */ + if (lp == NULL) { +done: if (diffp != NULL) /* XXX */ + *diffp = 0; + return (0); + } + + /* Store away the values of the list and leftright edit options. */ + listset = O_ISSET(sp, O_LIST); + leftright = O_ISSET(sp, O_LEFTRIGHT); + + /* + * Initialize the pointer into the buffer and screen and current + * offsets. + */ + p = lp; + curoff = scno = 0; + + /* Leading number if O_NUMBER option set. */ + if (O_ISSET(sp, O_NUMBER)) + scno += O_NUMBER_LENGTH; + + /* Macro to return the display length of any signal character. */ +#define CHLEN(val) (ch = *(u_char *)p++) == '\t' && \ + !listset ? TAB_OFF(val) : KEY_LEN(sp, ch); + + /* + * If folding screens (the historic vi screen format), past the end + * of the current screen, and the character was a tab, reset the + * current screen column to 0, and the total screen columns to the + * last column of the screen. Otherwise, display the rest of the + * character in the next screen. + */ +#define TAB_RESET { \ + curoff += chlen; \ + if (!leftright && curoff >= sp->cols) \ + if (ch == '\t') { \ + curoff = 0; \ + scno -= scno % sp->cols; \ + } else \ + curoff -= sp->cols; \ +} + if (cnop == NULL) + while (len--) { + chlen = CHLEN(curoff); + last = scno; + scno += chlen; + TAB_RESET; + } + else + for (cno = *cnop;; --cno) { + chlen = CHLEN(curoff); + last = scno; + scno += chlen; + TAB_RESET; + if (cno == 0) + break; + } + + /* Add the trailing '$' if the O_LIST option set. */ + if (listset && cnop == NULL) + scno += KEY_LEN(sp, '$'); + + /* + * The text input screen code needs to know how much additional + * room the last two characters required, so that it can handle + * tab character displays correctly. + */ + if (diffp != NULL) + *diffp = scno - last; + return (scno); +} + +/* + * vs_rcm -- + * Return the physical column from the line that will display a + * character closest to the currently most attractive character + * position (which is stored as a screen column). + * + * PUBLIC: size_t vs_rcm __P((SCR *, recno_t, int)); + */ +size_t +vs_rcm(sp, lno, islast) + SCR *sp; + recno_t lno; + int islast; +{ + size_t len; + + /* Last character is easy, and common. */ + if (islast) { + if (db_get(sp, lno, 0, NULL, &len) || len == 0) + return (0); + return (len - 1); + } + + /* First character is easy, and common. */ + if (sp->rcm == 0) + return (0); + + return (vs_colpos(sp, lno, sp->rcm)); +} + +/* + * vs_colpos -- + * Return the physical column from the line that will display a + * character closest to the specified screen column. + * + * PUBLIC: size_t vs_colpos __P((SCR *, recno_t, size_t)); + */ +size_t +vs_colpos(sp, lno, cno) + SCR *sp; + recno_t lno; + size_t cno; +{ + size_t chlen, curoff, len, llen, off, scno; + int ch, leftright, listset; + char *lp, *p; + + /* Need the line to go any further. */ + (void)db_get(sp, lno, 0, &lp, &llen); + + /* Missing or empty lines are easy. */ + if (lp == NULL || llen == 0) + return (0); + + /* Store away the values of the list and leftright edit options. */ + listset = O_ISSET(sp, O_LIST); + leftright = O_ISSET(sp, O_LEFTRIGHT); + + /* Discard screen (logical) lines. */ + off = cno / sp->cols; + cno %= sp->cols; + for (scno = 0, p = lp, len = llen; off--;) { + for (; len && scno < sp->cols; --len) + scno += CHLEN(scno); + + /* + * If reached the end of the physical line, return the last + * physical character in the line. + */ + if (len == 0) + return (llen - 1); + + /* + * If folding screens (the historic vi screen format), past + * the end of the current screen, and the character was a tab, + * reset the current screen column to 0. Otherwise, the rest + * of the character is displayed in the next screen. + */ + if (leftright && ch == '\t') + scno = 0; + else + scno -= sp->cols; + } + + /* Step through the line until reach the right character or EOL. */ + for (curoff = scno; len--;) { + chlen = CHLEN(curoff); + + /* + * If we've reached the specific character, there are three + * cases. + * + * 1: scno == cno, i.e. the current character ends at the + * screen character we care about. + * a: off < llen - 1, i.e. not the last character in + * the line, return the offset of the next character. + * b: else return the offset of the last character. + * 2: scno != cno, i.e. this character overruns the character + * we care about, return the offset of this character. + */ + if ((scno += chlen) >= cno) { + off = p - lp; + return (scno == cno ? + (off < llen - 1 ? off : llen - 1) : off - 1); + } + + TAB_RESET; + } + + /* No such character; return the start of the last character. */ + return (llen - 1); +} |