Logo Search packages:      
Sourcecode: vile version File versions

basic.c

/*
 * The routines in this file move the cursor around on the screen. They
 * compute a new value for the cursor, then adjust ".". The display code
 * always updates the cursor location, so only moves between lines, or
 * functions that adjust the top line in the window and invalidate the
 * framing, are hard.
 *
 * $Header: /usr/build/vile/vile/RCS/basic.c,v 1.119 2003/07/27 19:01:43 tom Exp $
 *
 */

#include    "estruct.h"
#include    "edef.h"

#define     RegexpLen(exp) ((exp->mlen) ? (int)(exp->mlen) : 1)

/* utility routine for 'forwpage()' and 'backpage()' */
static int
full_pages(int f, int n)
{
    if (f == FALSE) {
      n = curwp->w_ntrows - 2;      /* Default scroll.      */
      if (n <= 0)       /* Don't blow up if the */
          n = 1;        /* window is tiny.  */
    }
#if OPT_CVMVAS
    else if (n > 0)           /* Convert from pages */
      n *= curwp->w_ntrows;   /* to lines.  */
#endif
    return n;
}

/* utility routine for 'forwhpage()' and 'backhpage()' */
static int
half_pages(int f, int n)
{
    if (f == FALSE) {
      n = curwp->w_ntrows / 2;      /* Default scroll.  */
      if (n <= 0)       /* Forget the overlap */
          n = 1;        /* if tiny window.  */
    }
#if OPT_CVMVAS
    else if (n > 0)           /* Convert from pages */
      n *= curwp->w_ntrows / 2;     /* to lines.  */
#endif
    return n;
}

static void
skipblanksf(void)
{
    while (lforw(DOT.l) != buf_head(curbp) && is_empty_line(DOT))
      DOT.l = lforw(DOT.l);
}

static void
skipblanksb(void)
{
    while (lback(DOT.l) != buf_head(curbp) && is_empty_line(DOT))
      DOT.l = lback(DOT.l);
}

/*
 * Implements the vi "0" command.
 *
 * Move the cursor to the beginning of the current line.
 */
/* ARGSUSED */
int
gotobol(int f GCC_UNUSED, int n GCC_UNUSED)
{
    DOT.o = w_left_margin(curwp);
    return mvleftwind(TRUE, w_val(curwp, WVAL_SIDEWAYS));
}

/*
 * Move the cursor backwards by "n" characters. If "n" is less than zero call
 * "forwchar" to actually do the move. Otherwise compute the new cursor
 * location. Error if you try and move out of the buffer. Set the flag if the
 * line pointer for dot changes.
 */
int
backchar(int f, int n)
{
    LINE *lp;

    if (f == FALSE)
      n = 1;
    if (n < 0)
      return (forwchar(f, -n));
    while (n--) {
      if (DOT.o == w_left_margin(curwp)) {
          if ((lp = lback(DOT.l)) == buf_head(curbp))
            return (FALSE);
          DOT.l = lp;
          DOT.o = llength(lp);
          curwp->w_flag |= WFMOVE;
      } else
          DOT.o--;
    }
    return (TRUE);
}

/*
 * Implements the vi "h" command.
 *
 * Move the cursor backwards by "n" characters. Stop at beginning of line.
 */
int
backchar_to_bol(int f, int n)
{

    if (f == FALSE)
      n = 1;
    if (n < 0)
      return forwchar_to_eol(f, -n);
    while (n--) {
      if (DOT.o == w_left_margin(curwp))
          return doingopcmd;
      else
          DOT.o--;
    }
    return TRUE;
}

/*
 * Implements the vi "$" command.
 *
 * Move the cursor to the end of the current line.  Trivial.
 */
int
gotoeol(int f, int n)
{
    if (f == TRUE) {
      if (n > 0)
          --n;
      else if (n < 0)
          ++n;
      if (forwline(f, n) != TRUE)
          return FALSE;
    }
    DOT.o = llength(DOT.l);
    curgoal = VL_HUGE;
    return (TRUE);
}

/*
 * Move the cursor forwards by "n" characters. If "n" is less than zero call
 * "backchar" to actually do the move. Otherwise compute the new cursor
 * location, and move ".". Error if you try and move off the end of the
 * buffer. Set the flag if the line pointer for dot changes.
 */
int
forwchar(int f, int n)
{
    if (f == FALSE)
      n = 1;
    if (n < 0)
      return (backchar(f, -n));
    while (n--) {
      /* if an explicit arg was given, allow us to land
         on the newline, else skip it */
      if (is_at_end_of_line(DOT) ||
          (f == FALSE && !insertmode &&
           llength(DOT.l) && DOT.o == llength(DOT.l) - 1)
          ) {
          if (is_header_line(DOT, curbp) ||
            is_last_line(DOT, curbp))
            return (FALSE);
          DOT.l = lforw(DOT.l);
          DOT.o = w_left_margin(curwp);
          curwp->w_flag |= WFMOVE;
      } else
          DOT.o++;
    }
    return (TRUE);
}

/*
 * Implements the vi "l" command.
 *
 * Move the cursor forwards by "n" characters. Don't go past end-of-line
 *
 * If the flag 'doingopcmd' is set, implements a vi "l"-like motion for
 * internal use.  The end-of-line test is off-by-one from the true "l" command
 * to allow for substitutions at the end of a line.
 */
int
forwchar_to_eol(int f, int n)
{
    int rc = TRUE;
    int nwas = n;
    int lim;

    if (f == FALSE)
      n = 1;
    if (n < 0) {
      rc = backchar_to_bol(f, -n);
    } else if (n != 0) {

      /* normally, we're confined to the text on the line itself.  if
         we're doing an opcmd, then we're allowed to move to the newline
         as well, to take care of the internal cases:  's', 'x', and '~'. */
      if (doingopcmd || insertmode)
          lim = llength(DOT.l);
      else
          lim = llength(DOT.l) - 1;
      do {
          if (DOT.o >= lim) {
            rc = (n != nwas); /* return ok if we moved at all */
            break;
          } else {
            DOT.o++;
          }
      } while (--n != 0);
    }
    return rc;
}

/*
 * Implements the vi "G" command.
 *
 * Move to a particular line (the argument).  Count from bottom of file if
 * argument is negative.
 */
int
gotoline(int f, int n)
{
    int status;               /* status return */

    MARK odot;

    if (f == FALSE) {
      return (gotoeob(f, n));
    }

    if (n == 0)               /* if a bogus argument...then leave */
      return (FALSE);

    odot = DOT;

    DOT.o = w_left_margin(curwp);
    if (n < 0) {
      DOT.l = lback(buf_head(curbp));
      status = backline(f, -n - 1);
    } else {
      DOT.l = lforw(buf_head(curbp));
      status = forwline(f, n - 1);
    }
    if (status != TRUE) {
      DOT = odot;
      return status;
    }
    (void) firstnonwhite(FALSE, 1);
    curwp->w_flag |= WFMOVE;
    return TRUE;
}

/*
 * Goto the beginning of the buffer. Massive adjustment of dot. This is
 * considered to be hard motion; it really isn't if the original value of dot
 * is the same as the new value of dot.
 */
/* ARGSUSED */
int
gotobob(int f GCC_UNUSED, int n GCC_UNUSED)
{
    DOT.l = lforw(buf_head(curbp));
    DOT.o = w_left_margin(curwp);
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}

/*
 * Move to the end of the buffer. Dot is always put at the end of the file.
 */
/* ARGSUSED */
int
gotoeob(int f GCC_UNUSED, int n GCC_UNUSED)
{
    DOT.l = lback(buf_head(curbp));
    curwp->w_flag |= WFMOVE;
    return firstnonwhite(FALSE, 1);
}

/*
 * Implements the vi "H" command.
 *
 * Move to first (or nth) line in window
 */
int
gotobos(int f, int n)
{
    int nn = curwp->w_ntrows;
    if (!f || n <= 0)
      n = 1;

    DOT.l = curwp->w_line.l;
    while (--n != 0) {
      if (is_last_line(DOT, curbp))
          break;
      nn -= line_height(curwp, DOT.l);
      DOT.l = lforw(DOT.l);
    }

    if (nn <= 0)        /* we went past the end of window */
      curwp->w_flag |= WFMOVE;
    return firstnonwhite(FALSE, 1);
}

/*
 * Implements the vi "M" command.
 *
 * Move to the middle of lines displayed in window
 */
/* ARGSUSED */
int
gotomos(int f GCC_UNUSED, int n)
{
    LINEPTR lp, head;
    int half = (curwp->w_ntrows + 1) / 2;

    head = buf_head(curbp);
    for (n = 0, lp = curwp->w_line.l; lp != head; lp = lforw(lp)) {
      if (n < half)
          DOT.l = lp;
      if ((n += line_height(curwp, lp)) >= curwp->w_ntrows)
          break;
    }
    if (n < curwp->w_ntrows) {      /* then we hit eof before eos */
      half = (n + 1) / 2;     /* go back up */
      for (n = 0, lp = curwp->w_line.l; lp != head; lp = lforw(lp)) {
          DOT.l = lp;
          if ((n += line_height(curwp, lp)) >= half)
            break;
      }
    }

    return firstnonwhite(FALSE, 1);
}

/*
 * Implements the vi "L" command.
 *
 * Move to the last (or nth last) line in window
 */
int
gotoeos(int f, int n)
{
    int nn;
    if (f == FALSE || n <= 0)
      n = 1;

    /* first get to the end */
    DOT.l = curwp->w_line.l;
    nn = curwp->w_ntrows;
    while ((nn -= line_height(curwp, DOT.l)) > 0) {
      if (is_last_line(DOT, curbp))
          break;
      DOT.l = lforw(DOT.l);
    }
#ifdef WMDLINEWRAP
    /* adjust if we pointed to a line-fragment */
    if (w_val(curwp, WMDLINEWRAP)
      && nn < 0
      && DOT.l != curwp->w_line.l)
      DOT.l = lback(DOT.l);
#endif
    /* and then go back up */
    /* (we're either at eos or eof) */
    while (--n != 0) {
      if (sameline(DOT, curwp->w_line))
          break;
      DOT.l = lback(DOT.l);
    }
    return firstnonwhite(FALSE, 1);
}

/*
 * Implements the vi "j" command.
 *
 * Move forward by full lines. If the number of lines to move is less than
 * zero, call the backward line function to actually do it. The last command
 * controls how the goal column is set.
 */
int
forwline(int f, int n)
{
    LINE *dlp;

    if (f == FALSE)
      n = 1;
    if (n < 0)
      return (backline(f, -n));
    if (n == 0)
      return TRUE;

    /* set the "goal" column if necessary */
    if (curgoal < 0)
      curgoal = getccol(FALSE);

    /* loop downwards */
    dlp = DOT.l;
    do {
      LINE *nlp = lforw(dlp);
      if (nlp == buf_head(curbp)) {
          return FALSE;
      }
      dlp = nlp;
    } while (--n != 0);

    /* set dot */
    DOT.l = dlp;
    DOT.o = getgoal(dlp);
    curwp->w_flag |= WFMOVE;
    return TRUE;
}
/*
 * Implements the vi "^" command.
 *
 * Move to the first nonwhite character on the current line.  No errors are
 * returned.
 */
/* ARGSUSED */
int
firstnonwhite(int f GCC_UNUSED, int n GCC_UNUSED)
{
    DOT.o = firstchar(DOT.l);
    if (DOT.o < w_left_margin(curwp)) {
      if (llength(DOT.l) <= w_left_margin(curwp))
          DOT.o = w_left_margin(curwp);
      else
          DOT.o = llength(DOT.l) - 1;
    }
    return TRUE;
}

/* ARGSUSED */
#if !SMALLER
int
lastnonwhite(int f GCC_UNUSED, int n GCC_UNUSED)
{
    DOT.o = lastchar(DOT.l);
    if (DOT.o < w_left_margin(curwp))
      DOT.o = w_left_margin(curwp);
    return TRUE;
}
#endif

/* return the offset of the first non-white character on the line,
      or -1 if there are no non-white characters on the line */
int
firstchar(LINE *lp)
{
    int off = w_left_margin(curwp);
    while (off < llength(lp) && isBlank(lgetc(lp, off)))
      off++;
    if (off == llength(lp))
      return -1;
    return off;
}

/* return the offset of the next non-white character on the line,
      or -1 if there are no more non-white characters on the line */
int
nextchar(LINE *lp, int off)
{
    while (off < llength(lp)) {
      if (!isSpace(lgetc(lp, off)))
          return off;
      off++;
    }
    return -1;
}

/* return the offset of the last non-white character on the line
      or -1 if there are no non-white characters on the line */
int
lastchar(LINE *lp)
{
    int off = llength(lp) - 1;
    while (off >= 0 && isSpace(lgetc(lp, off)))
      off--;
    return off;
}

/*
 * Implements the vi "^M" command.
 *
 * Like 'forwline()', but goes to the first non-white character position.
 */
int
forwbline(int f, int n)
{
    int s;

    if (f == FALSE)
      n = 1;
    if ((s = forwline(f, n)) != TRUE)
      return (s);
    return firstnonwhite(FALSE, 1);
}

/*
 * Implements the vi "-" command.
 *
 * Like 'backline()', but goes to the first non-white character position.
 */
int
backbline(int f, int n)
{
    int s;

    if (f == FALSE)
      n = 1;
    if ((s = backline(f, n)) != TRUE)
      return (s);
    return firstnonwhite(FALSE, 1);
}

/*
 * Implements the vi "k" command.
 *
 * This function is like "forwline", but goes backwards.
 */
int
backline(int f, int n)
{
    LINE *dlp;

    if (f == FALSE)
      n = 1;
    if (n < 0)
      return (forwline(f, -n));

    /* can't move up? */
    if (is_first_line(DOT, curbp))
      return (FALSE);

    /* set the "goal" column if necessary */
    if (curgoal < 0)
      curgoal = getccol(FALSE);

    /* loop upwards */
    dlp = DOT.l;
    while (n-- && lback(dlp) != buf_head(curbp))
      dlp = lback(dlp);

    /* set dot */
    DOT.l = dlp;
    DOT.o = getgoal(dlp);
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}

/*
 * Go to the beginning of the current paragraph.
 */
int
gotobop(int f, int n)
{
    MARK odot;
    int was_on_empty;
    int fc;

    if (!f)
      n = 1;

    was_on_empty = is_empty_line(DOT);
    odot = DOT;

    fc = firstchar(DOT.l);
    if (doingopcmd &&
      ((fc >= 0 && DOT.o <= fc) || fc < 0) &&
      !is_first_line(DOT, curbp)) {
      backchar(TRUE, DOT.o + 1);
      pre_op_dot = DOT;
    }
    while (n) {
      if (findpat(TRUE, 1, b_val_rexp(curbp, VAL_PARAGRAPHS)->reg,
                REVERSE) != TRUE) {
          (void) gotobob(f, n);
      } else if (is_empty_line(DOT)) {
          /* special case -- if we found an empty line,
             and it's adjacent to where we started,
             skip all adjacent empty lines, and try again */
          if ((was_on_empty && lforw(DOT.l) == odot.l) ||
            (n > 0 && llength(lforw(DOT.l)) == 0)) {
            /* then we haven't really found what we
               wanted.  keep going */
            skipblanksb();
            continue;
          }
      }
      n--;
    }
    if (doingopcmd) {
      fc = firstchar(DOT.l);
      if (!sameline(DOT, odot) &&
          (pre_op_dot.o > lastchar(pre_op_dot.l)) &&
          ((fc >= 0 && DOT.o <= fc) || fc < 0)) {
          regionshape = FULLLINE;
      }
    }
    return TRUE;
}

/*
 * Go to the end of the current paragraph.
 */
int
gotoeop(int f, int n)
{
    MARK odot;
    int was_at_bol;
    int was_on_empty;
    int fc;

    if (!f)
      n = 1;

    fc = firstchar(DOT.l);
    was_on_empty = is_empty_line(DOT);
    was_at_bol = ((fc >= 0 && DOT.o <= fc) || fc < 0);
    odot = DOT;

    while (n) {
      if (findpat(TRUE, 1, b_val_rexp(curbp, VAL_PARAGRAPHS)->reg,
                FORWARD) != TRUE) {
          DOT = curbp->b_line;
      } else if (is_empty_line(DOT)) {
          /* special case -- if we found an empty line. */
          /* either as the very next line, or at the end of
             our search */
          if ((was_on_empty && lback(DOT.l) == odot.l) ||
            (n > 0 && llength(lback(DOT.l)) == 0)) {
            /* then we haven't really found what we
               wanted.  keep going */
            skipblanksf();
            continue;
          }
      }
      n--;
    }
    if (doingopcmd) {
      /* if we're now at the beginning of a line and we can back up,
         do so to avoid eating the newline and leading whitespace */
      fc = firstchar(DOT.l);
      if (((fc >= 0 && DOT.o <= fc) || fc < 0) &&
          !is_first_line(DOT, curbp) &&
          !sameline(DOT, odot)) {
          backchar(TRUE, DOT.o + 1);
      }
      /* if we started at the start of line, eat the whole line */
      if (!sameline(DOT, odot) && was_at_bol)
          regionshape = FULLLINE;
    }
    return TRUE;
}

#if OPT_STUTTER_SEC_CMD
getstutter(void)
{
    int thiskey;
    if (!clexec) {
      thiskey = lastkey;
      kbd_seq();
      if (thiskey != lastkey) {
          return FALSE;
      }
    }
    return TRUE;
}
#endif

/*
 * Go to the beginning of the current section (or paragraph if no section
 * marker found).
 */
int
gotobosec(int f, int n)
{
#if OPT_STUTTER_SEC_CMD
    if (!getstutter())
      return FALSE;
#endif
    if (findpat(f, n, b_val_rexp(curbp, VAL_SECTIONS)->reg,
            REVERSE) != TRUE) {
      (void) gotobob(f, n);
    }
    return TRUE;
}

/*
 * Go to the end of the current section (or paragraph if no section marker
 * found).
 */
int
gotoeosec(int f, int n)
{
#if OPT_STUTTER_SEC_CMD
    if (!getstutter())
      return FALSE;
#endif
    if (findpat(f, n, b_val_rexp(curbp, VAL_SECTIONS)->reg,
            FORWARD) != TRUE) {
      DOT = curbp->b_line;
    }
    return TRUE;
}

/*
 * Go to the beginning of the current sentence. If we skip into an empty line
 * (from a non-empty line), return at that point -- that's what vi does.
 */
int
gotobosent(int f, int n)
{
    MARK savepos;
    int looped = 0;
    int extra;
    int empty = is_empty_line(DOT);
    regexp *exp;
    int s = TRUE;

    savepos = DOT;
    exp = b_val_rexp(curbp, VAL_SENTENCES)->reg;

    while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT)))) {
      s = backchar(TRUE, 1);
      if (is_empty_line(DOT) && !empty)
          return TRUE;
    }
  top:
    extra = 0;
    if (findpat(f, n, exp, REVERSE) != TRUE) {
      return gotobob(f, n);
    }
    s = forwchar(TRUE, RegexpLen(exp));
    while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT)))) {
      s = forwchar(TRUE, 1);
      extra++;
    }
    if (n == 1 && samepoint(savepos, DOT)) {    /* try again */
      if (looped > 10)
          return FALSE;
      s = backchar(TRUE, RegexpLen(exp) + extra + looped);
      while (s && is_at_end_of_line(DOT)) {
          if (!empty && is_empty_line(DOT))
            return TRUE;
          s = backchar(TRUE, 1);
      }
      looped++;
      goto top;

    }
    return TRUE;
}

/*
 * Go to the end of the current sentence.  Like gotobosent(), if we skip into
 * an empty line, return at that point.
 */
int
gotoeosent(int f, int n)
{
    regexp *exp;
    int s;
    int empty = is_empty_line(DOT);

    exp = b_val_rexp(curbp, VAL_SENTENCES)->reg;
    /* if we're on the end of a sentence now, don't bother scanning
       further, or we'll miss the immediately following sentence */
    if (!(lregexec(exp, DOT.l, DOT.o, llength(DOT.l)) &&
        exp->startp[0] - DOT.l->l_text == DOT.o)) {
      if (findpat(f, n, exp, FORWARD) != TRUE) {
          DOT = curbp->b_line;
      } else if (empty || !is_at_end_of_line(DOT)) {
          s = forwchar(TRUE, RegexpLen(exp));
          while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT)))) {
            LINEPTR lp = DOT.l;
            s = forwchar(TRUE, 1);
            if (lp != DOT.l)
                break;
          }
      }
    } else {
      s = forwchar(TRUE, RegexpLen(exp));
      while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT))))
          s = forwchar(TRUE, 1);
    }
    return TRUE;
}

/*
 * This routine, given a pointer to a LINE, and the current cursor goal
 * column, return the best choice for the offset. The offset is returned.
 * Used by "C-N" and "C-P".
 */
int
getgoal(LINE *dlp)
{
    int c;
    int col;
    int newcol;
    int dbo;

    col = 0;
    dbo = w_left_margin(curwp);
    while (dbo < llength(dlp)) {
      c = lgetc(dlp, dbo);
      newcol = next_column(c, col);
      if (newcol > curgoal)
          break;
      col = newcol;
      ++dbo;
    }
    return (dbo);
}

int
next_sw(int col)
{
    int width = shiftwid_val(curbp);
    return (((col / width) + 1) * width);
}

int
next_tabcol(int col)
{
    int t = tabstop_val(curbp);
    return (((col / t) + 1) * t);
}

/* return the next column index, given the current char and column */
int
next_column(int c, int col)
{
    if (c == '\t')
      return next_tabcol(col);
    else if (!isPrint(c))
      return col + ((c & HIGHBIT) ? 4 : 2);
    else
      return col + 1;
}

/*
 * Implements the vi "^F" command.
 *
 * Scroll forward by a specified number of lines, or by a full page if no
 * argument.
 */
int
forwpage(int f, int n)
{
    LINEPTR lp;
    int status;

    if ((n = full_pages(f, n)) < 0)
      return backpage(f, -n);

    if ((status = (lforw(DOT.l) != buf_head(curbp))) == TRUE) {
      lp = curwp->w_line.l;
      n -= line_height(curwp, lp);
      while (lp != buf_head(curbp)) {
          lp = lforw(lp);
          if ((n -= line_height(curwp, lp)) < 0)
            break;
      }
      if (n < 0)
          curwp->w_line.l = lp;
      DOT.l = lp;
      (void) firstnonwhite(FALSE, 1);
      curwp->w_flag |= WFHARD | WFMODE;
    }
    return status;
}

/*
 * Implements the vi "^B" command.
 *
 * This command is like "forwpage", but it goes backwards.
 */
int
backpage(int f, int n)
{
    LINEPTR lp;
    int status;

    if ((n = full_pages(f, n)) < 0)
      return forwpage(f, -n);

    lp = curwp->w_line.l;
    if (lback(lp) != buf_head(curbp)) {
      while ((n -= line_height(curwp, lp)) >= 0
             && lback(lp) != buf_head(curbp))
          lp = lback(lp);
      curwp->w_line.l = lp;
      (void) gotoeos(FALSE, 1);
      curwp->w_flag |= WFHARD | WFMODE;
      status = TRUE;
    } else if (DOT.l != lp) {
      DOT.l = lp;
      curwp->w_flag |= WFHARD | WFMODE;
      status = TRUE;
    } else {
      status = FALSE;
    }
    return status;
}

/*
 * Implements the vi "^D" command.
 *
 * Scroll forward by a half-page.  If a repeat count is given, interpret that
 * as the number of half-pages to scroll.
 *
 * Unlike vi, the OPT_CVMVAS option causes the repeat-count to be interpreted as
 * half-page, rather than lines.
 */
int
forwhpage(int f, int n)
{
    LINEPTR llp, dlp;
    int status;

    if ((n = half_pages(f, n)) < 0)
      return backhpage(f, -n);

    llp = curwp->w_line.l;
    dlp = DOT.l;
    if ((status = (lforw(dlp) != buf_head(curbp))) == TRUE) {
      n -= line_height(curwp, dlp);
      while (lforw(dlp) != buf_head(curbp)) {
          llp = lforw(llp);
          dlp = lforw(dlp);
          if ((n -= line_height(curwp, dlp)) < 0)
            break;
      }
      curwp->w_line.l = llp;
      DOT.l = dlp;
      curwp->w_flag |= WFHARD | WFKILLS;
    }
    (void) firstnonwhite(FALSE, 1);
    return status;
}

/*
 * Implements the vi "^U" command.
 *
 * This command is like "forwpage", but it goes backwards.  It returns false
 * only if the cursor is on the first line of the buffer.
 *
 * Unlike vi, the OPT_CVMVAS option causes the repeat-count to be interpreted as
 * half-pages, rather than lines.
 */
int
backhpage(int f, int n)
{
    LINEPTR llp, dlp;
    int status;

    if ((n = half_pages(f, n)) < 0)
      return forwhpage(f, -n);

    llp = curwp->w_line.l;
    dlp = DOT.l;
    if ((status = (lback(dlp) != buf_head(curbp))) == TRUE) {
      n -= line_height(curwp, dlp);
      while (lback(dlp) != buf_head(curbp)) {
          llp = lback(llp);
          dlp = lback(dlp);
          if ((n -= line_height(curwp, dlp)) < 0)
            break;
      }
      curwp->w_line.l = llp;
      DOT.l = dlp;
      curwp->w_flag |= WFHARD | WFINS;
    }
    (void) firstnonwhite(FALSE, 1);
    return status;
}

/*
 * Move forward n rows on the screen, staying in the same column.  It's ok to
 * scroll, too.
 */
int
forw_row(int f, int n)
{
    int code = TRUE;
    int col, next;

    if (f == FALSE)
      n = 1;
    if (n < 0) {
      code = back_row(f, -n);
    } else if (n > 0) {

      /* set the "goal" column if necessary */
      if (curgoal < 0)
          curgoal = getccol(FALSE);

      col = curgoal;
      next = col;

      while ((n-- > 0) && (code == TRUE)) {
          int save = next;
          next += term.cols;
          if (gotocol(TRUE, next + 1) == FALSE) {
            curgoal %= term.cols;
            gotocol(TRUE, save);
            code = forwline(TRUE, 1);
          } else {
            curgoal = next;
          }
      }
    }
    return code;
}

/*
 * Move back n rows on the screen, staying in the same column.  It's ok to
 * scroll, too.
 */
int
back_row(int f, int n)
{
    int code = TRUE;
    int col, next;

    if (f == FALSE)
      n = 1;
    if (n < 0) {
      code = forw_row(f, -n);
    } else if (n > 0) {

      /* set the "goal" column if necessary */
      if (curgoal < 0)
          curgoal = getccol(FALSE);

      col = curgoal;
      next = col;

      while ((n-- > 0) && (code == TRUE)) {
          next -= term.cols;
          if (next < 0) {
            if ((code = backline(TRUE, 1)) == TRUE
                && llength(DOT.l) >= curgoal) {
                next = llength(DOT.l) / term.cols;
                next = (next * term.cols) + curgoal;
                curgoal = next;
            }
          } else {
            if ((code = gotocol(TRUE, next + 1)) == TRUE)
                curgoal = next;
          }
      }
    }
    return code;
}

#if OPT_SHOW_MARKS
static int
show_mark(int count, BUFFER *bp, MARK mark, int name)
{
    static int tab = 8;       /* no b_val(bp,VAL_TAB) -- set_rdonly uses 8 */
    static int stop;

    if (!samepoint(mark, nullmark)) {

      if (!count) {
          bprintf("\nMark  Line     Column");
          if (stop == 0) {
            stop = ((DOT.o + tab - 1) / tab) * tab;
          }
          bprintf("%*PText", stop - DOT.o, ' ');
          bprintf("\n----  -------- ------");
          bprintf("%*P----", stop - DOT.o, ' ');
      }

      bprintf("\n%c     %8d %8d",
            name,
            line_no(bp, mark.l),
            mk_to_vcol(mark, FALSE, bp, 0, 0) + 1);
      if (llength(mark.l) > 0) {
          bprintf("%*P%*S",
                stop - DOT.o, ' ',
                llength(mark.l),
                mark.l->l_text);
      }
      return 1;
    }
    return 0;
}

static void
makemarkslist(int value GCC_UNUSED, void *dummy GCC_UNUSED)
{
    BUFFER *bp = (BUFFER *) dummy;
    WINDOW *wp;
    int n;
    int done = 0;

    bprintf("Named marks for %s\n", bp->b_bname);
    set_b_val(curbp, VAL_TAB, b_val(bp, VAL_TAB));

    for_each_window(wp) {
      if (wp->w_bufp == bp) {
          done += show_mark(done, bp, wp->w_lastdot, SQUOTE);
      }
    }
    if (bp->b_nmmarks != 0) {
      for (n = 0; n < NMARKS; n++) {
          done += show_mark(done, bp, bp->b_nmmarks[n], n + 'a');
      }
    }
#if OPT_SELECTIONS
    if (bp == sel_buffer()) {
      MARK left, right;
      if (sel_get_leftmark(&left)
          && sel_get_rightmark(&right)
          && (left.l != right.l
            || left.o != right.o)) {
          done += show_mark(done, bp, left, '<');
          done += show_mark(done, bp, right, '>');
      }
    }
#endif
    if (done)
      bprintf("\n\n%d mark%s", done, PLURAL(done));
    else
      bprintf("\n(none)");
}

int
showmarks(int f, int n GCC_UNUSED)
{
    return liststuff(MARKS_BufName, FALSE, makemarkslist, f, (void *) curbp);
}

#if OPT_UPBUFF
/* ARGSUSED */
static int
update_marklist(BUFFER *bp GCC_UNUSED)
{
    return showmarks(FALSE, 1);
}
#endif
#endif /* OPT_SHOW_MARKS */

static int
invalid_nmmark(void)
{
    mlforce("[Invalid mark name]");
    return FALSE;
}

int
can_set_nmmark(int c)
{
    if (isLower(c) || c == SQUOTE)
      return TRUE;
#if OPT_SELECTIONS
    if (c == '<' || c == '>')
      return TRUE;
#endif
    return FALSE;
}

static int
get_nmmark(int c, MARK *markp)
{
    int result = TRUE;

    *markp = nullmark;
    if (c == SQUOTE) {        /* use the 'last dot' mark */
      *markp = curwp->w_lastdot;
    } else {
#if OPT_SELECTIONS
      if (c == '<') {
          result = sel_get_leftmark(markp);
      } else if (c == '>') {
          result = sel_get_rightmark(markp);
      } else
#endif
      if (curbp->b_nmmarks != NULL) {
          *markp = curbp->b_nmmarks[c - 'a'];
      } else {
          result = FALSE;
      }
    }
    TRACE(("get_nmmark(%c) ->%d.%d\n",
         c, result ? line_no(curbp, markp->l) : 0, markp->o));
    return result;
}

int
show_mark_is_set(int c)
{
    if (isLower(c))
      mlwrite("[Mark '%c' set]", c);
    update_scratch(MARKS_BufName, update_marklist);
    return TRUE;
}

/*
 * Implements the vi "m" command.
 *
 * Set the named mark in the current window to the value of "." in the window.
 */
/* ARGSUSED */
int
setnmmark(int f GCC_UNUSED, int n GCC_UNUSED)
{
    int c, i;

    if (clexec || isnamedcmd) {
      int status;
      static char cbuf[2];
      if ((status = mlreply("Set mark: ", cbuf, 2)) != TRUE)
          return status;
      c = cbuf[0];
    } else {
      c = keystroke();
      if (ABORTED(c))
          return ABORT;
    }

    if (c < 'a' || c > 'z') {
      return invalid_nmmark();
    }

    if (curbp->b_nmmarks == NULL) {

      beginDisplay();
      curbp->b_nmmarks = typeallocn(struct MARK, NMARKS);
      endofDisplay();

      if (curbp->b_nmmarks == NULL)
          return no_memory("named-marks");
      for (i = 0; i < NMARKS; i++) {
          curbp->b_nmmarks[i] = nullmark;
      }
    }

    curbp->b_nmmarks[c - 'a'] = DOT;
    return show_mark_is_set(c);
}

/*
 * Get the name of the mark to use.  interactively, "last dot" is represented
 * by stuttering the goto-mark command.  From the command line, it's always
 * named ' or `.  I suppose this is questionable.
 */
static int
getnmmarkname(int *cp)
{
    int c;
    int thiskey;
    int useldmark;

    if (clexec || isnamedcmd) {
      int status;
      static char cbuf[2];
      if ((status = mlreply("Goto mark: ", cbuf, 2)) != TRUE)
          return status;
      c = cbuf[0];
      useldmark = (c == SQUOTE || c == BQUOTE);
    } else {
      thiskey = lastkey;
      c = keystroke();
      if (ABORTED(c))
          return ABORT;
      useldmark = (lastkey == thiskey);   /* usually '' or `` */
    }

    if (useldmark)
      c = SQUOTE;

    *cp = c;
    TRACE(("getnmmarkname(%c)\n", c));
    return TRUE;
}

/* ARGSUSED */
int
golinenmmark(int f GCC_UNUSED, int n GCC_UNUSED)
{
    int c;
    int s;

    s = getnmmarkname(&c);
    if (s != TRUE)
      return s;
    s = gonmmark(c);
    if (s != TRUE)
      return s;

    return firstnonwhite(FALSE, 1);
}

/* ARGSUSED */
int
goexactnmmark(int f GCC_UNUSED, int n GCC_UNUSED)
{
    int c;
    int s;

    s = getnmmarkname(&c);
    if (s != TRUE)
      return s;

    return gonmmark(c);
}

/* ARGSUSED */
int
gorectnmmark(int f GCC_UNUSED, int n GCC_UNUSED)
{
    int c;
    int s;

    s = getnmmarkname(&c);
    if (s != TRUE)
      return s;

    regionshape = RECTANGLE;
    return gonmmark(c);
}

int
gonmmark(int c)
{
    MARK my_mark;
    MARK tmark;

    TRACE(("gonmmark(%c)\n", c));

    if (!can_set_nmmark(c)) {
      return invalid_nmmark();
    }

    /* save current dot */
    tmark = DOT;

    /* if we have any named marks, and the one we want isn't null */
    if (!get_nmmark(c, &my_mark) || !restore_dot(my_mark)) {
      mlforce("[Mark not set]");
      return (FALSE);
    }

    if (!doingopcmd)          /* reset last-dot-mark to old dot */
      curwp->w_lastdot = tmark;

    show_mark_is_set(0);
    return (TRUE);
}

/*
 * Set the mark in the current window to the value of "." in the window. No
 * errors are possible.
 */
int
setmark(void)
{
    MK = DOT;
    return (TRUE);
}

/* ARGSUSED */
int
gomark(int f GCC_UNUSED, int n GCC_UNUSED)
{
    DOT = MK;
    curwp->w_flag |= WFMOVE;
    return (TRUE);
}

/* this odd routine puts us at the internal mark, plus an offset of lines */
/*  n == 1 leaves us at mark, n == 2 one line down, etc. */
/*  this is for the use of stuttered commands, and line oriented regions */
int
godotplus(int f, int n)
{
    int s;
    if (!f || n == 1) {
      return firstnonwhite(FALSE, 1);
    }
    if (n < 1)
      return (FALSE);
    s = forwline(TRUE, n - 1);
    if (s && is_header_line(DOT, curbp))
      s = backline(FALSE, 1);
    if (s == TRUE)
      (void) firstnonwhite(FALSE, 1);
    return s;
}

/*
 * Swap the values of "." and "mark" in the current window. This is pretty
 * easy, because all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible error is "no mark".
 */
void
swapmark(void)
{
    MARK odot;

    if (samepoint(MK, nullmark)) {
      mlforce("BUG: No mark ");
      return;
    }
    odot = DOT;
    DOT = MK;
    MK = odot;
    curwp->w_flag |= WFMOVE;
    return;
}

#if OPT_MOUSE
/*
 * Given row & column from the screen, set the MK value.
 * The resulting position will not be past end-of-buffer unless the buffer
 * is empty.
 */
int
setwmark(int row, int col)
{
    MARK save;
    LINEPTR dlp;

    save = DOT;
    if (row == mode_row(curwp)) {
      (void) gotoeos(FALSE, 1);
      DOT.l = lforw(DOT.l);
      DOT.o = w_left_margin(curwp);
    } else {                  /* move to the right row */
      row -= curwp->w_toprow + curwp->w_line.o;
      /*
       * In the statement above, wp->w_line.o will
       * normally be zero.  But when the top line of the
       * window is wrapped and the beginning of the line
       * is not visible, it will have the number of
       * lines that would appear before the top line
       * negated.  (E.g, if the wrapped line occupied
       * 2 lines before the top window line, then wp->w_line.o
       * will have the value -2.)
       */

      dlp = curwp->w_line.l;  /* get pointer to 1st line */
      while ((row -= line_height(curwp, dlp)) >= 0
             && dlp != buf_head(curbp))
          dlp = lforw(dlp);
      DOT.l = dlp;            /* set dot line pointer */

      /* now move the dot over until near the requested column */
#ifdef WMDLINEWRAP
      if (w_val(curwp, WMDLINEWRAP))
          col += term.cols * (row + line_height(curwp, dlp));
#endif
      DOT.o = col2offs(curwp, dlp, col);

#ifdef OPT_MOUSE_NEWLINE
      /* don't allow the cursor to be set past end of line unless we
       * are in insert mode
       */
      if (DOT.o >= llength(dlp) && DOT.o > w_left_margin(curwp) &&
          !insertmode)
          DOT.o--;
#endif
    }
    if (is_header_line(DOT, curwp->w_bufp)) {
      DOT.l = lback(DOT.l);
      DOT.o = llength(DOT.l);
    }
    MK = DOT;
    DOT = save;
    return TRUE;
}

/*
 * Given row & column from the screen, set the curwp and DOT values.
 */
int
setcursor(int row, int col)
{
    WINDOW *wp0 = curwp;
    WINDOW *wp1;
    MARK saveMK;

    if ((wp1 = row2window(row)) == 0)
      return FALSE;
    if (doingsweep && curwp != wp1)
      return FALSE;
    saveMK = MK;
    if (set_curwp(wp1)
      && setwmark(row, col)) {
      if (insertmode != FALSE
          && b_val(wp1->w_bufp, MDVIEW)
          && b_val(wp1->w_bufp, MDSHOWMODE)) {
          if (b_val(wp0->w_bufp, MDSHOWMODE))
            wp0->w_flag |= WFMODE;
          if (b_val(wp1->w_bufp, MDSHOWMODE))
            wp1->w_flag |= WFMODE;
          insertmode = FALSE;
      }
      DOT = MK;
      if (wp0 == wp1)
          MK = saveMK;
      curwp->w_flag |= WFMOVE;
      return TRUE;
    }

    return FALSE;
}
#endif

Generated by  Doxygen 1.6.0   Back to index