summaryrefslogtreecommitdiff
path: root/usr.bin/vi/svi/svi_relative.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/vi/svi/svi_relative.c')
-rw-r--r--usr.bin/vi/svi/svi_relative.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/usr.bin/vi/svi/svi_relative.c b/usr.bin/vi/svi/svi_relative.c
new file mode 100644
index 00000000000..31575b975f9
--- /dev/null
+++ b/usr.bin/vi/svi/svi_relative.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)svi_relative.c 8.18 (Berkeley) 8/17/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <bitstring.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "compat.h"
+#include <db.h>
+#include <regex.h>
+
+#include "vi.h"
+#include "svi_screen.h"
+
+static size_t svi_screens
+ __P((SCR *, EXF *, char *, size_t, recno_t, size_t *));
+
+/*
+ * svi_column --
+ * Return the logical column of the cursor.
+ */
+int
+svi_column(sp, ep, cp)
+ SCR *sp;
+ EXF *ep;
+ size_t *cp;
+{
+ size_t col;
+
+ col = SVP(sp)->sc_col;
+ if (O_ISSET(sp, O_NUMBER))
+ col -= O_NUMBER_LENGTH;
+ *cp = col;
+ return (0);
+}
+
+/*
+ * svi_opt_screens --
+ * Return the screen columns 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.
+ */
+size_t
+svi_opt_screens(sp, ep, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t cols, screens;
+
+ /*
+ * 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 (SVP(sp)->ss_lno == lno)
+ return (SVP(sp)->ss_screens);
+ } else if (*cnop == 0)
+ return (1);
+
+ /* Figure out how many columns the line/column needs. */
+ cols = svi_screens(sp, ep, NULL, 0, lno, cnop);
+
+ /* Leading number if O_NUMBER option set. */
+ if (O_ISSET(sp, O_NUMBER))
+ cols += O_NUMBER_LENGTH;
+
+ /* Trailing '$' if O_LIST option set. */
+ if (O_ISSET(sp, O_LIST) && cnop == NULL)
+ cols += KEY_LEN(sp, '$');
+
+ screens = (cols / sp->cols + (cols % sp->cols ? 1 : 0));
+ if (screens == 0)
+ screens = 1;
+
+ /* Cache the value. */
+ if (cnop == NULL) {
+ SVP(sp)->ss_lno = lno;
+ SVP(sp)->ss_screens = screens;
+ }
+ return (screens);
+}
+
+/*
+ * svi_screens --
+ * Return the screen columns necessary to display the line, or,
+ * if specified, the physical character column within the line.
+ */
+static size_t
+svi_screens(sp, ep, lp, llen, lno, cnop)
+ SCR *sp;
+ EXF *ep;
+ char *lp;
+ size_t llen;
+ recno_t lno;
+ size_t *cnop;
+{
+ size_t chlen, cno, len, scno, tab_off;
+ int ch, listset;
+ char *p;
+
+ /* Need the line to go any further. */
+ if (lp == NULL)
+ lp = file_gline(sp, ep, lno, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ listset = O_ISSET(sp, O_LIST);
+
+#define SET_CHLEN { \
+ chlen = (ch = *(u_char *)p++) == '\t' && \
+ !listset ? TAB_OFF(sp, tab_off) : KEY_LEN(sp, ch); \
+}
+#define TAB_RESET { \
+ /* \
+ * If past the end of the screen, and the character was a tab, \
+ * reset the screen column to 0. Otherwise, display the rest \
+ * of the character on the next line. \
+ */ \
+ if ((tab_off += chlen) >= sp->cols) \
+ if (ch == '\t') { \
+ tab_off = 0; \
+ scno -= scno % sp->cols; \
+ } else \
+ tab_off -= sp->cols; \
+}
+ p = lp;
+ len = llen;
+ scno = tab_off = 0;
+ if (cnop == NULL)
+ while (len--) {
+ SET_CHLEN;
+ scno += chlen;
+ TAB_RESET;
+ }
+ else
+ for (cno = *cnop; len--; --cno) {
+ SET_CHLEN;
+ scno += chlen;
+ TAB_RESET;
+ if (cno == 0)
+ break;
+ }
+ return (scno);
+}
+
+/*
+ * svi_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).
+ */
+size_t
+svi_rcm(sp, ep, lno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+{
+ size_t len;
+
+ /* Last character is easy, and common. */
+ if (sp->rcm_last)
+ return (file_gline(sp,
+ ep, lno, &len) == NULL || len == 0 ? 0 : len - 1);
+
+ /* First character is easy, and common. */
+ if (HMAP->off == 1 && sp->rcm == 0)
+ return (0);
+
+ /*
+ * Get svi_cm_private() to do the hard work. If doing leftright
+ * scrolling, we use the current screen offset, otherwise, use
+ * the first screen, i.e. an offset of 1.
+ *
+ * XXX
+ * I'm not sure that an offset of 1 is right. What happens is that
+ * the vi main loop calls us for the VM_RCM case. By using an offset
+ * of 1, we're assuming that every VM_RCM command changes lines, and
+ * that we want to position on the first screen for that line. This
+ * is currently the way it works, but it's not clean. I'd prefer it if
+ * we could find the SMAP entry the cursor references, and use that
+ * screen offset. Unfortunately, that's not going to be easy, as we
+ * don't keep that information around and it may be expensive to get.
+ */
+ return (svi_cm_private(sp, ep, lno,
+ O_ISSET(sp, O_LEFTRIGHT) ? HMAP->off : 1, sp->rcm));
+}
+
+/*
+ * svi_cm_public --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column.
+ *
+ * The extra interface is because it's called by vi, which doesn't
+ * have a handle on the SMAP structure.
+ */
+size_t
+svi_cm_public(sp, ep, lno, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t cno;
+{
+ return (svi_cm_private(sp, ep, lno, HMAP->off, cno));
+}
+
+/*
+ * svi_cm_private --
+ * Return the physical column from the line that will display a
+ * character closest to the specified screen column, taking into
+ * account the screen offset.
+ *
+ * The offset is for the commands that move logical distances, i.e.
+ * if it's a logical scroll the closest physical distance is based
+ * on the logical line, not the physical line.
+ */
+size_t
+svi_cm_private(sp, ep, lno, off, cno)
+ SCR *sp;
+ EXF *ep;
+ recno_t lno;
+ size_t off, cno;
+{
+ size_t chlen, len, llen, scno, tab_off;
+ int ch, listset;
+ char *lp, *p;
+
+ /* Need the line to go any further. */
+ lp = file_gline(sp, ep, lno, &llen);
+
+ /* Missing or empty lines are easy. */
+ if (lp == NULL || llen == 0)
+ return (0);
+
+ listset = O_ISSET(sp, O_LIST);
+
+ /* Discard screen (logical) lines. */
+ for (scno = 0, p = lp, len = llen; --off;) {
+ for (; len && scno < sp->cols; --len)
+ scno += (ch = *(u_char *)p++) == '\t' &&
+ !listset ? TAB_OFF(sp, scno) : KEY_LEN(sp, ch);
+
+ /*
+ * If reached the end of the physical line, return
+ * the last physical character in the line.
+ */
+ if (len == 0)
+ return (llen - 1);
+
+ /*
+ * If the character was a tab, reset the screen column to 0.
+ * Otherwise, the rest of the character is displayed on the
+ * next line.
+ */
+ if (ch == '\t')
+ scno = 0;
+ else
+ scno -= sp->cols;
+ }
+
+ /* Step through the line until reach the right character or EOL. */
+ for (tab_off = scno; len--;) {
+ SET_CHLEN;
+
+ /*
+ * 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);
+}