Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/basic.c
- This file was added.
/* $OpenBSD: basic.c,v 1.50 2021/02/27 13:24:52 lum Exp $ */ | |||||
/* This file is in the public domain */ | |||||
/* | |||||
* Basic cursor motion commands. | |||||
* | |||||
* The routines in this file are the basic | |||||
* command functions for moving the cursor around on | |||||
* the screen, setting mark, and swapping dot with | |||||
* mark. Only moves between lines, which might make the | |||||
* current buffer framing bad, are hard. | |||||
*/ | |||||
#include <sys/queue.h> | |||||
#include <ctype.h> | |||||
#include <limits.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include "def.h" | |||||
#define percint(n1, n2) ((n1 * (int) n2) * 0.1) | |||||
/* | |||||
* Go to beginning of line. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
gotobol(int f, int n) | |||||
{ | |||||
if (n == 0) | |||||
return (TRUE); | |||||
curwp->w_doto = 0; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Move cursor backwards. Do the | |||||
* right thing if the count is less than | |||||
* 0. Error if you try to move back from | |||||
* the beginning of the buffer. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
backchar(int f, int n) | |||||
{ | |||||
struct line *lp; | |||||
if (n < 0) | |||||
return (forwchar(f, -n)); | |||||
while (n--) { | |||||
if (curwp->w_doto == 0) { | |||||
if ((lp = lback(curwp->w_dotp)) == curbp->b_headp) { | |||||
if (!(f & FFRAND)) | |||||
(void)dobeep_msg("Beginning " | |||||
"of buffer"); | |||||
return (FALSE); | |||||
} | |||||
curwp->w_dotp = lp; | |||||
curwp->w_doto = llength(lp); | |||||
curwp->w_rflag |= WFMOVE; | |||||
curwp->w_dotline--; | |||||
} else | |||||
curwp->w_doto--; | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Go to end of line. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
gotoeol(int f, int n) | |||||
{ | |||||
if (n == 0) | |||||
return (TRUE); | |||||
curwp->w_doto = llength(curwp->w_dotp); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Move cursor forwards. Do the | |||||
* right thing if the count is less than | |||||
* 0. Error if you try to move forward | |||||
* from the end of the buffer. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
forwchar(int f, int n) | |||||
{ | |||||
if (n < 0) | |||||
return (backchar(f, -n)); | |||||
while (n--) { | |||||
if (curwp->w_doto == llength(curwp->w_dotp)) { | |||||
curwp->w_dotp = lforw(curwp->w_dotp); | |||||
if (curwp->w_dotp == curbp->b_headp) { | |||||
curwp->w_dotp = lback(curwp->w_dotp); | |||||
if (!(f & FFRAND)) | |||||
(void)dobeep_msg("End of buffer"); | |||||
return (FALSE); | |||||
} | |||||
curwp->w_doto = 0; | |||||
curwp->w_dotline++; | |||||
curwp->w_rflag |= WFMOVE; | |||||
} else | |||||
curwp->w_doto++; | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Go to the beginning of the buffer. Setting WFFULL is conservative, | |||||
* but almost always the case. A universal argument of higher than 9 | |||||
* puts the cursor back to the end of buffer. | |||||
*/ | |||||
int | |||||
gotobob(int f, int n) | |||||
{ | |||||
if (!curwp->w_markp) | |||||
(void) setmark(f, n); | |||||
curwp->w_dotp = bfirstlp(curbp); | |||||
curwp->w_doto = 0; | |||||
curwp->w_rflag |= WFFULL; | |||||
curwp->w_dotline = 1; | |||||
if (f & FFOTHARG && n > 0) { | |||||
if (n > 9) | |||||
gotoeob(0, 0); | |||||
else | |||||
forwline(f, percint(curwp->w_bufp->b_lines, n) - 1); | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Go to the end of the buffer. Leave dot 3 lines from the bottom of the | |||||
* window if buffer length is longer than window length; same as emacs. | |||||
* Setting WFFULL is conservative, but almost always the case. A universal | |||||
* argument of higher than 9 puts the cursor back to the start of buffer. | |||||
*/ | |||||
int | |||||
gotoeob(int f, int n) | |||||
{ | |||||
int ln; | |||||
struct line *lp; | |||||
if (!curwp->w_markp) | |||||
(void) setmark(f, n); | |||||
curwp->w_dotp = blastlp(curbp); | |||||
curwp->w_doto = llength(curwp->w_dotp); | |||||
curwp->w_dotline = curwp->w_bufp->b_lines; | |||||
lp = curwp->w_dotp; | |||||
ln = curwp->w_ntrows - 3; | |||||
if (ln < curwp->w_bufp->b_lines && ln >= 3) { | |||||
while (ln--) | |||||
curwp->w_dotp = lback(curwp->w_dotp); | |||||
curwp->w_linep = curwp->w_dotp; | |||||
curwp->w_dotp = lp; | |||||
} | |||||
if (f & FFOTHARG && n > 0) { | |||||
if (n > 9) | |||||
gotobob(0, 0); | |||||
else | |||||
backline(f, percint(curwp->w_bufp->b_lines, n)); | |||||
} | |||||
curwp->w_rflag |= WFFULL; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* 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. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
forwline(int f, int n) | |||||
{ | |||||
struct line *dlp; | |||||
if (n < 0) | |||||
return (backline(f | FFRAND, -n)); | |||||
if ((dlp = curwp->w_dotp) == curbp->b_headp) { | |||||
if (!(f & FFRAND)) | |||||
(void)dobeep_msg("End of buffer"); | |||||
return(TRUE); | |||||
} | |||||
if ((lastflag & CFCPCN) == 0) /* Fix goal. */ | |||||
setgoal(); | |||||
thisflag |= CFCPCN; | |||||
if (n == 0) | |||||
return (TRUE); | |||||
while (n--) { | |||||
dlp = lforw(dlp); | |||||
if (dlp == curbp->b_headp) { | |||||
curwp->w_dotp = lback(dlp); | |||||
curwp->w_doto = llength(curwp->w_dotp); | |||||
curwp->w_rflag |= WFMOVE; | |||||
if (!(f & FFRAND)) | |||||
(void)dobeep_msg("End of buffer"); | |||||
return (TRUE); | |||||
} | |||||
curwp->w_dotline++; | |||||
} | |||||
curwp->w_rflag |= WFMOVE; | |||||
curwp->w_dotp = dlp; | |||||
curwp->w_doto = getgoal(dlp); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* This function is like "forwline", but | |||||
* goes backwards. The scheme is exactly the same. | |||||
* Check for arguments that are less than zero and | |||||
* call your alternate. Figure out the new line and | |||||
* call "movedot" to perform the motion. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
backline(int f, int n) | |||||
{ | |||||
struct line *dlp; | |||||
if (n < 0) | |||||
return (forwline(f | FFRAND, -n)); | |||||
if ((lastflag & CFCPCN) == 0) /* Fix goal. */ | |||||
setgoal(); | |||||
thisflag |= CFCPCN; | |||||
dlp = curwp->w_dotp; | |||||
if (lback(dlp) == curbp->b_headp) { | |||||
if (!(f & FFRAND)) | |||||
(void)dobeep_msg("Beginning of buffer"); | |||||
return(TRUE); | |||||
} | |||||
while (n-- && lback(dlp) != curbp->b_headp) { | |||||
dlp = lback(dlp); | |||||
curwp->w_dotline--; | |||||
} | |||||
if (n > 0 && !(f & FFRAND)) | |||||
(void)dobeep_msg("Beginning of buffer"); | |||||
curwp->w_dotp = dlp; | |||||
curwp->w_doto = getgoal(dlp); | |||||
curwp->w_rflag |= WFMOVE; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Set the current goal column, which is saved in the external variable | |||||
* "curgoal", to the current cursor column. The column is never off | |||||
* the edge of the screen; it's more like display then show position. | |||||
*/ | |||||
void | |||||
setgoal(void) | |||||
{ | |||||
curgoal = getcolpos(curwp); /* Get the position. */ | |||||
/* we can now display past end of display, don't chop! */ | |||||
} | |||||
/* | |||||
* This routine looks at a line (pointed | |||||
* to by the LINE pointer "dlp") and the current | |||||
* vertical motion goal column (set by the "setgoal" | |||||
* routine above) and returns the best offset to use | |||||
* when a vertical motion is made into the line. | |||||
*/ | |||||
int | |||||
getgoal(struct line *dlp) | |||||
{ | |||||
int c, i, col = 0; | |||||
char tmp[5]; | |||||
for (i = 0; i < llength(dlp); i++) { | |||||
c = lgetc(dlp, i); | |||||
if (c == '\t' | |||||
#ifdef NOTAB | |||||
&& !(curbp->b_flag & BFNOTAB) | |||||
#endif | |||||
) { | |||||
col |= 0x07; | |||||
col++; | |||||
} else if (ISCTRL(c) != FALSE) { | |||||
col += 2; | |||||
} else if (isprint(c)) | |||||
col++; | |||||
else { | |||||
col += snprintf(tmp, sizeof(tmp), "\\%o", c); | |||||
} | |||||
if (col > curgoal) | |||||
break; | |||||
} | |||||
return (i); | |||||
} | |||||
/* | |||||
* Scroll forward by a specified number | |||||
* of lines, or by a full page if no argument. | |||||
* The "2" is the window overlap (this is the default | |||||
* value from ITS EMACS). Because the top line in | |||||
* the window is zapped, we have to do a hard | |||||
* update and get it back. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
forwpage(int f, int n) | |||||
{ | |||||
struct line *lp; | |||||
if (!(f & FFARG)) { | |||||
n = curwp->w_ntrows - 2; /* Default scroll. */ | |||||
if (n <= 0) /* Forget the overlap */ | |||||
n = 1; /* if tiny window. */ | |||||
} else if (n < 0) | |||||
return (backpage(f | FFRAND, -n)); | |||||
lp = curwp->w_linep; | |||||
while (n--) | |||||
if ((lp = lforw(lp)) == curbp->b_headp) { | |||||
(void)dobeep_msg("End of buffer"); | |||||
return(TRUE); | |||||
} | |||||
curwp->w_linep = lp; | |||||
curwp->w_rflag |= WFFULL; | |||||
/* if in current window, don't move dot */ | |||||
for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp)) | |||||
if (lp == curwp->w_dotp) | |||||
return (TRUE); | |||||
/* Advance the dot the slow way, for line nos */ | |||||
while (curwp->w_dotp != curwp->w_linep) { | |||||
curwp->w_dotp = lforw(curwp->w_dotp); | |||||
curwp->w_dotline++; | |||||
} | |||||
curwp->w_doto = 0; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* This command is like "forwpage", | |||||
* but it goes backwards. The "2", like above, | |||||
* is the overlap between the two windows. The | |||||
* value is from the ITS EMACS manual. The | |||||
* hard update is done because the top line in | |||||
* the window is zapped. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
backpage(int f, int n) | |||||
{ | |||||
struct line *lp, *lp2; | |||||
if (!(f & FFARG)) { | |||||
n = curwp->w_ntrows - 2; /* Default scroll. */ | |||||
if (n <= 0) /* Don't blow up if the */ | |||||
return (backline(f, 1));/* window is tiny. */ | |||||
} else if (n < 0) | |||||
return (forwpage(f | FFRAND, -n)); | |||||
lp = lp2 = curwp->w_linep; | |||||
while (n-- && lback(lp) != curbp->b_headp) { | |||||
lp = lback(lp); | |||||
} | |||||
if (lp == curwp->w_linep) | |||||
(void)dobeep_msg("Beginning of buffer"); | |||||
curwp->w_linep = lp; | |||||
curwp->w_rflag |= WFFULL; | |||||
/* if in current window, don't move dot */ | |||||
for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp)) | |||||
if (lp == curwp->w_dotp) | |||||
return (TRUE); | |||||
lp2 = lforw(lp2); | |||||
/* Move the dot the slow way, for line nos */ | |||||
while (curwp->w_dotp != lp2) { | |||||
if (curwp->w_dotline <= curwp->w_ntrows) | |||||
goto out; | |||||
curwp->w_dotp = lback(curwp->w_dotp); | |||||
curwp->w_dotline--; | |||||
} | |||||
out: | |||||
curwp->w_doto = 0; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* These functions are provided for compatibility with Gosling's Emacs. They | |||||
* are used to scroll the display up (or down) one line at a time. | |||||
*/ | |||||
int | |||||
forw1page(int f, int n) | |||||
{ | |||||
if (!(f & FFARG)) { | |||||
n = 1; | |||||
f = FFUNIV; | |||||
} | |||||
forwpage(f | FFRAND, n); | |||||
return (TRUE); | |||||
} | |||||
int | |||||
back1page(int f, int n) | |||||
{ | |||||
if (!(f & FFARG)) { | |||||
n = 1; | |||||
f = FFUNIV; | |||||
} | |||||
backpage(f | FFRAND, n); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Page the other window. Check to make sure it exists, then | |||||
* nextwind, forwpage and restore window pointers. | |||||
*/ | |||||
int | |||||
pagenext(int f, int n) | |||||
{ | |||||
struct mgwin *wp; | |||||
if (wheadp->w_wndp == NULL) | |||||
return(dobeep_msg("No other window")); | |||||
wp = curwp; | |||||
(void) nextwind(f, n); | |||||
(void) forwpage(f, n); | |||||
curwp = wp; | |||||
curbp = wp->w_bufp; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Internal set mark routine, used by other functions (daveb). | |||||
*/ | |||||
void | |||||
isetmark(void) | |||||
{ | |||||
curwp->w_markp = curwp->w_dotp; | |||||
curwp->w_marko = curwp->w_doto; | |||||
curwp->w_markline = curwp->w_dotline; | |||||
} | |||||
/* | |||||
* Set the mark in the current window | |||||
* to the value of dot. A message is written to | |||||
* the echo line. (ewprintf knows about macros) | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
setmark(int f, int n) | |||||
{ | |||||
isetmark(); | |||||
ewprintf("Mark set"); | |||||
return (TRUE); | |||||
} | |||||
/* Clear the mark, if set. */ | |||||
/* ARGSUSED */ | |||||
int | |||||
clearmark(int f, int n) | |||||
{ | |||||
if (!curwp->w_markp) | |||||
return (FALSE); | |||||
curwp->w_markp = NULL; | |||||
curwp->w_marko = 0; | |||||
curwp->w_markline = 0; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Swap the values of "dot" 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". | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
swapmark(int f, int n) | |||||
{ | |||||
struct line *odotp; | |||||
int odoto, odotline; | |||||
if (curwp->w_markp == NULL) | |||||
return(dobeep_msg("No mark in this window")); | |||||
odotp = curwp->w_dotp; | |||||
odoto = curwp->w_doto; | |||||
odotline = curwp->w_dotline; | |||||
curwp->w_dotp = curwp->w_markp; | |||||
curwp->w_doto = curwp->w_marko; | |||||
curwp->w_dotline = curwp->w_markline; | |||||
curwp->w_markp = odotp; | |||||
curwp->w_marko = odoto; | |||||
curwp->w_markline = odotline; | |||||
curwp->w_rflag |= WFMOVE; | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Go to a specific line, mostly for | |||||
* looking up errors in C programs, which give the | |||||
* error a line number. If an argument is present, then | |||||
* it is the line number, else prompt for a line number | |||||
* to use. | |||||
*/ | |||||
/* ARGSUSED */ | |||||
int | |||||
gotoline(int f, int n) | |||||
{ | |||||
char buf[32], *bufp; | |||||
const char *err; | |||||
if (!(f & FFARG)) { | |||||
if ((bufp = eread("Goto line: ", buf, sizeof(buf), | |||||
EFNUL | EFNEW | EFCR)) == NULL) | |||||
return (ABORT); | |||||
if (bufp[0] == '\0') | |||||
return (ABORT); | |||||
n = (int)strtonum(buf, INT_MIN, INT_MAX, &err); | |||||
if (err) | |||||
return(dobeep_msgs("Line number %s", err)); | |||||
} | |||||
return(setlineno(n)); | |||||
} | |||||
/* | |||||
* Set the line number and switch to it. | |||||
*/ | |||||
int | |||||
setlineno(int n) | |||||
{ | |||||
struct line *clp; | |||||
if (n >= 0) { | |||||
if (n == 0) | |||||
n++; | |||||
curwp->w_dotline = n; | |||||
clp = lforw(curbp->b_headp); /* "clp" is first line */ | |||||
while (--n > 0) { | |||||
if (lforw(clp) == curbp->b_headp) { | |||||
curwp->w_dotline = curwp->w_bufp->b_lines; | |||||
break; | |||||
} | |||||
clp = lforw(clp); | |||||
} | |||||
} else { | |||||
curwp->w_dotline = curwp->w_bufp->b_lines + n; | |||||
clp = lback(curbp->b_headp); /* "clp" is last line */ | |||||
while (n < 0) { | |||||
if (lback(clp) == curbp->b_headp) { | |||||
curwp->w_dotline = 1; | |||||
break; | |||||
} | |||||
clp = lback(clp); | |||||
n++; | |||||
} | |||||
} | |||||
curwp->w_dotp = clp; | |||||
curwp->w_doto = 0; | |||||
curwp->w_rflag |= WFMOVE; | |||||
return (TRUE); | |||||
} |