]> pd.if.org Git - pd_readline/blobdiff - mg/basic.c
Added mg from an OpenBSD mirror site. Many thanks to the OpenBSD team and the mg...
[pd_readline] / mg / basic.c
diff --git a/mg/basic.c b/mg/basic.c
new file mode 100644 (file)
index 0000000..160d9c1
--- /dev/null
@@ -0,0 +1,532 @@
+/*     $OpenBSD: basic.c,v 1.37 2012/06/18 09:26:03 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 "def.h"
+
+#include <ctype.h>
+
+/*
+ * Go to beginning of line.
+ */
+/* ARGSUSED */
+int
+gotobol(int f, int n)
+{
+       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))
+                                       ewprintf("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)
+{
+       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))
+                                       ewprintf("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.
+ */
+int
+gotobob(int f, int n)
+{
+       (void) setmark(f, n);
+       curwp->w_dotp = bfirstlp(curbp);
+       curwp->w_doto = 0;
+       curwp->w_rflag |= WFFULL;
+       curwp->w_dotline = 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.
+ */
+int
+gotoeob(int f, int n)
+{
+       struct line     *lp;
+       
+       (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;
+       n = curwp->w_ntrows - 3;
+
+       if (n < curwp->w_bufp->b_lines && n >= 3) {
+               while (n--)
+                       curwp->w_dotp = lback(curwp->w_dotp);
+
+               curwp->w_linep = curwp->w_dotp;
+               curwp->w_dotp = lp;
+       }
+       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)
+               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;
+                       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;
+       while (n-- && lback(dlp) != curbp->b_headp) {
+               dlp = lback(dlp);
+               curwp->w_dotline--;
+       }
+       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();          /* 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) {
+                       ttbeep();
+                       ewprintf("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) {
+               ttbeep();
+               ewprintf("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)
+                        return (TRUE);
+               curwp->w_dotp = lback(curwp->w_dotp);
+               curwp->w_dotline--;
+       }
+       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) {
+               ewprintf("No other window");
+               return (FALSE);
+       }
+       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) {
+               ewprintf("No mark in this window");
+               return (FALSE);
+       }
+       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)
+{
+       struct line  *clp;
+       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) {
+                       ewprintf("Line number %s", err);
+                       return (FALSE);
+               }
+       }
+       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);
+}