home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / basic.c < prev    next >
C/C++ Source or Header  |  1998-07-02  |  27KB  |  1,291 lines

  1. /*
  2.  * The routines in this file move the cursor around on the screen. They
  3.  * compute a new value for the cursor, then adjust ".". The display code
  4.  * always updates the cursor location, so only moves between lines, or
  5.  * functions that adjust the top line in the window and invalidate the
  6.  * framing, are hard.
  7.  *
  8.  * $Header: /usr/build/vile/vile/RCS/basic.c,v 1.99 1998/07/03 00:07:41 tom Exp $
  9.  *
  10.  */
  11.  
  12. #include    "estruct.h"
  13. #include    "edef.h"
  14.  
  15. #define    RegexpLen(exp) ((exp->mlen) ? (int)(exp->mlen) : 1)
  16.  
  17. static    int    getnmmarkname ( int *cp );
  18. static    void    skipblanksb (void);
  19. static    void    skipblanksf (void);
  20.  
  21. /* utility routine for 'forwpage()' and 'backpage()' */
  22. static int
  23. full_pages(int f, int n)
  24. {
  25.     if (f == FALSE) {
  26.         n = curwp->w_ntrows - 2;    /* Default scroll.    */
  27.         if (n <= 0)            /* Don't blow up if the */
  28.             n = 1;            /* window is tiny.    */
  29.     }
  30. #if    OPT_CVMVAS
  31.     else if (n > 0)                /* Convert from pages    */
  32.         n *= curwp->w_ntrows;        /* to lines.        */
  33. #endif
  34.     return n;
  35. }
  36.  
  37. /* utility routine for 'forwhpage()' and 'backhpage()' */
  38. static int
  39. half_pages(int f, int n)
  40. {
  41.     if (f == FALSE) {
  42.         n = curwp->w_ntrows / 2;    /* Default scroll.    */
  43.         if (n <= 0)            /* Forget the overlap    */
  44.             n = 1;            /* if tiny window.    */
  45.     }
  46. #if    OPT_CVMVAS
  47.     else if (n > 0)                /* Convert from pages    */
  48.         n *= curwp->w_ntrows/2;        /* to lines.        */
  49. #endif
  50.     return n;
  51. }
  52.  
  53. /*
  54.  * Implements the vi "0" command.
  55.  *
  56.  * Move the cursor to the beginning of the current line.
  57.  */
  58. /* ARGSUSED */
  59. int
  60. gotobol(int f GCC_UNUSED, int n GCC_UNUSED)
  61. {
  62.     DOT.o  = w_left_margin(curwp);
  63.     return mvleftwind(TRUE, -w_val(curwp,WVAL_SIDEWAYS));
  64. }
  65.  
  66. /*
  67.  * Move the cursor backwards by "n" characters. If "n" is less than zero call
  68.  * "forwchar" to actually do the move. Otherwise compute the new cursor
  69.  * location. Error if you try and move out of the buffer. Set the flag if the
  70.  * line pointer for dot changes.
  71.  */
  72. int
  73. backchar(int f, int n)
  74. {
  75.     register LINE    *lp;
  76.  
  77.     if (f == FALSE) n = 1;
  78.     if (n < 0)
  79.         return (forwchar(f, -n));
  80.     while (n--) {
  81.         if (DOT.o == w_left_margin(curwp)) {
  82.             if ((lp=lback(DOT.l)) == buf_head(curbp))
  83.                 return (FALSE);
  84.             DOT.l  = lp;
  85.             DOT.o  = llength(lp);
  86.             curwp->w_flag |= WFMOVE;
  87.         } else
  88.             DOT.o--;
  89.     }
  90.     return (TRUE);
  91. }
  92.  
  93. /*
  94.  * Implements the vi "h" command.
  95.  *
  96.  * Move the cursor backwards by "n" characters. Stop at beginning of line.
  97.  */
  98. int
  99. backchar_to_bol(int f, int n)
  100. {
  101.  
  102.     if (f == FALSE) n = 1;
  103.     if (n < 0)
  104.         return forwchar_to_eol(f, -n);
  105.     while (n--) {
  106.         if (DOT.o == w_left_margin(curwp))
  107.             return doingopcmd;
  108.         else
  109.             DOT.o--;
  110.     }
  111.     return TRUE;
  112. }
  113.  
  114. /*
  115.  * Implements the vi "$" command.
  116.  *
  117.  * Move the cursor to the end of the current line.  Trivial.
  118.  */
  119. int
  120. gotoeol(int f, int n)
  121. {
  122.     if (f == TRUE) {
  123.         if (n > 0)
  124.             --n;
  125.         else if (n < 0)
  126.             ++n;
  127.         if (forwline(f,n) != TRUE)
  128.             return FALSE;
  129.     }
  130.     DOT.o  = llength(DOT.l);
  131.     curgoal = HUGE;
  132.     return (TRUE);
  133. }
  134.  
  135. /*
  136.  * Move the cursor forwards by "n" characters. If "n" is less than zero call
  137.  * "backchar" to actually do the move. Otherwise compute the new cursor
  138.  * location, and move ".". Error if you try and move off the end of the
  139.  * buffer. Set the flag if the line pointer for dot changes.
  140.  */
  141. int
  142. forwchar(int f, int n)
  143. {
  144.     if (f == FALSE) n = 1;
  145.     if (n < 0)
  146.         return (backchar(f, -n));
  147.     while (n--) {
  148.         /* if an explicit arg was given, allow us to land
  149.             on the newline, else skip it */
  150.         if (is_at_end_of_line(DOT) ||
  151.                 (f == FALSE && !insertmode &&
  152.                 llength(DOT.l) && DOT.o == llength(DOT.l) - 1)
  153.                                 ) {
  154.             if (is_header_line(DOT, curbp) ||
  155.                     is_last_line(DOT,curbp))
  156.                 return (FALSE);
  157.             DOT.l  = lforw(DOT.l);
  158.             DOT.o  = w_left_margin(curwp);
  159.             curwp->w_flag |= WFMOVE;
  160.         } else
  161.             DOT.o++;
  162.     }
  163.     return (TRUE);
  164. }
  165.  
  166.  
  167. /*
  168.  * Implements the vi "l" command.
  169.  *
  170.  * Move the cursor forwards by "n" characters. Don't go past end-of-line
  171.  *
  172.  * If the flag 'doingopcmd' is set, implements a vi "l"-like motion for
  173.  * internal use.  The end-of-line test is off-by-one from the true "l" command
  174.  * to allow for substitutions at the end of a line.
  175.  */
  176. int
  177. forwchar_to_eol(int f, int n)
  178. {
  179.     int nwas = n;
  180.     int lim;
  181.     if (f == FALSE) n = 1;
  182.     if (n < 0) return backchar_to_bol(f, -n);
  183.     if (n == 0) return TRUE;
  184.  
  185.     /* normally, we're confined to the text on the line itself.  if
  186.       we're doing an opcmd, then we're allowed to move to the newline
  187.       as well, to take care of the internal cases:  's', 'x', and '~'. */
  188.     if (doingopcmd || insertmode)
  189.         lim = llength(DOT.l);
  190.     else
  191.         lim = llength(DOT.l) - 1;
  192.     do {
  193.         if (DOT.o >= lim)
  194.             return n != nwas; /* return ok if we moved at all */
  195.         else
  196.             DOT.o++;
  197.     } while (--n);
  198.     return TRUE;
  199. }
  200.  
  201. /*
  202.  * Implements the vi "G" command.
  203.  *
  204.  * Move to a particular line (the argument).  Count from bottom of file if
  205.  * argument is negative.
  206.  */
  207. int
  208. gotoline(int f, int n)
  209. {
  210.     register int status;    /* status return */
  211.  
  212.     MARK odot;
  213.  
  214.     if (f == FALSE) {
  215.         return(gotoeob(f,n));
  216.     }
  217.  
  218.     if (n == 0)        /* if a bogus argument...then leave */
  219.         return(FALSE);
  220.  
  221.     odot = DOT;
  222.  
  223.     DOT.o  = w_left_margin(curwp);
  224.     if (n < 0) {
  225.         DOT.l  = lback(buf_head(curbp));
  226.         status = backline(f, -n - 1 );
  227.     } else {
  228.         DOT.l  = lforw(buf_head(curbp));
  229.         status = forwline(f, n-1);
  230.     }
  231.     if (status != TRUE) {
  232.         DOT = odot;
  233.         return status;
  234.     }
  235.     (void)firstnonwhite(FALSE,1);
  236.     curwp->w_flag |= WFMOVE;
  237.     return TRUE;
  238. }
  239. /*
  240.  * Goto the beginning of the buffer. Massive adjustment of dot. This is
  241.  * considered to be hard motion; it really isn't if the original value of dot
  242.  * is the same as the new value of dot.
  243.  */
  244. /* ARGSUSED */
  245. int
  246. gotobob(int f GCC_UNUSED, int n GCC_UNUSED)
  247. {
  248.     DOT.l  = lforw(buf_head(curbp));
  249.     DOT.o  = w_left_margin(curwp);
  250.     curwp->w_flag |= WFMOVE;
  251.     return (TRUE);
  252. }
  253.  
  254. /*
  255.  * Move to the end of the buffer. Dot is always put at the end of the file.
  256.  */
  257. /* ARGSUSED */
  258. int
  259. gotoeob(int f GCC_UNUSED, int n GCC_UNUSED)
  260. {
  261.     DOT.l  = lback(buf_head(curbp));
  262.     curwp->w_flag |= WFMOVE;
  263.     return firstnonwhite(FALSE,1);
  264. }
  265.  
  266. /*
  267.  * Implements the vi "H" command.
  268.  *
  269.  * Move to first (or nth) line in window
  270.  */
  271. int
  272. gotobos(int f, int n)
  273. {
  274.     int    nn = curwp->w_ntrows;
  275.     if (!f || n <= 0)
  276.         n = 1;
  277.  
  278.     DOT.l = curwp->w_line.l;
  279.     while (--n) {
  280.         if (is_last_line(DOT,curbp))
  281.             break;
  282.         nn -= line_height(curwp, DOT.l);
  283.         DOT.l = lforw(DOT.l);
  284.     }
  285.  
  286.     if (nn <= 0)        /* we went past the end of window */
  287.         curwp->w_flag |= WFMOVE;
  288.     return firstnonwhite(FALSE,1);
  289. }
  290.  
  291. /*
  292.  * Implements the vi "M" command.
  293.  *
  294.  * Move to the middle of lines displayed in window
  295.  */
  296. /* ARGSUSED */
  297. int
  298. gotomos(int f GCC_UNUSED, int n)
  299. {
  300.     register LINEPTR lp, head;
  301.     int    half = (curwp->w_ntrows+1) / 2;
  302.  
  303.     head = buf_head(curbp);
  304.     for (n = 0, lp = curwp->w_line.l; lp != head; lp = lforw(lp)) {
  305.         if (n < half)
  306.             DOT.l = lp;
  307.         if ((n += line_height(curwp, lp)) >= curwp->w_ntrows)
  308.             break;
  309.     }
  310.     if (n < curwp->w_ntrows) {    /* then we hit eof before eos */
  311.         half = (n+1) / 2;    /* go back up */
  312.         for (n = 0, lp = curwp->w_line.l; lp != head; lp = lforw(lp)) {
  313.             DOT.l = lp;
  314.             if ((n += line_height(curwp, lp)) >= half)
  315.                 break;
  316.         }
  317.     }
  318.  
  319.     return firstnonwhite(FALSE,1);
  320. }
  321.  
  322. /*
  323.  * Implements the vi "L" command.
  324.  *
  325.  * Move to the last (or nth last) line in window
  326.  */
  327. int
  328. gotoeos(int f, int n)
  329. {
  330.     int nn;
  331.     if (f == FALSE || n <= 0)
  332.         n = 1;
  333.  
  334.     /* first get to the end */
  335.     DOT.l = curwp->w_line.l;
  336.     nn = curwp->w_ntrows;
  337.     while ((nn -= line_height(curwp,DOT.l)) > 0) {
  338.         if (is_last_line(DOT,curbp))
  339.             break;
  340.         DOT.l = lforw(DOT.l);
  341.     }
  342. #ifdef WMDLINEWRAP
  343.     /* adjust if we pointed to a line-fragment */
  344.     if (w_val(curwp,WMDLINEWRAP)
  345.      && nn < 0
  346.      && DOT.l != curwp->w_line.l)
  347.         DOT.l = lback(DOT.l);
  348. #endif
  349.     /* and then go back up */
  350.     /* (we're either at eos or eof) */
  351.     while (--n) {
  352.         if (sameline(DOT, curwp->w_line))
  353.             break;
  354.         DOT.l = lback(DOT.l);
  355.     }
  356.     return firstnonwhite(FALSE,1);
  357. }
  358.  
  359. /*
  360.  * Implements the vi "j" command.
  361.  *
  362.  * Move forward by full lines. If the number of lines to move is less than
  363.  * zero, call the backward line function to actually do it. The last command
  364.  * controls how the goal column is set.
  365.  */
  366. int
  367. forwline(int f, int n)
  368. {
  369.     register LINE    *dlp;
  370.  
  371.     if (f == FALSE) n = 1;
  372.     if (n < 0) return (backline(f, -n));
  373.     if (n == 0) return TRUE;
  374.  
  375.     /* if the last command was not a line move,
  376.        reset the goal column */
  377.     if (curgoal < 0)
  378.         curgoal = getccol(FALSE);
  379.  
  380.     /* and move the point down */
  381.     dlp = DOT.l;
  382.     do {
  383.         register LINE *nlp = lforw(dlp);
  384.         if (nlp == buf_head(curbp)) {
  385.             return FALSE;
  386.         }
  387.         dlp = nlp;
  388.     } while (--n);
  389.  
  390.     /* resetting the current position */
  391.     DOT.l  = dlp;
  392.     DOT.o  = getgoal(dlp);
  393.     curwp->w_flag |= WFMOVE;
  394.     return TRUE;
  395. }
  396. /*
  397.  * Implements the vi "^" command.
  398.  *
  399.  * Move to the first nonwhite character on the current line.  No errors are
  400.  * returned.
  401.  */
  402. /* ARGSUSED */
  403. int
  404. firstnonwhite(int f GCC_UNUSED, int n GCC_UNUSED)
  405. {
  406.     DOT.o  = firstchar(DOT.l);
  407.     if (DOT.o < w_left_margin(curwp)) {
  408.         if (llength(DOT.l) <= w_left_margin(curwp))
  409.             DOT.o = w_left_margin(curwp);
  410.         else
  411.             DOT.o = llength(DOT.l) - 1;
  412.     }
  413.     return TRUE;
  414. }
  415.  
  416. /* ARGSUSED */
  417. #if !SMALLER
  418. int
  419. lastnonwhite(int f GCC_UNUSED, int n GCC_UNUSED)
  420. {
  421.     DOT.o  = lastchar(DOT.l);
  422.     if (DOT.o < w_left_margin(curwp))
  423.         DOT.o = w_left_margin(curwp);
  424.     return TRUE;
  425. }
  426. #endif
  427.  
  428. /* return the offset of the first non-white character on the line,
  429.     or -1 if there are no non-white characters on the line */
  430. int
  431. firstchar(LINE *lp)
  432. {
  433.     int off = w_left_margin(curwp);
  434.     while ( off < llength(lp) && isBlank(lgetc(lp, off)) )
  435.         off++;
  436.     if (off == llength(lp))
  437.         return -1;
  438.     return off;
  439. }
  440.  
  441. /* return the offset of the next non-white character on the line,
  442.     or -1 if there are no more non-white characters on the line */
  443. int
  444. nextchar(LINE *lp, int off)
  445. {
  446.     while (off < llength(lp)) {
  447.         if (!isSpace(lgetc(lp,off)))
  448.             return off;
  449.         off++;
  450.     }
  451.     return -1;
  452. }
  453.  
  454. /* return the offset of the last non-white character on the line
  455.     or -1 if there are no non-white characters on the line */
  456. int
  457. lastchar(LINE *lp)
  458. {
  459.     int off = llength(lp)-1;
  460.     while ( off >= 0 && isSpace(lgetc(lp, off)) )
  461.         off--;
  462.     return off;
  463. }
  464.  
  465. /*
  466.  * Implements the vi "^M" command.
  467.  *
  468.  * Like 'forwline()', but goes to the first non-white character position.
  469.  */
  470. int
  471. forwbline(int f, int n)
  472. {
  473.     int s;
  474.  
  475.     if (f == FALSE) n = 1;
  476.     if ((s = forwline(f,n)) != TRUE)
  477.         return (s);
  478.     return firstnonwhite(FALSE,1);
  479. }
  480.  
  481. /*
  482.  * Implements the vi "-" command.
  483.  *
  484.  * Like 'backline()', but goes to the first non-white character position.
  485.  */
  486. int
  487. backbline(int f, int n)
  488. {
  489.     int s;
  490.  
  491.     if (f == FALSE) n = 1;
  492.     if ((s = backline(f,n)) != TRUE)
  493.         return (s);
  494.     return firstnonwhite(FALSE,1);
  495. }
  496.  
  497. /*
  498.  * Implements the vi "k" command.
  499.  *
  500.  * This function is like "forwline", but goes backwards.
  501.  */
  502. int
  503. backline(int f, int n)
  504. {
  505.     register LINE    *dlp;
  506.  
  507.     if (f == FALSE) n = 1;
  508.     if (n < 0)
  509.         return (forwline(f, -n));
  510.  
  511.     /* if we are on the first line as we start....fail the command */
  512.     if (is_first_line(DOT, curbp))
  513.         return(FALSE);
  514.  
  515.     /* if the last command was not note a line move,
  516.        reset the goal column */
  517.     if (curgoal < 0)
  518.         curgoal = getccol(FALSE);
  519.  
  520.     /* and move the point up */
  521.     dlp = DOT.l;
  522.     while (n-- && lback(dlp) != buf_head(curbp))
  523.         dlp = lback(dlp);
  524.  
  525.     /* reseting the current position */
  526.     DOT.l  = dlp;
  527.     DOT.o  = getgoal(dlp);
  528.     curwp->w_flag |= WFMOVE;
  529.     return (TRUE);
  530. }
  531.  
  532. /*
  533.  * Go to the beginning of the current paragraph.
  534.  */
  535. int
  536. gotobop(int f, int n)
  537. {
  538.     MARK odot;
  539.     int was_on_empty;
  540.     int fc;
  541.  
  542.     if (!f) n = 1;
  543.  
  544.     was_on_empty = is_empty_line(DOT);
  545.     odot = DOT;
  546.  
  547.     fc = firstchar(DOT.l);
  548.     if (doingopcmd &&
  549.         ((fc >= 0 && DOT.o <= fc) || fc < 0) &&
  550.         !is_first_line(DOT,curbp)) {
  551.         backchar(TRUE,DOT.o+1);
  552.         pre_op_dot = DOT;
  553.     }
  554.     while (n) {
  555.         if (findpat(TRUE, 1, b_val_rexp(curbp,VAL_PARAGRAPHS)->reg,
  556.                             REVERSE) != TRUE) {
  557.             (void)gotobob(f,n);
  558.         } else if (is_empty_line(DOT)) {
  559.             /* special case -- if we found an empty line,
  560.                 and it's adjacent to where we started,
  561.                 skip all adjacent empty lines, and try again */
  562.             if ( (was_on_empty && lforw(DOT.l) == odot.l) ||
  563.                 (n > 0 && llength(lforw(DOT.l)) == 0) ) {
  564.                 /* then we haven't really found what we
  565.                     wanted.  keep going */
  566.                 skipblanksb();
  567.                 continue;
  568.             }
  569.         }
  570.         n--;
  571.     }
  572.     if (doingopcmd) {
  573.         fc = firstchar(DOT.l);
  574.         if (!sameline(DOT,odot) &&
  575.             (pre_op_dot.o > lastchar(pre_op_dot.l)) &&
  576.             ((fc >= 0 && DOT.o <= fc) || fc < 0)) {
  577.             regionshape = FULLLINE;
  578.         }
  579.     }
  580.     return TRUE;
  581. }
  582.  
  583. /*
  584.  * Go to the end of the current paragraph.
  585.  */
  586. int
  587. gotoeop(int f, int n)
  588. {
  589.     MARK odot;
  590.     int was_at_bol;
  591.     int was_on_empty;
  592.     int fc;
  593.  
  594.     if (!f) n = 1;
  595.  
  596.     fc = firstchar(DOT.l);
  597.     was_on_empty = is_empty_line(DOT);
  598.     was_at_bol = ((fc >= 0 && DOT.o <= fc) || fc < 0);
  599.     odot = DOT;
  600.  
  601.     while (n) {
  602.         if (findpat(TRUE, 1, b_val_rexp(curbp,VAL_PARAGRAPHS)->reg,
  603.                         FORWARD) != TRUE) {
  604.             DOT = curbp->b_line;
  605.         } else if (is_empty_line(DOT)) {
  606.             /* special case -- if we found an empty line. */
  607.             /* either as the very next line, or at the end of
  608.                 our search */
  609.             if ( (was_on_empty && lback(DOT.l) == odot.l) ||
  610.                 (n > 0 && llength(lback(DOT.l)) == 0) ) {
  611.                 /* then we haven't really found what we
  612.                     wanted.  keep going */
  613.                 skipblanksf();
  614.                 continue;
  615.             }
  616.         }
  617.         n--;
  618.     }
  619.     if (doingopcmd) {
  620.         /* if we're now at the beginning of a line and we can back up,
  621.           do so to avoid eating the newline and leading whitespace */
  622.         fc = firstchar(DOT.l);
  623.         if (((fc >= 0 && DOT.o <= fc) || fc < 0) &&
  624.             !is_first_line(DOT,curbp) &&
  625.             !sameline(DOT,odot) ) {
  626.             backchar(TRUE,DOT.o+1);
  627.         }
  628.         /* if we started at the start of line, eat the whole line */
  629.         if (!sameline(DOT,odot) && was_at_bol)
  630.             regionshape = FULLLINE;
  631.     }
  632.     return TRUE;
  633. }
  634.  
  635. static void
  636. skipblanksf(void)
  637. {
  638.     while (lforw(DOT.l) != buf_head(curbp) && is_empty_line(DOT))
  639.         DOT.l = lforw(DOT.l);
  640. }
  641.  
  642. static void
  643. skipblanksb(void)
  644. {
  645.     while (lback(DOT.l) != buf_head(curbp) && is_empty_line(DOT))
  646.         DOT.l = lback(DOT.l);
  647. }
  648.  
  649. #if OPT_STUTTER_SEC_CMD
  650. getstutter(void)
  651. {
  652.     int thiskey;
  653.     if (!clexec) {
  654.         thiskey = lastkey;
  655.         kbd_seq();
  656.         if (thiskey != lastkey) {
  657.             return FALSE;
  658.         }
  659.     }
  660.     return TRUE;
  661. }
  662. #endif
  663.  
  664. /*
  665.  * Go to the beginning of the current section (or paragraph if no section
  666.  * marker found).
  667.  */
  668. int
  669. gotobosec(int f, int n)
  670. {
  671. #if OPT_STUTTER_SEC_CMD
  672.     if (!getstutter())
  673.         return FALSE;
  674. #endif
  675.     if (findpat(f, n, b_val_rexp(curbp,VAL_SECTIONS)->reg,
  676.                             REVERSE) != TRUE) {
  677.         (void)gotobob(f,n);
  678.     }
  679.     return TRUE;
  680. }
  681.  
  682. /*
  683.  * Go to the end of the current section (or paragraph if no section marker
  684.  * found).
  685.  */
  686. int
  687. gotoeosec(int f, int n)
  688. {
  689. #if OPT_STUTTER_SEC_CMD
  690.     if (!getstutter())
  691.         return FALSE;
  692. #endif
  693.     if (findpat(f, n, b_val_rexp(curbp,VAL_SECTIONS)->reg,
  694.                             FORWARD) != TRUE) {
  695.         DOT = curbp->b_line;
  696.     }
  697.     return TRUE;
  698. }
  699.  
  700. /*
  701.  * Go to the beginning of the current sentence. If we skip into an empty line
  702.  * (from a non-empty line), return at that point -- that's what vi does.
  703.  */
  704. int
  705. gotobosent(int f, int n)
  706. {
  707.     MARK savepos;
  708.     int looped = 0;
  709.     int extra;
  710.     int empty = is_empty_line(DOT);
  711.     register regexp *exp;
  712.     register int s = TRUE;
  713.  
  714.     savepos = DOT;
  715.     exp = b_val_rexp(curbp,VAL_SENTENCES)->reg;
  716.  
  717.     while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT)))) {
  718.         s = backchar(TRUE,1);
  719.         if (is_at_end_of_line(DOT) && !empty)
  720.             return TRUE;
  721.     }
  722.  top:
  723.     extra = 0;
  724.     if (findpat(f, n, exp, REVERSE) != TRUE) {
  725.         return gotobob(f,n);
  726.     }
  727.     s = forwchar(TRUE, RegexpLen(exp));
  728.     while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT)))) {
  729.         s = forwchar(TRUE,1);
  730.         extra++;
  731.     }
  732.     if (n == 1 && samepoint(savepos,DOT)) { /* try again */
  733.         if (looped > 10)
  734.             return FALSE;
  735.         s = backchar(TRUE, RegexpLen(exp) + extra + looped);
  736.         while (s && is_at_end_of_line(DOT)) {
  737.             if (!empty && is_empty_line(DOT))
  738.                 return TRUE;
  739.             s = backchar(TRUE,1);
  740.         }
  741.         looped++;
  742.         goto top;
  743.  
  744.     }
  745.     return TRUE;
  746. }
  747.  
  748. /*
  749.  * Go to the end of the current sentence.  Like gotobosent(), if we skip into
  750.  * an empty line, return at that point.
  751.  */
  752. int
  753. gotoeosent(int f, int n)
  754. {
  755.     register regexp *exp;
  756.     register int s;
  757.     int empty = is_empty_line(DOT);
  758.  
  759.     exp = b_val_rexp(curbp,VAL_SENTENCES)->reg;
  760.     /* if we're on the end of a sentence now, don't bother scanning
  761.         further, or we'll miss the immediately following sentence */
  762.     if (!(lregexec(exp, DOT.l, DOT.o, llength(DOT.l)) &&
  763.                 exp->startp[0] - DOT.l->l_text == DOT.o)) {
  764.         if (findpat(f, n, exp, FORWARD) != TRUE) {
  765.             DOT = curbp->b_line;
  766.             return TRUE;
  767.         } else {
  768.             if (!empty && is_at_end_of_line(DOT))
  769.                 return TRUE;
  770.         }
  771.     }
  772.     s = forwchar(TRUE, RegexpLen(exp));
  773.     while (s && (is_at_end_of_line(DOT) || isSpace(char_at(DOT)))) {
  774.         s = forwchar(TRUE,1);
  775.     }
  776.     return TRUE;
  777. }
  778.  
  779.  
  780. /*
  781.  * This routine, given a pointer to a LINE, and the current cursor goal
  782.  * column, return the best choice for the offset. The offset is returned.
  783.  * Used by "C-N" and "C-P".
  784.  */
  785. int
  786. getgoal(LINE *dlp)
  787. {
  788.     register int    c;
  789.     register int    col;
  790.     register int    newcol;
  791.     register int    dbo;
  792.  
  793.     col = 0;
  794.     dbo = w_left_margin(curwp);
  795.     while (dbo < llength(dlp)) {
  796.         c = lgetc(dlp, dbo);
  797.         newcol = next_column(c,col);
  798.         if (newcol > curgoal)
  799.             break;
  800.         col = newcol;
  801.         ++dbo;
  802.     }
  803.     return (dbo);
  804. }
  805.  
  806. int
  807. next_sw(int col)
  808. {
  809.     int width = shiftwid_val(curbp);
  810.     return (((col / width) + 1) * width);
  811. }
  812.  
  813. /* return the next column index, given the current char and column */
  814. int
  815. next_column(int c, int col)
  816. {
  817.     if (c == '\t')
  818.         return nextab(col);
  819.     else if (!isPrint(c))
  820.         return col+2;
  821.     else
  822.         return col+1;
  823. }
  824.  
  825. /*
  826.  * Implements the vi "^F" command.
  827.  *
  828.  * Scroll forward by a specified number of lines, or by a full page if no
  829.  * argument.
  830.  */
  831. int
  832. forwpage(int f, int n)
  833. {
  834.     register LINEPTR lp;
  835.     int    status;
  836.  
  837.     if ((n = full_pages(f,n)) < 0)
  838.         return backpage(f, -n);
  839.  
  840.     if ((status = (lforw(DOT.l) != buf_head(curbp))) == TRUE) {
  841.         lp = curwp->w_line.l;
  842.         n -= line_height(curwp,lp);
  843.         while (lp != buf_head(curbp)) {
  844.             lp = lforw(lp);
  845.             if ((n -= line_height(curwp,lp)) < 0)
  846.                 break;
  847.         }
  848.         if (n < 0)
  849.             curwp->w_line.l = lp;
  850.         DOT.l  = lp;
  851.         (void)firstnonwhite(FALSE,1);
  852.         curwp->w_flag |= WFHARD|WFMODE;
  853.     }
  854.     return status;
  855. }
  856.  
  857. /*
  858.  * Implements the vi "^B" command.
  859.  *
  860.  * This command is like "forwpage", but it goes backwards.
  861.  */
  862. int
  863. backpage(int f, int n)
  864. {
  865.     register LINEPTR lp;
  866.     int    status;
  867.  
  868.     if ((n = full_pages(f,n)) < 0)
  869.         return forwpage(f, -n);
  870.  
  871.     lp = curwp->w_line.l;
  872.     if (lback(lp) != buf_head(curbp)) {
  873.         while ((n -= line_height(curwp,lp)) >= 0
  874.           &&   lback(lp) != buf_head(curbp))
  875.             lp = lback(lp);
  876.         curwp->w_line.l = lp;
  877.         (void)gotoeos(FALSE,1);
  878.         curwp->w_flag |= WFHARD|WFMODE;
  879.         status = TRUE;
  880.     } else if (DOT.l != lp) {
  881.         DOT.l = lp;
  882.         curwp->w_flag |= WFHARD|WFMODE;
  883.         status = TRUE;
  884.     } else {
  885.         status = FALSE;
  886.     }
  887.     return status;
  888. }
  889.  
  890. /*
  891.  * Implements the vi "^D" command.
  892.  *
  893.  * Scroll forward by a half-page.  If a repeat count is given, interpret that
  894.  * as the number of half-pages to scroll.
  895.  *
  896.  * Unlike vi, the OPT_CVMVAS option causes the repeat-count to be interpreted as
  897.  * half-page, rather than lines.
  898.  */
  899. int
  900. forwhpage(int f, int n)
  901. {
  902.     register LINEPTR  llp, dlp;
  903.     int    status;
  904.  
  905.     if ((n = half_pages(f,n)) < 0)
  906.         return backhpage(f, -n);
  907.  
  908.     llp = curwp->w_line.l;
  909.     dlp = DOT.l;
  910.     if ((status = (lforw(dlp) != buf_head(curbp))) == TRUE) {
  911.         n -= line_height(curwp,dlp);
  912.         while (lforw(dlp) != buf_head(curbp)) {
  913.             llp = lforw(llp);
  914.             dlp = lforw(dlp);
  915.             if ((n -= line_height(curwp,dlp)) < 0)
  916.                 break;
  917.         }
  918.         curwp->w_line.l = llp;
  919.         DOT.l  = dlp;
  920.         curwp->w_flag |= WFHARD|WFKILLS;
  921.     }
  922.     (void)firstnonwhite(FALSE,1);
  923.     return status;
  924. }
  925.  
  926. /*
  927.  * Implements the vi "^U" command.
  928.  *
  929.  * This command is like "forwpage", but it goes backwards.  It returns false
  930.  * only if the cursor is on the first line of the buffer.
  931.  *
  932.  * Unlike vi, the OPT_CVMVAS option causes the repeat-count to be interpreted as
  933.  * half-pages, rather than lines.
  934.  */
  935. int
  936. backhpage(int f, int n)
  937. {
  938.     register LINEPTR llp, dlp;
  939.     int    status;
  940.  
  941.     if ((n = half_pages(f,n)) < 0)
  942.         return forwhpage(f, -n);
  943.  
  944.     llp = curwp->w_line.l;
  945.     dlp = DOT.l;
  946.     if ((status = (lback(dlp) != buf_head(curbp))) == TRUE) {
  947.         n -= line_height(curwp,dlp);
  948.         while (lback(dlp) != buf_head(curbp)) {
  949.             llp = lback(llp);
  950.             dlp = lback(dlp);
  951.             if ((n -= line_height(curwp,dlp)) < 0)
  952.                 break;
  953.         }
  954.         curwp->w_line.l = llp;
  955.         DOT.l  = dlp;
  956.         curwp->w_flag |= WFHARD|WFINS;
  957.     }
  958.     (void)firstnonwhite(FALSE,1);
  959.     return status;
  960. }
  961.  
  962. /*
  963.  * Implements the vi "m" command.
  964.  *
  965.  * Set the named mark in the current window to the value of "." in the window.
  966.  */
  967. /* ARGSUSED */
  968. int
  969. setnmmark(int f GCC_UNUSED, int n GCC_UNUSED)
  970. {
  971.     int c,i;
  972.  
  973.     if (clexec || isnamedcmd) {
  974.         int status;
  975.         static char cbuf[2];
  976.         if ((status=mlreply("Set mark: ", cbuf, 2)) != TRUE)
  977.             return status;
  978.         c = cbuf[0];
  979.     } else {
  980.         c = keystroke();
  981.         if (ABORTED(c))
  982.             return ABORT;
  983.     }
  984.  
  985.     if (c < 'a' || c > 'z') {
  986.         mlforce("[Invalid mark name]");
  987.         return FALSE;
  988.     }
  989.  
  990.     if (curbp->b_nmmarks == NULL) {
  991.         curbp->b_nmmarks = typeallocn(struct MARK,26);
  992.         if (curbp->b_nmmarks == NULL)
  993.             return no_memory("named-marks");
  994.         for (i = 0; i < 26; i++) {
  995.             curbp->b_nmmarks[i] = nullmark;
  996.         }
  997.     }
  998.  
  999.     curbp->b_nmmarks[c-'a'] = DOT;
  1000.     mlwrite("[Mark %c set]",c);
  1001.     return TRUE;
  1002. }
  1003.  
  1004. /* ARGSUSED */
  1005. int
  1006. golinenmmark(int f GCC_UNUSED, int n GCC_UNUSED)
  1007. {
  1008.     int c;
  1009.     register int s;
  1010.  
  1011.     s = getnmmarkname(&c);
  1012.     if (s != TRUE)
  1013.         return s;
  1014.     s = gonmmark(c);
  1015.     if (s != TRUE)
  1016.         return s;
  1017.  
  1018.     return firstnonwhite(FALSE,1);
  1019.  
  1020. }
  1021.  
  1022. /* ARGSUSED */
  1023. int
  1024. goexactnmmark(int f GCC_UNUSED, int n GCC_UNUSED)
  1025. {
  1026.     int c;
  1027.     register int s;
  1028.  
  1029.     s = getnmmarkname(&c);
  1030.     if (s != TRUE)
  1031.         return s;
  1032.  
  1033.     return gonmmark(c);
  1034. }
  1035.  
  1036. /* ARGSUSED */
  1037. int
  1038. gorectnmmark(int f GCC_UNUSED, int n GCC_UNUSED)
  1039. {
  1040.     int c;
  1041.     register int s;
  1042.  
  1043.     s = getnmmarkname(&c);
  1044.     if (s != TRUE)
  1045.         return s;
  1046.  
  1047.     regionshape = RECTANGLE;
  1048.     return gonmmark(c);
  1049. }
  1050.  
  1051. /* get the name of the mark to use.  interactively, "last dot" is
  1052.     represented by stuttering the goto-mark command.  from
  1053.     the command line, it's always named ' or `.  I suppose
  1054.     this is questionable. */
  1055. static int
  1056. getnmmarkname(int *cp)
  1057. {
  1058.     int c;
  1059.     int thiskey;
  1060.     int useldmark;
  1061.  
  1062.     if (clexec || isnamedcmd) {
  1063.         int status;
  1064.         static char cbuf[2];
  1065.         if ((status=mlreply("Goto mark: ", cbuf, 2)) != TRUE)
  1066.             return status;
  1067.         c = cbuf[0];
  1068.         useldmark = (c == '\'' || c == '`');
  1069.     } else {
  1070.         thiskey = lastkey;
  1071.         c = keystroke();
  1072.         if (ABORTED(c))
  1073.             return ABORT;
  1074.         useldmark = (lastkey == thiskey);  /* usually '' or `` */
  1075.     }
  1076.  
  1077.     if (useldmark)
  1078.         c = '\'';
  1079.  
  1080.     *cp = c;
  1081.     return TRUE;
  1082. }
  1083.  
  1084. int
  1085. gonmmark(int c)
  1086. {
  1087.     register MARK *markp;
  1088.     MARK tmark;
  1089.     int found;
  1090.  
  1091.     if (!isLower(c) && c != '\'') {
  1092.         mlforce("[Invalid mark name]");
  1093.         return FALSE;
  1094.     }
  1095.  
  1096.     markp = NULL;
  1097.  
  1098.     if (c == '\'') { /* use the 'last dot' mark */
  1099.         markp = &(curwp->w_lastdot);
  1100.     } else if (curbp->b_nmmarks != NULL) {
  1101.         markp = &(curbp->b_nmmarks[c-'a']);
  1102.     }
  1103.  
  1104.     found = FALSE;
  1105.     /* if we have any named marks, and the one we want isn't null */
  1106.     if (markp != NULL && !samepoint((*markp), nullmark)) {
  1107.         register LINE *lp;
  1108.         for_each_line(lp, curbp) {
  1109.             if ((*markp).l == lp) {
  1110.                 found = TRUE;
  1111.                 break;
  1112.             }
  1113.         }
  1114.     }
  1115.     if (!found) {
  1116.         mlforce("[Mark not set]");
  1117.         return (FALSE);
  1118.     }
  1119.  
  1120.     /* save current dot */
  1121.     tmark = DOT;
  1122.  
  1123.     /* move to the selected mark */
  1124.     DOT = *markp;
  1125.  
  1126.     if (!doingopcmd)    /* reset last-dot-mark to old dot */
  1127.         curwp->w_lastdot = tmark;
  1128.  
  1129.     curwp->w_flag |= WFMOVE;
  1130.     return (TRUE);
  1131. }
  1132.  
  1133. /*
  1134.  * Set the mark in the current window to the value of "." in the window. No
  1135.  * errors are possible.
  1136.  */
  1137. int
  1138. setmark(void)
  1139. {
  1140.     MK = DOT;
  1141.     return (TRUE);
  1142. }
  1143.  
  1144. /* ARGSUSED */
  1145. int
  1146. gomark(int f GCC_UNUSED, int n GCC_UNUSED)
  1147. {
  1148.     DOT = MK;
  1149.     curwp->w_flag |= WFMOVE;
  1150.     return (TRUE);
  1151. }
  1152.  
  1153. /* this odd routine puts us at the internal mark, plus an offset of lines */
  1154. /*  n == 1 leaves us at mark, n == 2 one line down, etc. */
  1155. /*  this is for the use of stuttered commands, and line oriented regions */
  1156. int
  1157. godotplus(int f, int n)
  1158. {
  1159.     int s;
  1160.     if (!f || n == 1) {
  1161.         return firstnonwhite(FALSE,1);
  1162.     }
  1163.     if (n < 1)
  1164.         return (FALSE);
  1165.     s = forwline(TRUE,n-1);
  1166.     if (s && is_header_line(DOT, curbp))
  1167.         s = backline(FALSE,1);
  1168.     if (s == TRUE)
  1169.         (void)firstnonwhite(FALSE,1);
  1170.     return s;
  1171. }
  1172.  
  1173. /*
  1174.  * Swap the values of "." and "mark" in the current window. This is pretty
  1175.  * easy, because all of the hard work gets done by the standard routine
  1176.  * that moves the mark about. The only possible error is "no mark".
  1177.  */
  1178. void
  1179. swapmark(void)
  1180. {
  1181.     MARK odot;
  1182.  
  1183.     if (samepoint(MK, nullmark)) {
  1184.         mlforce("BUG: No mark ");
  1185.         return;
  1186.     }
  1187.     odot = DOT;
  1188.     DOT = MK;
  1189.     MK = odot;
  1190.     curwp->w_flag |= WFMOVE;
  1191.     return;
  1192. }
  1193.  
  1194. #if OPT_MOUSE
  1195. /*
  1196.  * Given row & column from the screen, set the MK value.
  1197.  * The resulting position will not be past end-of-buffer unless the buffer
  1198.  * is empty.
  1199.  */
  1200. int
  1201. setwmark(int row, int col)
  1202. {
  1203.     MARK    save;
  1204.     register LINEPTR dlp;
  1205.  
  1206.     save = DOT;
  1207.     if (row == mode_row(curwp)) {
  1208.         (void) gotoeos(FALSE,1);
  1209.         DOT.l = lforw(DOT.l);
  1210.         DOT.o = w_left_margin(curwp);
  1211.     } else {    /* move to the right row */
  1212.         row -= curwp->w_toprow + curwp->w_line.o;
  1213.             /*
  1214.              * In the statement above, wp->w_line.o will
  1215.              * normally be zero.  But when the top line of the
  1216.              * window is wrapped and the beginning of the line
  1217.              * is not visible, it will have the number of
  1218.              * lines that would appear before the top line
  1219.              * negated.  (E.g, if the wrapped line occupied
  1220.              * 2 lines before the top window line, then wp->w_line.o
  1221.              * will have the value -2.)
  1222.              */
  1223.  
  1224.         dlp = curwp->w_line.l;    /* get pointer to 1st line */
  1225.         while ((row -= line_height(curwp,dlp)) >= 0
  1226.           &&   dlp != buf_head(curbp))
  1227.             dlp = lforw(dlp);
  1228.         DOT.l = dlp;            /* set dot line pointer */
  1229.  
  1230.         /* now move the dot over until near the requested column */
  1231. #ifdef WMDLINEWRAP
  1232.         if (w_val(curwp,WMDLINEWRAP))
  1233.             col += term.t_ncol * (row+line_height(curwp,dlp));
  1234. #endif
  1235.         DOT.o = col2offs(curwp, dlp, col);
  1236.  
  1237. #ifdef OPT_MOUSE_NEWLINE
  1238.         /* don't allow the cursor to be set past end of line unless we
  1239.          * are in insert mode
  1240.          */
  1241.         if (DOT.o >= llength(dlp) && DOT.o > w_left_margin(curwp) &&
  1242.                     !insertmode)
  1243.             DOT.o--;
  1244. #endif
  1245.     }
  1246.     if (is_header_line(DOT, curwp->w_bufp)) {
  1247.         DOT.l = lback(DOT.l);
  1248.         DOT.o = llength(DOT.l);
  1249.     }
  1250.     MK  = DOT;
  1251.     DOT = save;
  1252.     return TRUE;
  1253. }
  1254.  
  1255. /*
  1256.  * Given row & column from the screen, set the curwp and DOT values.
  1257.  */
  1258. int
  1259. setcursor (int row, int col)
  1260. {
  1261.     register WINDOW *wp0 = curwp;
  1262.     register WINDOW *wp1;
  1263.     MARK saveMK;
  1264.  
  1265.     if ((wp1 = row2window(row)) == 0)
  1266.         return FALSE;
  1267.     if (doingsweep && curwp != wp1)
  1268.         return FALSE;
  1269.     saveMK = MK;
  1270.     if (set_curwp(wp1)
  1271.      && setwmark(row, col)) {
  1272.         if (insertmode != FALSE
  1273.          && b_val(wp1->w_bufp, MDVIEW)
  1274.          && b_val(wp1->w_bufp, MDSHOWMODE)) {
  1275.             if (b_val(wp0->w_bufp, MDSHOWMODE))
  1276.                 wp0->w_flag |= WFMODE;
  1277.             if (b_val(wp1->w_bufp, MDSHOWMODE))
  1278.                 wp1->w_flag |= WFMODE;
  1279.             insertmode = FALSE;
  1280.         }
  1281.         DOT = MK;
  1282.         if (wp0 == wp1)
  1283.             MK = saveMK;
  1284.         curwp->w_flag |= WFMOVE;
  1285.         return TRUE;
  1286.     }
  1287.  
  1288.     return FALSE;
  1289. }
  1290. #endif
  1291.