Page MenuHomeFreeBSD

D34411.diff
No OneTemporary

D34411.diff

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: contrib/mg/NOTES.md
===================================================================
--- /dev/null
+++ contrib/mg/NOTES.md
@@ -0,0 +1,5 @@
+NOTES
+
+* On some Linux distributions, the backspace key doesn't work
+ correctly. This can be fixed with `bsmap-mode`; this should probably
+ be placed in the user's `$HOME/.mg` file.
Index: contrib/mg/README-Mg
===================================================================
--- /dev/null
+++ contrib/mg/README-Mg
@@ -0,0 +1,93 @@
+[This is an edited version of the original mg README, updated slightly to
+reflect changes in the last 20 years.]
+
+
+Mg (mg) is a Public Domain EMACS style editor. It is "broadly"
+compatible with GNU Emacs, the latest creation of Richard M.
+Stallman, Chief GNUisance and inventor of Emacs. GNU Emacs (and other
+portions of GNU as they are released) are essentially free, (there are
+handling charges for obtaining it) and so is Mg. You may never have
+to learn another editor. (But probably will, at least long enough to
+port Mg...) Mg was formerly named MicroGnuEmacs, the name change was
+done at the request of Richard Stallman.
+
+Mg is not associated with the GNU project, and it does not have the
+copyright restrictions present in GNU Emacs. (However, some modules
+do have copyright notices.) The Mg authors individually may or may
+not agree with the opinions expressed by Richard Stallman in "The GNU
+Manifesto".
+
+This program is intended to be a small, fast, and portable editor for
+people who can't (or don't want to) run real Emacs for one reason
+or another. It is compatible with GNU because there shouldn't be
+any reason to learn more than one Emacs flavor.
+
+
+Beyond the work of Dave Conroy, author of the original public domain
+v30, the current version contains the work of:
+
+ blarson@ecla.usc.edu Bob Larson
+ mic@emx.utexas.edu Mic Kaczmarczik
+ mwm@violet.berkeley.edu Mike Meyer
+ sandra@cs.utah.edu Sandra Loosemore
+ mp1u+@andrew.cmu.edu Michael Portuesi
+ RCKG01M@CALSTATE.BITNET Stephen Walton
+ hakanson@mist.cs.orst.edu Marion Hakanson
+
+People who have worked on previous versions of Mg:
+
+ rtech!daveb@sun.com Dave Brower
+
+Early release history:
+
+* Nov 16, 1986: First release to mod.sources
+* Mar 3, 1987: First Release (mg1a) via comp.sources.unix
+* May 26, 1988: Second release: (mg2a) via comp.sources.misc
+* Jan 26, 1992: Linux port released by Charles Hedrick. This version
+ later makes its way onto tsx-11, Infomagic, and various other Linux
+ repositories.
+* Feb 25, 2000: First import into the OpenBSD tree, where it is
+ currently maintained with contributions from many others.
+
+----------------------------------------------------------------------
+
+Known limitations:
+
+Recursive bindings may cause help and key rebinding code to go into
+an infinite loop, aborting with a stack overflow.
+
+Overwrite mode does not work in macros. (Characters are inserted
+rather than overwriting.)
+
+Dired mode has some problems: Rename does not update the buffer.
+Doing a dired again will update the buffer (whether it needs it or
+not) and will lose any marks for deletion. .. and . are not
+recognized as special cases.
+
+On systems with 16 bit integers, the kill buffer cannot exceed 32767
+bytes.
+
+Unlike GNU Emacs, Mg's minibuffer isn't multi-line aware and hence
+some commands like "shell-command-on-region" always pop up a buffer to
+display output irrespective of output's size.
+
+While navigating source code using Mg's cscope commands, the cursor
+is always at the match location rather than in *cscope* buffer. Mg uses
+the same keybindings of GNU Emacs's xcscope package for it's cscope commands.
+As Mg's keybindings are case-insensitive some of the commands don't have a
+default keybinding.
+
+New implementation oddities:
+
+insert and define-key are new commands corresponding to the mocklisp
+functions in GNU Emacs. (Mg does not have non-command functions.)
+(Mg's insert will only insert one string.)
+
+The display wrap code does not work at all like that of GNU emacs.
+
+Some commands that do not mimic emacs exactly don't have a "standard"
+emacs name. For example 'backup-to-home-directory' is only a partial
+implementation of emacs' range of commands that allow a user to
+customise the backup file location. If a more complete implementation
+were coded of these commands the non standard commands would probably
+be removed.
Index: contrib/mg/README.md
===================================================================
--- /dev/null
+++ contrib/mg/README.md
@@ -0,0 +1,51 @@
+mg
+==
+This is a portable version of the Mg editor from OpenBSD.
+
+Mg is intended to be a small, fast, and portable editor for people who
+can't (or don't want to) run emacs for one reason or another, or are not
+familiar with the vi editor. It is compatible with emacs because there
+shouldn't be any reason to learn more editor types than emacs or vi.
+
+This repository aggressively tracks upstream.
+
+Compiling
+---------
+`mg` has a simple configure script that generates a `POSIX` `Makefile`.
+```
+$ ./configure
+$ make
+$ sudo make install
+```
+
+Dependencies
+------------
+By default, you need the ncurses library.
+
+NetBSD users will use the in-base NetBSD curses library.
+
+If you do not have the ncurses library, you can call `configure` with the
+`--with-builtin-curses` flag to compile with a simplified version of the
+NetBSD curses library. In this setup, there are no dependencies other than
+the system's libc.
+
+Testing
+-------
+Tested on recent versions of Arch, Alpine, Cygwin, Debian, DragonFly BSD,
+FreeBSD, Mac OS X (10.4 or later), NetBSD, Slackware, and Ubuntu.
+
+Licensing
+---------
+Files originating from `mg` are Public Domain. Files needed for portability
+have their own individual license headers.
+All licenses are ISC or BSD.
+
+Commonly asked questions
+------------------------
+`mg` does not yet support UTF-8. If you would like to work on this, please
+reach out to the tech@ mailing list on OpenBSD.
+
+Get a tarball
+-------------
+See the Releases tab on GitHub.
+The latest version is mg-7.0.
Index: contrib/mg/autoexec.c
===================================================================
--- /dev/null
+++ contrib/mg/autoexec.c
@@ -0,0 +1,116 @@
+/* $OpenBSD: autoexec.c,v 1.18 2021/04/21 14:45:28 lum Exp $ */
+/* this file is in the public domain */
+/* Author: Vincent Labrecque <vincent@openbsd.org> April 2002 */
+
+#include <sys/queue.h>
+#include <fnmatch.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+#include "funmap.h"
+
+struct autoexec {
+ SLIST_ENTRY(autoexec) next; /* link in the linked list */
+ const char *pattern; /* Pattern to match to filenames */
+ PF fp;
+};
+
+static SLIST_HEAD(, autoexec) autos;
+static int ready;
+
+
+#define AUTO_GROW 8
+/*
+ * Return a NULL terminated array of function pointers to be called
+ * when we open a file that matches <fname>. The list must be free(ed)
+ * after use.
+ */
+PF *
+find_autoexec(const char *fname)
+{
+ PF *pfl, *npfl;
+ int have, used;
+ struct autoexec *ae;
+
+ if (!ready)
+ return (NULL);
+
+ pfl = NULL;
+ have = 0;
+ used = 0;
+ SLIST_FOREACH(ae, &autos, next) {
+ if (fnmatch(ae->pattern, fname, 0) == 0) {
+ if (used >= have) {
+ npfl = reallocarray(pfl, have + AUTO_GROW + 1,
+ sizeof(PF));
+ if (npfl == NULL)
+ panic("out of memory");
+ pfl = npfl;
+ have += AUTO_GROW;
+ }
+ pfl[used++] = ae->fp;
+ }
+ }
+ if (used)
+ pfl[used] = NULL;
+
+ return (pfl);
+}
+
+int
+add_autoexec(const char *pattern, const char *func)
+{
+ PF fp;
+ struct autoexec *ae;
+
+ if (!ready) {
+ SLIST_INIT(&autos);
+ ready = 1;
+ }
+ fp = name_function(func);
+ if (fp == NULL)
+ return (FALSE);
+ ae = malloc(sizeof(*ae));
+ if (ae == NULL)
+ return (FALSE);
+ ae->fp = fp;
+ ae->pattern = strdup(pattern);
+ if (ae->pattern == NULL) {
+ free(ae);
+ return (FALSE);
+ }
+ SLIST_INSERT_HEAD(&autos, ae, next);
+
+ return (TRUE);
+}
+
+/*
+ * Register an auto-execute hook; that is, specify a filename pattern
+ * (conforming to the shell's filename globbing rules) and an associated
+ * function to execute when a file matching the specified pattern
+ * is read into a buffer.
+*/
+/* ARGSUSED */
+int
+auto_execute(int f, int n)
+{
+ char patbuf[BUFSIZE], funcbuf[BUFSIZE], *patp, *funcp;
+ int s;
+
+ if ((patp = eread("Filename pattern: ", patbuf, sizeof(patbuf),
+ EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (patp[0] == '\0')
+ return (FALSE);
+ if ((funcp = eread("Execute: ", funcbuf, sizeof(funcbuf),
+ EFNEW | EFCR | EFFUNC)) == NULL)
+ return (ABORT);
+ else if (funcp[0] == '\0')
+ return (FALSE);
+ if ((s = add_autoexec(patp, funcp)) != TRUE)
+ return (s);
+ return (TRUE);
+}
Index: contrib/mg/basic.c
===================================================================
--- /dev/null
+++ contrib/mg/basic.c
@@ -0,0 +1,580 @@
+/* $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);
+}
Index: contrib/mg/bell.c
===================================================================
--- /dev/null
+++ contrib/mg/bell.c
@@ -0,0 +1,90 @@
+/* $OpenBSD: bell.c,v 1.6 2021/05/06 12:44:21 lum Exp $ */
+
+/*
+ * This file is in the public domain.
+ *
+ * Author: Mark Lumsden <mark@showcomplex.com>
+ *
+ */
+
+/*
+ * Control how mg communicates with the user.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "def.h"
+#include "macro.h"
+
+void
+bellinit(void)
+{
+ doaudiblebell = 1;
+ dovisiblebell = 0;
+}
+
+int
+dobeep_num(const char *msg, int n)
+{
+ ewprintf("%s %d", msg, n);
+ dobeep();
+ return (FALSE);
+}
+
+int
+dobeep_msgs(const char *msg, const char *s)
+{
+ ewprintf("%s %s", msg, s);
+ dobeep();
+ return (FALSE);
+}
+
+int
+dobeep_msg(const char *msg)
+{
+ ewprintf("%s", msg);
+ dobeep();
+ return (FALSE);
+}
+
+void
+dobeep(void)
+{
+ if (doaudiblebell) {
+ ttbeep();
+ }
+ if (dovisiblebell) {
+ sgarbf = TRUE;
+ update(CNONE);
+ if (inmacro) /* avoid delaying macro execution. */
+ return;
+ usleep(50000);
+ }
+}
+
+/* ARGSUSED */
+int
+toggleaudiblebell(int f, int n)
+{
+ if (f & FFARG)
+ doaudiblebell = n > 0;
+ else
+ doaudiblebell = !doaudiblebell;
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+togglevisiblebell(int f, int n)
+{
+ if (f & FFARG)
+ dovisiblebell = n > 0;
+ else
+ dovisiblebell = !dovisiblebell;
+
+ return (TRUE);
+}
Index: contrib/mg/buffer.c
===================================================================
--- /dev/null
+++ contrib/mg/buffer.c
@@ -0,0 +1,1070 @@
+/* $OpenBSD: buffer.c,v 1.112 2021/03/26 15:02:10 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Buffer handling.
+ */
+
+#include <sys/queue.h>
+#include <errno.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+#include "kbd.h" /* needed for modes */
+
+#define DIFFTOOL "/usr/bin/diff"
+
+static struct buffer *makelist(void);
+static struct buffer *bnew(const char *);
+
+static int usebufname(const char *);
+
+/* Flag for global working dir */
+extern int globalwd;
+
+/* ARGSUSED */
+int
+togglereadonlyall(int f, int n)
+{
+ struct buffer *bp = NULL;
+ int len = 0;
+
+ allbro = !allbro;
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ len = strlen(bp->b_bname);
+ if (bp->b_bname[0] != '*' && bp->b_bname[len - 1] != '*') {
+ if (allbro)
+ bp->b_flag |= BFREADONLY;
+ else
+ bp->b_flag &= ~BFREADONLY;
+ }
+ }
+ curwp->w_rflag |= WFMODE;
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+togglereadonly(int f, int n)
+{
+ int s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (!(curbp->b_flag & BFREADONLY))
+ curbp->b_flag |= BFREADONLY;
+ else {
+ curbp->b_flag &= ~BFREADONLY;
+ if (curbp->b_flag & BFCHG)
+ ewprintf("Warning: Buffer was modified");
+ }
+ curwp->w_rflag |= WFMODE;
+
+ return (TRUE);
+}
+
+/* Switch to the named buffer.
+ * If no name supplied, switch to the default (alternate) buffer.
+ */
+int
+usebufname(const char *bufp)
+{
+ struct buffer *bp = NULL;
+ int ret;
+
+ if (bufp == NULL) {
+ if ((bp = bfind("*scratch*", TRUE)) == NULL)
+ return(FALSE);
+ } else if (bufp[0] == '\0' && curbp->b_altb != NULL)
+ bp = curbp->b_altb;
+ else if ((bp = bfind(bufp, TRUE)) == NULL)
+ return (FALSE);
+
+ /* and put it in current window */
+ curbp = bp;
+ ret = showbuffer(bp, curwp, WFFRAME | WFFULL);
+ eerase();
+
+ return (ret);
+}
+
+/*
+ * Attach a buffer to a window. The values of dot and mark come
+ * from the buffer if the use count is 0. Otherwise, they come
+ * from some other window. *scratch* is the default alternate
+ * buffer.
+ */
+/* ARGSUSED */
+int
+usebuffer(int f, int n)
+{
+ char bufn[NBUFN], *bufp;
+
+ /* Get buffer to use from user */
+ if (curbp->b_altb == NULL)
+ bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF);
+ else
+ bufp = eread("Switch to buffer (default %s): ", bufn, NBUFN,
+ EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
+
+ if (bufp == NULL)
+ return (ABORT);
+
+ return (usebufname(bufp));
+}
+
+/*
+ * pop to buffer asked for by the user.
+ */
+/* ARGSUSED */
+int
+poptobuffer(int f, int n)
+{
+ struct buffer *bp;
+ struct mgwin *wp;
+ char bufn[NBUFN], *bufp;
+
+ /* Get buffer to use from user */
+ if ((curbp->b_altb == NULL) &&
+ ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL))
+ bufp = eread("Switch to buffer in other window: ", bufn, NBUFN,
+ EFNEW | EFBUF);
+ else
+ bufp = eread("Switch to buffer in other window (default %s): ",
+ bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
+ if (bufp == NULL)
+ return (ABORT);
+ if (bufp[0] == '\0' && curbp->b_altb != NULL)
+ bp = curbp->b_altb;
+ else if ((bp = bfind(bufp, TRUE)) == NULL)
+ return (FALSE);
+ if (bp == curbp)
+ return (splitwind(f, n));
+ /* and put it in a new, non-ephemeral window */
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ return (TRUE);
+}
+
+/*
+ * Dispose of a buffer, by name.
+ * Ask for the name (unless called by dired mode). Look it up (don't
+ * get too upset if it isn't there at all!). Clear the buffer (ask
+ * if the buffer has been changed). Then free the header
+ * line and the buffer header. Bound to "C-x k".
+ */
+/* ARGSUSED */
+int
+killbuffer_cmd(int f, int n)
+{
+ struct buffer *bp;
+ char bufn[NBUFN], *bufp;
+ int ret;
+
+ if (f & FFRAND) /* dired mode 'q' */
+ bp = curbp;
+ else if ((bufp = eread("Kill buffer (default %s): ", bufn, NBUFN,
+ EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ bp = curbp;
+ else if ((bp = bfind(bufp, FALSE)) == NULL)
+ return (FALSE);
+ ret = killbuffer(bp);
+ eerase();
+
+ return (ret);
+}
+
+int
+killbuffer(struct buffer *bp)
+{
+ struct buffer *bp1;
+ struct buffer *bp2;
+ struct mgwin *wp;
+ int s;
+ struct undo_rec *rec;
+
+ /*
+ * Find some other buffer to display. Try the alternate buffer,
+ * then the first different buffer in the buffer list. If there's
+ * only one buffer, create buffer *scratch* and make it the alternate
+ * buffer. Return if *scratch* is only buffer...
+ */
+ if ((bp1 = bp->b_altb) == NULL) {
+ /* only one buffer. see if it's *scratch* */
+ if (bp == bfind("*scratch*", FALSE))
+ return (TRUE);
+ /* create *scratch* for alternate buffer */
+ if ((bp1 = bfind("*scratch*", TRUE)) == NULL)
+ return (FALSE);
+ }
+ if ((s = bclear(bp)) != TRUE)
+ return (s);
+ for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) {
+ if (wp->w_bufp == bp) {
+ bp2 = bp1->b_altb; /* save alternate buffer */
+ if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL))
+ bp1->b_altb = bp2;
+ else
+ bp1 = bp2;
+ }
+ }
+ if (bp == curbp)
+ curbp = bp1;
+ free(bp->b_headp); /* Release header line. */
+ bp2 = NULL; /* Find the header. */
+ bp1 = bheadp;
+ while (bp1 != bp) {
+ if (bp1->b_altb == bp)
+ bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
+ bp2 = bp1;
+ bp1 = bp1->b_bufp;
+ }
+ bp1 = bp1->b_bufp; /* Next one in chain. */
+ if (bp2 == NULL) /* Unlink it. */
+ bheadp = bp1;
+ else
+ bp2->b_bufp = bp1;
+ while (bp1 != NULL) { /* Finish with altb's */
+ if (bp1->b_altb == bp)
+ bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb;
+ bp1 = bp1->b_bufp;
+ }
+
+ while ((rec = TAILQ_FIRST(&bp->b_undo))) {
+ TAILQ_REMOVE(&bp->b_undo, rec, next);
+ free_undo_record(rec);
+ }
+
+ free(bp->b_bname); /* Release name block */
+ free(bp); /* Release buffer block */
+ return (TRUE);
+}
+
+/*
+ * Save some buffers - just call anycb with the arg flag.
+ */
+/* ARGSUSED */
+int
+savebuffers(int f, int n)
+{
+ if (anycb(f) == ABORT)
+ return (ABORT);
+ return (TRUE);
+}
+
+/*
+ * Listing buffers.
+ */
+static int listbuf_ncol;
+
+static int listbuf_goto_buffer(int f, int n);
+static int listbuf_goto_buffer_one(int f, int n);
+static int listbuf_goto_buffer_helper(int f, int n, int only);
+
+static PF listbuf_pf[] = {
+ listbuf_goto_buffer
+};
+static PF listbuf_one[] = {
+ listbuf_goto_buffer_one
+};
+
+
+static struct KEYMAPE (2) listbufmap = {
+ 2,
+ 2,
+ rescan,
+ {
+ {
+ CCHR('M'), CCHR('M'), listbuf_pf, NULL
+ },
+ {
+ '1', '1', listbuf_one, NULL
+ }
+ }
+};
+
+/*
+ * Display the buffer list. This is done
+ * in two parts. The "makelist" routine figures out
+ * the text, and puts it in a buffer. "popbuf"
+ * then pops the data onto the screen. Bound to
+ * "C-x C-b".
+ */
+/* ARGSUSED */
+int
+listbuffers(int f, int n)
+{
+ static int initialized = 0;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ if (!initialized) {
+ maps_add((KEYMAP *)&listbufmap, "listbufmap");
+ initialized = 1;
+ }
+
+ if ((bp = makelist()) == NULL || (wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */
+ wp->w_doto = bp->b_doto;
+ bp->b_modes[0] = name_mode("fundamental");
+ bp->b_modes[1] = name_mode("listbufmap");
+ bp->b_nmodes = 1;
+
+ return (TRUE);
+}
+
+/*
+ * This routine rebuilds the text for the
+ * list buffers command. Return pointer
+ * to new list if everything works.
+ * Return NULL if there is an error (if
+ * there is no memory).
+ */
+static struct buffer *
+makelist(void)
+{
+ int w = ncol / 2;
+ struct buffer *bp, *blp;
+ struct line *lp;
+
+ if ((blp = bfind("*Buffer List*", TRUE)) == NULL)
+ return (NULL);
+ if (bclear(blp) != TRUE)
+ return (NULL);
+ blp->b_flag &= ~BFCHG; /* Blow away old. */
+ blp->b_flag |= BFREADONLY;
+
+ listbuf_ncol = ncol; /* cache ncol for listbuf_goto_buffer */
+
+ if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size File") == FALSE ||
+ addlinef(blp, "%-*s%s", w, " -- ------", "---- ----") == FALSE)
+ return (NULL);
+
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ RSIZE nbytes;
+
+ nbytes = 0; /* Count bytes in buf. */
+ if (bp != blp) {
+ lp = bfirstlp(bp);
+ while (lp != bp->b_headp) {
+ nbytes += llength(lp) + 1;
+ lp = lforw(lp);
+ }
+ if (nbytes)
+ nbytes--; /* no bonus newline */
+ }
+
+ if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s",
+ (bp == curbp) ? '>' : ' ', /* current buffer ? */
+ ((bp->b_flag & BFCHG) != 0) ? '*' : ' ', /* changed ? */
+ ((bp->b_flag & BFREADONLY) != 0) ? '*' : ' ',
+ w - 5, /* four chars already written */
+ w - 5, /* four chars already written */
+ bp->b_bname, /* buffer name */
+ strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */
+ nbytes, /* buffer size */
+ w - 7, /* seven chars already written */
+ bp->b_fname) == FALSE)
+ return (NULL);
+ }
+ blp->b_dotp = bfirstlp(blp); /* put dot at beginning of
+ * buffer */
+ blp->b_doto = 0;
+ return (blp); /* All done */
+}
+
+static int
+listbuf_goto_buffer(int f, int n)
+{
+ return (listbuf_goto_buffer_helper(f, n, 0));
+}
+
+static int
+listbuf_goto_buffer_one(int f, int n)
+{
+ return (listbuf_goto_buffer_helper(f, n, 1));
+}
+
+static int
+listbuf_goto_buffer_helper(int f, int n, int only)
+{
+ struct buffer *bp;
+ struct mgwin *wp;
+ char *line = NULL;
+ int i, ret = FALSE;
+
+ if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$')
+ return(dobeep_msg("buffer name truncated"));
+
+ if ((line = malloc(listbuf_ncol/2)) == NULL)
+ return (FALSE);
+
+ memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5);
+ for (i = listbuf_ncol/2 - 6; i > 0; i--) {
+ if (line[i] != ' ') {
+ line[i + 1] = '\0';
+ break;
+ }
+ }
+ if (i == 0)
+ goto cleanup;
+
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ if (strcmp(bp->b_bname, line) == 0)
+ break;
+ }
+ if (bp == NULL)
+ goto cleanup;
+
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ goto cleanup;
+ curbp = bp;
+ curwp = wp;
+
+ if (only)
+ ret = (onlywind(FFRAND, 1));
+ else
+ ret = TRUE;
+
+cleanup:
+ free(line);
+
+ return (ret);
+}
+
+/*
+ * The argument "fmt" points to a format string. Append this line to the
+ * buffer. Handcraft the EOL on the end. Return TRUE if it worked and
+ * FALSE if you ran out of room.
+ */
+int
+addlinef(struct buffer *bp, char *fmt, ...)
+{
+ va_list ap;
+ struct line *lp;
+
+ if ((lp = lalloc(0)) == NULL)
+ return (FALSE);
+ va_start(ap, fmt);
+ if (vasprintf(&lp->l_text, fmt, ap) == -1) {
+ lfree(lp);
+ va_end(ap);
+ return (FALSE);
+ }
+ lp->l_used = strlen(lp->l_text);
+ va_end(ap);
+
+ bp->b_headp->l_bp->l_fp = lp; /* Hook onto the end */
+ lp->l_bp = bp->b_headp->l_bp;
+ bp->b_headp->l_bp = lp;
+ lp->l_fp = bp->b_headp;
+ bp->b_lines++;
+
+ return (TRUE);
+}
+
+/*
+ * Look through the list of buffers, giving the user a chance to save them.
+ * Return TRUE if there are any changed buffers afterwards. Buffers that don't
+ * have an associated file don't count. Return FALSE if there are no changed
+ * buffers. Return ABORT if an error occurs or if the user presses c-g.
+ */
+int
+anycb(int f)
+{
+ struct buffer *bp;
+ int s = FALSE, save = FALSE, save2 = FALSE, ret;
+ char pbuf[NFILEN + 11];
+
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) {
+ ret = snprintf(pbuf, sizeof(pbuf), "Save file %s",
+ bp->b_fname);
+ if (ret < 0 || ret >= sizeof(pbuf)) {
+ (void)dobeep_msg("Error: filename too long!");
+ return (UERROR);
+ }
+ if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) &&
+ (save2 = buffsave(bp)) == TRUE) {
+ bp->b_flag &= ~BFCHG;
+ upmodes(bp);
+ } else {
+ if (save2 == FIOERR)
+ return (save2);
+ s = TRUE;
+ }
+ if (save == ABORT)
+ return (save);
+ save = TRUE;
+ }
+ }
+ if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */
+ ewprintf("(No files need saving)");
+ return (s);
+}
+
+/*
+ * Search for a buffer, by name.
+ * If not found, and the "cflag" is TRUE,
+ * create a new buffer. Return pointer to the found
+ * (or new) buffer.
+ */
+struct buffer *
+bfind(const char *bname, int cflag)
+{
+ struct buffer *bp;
+
+ bp = bheadp;
+ while (bp != NULL) {
+ if (strcmp(bname, bp->b_bname) == 0)
+ return (bp);
+ bp = bp->b_bufp;
+ }
+ if (cflag != TRUE)
+ return (NULL);
+
+ bp = bnew(bname);
+
+ return (bp);
+}
+
+/*
+ * Create a new buffer and put it in the list of
+ * all buffers.
+ */
+static struct buffer *
+bnew(const char *bname)
+{
+ struct buffer *bp;
+ struct line *lp;
+ int i;
+ size_t len;
+
+ bp = calloc(1, sizeof(struct buffer));
+ if (bp == NULL) {
+ dobeep();
+ ewprintf("Can't get %d bytes", sizeof(struct buffer));
+ return (NULL);
+ }
+ if ((lp = lalloc(0)) == NULL) {
+ free(bp);
+ return (NULL);
+ }
+ bp->b_altb = bp->b_bufp = NULL;
+ bp->b_dotp = lp;
+ bp->b_doto = 0;
+ bp->b_markp = NULL;
+ bp->b_marko = 0;
+ bp->b_flag = defb_flag;
+ /* if buffer name starts and ends with '*', we ignore changes */
+ len = strlen(bname);
+ if (len) {
+ if (bname[0] == '*' && bname[len - 1] == '*')
+ bp->b_flag |= BFIGNDIRTY;
+ }
+ bp->b_nwnd = 0;
+ bp->b_headp = lp;
+ bp->b_nmodes = defb_nmodes;
+ TAILQ_INIT(&bp->b_undo);
+ bp->b_undoptr = NULL;
+ i = 0;
+ do {
+ bp->b_modes[i] = defb_modes[i];
+ } while (i++ < defb_nmodes);
+ bp->b_fname[0] = '\0';
+ bp->b_cwd[0] = '\0';
+ bzero(&bp->b_fi, sizeof(bp->b_fi));
+ lp->l_fp = lp;
+ lp->l_bp = lp;
+ bp->b_bufp = bheadp;
+ bheadp = bp;
+ bp->b_dotline = bp->b_markline = 1;
+ bp->b_lines = 1;
+ bp->b_nlseq = "\n"; /* use unix default */
+ bp->b_nlchr = bp->b_nlseq;
+ if ((bp->b_bname = strdup(bname)) == NULL) {
+ dobeep();
+ ewprintf("Can't get %d bytes", strlen(bname) + 1);
+ return (NULL);
+ }
+
+ return (bp);
+}
+
+/*
+ * This routine blows away all of the text
+ * in a buffer. If the buffer is marked as changed
+ * then we ask if it is ok to blow it away; this is
+ * to save the user the grief of losing text. The
+ * window chain is nearly always wrong if this gets
+ * called; the caller must arrange for the updates
+ * that are required. Return TRUE if everything
+ * looks good.
+ */
+int
+bclear(struct buffer *bp)
+{
+ struct line *lp;
+ int s;
+
+ /* Has buffer changed, and do we care? */
+ if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 &&
+ (s = eyesno("Buffer modified; kill anyway")) != TRUE)
+ return (s);
+ bp->b_flag &= ~BFCHG; /* Not changed */
+ while ((lp = lforw(bp->b_headp)) != bp->b_headp)
+ lfree(lp);
+ bp->b_dotp = bp->b_headp; /* Fix dot */
+ bp->b_doto = 0;
+ bp->b_markp = NULL; /* Invalidate "mark" */
+ bp->b_marko = 0;
+ bp->b_dotline = bp->b_markline = 1;
+ bp->b_lines = 1;
+
+ return (TRUE);
+}
+
+/*
+ * Display the given buffer in the given window. Flags indicated
+ * action on redisplay. Update modified flag so insert loop can check it.
+ */
+int
+showbuffer(struct buffer *bp, struct mgwin *wp, int flags)
+{
+ struct buffer *obp;
+ struct mgwin *owp;
+
+ /* Ensure file has not been modified elsewhere */
+ if (fchecktime(bp) != TRUE)
+ bp->b_flag |= BFDIRTY;
+
+ if (wp->w_bufp == bp) { /* Easy case! */
+ wp->w_rflag |= flags;
+ return (TRUE);
+ }
+ /* First, detach the old buffer from the window */
+ if ((bp->b_altb = obp = wp->w_bufp) != NULL) {
+ if (--obp->b_nwnd == 0) {
+ obp->b_dotp = wp->w_dotp;
+ obp->b_doto = wp->w_doto;
+ obp->b_markp = wp->w_markp;
+ obp->b_marko = wp->w_marko;
+ obp->b_dotline = wp->w_dotline;
+ obp->b_markline = wp->w_markline;
+ }
+ }
+ /* Now, attach the new buffer to the window */
+ wp->w_bufp = bp;
+
+ if (bp->b_nwnd++ == 0) { /* First use. */
+ wp->w_dotp = bp->b_dotp;
+ wp->w_doto = bp->b_doto;
+ wp->w_markp = bp->b_markp;
+ wp->w_marko = bp->b_marko;
+ wp->w_dotline = bp->b_dotline;
+ wp->w_markline = bp->b_markline;
+ } else
+ /* already on screen, steal values from other window */
+ for (owp = wheadp; owp != NULL; owp = wp->w_wndp)
+ if (wp->w_bufp == bp && owp != wp) {
+ wp->w_dotp = owp->w_dotp;
+ wp->w_doto = owp->w_doto;
+ wp->w_markp = owp->w_markp;
+ wp->w_marko = owp->w_marko;
+ wp->w_dotline = owp->w_dotline;
+ wp->w_markline = owp->w_markline;
+ break;
+ }
+ wp->w_rflag |= WFMODE | flags;
+ return (TRUE);
+}
+
+/*
+ * Augment a buffer name with a number, if necessary
+ *
+ * If more than one file of the same basename() is open,
+ * the additional buffers are named "file<2>", "file<3>", and
+ * so forth. This function adjusts a buffer name to
+ * include the number, if necessary.
+ */
+int
+augbname(char *bn, const char *fn, size_t bs)
+{
+ int count;
+ size_t remain, len;
+
+ if ((len = xbasename(bn, fn, bs)) >= bs)
+ return (FALSE);
+
+ remain = bs - len;
+ for (count = 2; bfind(bn, FALSE) != NULL; count++)
+ snprintf(bn + len, remain, "<%d>", count);
+
+ return (TRUE);
+}
+
+/*
+ * Pop the buffer we got passed onto the screen.
+ * Returns a status.
+ */
+struct mgwin *
+popbuf(struct buffer *bp, int flags)
+{
+ struct mgwin *wp;
+
+ if (bp->b_nwnd == 0) { /* Not on screen yet. */
+ /*
+ * Pick a window for a pop-up.
+ * If only one window, split the screen.
+ * Flag the new window as ephemeral
+ */
+ if (wheadp->w_wndp == NULL &&
+ splitwind(FFOTHARG, flags) == FALSE)
+ return (NULL);
+
+ /*
+ * Pick the uppermost window that isn't
+ * the current window. An LRU algorithm
+ * might be better. Return a pointer, or NULL on error.
+ */
+ wp = wheadp;
+
+ while (wp != NULL && wp == curwp)
+ wp = wp->w_wndp;
+ } else {
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == bp) {
+ wp->w_rflag |= WFFULL | WFFRAME;
+ return (wp);
+ }
+ }
+ }
+ if (!wp)
+ return (NULL);
+
+ if (showbuffer(bp, wp, WFFULL) != TRUE)
+ return (NULL);
+ return (wp);
+}
+
+/*
+ * Insert another buffer at dot. Very useful.
+ */
+/* ARGSUSED */
+int
+bufferinsert(int f, int n)
+{
+ struct buffer *bp;
+ struct line *clp;
+ int clo, nline;
+ char bufn[NBUFN], *bufp;
+
+ /* Get buffer to use from user */
+ if (curbp->b_altb != NULL)
+ bufp = eread("Insert buffer (default %s): ", bufn, NBUFN,
+ EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname);
+ else
+ bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF);
+ if (bufp == NULL)
+ return (ABORT);
+ if (bufp[0] == '\0' && curbp->b_altb != NULL)
+ bp = curbp->b_altb;
+ else if ((bp = bfind(bufp, FALSE)) == NULL)
+ return (FALSE);
+
+ if (bp == curbp)
+ return(dobeep_msg("Cannot insert buffer into self"));
+
+ /* insert the buffer */
+ nline = 0;
+ clp = bfirstlp(bp);
+ for (;;) {
+ for (clo = 0; clo < llength(clp); clo++)
+ if (linsert(1, lgetc(clp, clo)) == FALSE)
+ return (FALSE);
+ if ((clp = lforw(clp)) == bp->b_headp)
+ break;
+ if (enewline(FFRAND, 1) == FALSE) /* fake newline */
+ return (FALSE);
+ nline++;
+ }
+ if (nline == 1)
+ ewprintf("[Inserted 1 line]");
+ else
+ ewprintf("[Inserted %d lines]", nline);
+
+ clp = curwp->w_linep; /* cosmetic adjustment */
+ if (curwp->w_dotp == clp) { /* for offscreen insert */
+ while (nline-- && lback(clp) != curbp->b_headp)
+ clp = lback(clp);
+ curwp->w_linep = clp; /* adjust framing. */
+ curwp->w_rflag |= WFFULL;
+ }
+ return (TRUE);
+}
+
+/*
+ * Turn off the dirty bit on this buffer.
+ */
+/* ARGSUSED */
+int
+notmodified(int f, int n)
+{
+ struct mgwin *wp;
+
+ curbp->b_flag &= ~BFCHG;
+ wp = wheadp; /* Update mode lines. */
+ while (wp != NULL) {
+ if (wp->w_bufp == curbp)
+ wp->w_rflag |= WFMODE;
+ wp = wp->w_wndp;
+ }
+ ewprintf("Modification-flag cleared");
+ return (TRUE);
+}
+
+/*
+ * Popbuf and set all windows to top of buffer.
+ */
+int
+popbuftop(struct buffer *bp, int flags)
+{
+ struct mgwin *wp;
+
+ bp->b_dotp = bfirstlp(bp);
+ bp->b_doto = 0;
+ if (bp->b_nwnd != 0) {
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
+ if (wp->w_bufp == bp) {
+ wp->w_dotp = bp->b_dotp;
+ wp->w_doto = 0;
+ wp->w_rflag |= WFFULL;
+ }
+ }
+ return (popbuf(bp, flags) != NULL);
+}
+
+/*
+ * Return the working directory for the current buffer, terminated
+ * with a '/'. First, try to extract it from the current buffer's
+ * filename. If that fails, use global cwd.
+ */
+int
+getbufcwd(char *path, size_t plen)
+{
+ char cwd[NFILEN];
+
+ if (plen == 0)
+ return (FALSE);
+
+ if (globalwd == FALSE && curbp->b_cwd[0] != '\0') {
+ (void)strlcpy(path, curbp->b_cwd, plen);
+ } else {
+ if (getcwdir(cwd, sizeof(cwd)) == FALSE)
+ goto error;
+ (void)strlcpy(path, cwd, plen);
+ }
+ return (TRUE);
+error:
+ path[0] = '\0';
+ return (FALSE);
+}
+
+/*
+ * Ensures a buffer has not been modified elsewhere; e.g. on disk.
+ * Prompt the user if it has.
+ * Returns TRUE if it has NOT (i.e. buffer is ok to edit).
+ * FALSE or ABORT otherwise
+ */
+int
+checkdirty(struct buffer *bp)
+{
+ int s;
+
+ if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0)
+ if (fchecktime(bp) != TRUE)
+ bp->b_flag |= BFDIRTY;
+
+ if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) {
+ s = eynorr("File changed on disk; really edit the buffer");
+ switch (s) {
+ case TRUE:
+ bp->b_flag &= ~BFDIRTY;
+ bp->b_flag |= BFIGNDIRTY;
+ return (TRUE);
+ case REVERT:
+ dorevert();
+ return (FALSE);
+ default:
+ return (s);
+ }
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Revert the current buffer to whatever is on disk.
+ */
+/* ARGSUSED */
+int
+revertbuffer(int f, int n)
+{
+ char fbuf[NFILEN + 32];
+
+ if (curbp->b_fname[0] == 0)
+ return(dobeep_msg("Cannot revert buffer not associated "
+ "with any files."));
+
+ snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s",
+ curbp->b_fname);
+
+ if (eyorn(fbuf) == TRUE)
+ return dorevert();
+
+ return (FALSE);
+}
+
+int
+dorevert(void)
+{
+ int lineno;
+ struct undo_rec *rec;
+
+ if (access(curbp->b_fname, F_OK|R_OK) != 0) {
+ dobeep();
+ if (errno == ENOENT)
+ ewprintf("File %s no longer exists!",
+ curbp->b_fname);
+ else
+ ewprintf("File %s is no longer readable!",
+ curbp->b_fname);
+ return (FALSE);
+ }
+
+ /* Save our current line, so we can go back after reloading. */
+ lineno = curwp->w_dotline;
+
+ /* Prevent readin from asking if we want to kill the buffer. */
+ curbp->b_flag &= ~BFCHG;
+
+ /* Clean up undo memory */
+ while ((rec = TAILQ_FIRST(&curbp->b_undo))) {
+ TAILQ_REMOVE(&curbp->b_undo, rec, next);
+ free_undo_record(rec);
+ }
+
+ if (readin(curbp->b_fname))
+ return(setlineno(lineno));
+ return (FALSE);
+}
+
+/*
+ * Diff the current buffer to what is on disk.
+ */
+/*ARGSUSED */
+int
+diffbuffer(int f, int n)
+{
+ struct buffer *bp;
+ struct line *lp, *lpend;
+ size_t len;
+ int ret;
+ char *text, *ttext;
+ char * const argv[] =
+ {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL};
+
+ len = 0;
+
+ /* C-u is not supported */
+ if (n > 1)
+ return (ABORT);
+
+ if (access(DIFFTOOL, X_OK) != 0) {
+ dobeep();
+ ewprintf("%s not found or not executable.", DIFFTOOL);
+ return (FALSE);
+ }
+
+ if (curbp->b_fname[0] == 0)
+ return(dobeep_msg("Cannot diff buffer not associated with "
+ "any files."));
+
+ lpend = curbp->b_headp;
+ for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
+ len+=llength(lp);
+ if (lforw(lp) != lpend) /* no implied \n on last line */
+ len++;
+ }
+ if ((text = calloc(len + 1, sizeof(char))) == NULL)
+ return(dobeep_msg("Cannot allocate memory."));
+
+ ttext = text;
+
+ for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
+ if (llength(lp) != 0) {
+ memcpy(ttext, ltext(lp), llength(lp));
+ ttext += llength(lp);
+ }
+ if (lforw(lp) != lpend) /* no implied \n on last line */
+ *ttext++ = *curbp->b_nlchr;
+ }
+
+ bp = bfind("*Diff*", TRUE);
+ bp->b_flag |= BFREADONLY;
+ if (bclear(bp) != TRUE) {
+ free(text);
+ return (FALSE);
+ }
+
+ ret = pipeio(DIFFTOOL, argv, text, len, bp);
+
+ if (ret == TRUE) {
+ eerase();
+ if (lforw(bp->b_headp) == bp->b_headp)
+ addline(bp, "Diff finished (no differences).");
+ }
+
+ free(text);
+ return (ret);
+}
+
+/*
+ * Given a file name, either find the buffer it uses, or create a new
+ * empty buffer to put it in.
+ */
+struct buffer *
+findbuffer(char *fn)
+{
+ struct buffer *bp;
+ char bname[NBUFN], fname[NBUFN];
+
+ if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) {
+ (void)dobeep_msg("filename too long");
+ return (NULL);
+ }
+
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ if (strcmp(bp->b_fname, fname) == 0)
+ return (bp);
+ }
+ /* Not found. Create a new one, adjusting name first */
+ if (augbname(bname, fname, sizeof(bname)) == FALSE)
+ return (NULL);
+
+ bp = bfind(bname, TRUE);
+ return (bp);
+}
Index: contrib/mg/cdbr.h
===================================================================
--- /dev/null
+++ contrib/mg/cdbr.h
@@ -0,0 +1,64 @@
+/* $NetBSD: cdbr.h,v 1.1 2013/12/11 01:24:08 joerg Exp $ */
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _CDBR_H
+#define _CDBR_H
+
+#include <sys/cdefs.h>
+#if defined(_KERNEL) || defined(_STANDALONE)
+#include <sys/types.h>
+#else
+#include <inttypes.h>
+#include <stddef.h>
+#endif
+
+#define CDBR_DEFAULT 0
+
+struct cdbr;
+
+__BEGIN_DECLS
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+struct cdbr *cdbr_open(const char *, int);
+#endif
+struct cdbr *cdbr_open_mem(void *, size_t, int,
+ void (*)(void *, void *, size_t), void *);
+uint32_t cdbr_entries(struct cdbr *);
+int cdbr_get(struct cdbr *, uint32_t, const void **, size_t *);
+int cdbr_find(struct cdbr *, const void *, size_t,
+ const void **, size_t *);
+void cdbr_close(struct cdbr *);
+
+__END_DECLS
+
+#endif /* _CDBR_H */
Index: contrib/mg/cdbr.c
===================================================================
--- /dev/null
+++ contrib/mg/cdbr.c
@@ -0,0 +1,294 @@
+/* $NetBSD: cdbr.c,v 1.2 2017/01/10 23:06:06 christos Exp $ */
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cdbr.h"
+
+#define SET_ERRNO(val) errno = (val)
+
+#ifndef fast_divide32_prepare
+#define fast_divide32_prepare(d,m,s1,s2) (void)0
+#endif
+
+#ifndef fast_remainder32
+#define fast_remainder32(v,d,m,s1,s2) (v%d)
+#endif
+
+extern void mi_vector_hash(const void * __restrict, size_t, uint32_t,
+ uint32_t[3]);
+
+struct cdbr {
+ void (*unmap)(void *, void *, size_t);
+ void *cookie;
+ uint8_t *mmap_base;
+ size_t mmap_size;
+
+ uint8_t *hash_base;
+ uint8_t *offset_base;
+ uint8_t *data_base;
+
+ uint32_t data_size;
+ uint32_t entries;
+ uint32_t entries_index;
+ uint32_t seed;
+
+ uint8_t offset_size;
+ uint8_t index_size;
+
+ uint32_t entries_m;
+ uint32_t entries_index_m;
+ uint8_t entries_s1, entries_s2;
+ uint8_t entries_index_s1, entries_index_s2;
+};
+
+/* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */
+static uint32_t
+le32dec(const void *buf)
+{
+ uint8_t const *p = (uint8_t const *) buf;
+
+ return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+static void
+cdbr_unmap(void *cookie __unused, void *base, size_t size)
+{
+ munmap(base, size);
+}
+
+/* ARGSUSED */
+struct cdbr *
+cdbr_open(const char *path, int flags)
+{
+ void *base;
+ size_t size;
+ int fd;
+ struct cdbr *cdbr;
+ struct stat sb;
+
+ if ((fd = open(path, O_RDONLY)) == -1)
+ return NULL;
+ if (fstat(fd, &sb) == -1) {
+ close(fd);
+ return NULL;
+ }
+
+ if (sb.st_size >= SSIZE_MAX) {
+ close(fd);
+ SET_ERRNO(EINVAL);
+ return NULL;
+ }
+
+
+ size = (size_t)sb.st_size;
+ base = mmap(NULL, size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0);
+ close(fd);
+
+ if (base == MAP_FAILED)
+ return NULL;
+
+ cdbr = cdbr_open_mem(base, size, flags, cdbr_unmap, NULL);
+ if (cdbr == NULL)
+ munmap(base, size);
+ return cdbr;
+}
+#endif
+
+struct cdbr *
+cdbr_open_mem(void *base, size_t size, int flags __unused,
+ void (*unmap)(void *, void *, size_t), void *cookie)
+{
+ struct cdbr *cdbr;
+ uint8_t *buf = base;
+ if (size < 40 || memcmp(buf, "NBCDB\n\0\001", 8)) {
+ SET_ERRNO(EINVAL);
+ return NULL;
+ }
+
+ cdbr = malloc(sizeof(*cdbr));
+ cdbr->unmap = unmap;
+ cdbr->cookie = cookie;
+
+ cdbr->data_size = le32dec(buf + 24);
+ cdbr->entries = le32dec(buf + 28);
+ cdbr->entries_index = le32dec(buf + 32);
+ cdbr->seed = le32dec(buf + 36);
+
+ if (cdbr->data_size < 0x100)
+ cdbr->offset_size = 1;
+ else if (cdbr->data_size < 0x10000)
+ cdbr->offset_size = 2;
+ else
+ cdbr->offset_size = 4;
+
+ if (cdbr->entries_index < 0x100)
+ cdbr->index_size = 1;
+ else if (cdbr->entries_index < 0x10000)
+ cdbr->index_size = 2;
+ else
+ cdbr->index_size = 4;
+
+ cdbr->mmap_base = base;
+ cdbr->mmap_size = size;
+
+ cdbr->hash_base = cdbr->mmap_base + 40;
+ cdbr->offset_base = cdbr->hash_base + cdbr->entries_index * cdbr->index_size;
+ if (cdbr->entries_index * cdbr->index_size % cdbr->offset_size)
+ cdbr->offset_base += cdbr->offset_size -
+ cdbr->entries_index * cdbr->index_size % cdbr->offset_size;
+ cdbr->data_base = cdbr->offset_base + (cdbr->entries + 1) * cdbr->offset_size;
+
+ if (cdbr->hash_base < cdbr->mmap_base ||
+ cdbr->offset_base < cdbr->mmap_base ||
+ cdbr->data_base < cdbr->mmap_base ||
+ cdbr->data_base + cdbr->data_size < cdbr->mmap_base ||
+ cdbr->data_base + cdbr->data_size >
+ cdbr->mmap_base + cdbr->mmap_size) {
+ SET_ERRNO(EINVAL);
+ free(cdbr);
+ return NULL;
+ }
+
+ if (cdbr->entries) {
+ fast_divide32_prepare(cdbr->entries, &cdbr->entries_m,
+ &cdbr->entries_s1, &cdbr->entries_s2);
+ }
+ if (cdbr->entries_index) {
+ fast_divide32_prepare(cdbr->entries_index,
+ &cdbr->entries_index_m,
+ &cdbr->entries_index_s1, &cdbr->entries_index_s2);
+ }
+
+ return cdbr;
+}
+
+static uint32_t
+get_uintX(const uint8_t *addr, uint32_t idx, int size)
+{
+ addr += idx * size;
+
+ if (size == 4)
+ return /* LINTED */le32toh(*(const uint32_t *)addr);
+ else if (size == 2)
+ return /* LINTED */le16toh(*(const uint16_t *)addr);
+ else
+ return *addr;
+}
+
+uint32_t
+cdbr_entries(struct cdbr *cdbr)
+{
+
+ return cdbr->entries;
+}
+
+int
+cdbr_get(struct cdbr *cdbr, uint32_t idx, const void **data, size_t *data_len)
+{
+ uint32_t start, end;
+
+ if (idx >= cdbr->entries) {
+ SET_ERRNO(EINVAL);
+ return -1;
+ }
+
+ start = get_uintX(cdbr->offset_base, idx, cdbr->offset_size);
+ end = get_uintX(cdbr->offset_base, idx + 1, cdbr->offset_size);
+
+ if (start > end) {
+ SET_ERRNO(EIO);
+ return -1;
+ }
+
+ if (end > cdbr->data_size) {
+ SET_ERRNO(EIO);
+ return -1;
+ }
+
+ *data = cdbr->data_base + start;
+ *data_len = end - start;
+
+ return 0;
+}
+
+int
+cdbr_find(struct cdbr *cdbr, const void *key, size_t key_len,
+ const void **data, size_t *data_len)
+{
+ uint32_t hashes[3], idx;
+
+ if (cdbr->entries_index == 0) {
+ SET_ERRNO(EINVAL);
+ return -1;
+ }
+
+ mi_vector_hash(key, key_len, cdbr->seed, hashes);
+
+ hashes[0] = fast_remainder32(hashes[0], cdbr->entries_index,
+ cdbr->entries_index_m, cdbr->entries_index_s1,
+ cdbr->entries_index_s2);
+ hashes[1] = fast_remainder32(hashes[1], cdbr->entries_index,
+ cdbr->entries_index_m, cdbr->entries_index_s1,
+ cdbr->entries_index_s2);
+ hashes[2] = fast_remainder32(hashes[2], cdbr->entries_index,
+ cdbr->entries_index_m, cdbr->entries_index_s1,
+ cdbr->entries_index_s2);
+
+ idx = get_uintX(cdbr->hash_base, hashes[0], cdbr->index_size);
+ idx += get_uintX(cdbr->hash_base, hashes[1], cdbr->index_size);
+ idx += get_uintX(cdbr->hash_base, hashes[2], cdbr->index_size);
+
+ return cdbr_get(cdbr, fast_remainder32(idx, cdbr->entries,
+ cdbr->entries_m, cdbr->entries_s1, cdbr->entries_s2), data,
+ data_len);
+}
+
+void
+cdbr_close(struct cdbr *cdbr)
+{
+ if (cdbr->unmap)
+ (*cdbr->unmap)(cdbr->cookie, cdbr->mmap_base, cdbr->mmap_size);
+ free(cdbr);
+}
Index: contrib/mg/cdbw.h
===================================================================
--- /dev/null
+++ contrib/mg/cdbw.h
@@ -0,0 +1,55 @@
+/* $NetBSD: cdbw.h,v 1.2 2012/06/03 21:21:45 joerg Exp $ */
+/*-
+ * Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _CDBW_H
+#define _CDBW_H
+
+#include <sys/cdefs.h>
+#include <inttypes.h>
+#include <stddef.h>
+
+struct cdbw;
+
+struct cdbw *cdbw_open(void);
+int cdbw_put(struct cdbw *, const void *, size_t,
+ const void *, size_t);
+int cdbw_put_data(struct cdbw *, const void *, size_t,
+ uint32_t *);
+int cdbw_put_key(struct cdbw *, const void *, size_t,
+ uint32_t);
+uint32_t cdbw_stable_seeder(void);
+int cdbw_output(struct cdbw *, int, const char[16],
+ uint32_t (*)(void));
+void cdbw_close(struct cdbw *);
+
+#endif /* _CDBW_H */
Index: contrib/mg/cdbw.c
===================================================================
--- /dev/null
+++ contrib/mg/cdbw.c
@@ -0,0 +1,578 @@
+/* $NetBSD: cdbw.c,v 1.6 2017/11/11 18:05:31 alnsn Exp $ */
+/*-
+ * Copyright (c) 2009, 2010, 2015 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger and Alexander Nasonov.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/queue.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cdbw.h"
+
+void le32enc(void *, uint32_t);
+void mi_vector_hash(const void * __restrict, size_t, uint32_t,
+ uint32_t[3]);
+
+struct key_hash {
+ SLIST_ENTRY(key_hash) link;
+ uint32_t hashes[3];
+ uint32_t idx;
+ void *key;
+ size_t keylen;
+};
+
+SLIST_HEAD(key_hash_head, key_hash);
+
+struct cdbw {
+ size_t data_counter;
+ size_t data_allocated;
+ size_t data_size;
+ size_t *data_len;
+ void **data_ptr;
+
+ size_t hash_size;
+ struct key_hash_head *hash;
+ size_t key_counter;
+};
+
+ /* Max. data counter that allows the index size to be 32bit. */
+static const uint32_t max_data_counter = 0xccccccccU;
+
+struct cdbw *
+cdbw_open(void)
+{
+ struct cdbw *cdbw;
+ size_t i;
+
+ cdbw = calloc(sizeof(*cdbw), 1);
+ if (cdbw == NULL)
+ return NULL;
+
+ cdbw->hash_size = 1024;
+ cdbw->hash = calloc(cdbw->hash_size, sizeof(*cdbw->hash));
+ if (cdbw->hash == NULL) {
+ free(cdbw);
+ return NULL;
+ }
+
+ for (i = 0; i < cdbw->hash_size; ++i)
+ SLIST_INIT(cdbw->hash + i);
+
+ return cdbw;
+}
+
+int
+cdbw_put(struct cdbw *cdbw, const void *key, size_t keylen,
+ const void *data, size_t datalen)
+{
+ uint32_t idx;
+ int rv;
+
+ rv = cdbw_put_data(cdbw, data, datalen, &idx);
+ if (rv)
+ return rv;
+ rv = cdbw_put_key(cdbw, key, keylen, idx);
+ if (rv) {
+ --cdbw->data_counter;
+ free(cdbw->data_ptr[cdbw->data_counter]);
+ cdbw->data_size -= datalen;
+ return rv;
+ }
+ return 0;
+}
+
+int
+cdbw_put_data(struct cdbw *cdbw, const void *data, size_t datalen,
+ uint32_t *idx)
+{
+
+ if (cdbw->data_counter == max_data_counter)
+ return -1;
+
+ if (cdbw->data_size + datalen < cdbw->data_size ||
+ cdbw->data_size + datalen > 0xffffffffU)
+ return -1; /* Overflow */
+
+ if (cdbw->data_allocated == cdbw->data_counter) {
+ void **new_data_ptr;
+ size_t *new_data_len;
+ size_t new_allocated;
+
+ if (cdbw->data_allocated == 0)
+ new_allocated = 256;
+ else
+ new_allocated = cdbw->data_allocated * 2;
+
+ new_data_ptr = realloc(cdbw->data_ptr,
+ sizeof(*cdbw->data_ptr) * new_allocated);
+ if (new_data_ptr == NULL)
+ return -1;
+ cdbw->data_ptr = new_data_ptr;
+
+ new_data_len = realloc(cdbw->data_len,
+ sizeof(*cdbw->data_len) * new_allocated);
+ if (new_data_len == NULL)
+ return -1;
+ cdbw->data_len = new_data_len;
+
+ cdbw->data_allocated = new_allocated;
+ }
+
+ cdbw->data_ptr[cdbw->data_counter] = malloc(datalen);
+ if (cdbw->data_ptr[cdbw->data_counter] == NULL)
+ return -1;
+ memcpy(cdbw->data_ptr[cdbw->data_counter], data, datalen);
+ cdbw->data_len[cdbw->data_counter] = datalen;
+ cdbw->data_size += datalen;
+ *idx = cdbw->data_counter++;
+ return 0;
+}
+
+int
+cdbw_put_key(struct cdbw *cdbw, const void *key, size_t keylen, uint32_t idx)
+{
+ uint32_t hashes[3];
+ struct key_hash_head *head, *head2, *new_head;
+ struct key_hash *key_hash;
+ size_t new_hash_size, i;
+
+ if (idx >= cdbw->data_counter ||
+ cdbw->key_counter == max_data_counter)
+ return -1;
+
+ mi_vector_hash(key, keylen, 0, hashes);
+
+ head = cdbw->hash + (hashes[0] & (cdbw->hash_size - 1));
+ SLIST_FOREACH(key_hash, head, link) {
+ if (key_hash->keylen != keylen)
+ continue;
+ if (key_hash->hashes[0] != hashes[0])
+ continue;
+ if (key_hash->hashes[1] != hashes[1])
+ continue;
+ if (key_hash->hashes[2] != hashes[2])
+ continue;
+ if (memcmp(key, key_hash->key, keylen))
+ continue;
+ return -1;
+ }
+ key_hash = malloc(sizeof(*key_hash));
+ if (key_hash == NULL)
+ return -1;
+ key_hash->key = malloc(keylen);
+ if (key_hash->key == NULL) {
+ free(key_hash);
+ return -1;
+ }
+ memcpy(key_hash->key, key, keylen);
+ key_hash->hashes[0] = hashes[0];
+ key_hash->hashes[1] = hashes[1];
+ key_hash->hashes[2] = hashes[2];
+ key_hash->keylen = keylen;
+ key_hash->idx = idx;
+ SLIST_INSERT_HEAD(head, key_hash, link);
+ ++cdbw->key_counter;
+
+ if (cdbw->key_counter <= cdbw->hash_size)
+ return 0;
+
+ /* Try to resize the hash table, but ignore errors. */
+ new_hash_size = cdbw->hash_size * 2;
+ new_head = calloc(sizeof(*new_head), new_hash_size);
+ if (new_head == NULL)
+ return 0;
+
+ head = &cdbw->hash[hashes[0] & (cdbw->hash_size - 1)];
+ for (i = 0; i < new_hash_size; ++i)
+ SLIST_INIT(new_head + i);
+
+ for (i = 0; i < cdbw->hash_size; ++i) {
+ head = cdbw->hash + i;
+
+ while ((key_hash = SLIST_FIRST(head)) != NULL) {
+ SLIST_REMOVE_HEAD(head, link);
+ head2 = new_head +
+ (key_hash->hashes[0] & (new_hash_size - 1));
+ SLIST_INSERT_HEAD(head2, key_hash, link);
+ }
+ }
+ free(cdbw->hash);
+ cdbw->hash_size = new_hash_size;
+ cdbw->hash = new_head;
+
+ return 0;
+}
+
+void
+cdbw_close(struct cdbw *cdbw)
+{
+ struct key_hash_head *head;
+ struct key_hash *key_hash;
+ size_t i;
+
+ for (i = 0; i < cdbw->hash_size; ++i) {
+ head = cdbw->hash + i;
+ while ((key_hash = SLIST_FIRST(head)) != NULL) {
+ SLIST_REMOVE_HEAD(head, link);
+ free(key_hash->key);
+ free(key_hash);
+ }
+ }
+
+ for (i = 0; i < cdbw->data_counter; ++i)
+ free(cdbw->data_ptr[i]);
+ free(cdbw->data_ptr);
+ free(cdbw->data_len);
+ free(cdbw->hash);
+ free(cdbw);
+}
+
+uint32_t
+cdbw_stable_seeder(void)
+{
+ return 0;
+}
+
+/*
+ * The algorithm below is based on paper
+ * Cache-Oblivious Peeling of Random Hypergraphs by Djamal Belazzougui,
+ * Paolo Boldi, Giuseppe Ottaviano, Rossano Venturini, and Sebastiano
+ * Vigna.
+ * http://zola.di.unipi.it/rossano/wp-content/papercite-data/pdf/dcc14.pdf
+ */
+
+/*
+ * Data type for a valid oriented edge (v0, v1, v2), v1 < v2.
+ * The first vertex v0 is implicit and is determined by an index
+ * of the corresponding element in the state->oedges array.
+ * If the degree of v0 is greater than 1, other members don't
+ * make sense because they're a result of XORing multiple values.
+ */
+struct oedge {
+ uint32_t degree; /* Degree of v0. */
+ uint32_t verts[2]; /* v1 and v2 */
+ uint32_t edge;
+};
+
+struct edge {
+ uint32_t idx;
+
+ uint32_t left, middle, right;
+};
+
+struct state {
+ uint32_t data_entries;
+ uint32_t entries;
+ uint32_t keys;
+ uint32_t seed;
+
+ uint32_t *g;
+ char *visited;
+
+ struct oedge *oedges;
+ struct edge *edges;
+ uint32_t output_index;
+ uint32_t *output_order;
+};
+
+/*
+ * Add (delta == 1) or remove (delta == -1) the edge e from vertex v0.
+ */
+static void
+add_remove_edge(struct oedge *o, int delta, uint32_t e,
+ uint32_t v0, uint32_t v1, uint32_t v2)
+{
+
+ o[v0].verts[v1 < v2 ? 0 : 1] ^= v1;
+ o[v0].verts[v1 < v2 ? 1 : 0] ^= v2;
+ o[v0].degree += delta;
+ o[v0].edge ^= e;
+}
+
+static void
+add_edge(struct oedge *o, uint32_t e,
+ uint32_t v0, uint32_t v1, uint32_t v2)
+{
+
+ add_remove_edge(o, 1, e, v0, v1, v2);
+}
+
+static void
+remove_vertex(struct state *state, uint32_t v0)
+{
+ uint32_t e, v1, v2;
+ struct oedge *o = state->oedges;
+
+ if (o[v0].degree == 1) {
+ e = o[v0].edge;
+ v1 = o[v0].verts[0];
+ v2 = o[v0].verts[1];
+ o[v0].degree = 0;
+ add_remove_edge(o, -1, e, v1, v0, v2);
+ add_remove_edge(o, -1, e, v2, v0, v1);
+ state->output_order[--state->output_index] = e;
+ }
+}
+
+static int
+build_graph(struct cdbw *cdbw, struct state *state)
+{
+ struct key_hash_head *head;
+ struct key_hash *key_hash;
+ struct edge *e;
+ uint32_t hashes[3];
+ size_t i;
+
+ memset(state->oedges, 0, sizeof(struct oedge) * state->entries);
+
+ e = state->edges;
+ for (i = 0; i < cdbw->hash_size; ++i) {
+ head = &cdbw->hash[i];
+ SLIST_FOREACH(key_hash, head, link) {
+ e->idx = key_hash->idx;
+ mi_vector_hash(key_hash->key, key_hash->keylen,
+ state->seed, hashes);
+ e->left = hashes[0] % state->entries;
+ e->middle = hashes[1] % state->entries;
+ e->right = hashes[2] % state->entries;
+
+ if (e->left == e->middle)
+ return -1;
+ add_edge(state->oedges, e - state->edges,
+ e->right, e->left, e->middle);
+ if (e->left == e->right)
+ return -1;
+ add_edge(state->oedges, e - state->edges,
+ e->middle, e->left, e->right);
+ if (e->middle == e->right)
+ return -1;
+ add_edge(state->oedges, e - state->edges,
+ e->left, e->middle, e->right);
+
+ ++e;
+ }
+ }
+
+ state->output_index = state->keys;
+ for (i = 0; i < state->entries; ++i)
+ remove_vertex(state, i);
+
+ i = state->keys;
+ while (i > 0 && i > state->output_index) {
+ --i;
+ e = state->edges + state->output_order[i];
+ remove_vertex(state, e->left);
+ remove_vertex(state, e->middle);
+ remove_vertex(state, e->right);
+ }
+
+ return state->output_index == 0 ? 0 : -1;
+}
+
+static void
+assign_nodes(struct state *state)
+{
+ struct edge *e;
+ size_t i;
+
+ for (i = 0; i < state->keys; ++i) {
+ e = state->edges + state->output_order[i];
+
+ if (!state->visited[e->left]) {
+ state->g[e->left] =
+ (2 * state->data_entries + e->idx
+ - state->g[e->middle] - state->g[e->right])
+ % state->data_entries;
+ } else if (!state->visited[e->middle]) {
+ state->g[e->middle] =
+ (2 * state->data_entries + e->idx
+ - state->g[e->left] - state->g[e->right])
+ % state->data_entries;
+ } else {
+ state->g[e->right] =
+ (2 * state->data_entries + e->idx
+ - state->g[e->left] - state->g[e->middle])
+ % state->data_entries;
+ }
+ state->visited[e->left] = 1;
+ state->visited[e->middle] = 1;
+ state->visited[e->right] = 1;
+ }
+}
+
+static size_t
+compute_size(uint32_t size)
+{
+ if (size < 0x100)
+ return 1;
+ else if (size < 0x10000)
+ return 2;
+ else
+ return 4;
+}
+
+#define COND_FLUSH_BUFFER(n) do { \
+ if (__predict_false(cur_pos + (n) >= sizeof(buf))) { \
+ ret = write(fd, buf, cur_pos); \
+ if (ret == -1 || (size_t)ret != cur_pos) \
+ return -1; \
+ cur_pos = 0; \
+ } \
+} while (/* CONSTCOND */ 0)
+
+static int
+print_hash(struct cdbw *cdbw, struct state *state, int fd, const char *descr)
+{
+ uint32_t data_size;
+ uint8_t buf[90000];
+ size_t i, size, size2, cur_pos;
+ ssize_t ret;
+
+ memcpy(buf, "NBCDB\n\0", 7);
+ buf[7] = 1;
+ strncpy((char *)buf + 8, descr, 16);
+ le32enc(buf + 24, cdbw->data_size);
+ le32enc(buf + 28, cdbw->data_counter);
+ le32enc(buf + 32, state->entries);
+ le32enc(buf + 36, state->seed);
+ cur_pos = 40;
+
+ size = compute_size(state->entries);
+ for (i = 0; i < state->entries; ++i) {
+ COND_FLUSH_BUFFER(4);
+ le32enc(buf + cur_pos, state->g[i]);
+ cur_pos += size;
+ }
+ size2 = compute_size(cdbw->data_size);
+ size = size * state->entries % size2;
+ if (size != 0) {
+ size = size2 - size;
+ COND_FLUSH_BUFFER(4);
+ le32enc(buf + cur_pos, 0);
+ cur_pos += size;
+ }
+ for (data_size = 0, i = 0; i < cdbw->data_counter; ++i) {
+ COND_FLUSH_BUFFER(4);
+ le32enc(buf + cur_pos, data_size);
+ cur_pos += size2;
+ data_size += cdbw->data_len[i];
+ }
+ COND_FLUSH_BUFFER(4);
+ le32enc(buf + cur_pos, data_size);
+ cur_pos += size2;
+
+ for (i = 0; i < cdbw->data_counter; ++i) {
+ COND_FLUSH_BUFFER(cdbw->data_len[i]);
+ if (cdbw->data_len[i] < sizeof(buf)) {
+ memcpy(buf + cur_pos, cdbw->data_ptr[i],
+ cdbw->data_len[i]);
+ cur_pos += cdbw->data_len[i];
+ } else {
+ ret = write(fd, cdbw->data_ptr[i], cdbw->data_len[i]);
+ if (ret == -1 || (size_t)ret != cdbw->data_len[i])
+ return -1;
+ }
+ }
+ if (cur_pos != 0) {
+ ret = write(fd, buf, cur_pos);
+ if (ret == -1 || (size_t)ret != cur_pos)
+ return -1;
+ }
+ return 0;
+}
+
+int
+cdbw_output(struct cdbw *cdbw, int fd, const char descr[16],
+ uint32_t (*seedgen)(void))
+{
+ struct state state;
+ int rv;
+
+ if (cdbw->data_counter == 0 || cdbw->key_counter == 0) {
+ state.entries = 0;
+ state.seed = 0;
+ print_hash(cdbw, &state, fd, descr);
+ return 0;
+ }
+
+#if HAVE_NBTOOL_CONFIG_H
+ if (seedgen == NULL)
+ seedgen = cdbw_stable_seeder;
+#else
+ if (seedgen == NULL)
+ seedgen = arc4random;
+#endif
+
+ rv = 0;
+
+ state.keys = cdbw->key_counter;
+ state.data_entries = cdbw->data_counter;
+ state.entries = state.keys + (state.keys + 3) / 4;
+ if (state.entries < 10)
+ state.entries = 10;
+
+#define NALLOC(var, n) var = calloc(sizeof(*var), n)
+ NALLOC(state.g, state.entries);
+ NALLOC(state.visited, state.entries);
+ NALLOC(state.oedges, state.entries);
+ NALLOC(state.edges, state.keys);
+ NALLOC(state.output_order, state.keys);
+#undef NALLOC
+
+ if (state.g == NULL || state.visited == NULL || state.oedges == NULL ||
+ state.edges == NULL || state.output_order == NULL) {
+ rv = -1;
+ goto release;
+ }
+
+ state.seed = 0;
+ do {
+ if (seedgen == cdbw_stable_seeder)
+ ++state.seed;
+ else
+ state.seed = (*seedgen)();
+ } while (build_graph(cdbw, &state));
+
+ assign_nodes(&state);
+ rv = print_hash(cdbw, &state, fd, descr);
+
+release:
+ free(state.g);
+ free(state.visited);
+ free(state.oedges);
+ free(state.edges);
+ free(state.output_order);
+
+ return rv;
+}
Index: contrib/mg/chrdef.h
===================================================================
--- /dev/null
+++ contrib/mg/chrdef.h
@@ -0,0 +1,74 @@
+/* $OpenBSD: chrdef.h,v 1.10 2015/03/25 12:29:03 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * sys/default/chardef.h: character set specific #defines for Mg 2a
+ * Warning: System specific ones exist
+ */
+
+/*
+ * Casting should be at least as efficient as anding with 0xff,
+ * and won't have the size problems.
+ */
+#define CHARMASK(c) ((unsigned char) (c))
+
+/*
+ * These flags, and the macros below them,
+ * make up a do-it-yourself set of "ctype" macros that
+ * understand the DEC multinational set, and let me ask
+ * a slightly different set of questions.
+ */
+#define _MG_W 0x01 /* Word. */
+#define _MG_U 0x02 /* Upper case letter. */
+#define _MG_L 0x04 /* Lower case letter. */
+#define _MG_C 0x08 /* Control. */
+#define _MG_P 0x10 /* end of sentence punctuation */
+#define _MG_D 0x20 /* is decimal digit */
+
+#define ISWORD(c) ((cinfo[CHARMASK(c)]&_MG_W)!=0)
+#define ISCTRL(c) ((cinfo[CHARMASK(c)]&_MG_C)!=0)
+#define ISUPPER(c) ((cinfo[CHARMASK(c)]&_MG_U)!=0)
+#define ISLOWER(c) ((cinfo[CHARMASK(c)]&_MG_L)!=0)
+#define ISEOSP(c) ((cinfo[CHARMASK(c)]&_MG_P)!=0)
+#define ISDIGIT(c) ((cinfo[CHARMASK(c)]&_MG_D)!=0)
+#define TOUPPER(c) ((c)-0x20)
+#define TOLOWER(c) ((c)+0x20)
+
+/*
+ * Generally useful thing for chars
+ */
+#define CCHR(x) ((x) ^ 0x40) /* CCHR('?') == DEL */
+
+#define K00 256
+#define K01 257
+#define K02 258
+#define K03 259
+#define K04 260
+#define K05 261
+#define K06 262
+#define K07 263
+#define K08 264
+#define K09 265
+#define K0A 266
+#define K0B 267
+#define K0C 268
+#define K0D 269
+#define K0E 270
+#define K0F 271
+#define K10 272
+#define K11 273
+#define K12 274
+#define K13 275
+#define K14 276
+#define K15 277
+#define K16 278
+#define K17 279
+#define K18 280
+#define K19 281
+#define K1A 282
+#define K1B 283
+#define K1C 284
+#define K1D 285
+#define K1E 286
+#define K1F 287
Index: contrib/mg/cinfo.c
===================================================================
--- /dev/null
+++ contrib/mg/cinfo.c
@@ -0,0 +1,158 @@
+/* $OpenBSD: cinfo.c,v 1.18 2015/03/19 21:22:15 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Character class tables.
+ * Do it yourself character classification
+ * macros, that understand the multinational character set,
+ * and let me ask some questions the standard macros (in
+ * ctype.h) don't let you ask.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "def.h"
+
+/*
+ * This table, indexed by a character drawn
+ * from the 256 member character set, is used by my
+ * own character type macros to answer questions about the
+ * type of a character. It handles the full multinational
+ * character set, and lets me ask some questions that the
+ * standard "ctype" macros cannot ask.
+ */
+/*
+ * Due to incompatible behaviour between "standard" emacs and
+ * ctags word traversing, '_' character's value is changed on
+ * the fly in ctags mode, hence non-const.
+ */
+char cinfo[256] = {
+ _MG_C, _MG_C, _MG_C, _MG_C, /* 0x0X */
+ _MG_C, _MG_C, _MG_C, _MG_C,
+ _MG_C, _MG_C, _MG_C, _MG_C,
+ _MG_C, _MG_C, _MG_C, _MG_C,
+ _MG_C, _MG_C, _MG_C, _MG_C, /* 0x1X */
+ _MG_C, _MG_C, _MG_C, _MG_C,
+ _MG_C, _MG_C, _MG_C, _MG_C,
+ _MG_C, _MG_C, _MG_C, _MG_C,
+ 0, _MG_P, 0, 0, /* 0x2X */
+ _MG_W, _MG_W, 0, _MG_W,
+ 0, 0, 0, 0,
+ 0, 0, _MG_P, 0,
+ _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, /* 0x3X */
+ _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W,
+ _MG_D | _MG_W, _MG_D | _MG_W, 0, 0,
+ 0, 0, 0, _MG_P,
+ 0, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0x4X */
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0x5X */
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, 0,
+ 0, 0, 0, 0,
+ 0, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0x6X */
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0x7X */
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, 0,
+ 0, 0, 0, _MG_C,
+ 0, 0, 0, 0, /* 0x8X */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, /* 0x9X */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, /* 0xAX */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, /* 0xBX */
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0xCX */
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ 0, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0xDX */
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W,
+ _MG_U | _MG_W, _MG_U | _MG_W, 0, _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0xEX */
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ 0, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0xFX */
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W,
+ _MG_L | _MG_W, _MG_L | _MG_W, 0, 0
+};
+
+/*
+ * Find the name of a keystroke. Needs to be changed to handle 8-bit printing
+ * characters and function keys better. Returns a pointer to the terminating
+ * '\0'. Returns NULL on failure.
+ */
+char *
+getkeyname(char *cp, size_t len, int k)
+{
+ const char *np;
+ size_t copied;
+
+ if (k < 0)
+ k = CHARMASK(k); /* sign extended char */
+ switch (k) {
+ case CCHR('@'):
+ np = "C-SPC";
+ break;
+ case CCHR('I'):
+ np = "TAB";
+ break;
+ case CCHR('M'):
+ np = "RET";
+ break;
+ case CCHR('['):
+ np = "ESC";
+ break;
+ case ' ':
+ np = "SPC";
+ break; /* yuck again */
+ case CCHR('?'):
+ np = "DEL";
+ break;
+ default:
+ if (k >= KFIRST && k <= KLAST &&
+ (np = keystrings[k - KFIRST]) != NULL)
+ break;
+ if (k > CCHR('?')) {
+ *cp++ = '0';
+ *cp++ = ((k >> 6) & 7) + '0';
+ *cp++ = ((k >> 3) & 7) + '0';
+ *cp++ = (k & 7) + '0';
+ *cp = '\0';
+ return (cp);
+ } else if (k < ' ') {
+ *cp++ = 'C';
+ *cp++ = '-';
+ k = CCHR(k);
+ if (ISUPPER(k))
+ k = TOLOWER(k);
+ }
+ *cp++ = k;
+ *cp = '\0';
+ return (cp);
+ }
+ copied = strlcpy(cp, np, len);
+ if (copied >= len)
+ copied = len - 1;
+ return (cp + copied);
+}
Index: contrib/mg/cmode.c
===================================================================
--- /dev/null
+++ contrib/mg/cmode.c
@@ -0,0 +1,522 @@
+/* $OpenBSD: cmode.c,v 1.17 2019/07/11 18:20:18 lum Exp $ */
+/*
+ * This file is in the public domain.
+ *
+ * Author: Kjell Wooding <kjell@openbsd.org>
+ */
+
+/*
+ * Implement an non-irritating KNF-compliant mode for editing
+ * C code.
+ */
+
+#include <sys/queue.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "def.h"
+#include "funmap.h"
+#include "kbd.h"
+
+/* Pull in from modes.c */
+extern int changemode(int, int, char *);
+
+static int cc_strip_trailp = TRUE; /* Delete Trailing space? */
+static int cc_basic_indent = 8; /* Basic Indent multiple */
+static int cc_cont_indent = 4; /* Continued line indent */
+static int cc_colon_indent = -8; /* Label / case indent */
+
+static int getmatch(int, int);
+static int getindent(const struct line *, int *);
+static int in_whitespace(struct line *, int);
+static int findcolpos(const struct buffer *, const struct line *, int);
+static struct line *findnonblank(struct line *);
+static int isnonblank(const struct line *, int);
+
+void cmode_init(void);
+int cc_comment(int, int);
+
+/* Keymaps */
+
+static PF cmode_brace[] = {
+ cc_brace, /* } */
+};
+
+static PF cmode_cCP[] = {
+ compile, /* C-c P */
+};
+
+
+static PF cmode_cc[] = {
+ NULL, /* ^C */
+ rescan, /* ^D */
+ rescan, /* ^E */
+ rescan, /* ^F */
+ rescan, /* ^G */
+ rescan, /* ^H */
+ cc_tab, /* ^I */
+ rescan, /* ^J */
+ rescan, /* ^K */
+ rescan, /* ^L */
+ cc_lfindent, /* ^M */
+};
+
+static PF cmode_spec[] = {
+ cc_char, /* : */
+};
+
+static struct KEYMAPE (1) cmode_cmap = {
+ 1,
+ 1,
+ rescan,
+ {
+ { 'P', 'P', cmode_cCP, NULL }
+ }
+};
+
+static struct KEYMAPE (3) cmodemap = {
+ 3,
+ 3,
+ rescan,
+ {
+ { CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap },
+ { ':', ':', cmode_spec, NULL },
+ { '}', '}', cmode_brace, NULL }
+ }
+};
+
+/* Funtion, Mode hooks */
+
+void
+cmode_init(void)
+{
+ funmap_add(cmode, "c-mode", 0);
+ funmap_add(cc_char, "c-handle-special-char", 0);
+ funmap_add(cc_brace, "c-handle-special-brace", 0);
+ funmap_add(cc_tab, "c-tab-or-indent", 0);
+ funmap_add(cc_indent, "c-indent", 0);
+ funmap_add(cc_lfindent, "c-indent-and-newline", 0);
+ maps_add((KEYMAP *)&cmodemap, "c");
+}
+
+/*
+ * Enable/toggle c-mode
+ */
+int
+cmode(int f, int n)
+{
+ return(changemode(f, n, "c"));
+}
+
+/*
+ * Handle special C character - selfinsert then indent.
+ */
+int
+cc_char(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+ if (selfinsert(FFRAND, n) == FALSE)
+ return (FALSE);
+ return (cc_indent(FFRAND, n));
+}
+
+/*
+ * Handle special C character - selfinsert then indent.
+ */
+int
+cc_brace(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+ if (showmatch(FFRAND, 1) == FALSE)
+ return (FALSE);
+ return (cc_indent(FFRAND, n));
+}
+
+
+/*
+ * If we are in the whitespace at the beginning of the line,
+ * simply act as a regular tab. If we are not, indent
+ * current line according to whitespace rules.
+ */
+int
+cc_tab(int f, int n)
+{
+ int inwhitep = FALSE; /* In leading whitespace? */
+
+ inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp));
+
+ /* If empty line, or in whitespace */
+ if (llength(curwp->w_dotp) == 0 || inwhitep)
+ return (selfinsert(f, n));
+
+ return (cc_indent(FFRAND, 1));
+}
+
+/*
+ * Attempt to indent current line according to KNF rules.
+ */
+int
+cc_indent(int f, int n)
+{
+ int pi, mi; /* Previous indents (mi is ignored) */
+ int ci; /* current indent */
+ struct line *lp;
+ int ret;
+
+ if (n < 0)
+ return (FALSE);
+
+ undo_boundary_enable(FFRAND, 0);
+ if (cc_strip_trailp)
+ deltrailwhite(FFRAND, 1);
+
+ /*
+ * Search backwards for a non-blank, non-preprocessor,
+ * non-comment line
+ */
+
+ lp = findnonblank(curwp->w_dotp);
+
+ pi = getindent(lp, &mi);
+
+ /* Strip leading space on current line */
+ delleadwhite(FFRAND, 1);
+ /* current indent is computed only to current position */
+ (void)getindent(curwp->w_dotp, &ci);
+
+ if (pi + ci < 0)
+ ret = indent(FFOTHARG, 0);
+ else
+ ret = indent(FFOTHARG, pi + ci);
+
+ undo_boundary_enable(FFRAND, 1);
+
+ return (ret);
+}
+
+/*
+ * Indent-and-newline (technically, newline then indent)
+ */
+int
+cc_lfindent(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+ if (enewline(FFRAND, 1) == FALSE)
+ return (FALSE);
+ return (cc_indent(FFRAND, n));
+}
+
+/*
+ * Get the level of indention after line lp is processed
+ * Note getindent has two returns:
+ * curi = value if indenting current line.
+ * return value = value affecting subsequent lines.
+ */
+static int
+getindent(const struct line *lp, int *curi)
+{
+ int lo, co; /* leading space, current offset*/
+ int nicol = 0; /* position count */
+ int c = '\0'; /* current char */
+ int newind = 0; /* new index value */
+ int stringp = FALSE; /* in string? */
+ int escp = FALSE; /* Escape char? */
+ int lastc = '\0'; /* Last matched string delimeter */
+ int nparen = 0; /* paren count */
+ int obrace = 0; /* open brace count */
+ int cbrace = 0; /* close brace count */
+ int firstnwsp = FALSE; /* First nonspace encountered? */
+ int colonp = FALSE; /* Did we see a colon? */
+ int questionp = FALSE; /* Did we see a question mark? */
+ int slashp = FALSE; /* Slash? */
+ int astp = FALSE; /* Asterisk? */
+ int cpos = -1; /* comment position */
+ int cppp = FALSE; /* Preprocessor command? */
+
+ *curi = 0;
+
+ /* Compute leading space */
+ for (lo = 0; lo < llength(lp); lo++) {
+ if (!isspace(c = lgetc(lp, lo)))
+ break;
+ if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif /* NOTAB */
+ ) {
+ nicol |= 0x07;
+ }
+ nicol++;
+ }
+
+ /* If last line was blank, choose 0 */
+ if (lo == llength(lp))
+ nicol = 0;
+
+ newind = 0;
+ /* Compute modifiers */
+ for (co = lo; co < llength(lp); co++) {
+ c = lgetc(lp, co);
+ /* We have a non-whitespace char */
+ if (!firstnwsp && !isspace(c)) {
+ if (c == '#')
+ cppp = TRUE;
+ firstnwsp = TRUE;
+ }
+ if (c == '\\')
+ escp = !escp;
+ else if (stringp) {
+ if (!escp && (c == '"' || c == '\'')) {
+ /* unescaped string char */
+ if (getmatch(c, lastc))
+ stringp = FALSE;
+ }
+ } else if (c == '"' || c == '\'') {
+ stringp = TRUE;
+ lastc = c;
+ } else if (c == '(') {
+ nparen++;
+ } else if (c == ')') {
+ nparen--;
+ } else if (c == '{') {
+ obrace++;
+ firstnwsp = FALSE;
+ } else if (c == '}') {
+ cbrace++;
+ } else if (c == '?') {
+ questionp = TRUE;
+ } else if (c == ':') {
+ /* ignore (foo ? bar : baz) construct */
+ if (!questionp)
+ colonp = TRUE;
+ } else if (c == '/') {
+ /* first nonwhitespace? -> indent */
+ if (firstnwsp) {
+ /* If previous char asterisk -> close */
+ if (astp)
+ cpos = -1;
+ else
+ slashp = TRUE;
+ }
+ } else if (c == '*') {
+ /* If previous char slash -> open */
+ if (slashp)
+ cpos = co;
+ else
+ astp = TRUE;
+ } else if (firstnwsp) {
+ firstnwsp = FALSE;
+ }
+
+ /* Reset matches that apply to next character only */
+ if (c != '\\')
+ escp = FALSE;
+ if (c != '*')
+ astp = FALSE;
+ if (c != '/')
+ slashp = FALSE;
+ }
+ /*
+ * If not terminated with a semicolon, and brace or paren open.
+ * we continue
+ */
+ if (colonp) {
+ *curi += cc_colon_indent;
+ newind -= cc_colon_indent;
+ }
+
+ *curi -= (cbrace) * cc_basic_indent;
+ newind += obrace * cc_basic_indent;
+
+ if (nparen < 0)
+ newind -= cc_cont_indent;
+ else if (nparen > 0)
+ newind += cc_cont_indent;
+
+ *curi += nicol;
+
+ /* Ignore preprocessor. Otherwise, add current column */
+ if (cppp) {
+ newind = nicol;
+ *curi = 0;
+ } else {
+ newind += nicol;
+ }
+
+ if (cpos != -1)
+ newind = findcolpos(curbp, lp, cpos);
+
+ return (newind);
+}
+
+/*
+ * Given a delimeter and its purported mate, tell us if they
+ * match.
+ */
+static int
+getmatch(int c, int mc)
+{
+ int match = FALSE;
+
+ switch (c) {
+ case '"':
+ match = (mc == '"');
+ break;
+ case '\'':
+ match = (mc == '\'');
+ break;
+ case '(':
+ match = (mc == ')');
+ break;
+ case '[':
+ match = (mc == ']');
+ break;
+ case '{':
+ match = (mc == '}');
+ break;
+ }
+
+ return (match);
+}
+
+static int
+in_whitespace(struct line *lp, int len)
+{
+ int lo;
+ int inwhitep = FALSE;
+
+ for (lo = 0; lo < len; lo++) {
+ if (!isspace(lgetc(lp, lo)))
+ break;
+ if (lo == len - 1)
+ inwhitep = TRUE;
+ }
+
+ return (inwhitep);
+}
+
+
+/* convert a line/offset pair to a column position (for indenting) */
+static int
+findcolpos(const struct buffer *bp, const struct line *lp, int lo)
+{
+ int col, i, c;
+ char tmp[5];
+
+ /* determine column */
+ col = 0;
+
+ for (i = 0; i < lo; ++i) {
+ c = lgetc(lp, i);
+ if (c == '\t'
+#ifdef NOTAB
+ && !(bp->b_flag & BFNOTAB)
+#endif /* NOTAB */
+ ) {
+ col |= 0x07;
+ col++;
+ } else if (ISCTRL(c) != FALSE)
+ col += 2;
+ else if (isprint(c)) {
+ col++;
+ } else {
+ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
+ }
+
+ }
+ return (col);
+}
+
+/*
+ * Find a non-blank line, searching backwards from the supplied line pointer.
+ * For C, nonblank is non-preprocessor, non C++, and accounts
+ * for complete C-style comments.
+ */
+static struct line *
+findnonblank(struct line *lp)
+{
+ int lo;
+ int nonblankp = FALSE;
+ int commentp = FALSE;
+ int slashp;
+ int astp;
+ int c;
+
+ while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) {
+ lp = lback(lp);
+ slashp = FALSE;
+ astp = FALSE;
+
+ /* Potential nonblank? */
+ nonblankp = isnonblank(lp, llength(lp));
+
+ /*
+ * Search from end, removing complete C-style
+ * comments. If one is found, ignore it and
+ * test for nonblankness from where it starts.
+ */
+ for (lo = llength(lp) - 1; lo >= 0; lo--) {
+ if (!isspace(c = lgetc(lp, lo))) {
+ if (commentp) { /* find comment "open" */
+ if (c == '*')
+ astp = TRUE;
+ else if (astp && c == '/') {
+ commentp = FALSE;
+ /* whitespace to here? */
+ nonblankp = isnonblank(lp, lo);
+ }
+ } else { /* find comment "close" */
+ if (c == '/')
+ slashp = TRUE;
+ else if (slashp && c == '*')
+ /* found a comment */
+ commentp = TRUE;
+ }
+ }
+ }
+ }
+
+ /* Rewound to start of file? */
+ if (lback(lp) == curbp->b_headp && !nonblankp)
+ return (curbp->b_headp);
+
+ return (lp);
+}
+
+/*
+ * Given a line, scan forward to 'omax' and determine if we
+ * are all C whitespace.
+ * Note that preprocessor directives and C++-style comments
+ * count as whitespace. C-style comments do not, and must
+ * be handled elsewhere.
+ */
+static int
+isnonblank(const struct line *lp, int omax)
+{
+ int nonblankp = FALSE; /* Return value */
+ int slashp = FALSE; /* Encountered slash */
+ int lo; /* Loop index */
+ int c; /* char being read */
+
+ /* Scan from front for preprocessor, C++ comments */
+ for (lo = 0; lo < omax; lo++) {
+ if (!isspace(c = lgetc(lp, lo))) {
+ /* Possible nonblank line */
+ nonblankp = TRUE;
+ /* skip // and # starts */
+ if (c == '#' || (slashp && c == '/')) {
+ nonblankp = FALSE;
+ break;
+ } else if (!slashp && c == '/') {
+ slashp = TRUE;
+ continue;
+ }
+ }
+ slashp = FALSE;
+ }
+ return (nonblankp);
+}
Index: contrib/mg/common.h
===================================================================
--- /dev/null
+++ contrib/mg/common.h
@@ -0,0 +1,35 @@
+/*
+ * Support for non-OpenBSD systems.
+ */
+
+/* From OpenBSD sys/stat.h; glibc io/sys/stat.h agrees */
+#ifndef DEFFILEMODE
+#define DEFFILEMODE 0000666
+#endif
+
+/* From OpenBSD regex.h */
+#ifndef REG_STARTEND
+#define REG_STARTEND 00004
+#endif
+
+/* From OpenBSD sys/queue.h */
+#ifndef SLIST_FOREACH_SAFE
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+#endif
+
+/* From OpenBSD sys/queue.h */
+#ifndef TAILQ_END
+#define TAILQ_END(head) NULL
+#endif
+
+/* From OpenBSD sys/queue.h */
+#ifndef TAILQ_FOREACH_SAFE
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+#endif
Index: contrib/mg/compile.c
===================================================================
--- /dev/null
+++ contrib/mg/compile.c
@@ -0,0 +1,670 @@
+/* $NetBSD: compile.c,v 1.12 2017/05/04 09:46:30 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2010, 2011 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+/* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */
+static uint16_t
+le16dec(const void *buf)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+
+ return ((p[1] << 8) | p[0]);
+}
+
+static void
+le16enc(void *buf, uint32_t u)
+{
+ uint8_t *p = (uint8_t *) buf;
+
+ p[0] = u & 0xff;
+ p[1] = ((unsigned)u >> 8) & 0xff;
+}
+
+static void
+dowarn(int flags, const char *fmt, ...)
+{
+ va_list va;
+
+ errno = EINVAL;
+ if (flags & TIC_WARNING) {
+ va_start(va, fmt);
+ vwarnx(fmt, va);
+ va_end(va);
+ }
+}
+
+char *
+_ti_grow_tbuf(TBUF *tbuf, size_t len)
+{
+ char *buf;
+ size_t l;
+
+ l = tbuf->bufpos + len;
+ if (l > tbuf->buflen) {
+ if (tbuf->buflen == 0)
+ buf = malloc(l);
+ else
+ buf = realloc(tbuf->buf, l);
+ if (buf == NULL)
+ return NULL;
+ tbuf->buf = buf;
+ tbuf->buflen = l;
+ }
+ return tbuf->buf;
+}
+
+char *
+_ti_find_cap(TBUF *tbuf, char type, short ind)
+{
+ size_t n;
+ uint16_t num;
+ char *cap;
+
+ cap = tbuf->buf;
+ for (n = tbuf->entries; n > 0; n--) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if ((short)num == ind)
+ return cap;
+ switch (type) {
+ case 'f':
+ cap++;
+ break;
+ case 'n':
+ cap += sizeof(uint16_t);
+ break;
+ case 's':
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ cap += num;
+ break;
+ }
+ }
+
+ errno = ESRCH;
+ return NULL;
+}
+
+char *
+_ti_find_extra(TBUF *tbuf, const char *code)
+{
+ size_t n;
+ uint16_t num;
+ char *cap;
+
+ cap = tbuf->buf;
+ for (n = tbuf->entries; n > 0; n--) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (strcmp(cap, code) == 0)
+ return cap + num;
+ cap += num;
+ switch (*cap++) {
+ case 'f':
+ cap++;
+ break;
+ case 'n':
+ cap += sizeof(uint16_t);
+ break;
+ case 's':
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ cap += num;
+ break;
+ }
+ }
+
+ errno = ESRCH;
+ return NULL;
+}
+
+size_t
+_ti_store_extra(TIC *tic, int wrn, char *id, char type, char flag, short num,
+ char *str, size_t strl, int flags)
+{
+ size_t l;
+
+ if (strcmp(id, "use") != 0) {
+ if (_ti_find_extra(&tic->extras, id) != NULL)
+ return 0;
+ if (!(flags & TIC_EXTRA)) {
+ if (wrn != 0)
+ dowarn(flags, "%s: %s: unknown capability",
+ tic->name, id);
+ return 0;
+ }
+ }
+
+ l = strlen(id) + 1;
+ if (l > UINT16_T_MAX) {
+ dowarn(flags, "%s: %s: cap name is too long", tic->name, id);
+ return 0;
+ }
+
+ if (!_ti_grow_tbuf(&tic->extras,
+ l + strl + (sizeof(uint16_t) * 2) + 1))
+ return 0;
+ le16enc(tic->extras.buf + tic->extras.bufpos, (uint16_t)l);
+ tic->extras.bufpos += sizeof(uint16_t);
+ memcpy(tic->extras.buf + tic->extras.bufpos, id, l);
+ tic->extras.bufpos += l;
+ tic->extras.buf[tic->extras.bufpos++] = type;
+ switch (type) {
+ case 'f':
+ tic->extras.buf[tic->extras.bufpos++] = flag;
+ break;
+ case 'n':
+ le16enc(tic->extras.buf + tic->extras.bufpos, (uint16_t)num);
+ tic->extras.bufpos += sizeof(uint16_t);
+ break;
+ case 's':
+ le16enc(tic->extras.buf + tic->extras.bufpos, (uint16_t)strl);
+ tic->extras.bufpos += sizeof(uint16_t);
+ memcpy(tic->extras.buf + tic->extras.bufpos, str, strl);
+ tic->extras.bufpos += strl;
+ break;
+ }
+ tic->extras.entries++;
+ return 1;
+}
+
+ssize_t
+_ti_flatten(uint8_t **buf, const TIC *tic)
+{
+ size_t buflen, len, alen, dlen;
+ uint8_t *cap;
+
+ len = strlen(tic->name) + 1;
+ if (tic->alias == NULL)
+ alen = 0;
+ else
+ alen = strlen(tic->alias) + 1;
+ if (tic->desc == NULL)
+ dlen = 0;
+ else
+ dlen = strlen(tic->desc) + 1;
+ buflen = sizeof(char) +
+ sizeof(uint16_t) + len +
+ sizeof(uint16_t) + alen +
+ sizeof(uint16_t) + dlen +
+ (sizeof(uint16_t) * 2) + tic->flags.bufpos +
+ (sizeof(uint16_t) * 2) + tic->nums.bufpos +
+ (sizeof(uint16_t) * 2) + tic->strs.bufpos +
+ (sizeof(uint16_t) * 2) + tic->extras.bufpos;
+ *buf = malloc(buflen);
+ if (*buf == NULL)
+ return -1;
+
+ cap = *buf;
+ *cap++ = 1;
+ le16enc(cap, (uint16_t)len);
+ cap += sizeof(uint16_t);
+ memcpy(cap, tic->name, len);
+ cap += len;
+
+ le16enc(cap, (uint16_t)alen);
+ cap += sizeof(uint16_t);
+ if (tic->alias != NULL) {
+ memcpy(cap, tic->alias, alen);
+ cap += alen;
+ }
+ le16enc(cap, (uint16_t)dlen);
+ cap += sizeof(uint16_t);
+ if (tic->desc != NULL) {
+ memcpy(cap, tic->desc, dlen);
+ cap += dlen;
+ }
+
+ if (tic->flags.entries == 0) {
+ le16enc(cap, 0);
+ cap += sizeof(uint16_t);
+ } else {
+ le16enc(cap, (uint16_t)(tic->flags.bufpos + sizeof(uint16_t)));
+ cap += sizeof(uint16_t);
+ le16enc(cap, (uint16_t)tic->flags.entries);
+ cap += sizeof(uint16_t);
+ memcpy(cap, tic->flags.buf, tic->flags.bufpos);
+ cap += tic->flags.bufpos;
+ }
+
+ if (tic->nums.entries == 0) {
+ le16enc(cap, 0);
+ cap += sizeof(uint16_t);
+ } else {
+ le16enc(cap, (uint16_t)(tic->nums.bufpos + sizeof(uint16_t)));
+ cap += sizeof(uint16_t);
+ le16enc(cap, (uint16_t)tic->nums.entries);
+ cap += sizeof(uint16_t);
+ memcpy(cap, tic->nums.buf, tic->nums.bufpos);
+ cap += tic->nums.bufpos;
+ }
+
+ if (tic->strs.entries == 0) {
+ le16enc(cap, 0);
+ cap += sizeof(uint16_t);
+ } else {
+ le16enc(cap, (uint16_t)(tic->strs.bufpos + sizeof(uint16_t)));
+ cap += sizeof(uint16_t);
+ le16enc(cap, (uint16_t)tic->strs.entries);
+ cap += sizeof(uint16_t);
+ memcpy(cap, tic->strs.buf, tic->strs.bufpos);
+ cap += tic->strs.bufpos;
+ }
+
+ if (tic->extras.entries == 0) {
+ le16enc(cap, 0);
+ cap += sizeof(uint16_t);
+ } else {
+ le16enc(cap, (uint16_t)(tic->extras.bufpos + sizeof(uint16_t)));
+ cap += sizeof(uint16_t);
+ le16enc(cap, (uint16_t)tic->extras.entries);
+ cap += sizeof(uint16_t);
+ memcpy(cap, tic->extras.buf, tic->extras.bufpos);
+ cap += tic->extras.bufpos;
+ }
+
+ return cap - *buf;
+}
+
+static int
+encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str,
+ int flags)
+{
+ int slash, i, num;
+ char ch, *p, *s, last;
+
+ if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL)
+ return -1;
+ p = s = tbuf->buf + tbuf->bufpos;
+ slash = 0;
+ last = '\0';
+ /* Convert escape codes */
+ while ((ch = *str++) != '\0') {
+ if (ch == '\n') {
+ /* Following a newline, strip leading whitespace from
+ * capability strings. */
+ while (isspace((unsigned char)*str))
+ str++;
+ continue;
+ }
+ if (slash == 0 && ch == '\\') {
+ slash = 1;
+ continue;
+ }
+ if (slash == 0) {
+ if (last != '%' && ch == '^') {
+ ch = *str++;
+ if (((unsigned char)ch) >= 128)
+ dowarn(flags,
+ "%s: %s: illegal ^ character",
+ term, cap);
+ if (ch == '\0')
+ break;
+ if (ch == '?')
+ ch = '\177';
+ else if ((ch &= 037) == 0)
+ ch = (char)128;
+ } else if (!isprint((unsigned char)ch))
+ dowarn(flags,
+ "%s: %s: unprintable character",
+ term, cap);
+ *p++ = ch;
+ last = ch;
+ continue;
+ }
+ slash = 0;
+ if (ch >= '0' && ch <= '7') {
+ num = ch - '0';
+ for (i = 0; i < 2; i++) {
+ if (*str < '0' || *str > '7') {
+ if (isdigit((unsigned char)*str))
+ dowarn(flags,
+ "%s: %s: non octal"
+ " digit", term, cap);
+ else
+ break;
+ }
+ num = num * 8 + *str++ - '0';
+ }
+ if (num == 0)
+ num = 0200;
+ *p++ = (char)num;
+ continue;
+ }
+ switch (ch) {
+ case 'a':
+ *p++ = '\a';
+ break;
+ case 'b':
+ *p++ = '\b';
+ break;
+ case 'e': /* FALLTHROUGH */
+ case 'E':
+ *p++ = '\033';
+ break;
+ case 'f':
+ *p++ = '\014';
+ break;
+ case 'l': /* FALLTHROUGH */
+ case 'n':
+ *p++ = '\n';
+ break;
+ case 'r':
+ *p++ = '\r';
+ break;
+ case 's':
+ *p++ = ' ';
+ break;
+ case 't':
+ *p++ = '\t';
+ break;
+ default:
+ /* We should warn here */
+ case '^':
+ case ',':
+ case ':':
+ case '|':
+ *p++ = ch;
+ break;
+ }
+ last = ch;
+ }
+ *p++ = '\0';
+ tbuf->bufpos += (size_t)(p - s);
+ return 0;
+}
+
+char *
+_ti_get_token(char **cap, char sep)
+{
+ char esc, *token;
+
+ while (isspace((unsigned char)**cap))
+ (*cap)++;
+ if (**cap == '\0')
+ return NULL;
+
+ /* We can't use stresep(3) as ^ we need two escape chars */
+ esc = '\0';
+ for (token = *cap;
+ **cap != '\0' && (esc != '\0' || **cap != sep);
+ (*cap)++)
+ {
+ if (esc == '\0') {
+ if (**cap == '\\' || **cap == '^')
+ esc = **cap;
+ } else {
+ /* termcap /E/ is valid */
+ if (sep == ':' && esc == '\\' && **cap == 'E')
+ esc = 'x';
+ else
+ esc = '\0';
+ }
+ }
+
+ if (**cap != '\0')
+ *(*cap)++ = '\0';
+
+ return token;
+}
+
+TIC *
+_ti_compile(char *cap, int flags)
+{
+ char *token, *p, *e, *name, *desc, *alias;
+ signed char flag;
+ long cnum;
+ short ind, num;
+ size_t len;
+ TBUF buf;
+ TIC *tic;
+
+ name = _ti_get_token(&cap, ',');
+ if (name == NULL) {
+ dowarn(flags, "no seperator found: %s", cap);
+ return NULL;
+ }
+ desc = strrchr(name, '|');
+ if (desc != NULL)
+ *desc++ = '\0';
+ alias = strchr(name, '|');
+ if (alias != NULL)
+ *alias++ = '\0';
+
+ tic = calloc(sizeof(*tic), 1);
+ if (tic == NULL)
+ return NULL;
+
+ buf.buf = NULL;
+ buf.buflen = 0;
+
+ tic->name = strdup(name);
+ if (tic->name == NULL)
+ goto error;
+ if (alias != NULL && flags & TIC_ALIAS) {
+ tic->alias = strdup(alias);
+ if (tic->alias == NULL)
+ goto error;
+ }
+ if (desc != NULL && flags & TIC_DESCRIPTION) {
+ tic->desc = strdup(desc);
+ if (tic->desc == NULL)
+ goto error;
+ }
+
+ for (token = _ti_get_token(&cap, ',');
+ token != NULL && *token != '\0';
+ token = _ti_get_token(&cap, ','))
+ {
+ /* Skip commented caps */
+ if (!(flags & TIC_COMMENT) && token[0] == '.')
+ continue;
+
+ /* Obsolete entries */
+ if (token[0] == 'O' && token[1] == 'T') {
+ if (!(flags & TIC_EXTRA))
+ continue;
+ token += 2;
+ }
+
+ /* str cap */
+ p = strchr(token, '=');
+ if (p != NULL) {
+ *p++ = '\0';
+ /* Don't use the string if we already have it */
+ ind = (short)_ti_strindex(token);
+ if (ind != -1 &&
+ _ti_find_cap(&tic->strs, 's', ind) != NULL)
+ continue;
+
+ /* Encode the string to our scratch buffer */
+ buf.bufpos = 0;
+ if (encode_string(tic->name, token,
+ &buf, p, flags) == -1)
+ goto error;
+ if (buf.bufpos > UINT16_T_MAX) {
+ dowarn(flags, "%s: %s: string is too long",
+ tic->name, token);
+ continue;
+ }
+ if (!VALID_STRING(buf.buf)) {
+ dowarn(flags, "%s: %s: invalid string",
+ tic->name, token);
+ continue;
+ }
+
+ if (ind == -1)
+ _ti_store_extra(tic, 1, token, 's', -1, -2,
+ buf.buf, buf.bufpos, flags);
+ else {
+ if (!_ti_grow_tbuf(&tic->strs,
+ (sizeof(uint16_t) * 2) + buf.bufpos))
+ goto error;
+ le16enc(tic->strs.buf + tic->strs.bufpos, (uint16_t)ind);
+ tic->strs.bufpos += sizeof(uint16_t);
+ le16enc(tic->strs.buf + tic->strs.bufpos,
+ (uint16_t)buf.bufpos);
+ tic->strs.bufpos += sizeof(uint16_t);
+ memcpy(tic->strs.buf + tic->strs.bufpos,
+ buf.buf, buf.bufpos);
+ tic->strs.bufpos += buf.bufpos;
+ tic->strs.entries++;
+ }
+ continue;
+ }
+
+ /* num cap */
+ p = strchr(token, '#');
+ if (p != NULL) {
+ *p++ = '\0';
+ /* Don't use the number if we already have it */
+ ind = (short)_ti_numindex(token);
+ if (ind != -1 &&
+ _ti_find_cap(&tic->nums, 'n', ind) != NULL)
+ continue;
+
+ cnum = strtol(p, &e, 0);
+ if (*e != '\0') {
+ dowarn(flags, "%s: %s: not a number",
+ tic->name, token);
+ continue;
+ }
+ if (!VALID_NUMERIC(cnum)) {
+ dowarn(flags, "%s: %s: number out of range",
+ tic->name, token);
+ continue;
+ }
+ num = (short)cnum;
+ if (ind == -1)
+ _ti_store_extra(tic, 1, token, 'n', -1,
+ num, NULL, 0, flags);
+ else {
+ if (_ti_grow_tbuf(&tic->nums,
+ sizeof(uint16_t) * 2) == NULL)
+ goto error;
+ le16enc(tic->nums.buf + tic->nums.bufpos,
+ (uint16_t)ind);
+ tic->nums.bufpos += sizeof(uint16_t);
+ le16enc(tic->nums.buf + tic->nums.bufpos,
+ (uint16_t)num);
+ tic->nums.bufpos += sizeof(uint16_t);
+ tic->nums.entries++;
+ }
+ continue;
+ }
+
+ flag = 1;
+ len = strlen(token) - 1;
+ if (token[len] == '@') {
+ flag = CANCELLED_BOOLEAN;
+ token[len] = '\0';
+ }
+ ind = (short)_ti_flagindex(token);
+ if (ind == -1 && flag == CANCELLED_BOOLEAN) {
+ if ((ind = (short)_ti_numindex(token)) != -1) {
+ if (_ti_find_cap(&tic->nums, 'n', ind) != NULL)
+ continue;
+ if (_ti_grow_tbuf(&tic->nums,
+ sizeof(uint16_t) * 2) == NULL)
+ goto error;
+ le16enc(tic->nums.buf + tic->nums.bufpos,
+ (uint16_t)ind);
+ tic->nums.bufpos += sizeof(uint16_t);
+ le16enc(tic->nums.buf + tic->nums.bufpos,
+ (uint16_t)CANCELLED_NUMERIC);
+ tic->nums.bufpos += sizeof(uint16_t);
+ tic->nums.entries++;
+ continue;
+ } else if ((ind = (short)_ti_strindex(token)) != -1) {
+ if (_ti_find_cap(&tic->strs, 's', ind) != NULL)
+ continue;
+ if (_ti_grow_tbuf(&tic->strs,
+ (sizeof(uint16_t) * 2) + 1) == NULL)
+ goto error;
+ le16enc(tic->strs.buf + tic->strs.bufpos, (uint16_t)ind);
+ tic->strs.bufpos += sizeof(uint16_t);
+ le16enc(tic->strs.buf + tic->strs.bufpos, 0);
+ tic->strs.bufpos += sizeof(uint16_t);
+ tic->strs.entries++;
+ continue;
+ }
+ }
+ if (ind == -1)
+ _ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 0,
+ flags);
+ else if (_ti_find_cap(&tic->flags, 'f', ind) == NULL) {
+ if (_ti_grow_tbuf(&tic->flags, sizeof(uint16_t) + 1)
+ == NULL)
+ goto error;
+ le16enc(tic->flags.buf + tic->flags.bufpos,
+ (uint16_t)ind);
+ tic->flags.bufpos += sizeof(uint16_t);
+ tic->flags.buf[tic->flags.bufpos++] = flag;
+ tic->flags.entries++;
+ }
+ }
+
+ free(buf.buf);
+ return tic;
+
+error:
+ free(buf.buf);
+ _ti_freetic(tic);
+ return NULL;
+}
+
+void
+_ti_freetic(TIC *tic)
+{
+
+ if (tic != NULL) {
+ free(tic->name);
+ free(tic->alias);
+ free(tic->desc);
+ free(tic->extras.buf);
+ free(tic->flags.buf);
+ free(tic->nums.buf);
+ free(tic->strs.buf);
+ free(tic);
+ }
+}
Index: contrib/mg/compiled_terms.c
===================================================================
--- /dev/null
+++ contrib/mg/compiled_terms.c
@@ -0,0 +1,1650 @@
+/* DO NOT EDIT
+ * Automatically generated from terminfo */
+
+struct compiled_term {
+ const char *name;
+ const char *cap;
+ size_t caplen;
+};
+
+const struct compiled_term compiled_terms[] = {
+ {
+ "ansi",
+ "\001\005\000\141\156\163\151\000\000\000\000\000\016\000\004"
+ "\000\036\000\001\001\000\001\026\000\001\027\000\001\032\000"
+ "\006\000\016\000\010\000\025\000\003\000\021\000\100\000\004"
+ "\000\120\000\007\000\010\000\012\000\030\000\142\003\107\000"
+ "\200\001\013\000\033\133\045\151\045\144\073\045\144\122\000"
+ "\201\001\005\000\033\133\066\156\000\202\001\023\000\033\133"
+ "\077\045\133\073\060\061\062\063\064\065\066\067\070\071\135"
+ "\143\000\203\001\004\000\033\133\143\000\052\001\011\000\033"
+ "\133\063\071\073\064\071\155\000\132\001\012\000\033\133\064"
+ "\045\160\061\045\144\155\000\133\001\012\000\033\133\063\045"
+ "\160\061\045\144\155\000\110\000\005\000\033\133\070\155\000"
+ "\134\001\126\000\033\133\060\073\061\060\045\077\045\160\061"
+ "\045\164\073\067\045\073\045\077\045\160\062\045\164\073\064"
+ "\045\073\045\077\045\160\063\045\164\073\067\045\073\045\077"
+ "\045\160\064\045\164\073\065\045\073\045\077\045\160\066\045"
+ "\164\073\061\045\073\045\077\045\160\067\045\164\073\070\045"
+ "\073\045\077\045\160\071\045\164\073\061\061\045\073\155\000"
+ "\063\000\005\000\033\133\065\155\000\064\000\005\000\033\133"
+ "\061\155\000\105\000\005\000\033\133\067\155\000\135\000\006"
+ "\000\033\133\061\060\155\000\140\000\004\000\033\133\155\000"
+ "\143\000\004\000\033\133\155\000\125\000\010\000\033\133\060"
+ "\073\061\060\155\000\103\000\006\000\033\133\061\061\155\000"
+ "\112\000\005\000\033\133\067\155\000\116\000\005\000\033\133"
+ "\064\155\000\000\000\077\000\053\020\054\021\055\030\056\031"
+ "\060\333\140\004\141\261\146\370\147\361\150\260\152\331\153"
+ "\277\154\332\155\300\156\305\157\176\160\304\161\304\162\304"
+ "\163\137\164\303\165\264\166\301\167\302\170\263\171\363\172"
+ "\362\173\343\174\330\175\234\176\376\000\123\000\006\000\033"
+ "\133\061\060\155\000\061\000\006\000\033\133\061\061\155\000"
+ "\063\001\011\000\033\133\045\160\061\045\144\104\000\056\001"
+ "\011\000\033\133\045\160\061\045\144\102\000\065\001\011\000"
+ "\033\133\045\160\061\045\144\103\000\070\001\011\000\033\133"
+ "\045\160\061\045\144\101\000\054\001\011\000\033\133\045\160"
+ "\061\045\144\120\000\055\001\011\000\033\133\045\160\061\045"
+ "\144\115\000\122\000\011\000\033\133\045\160\061\045\144\130"
+ "\000\022\000\005\000\033\133\061\113\000\027\000\013\000\033"
+ "\133\045\151\045\160\061\045\144\107\000\166\001\004\000\033"
+ "\133\111\000\060\001\011\000\033\133\045\160\061\045\144\100"
+ "\000\062\001\011\000\033\133\045\160\061\045\144\114\000\061"
+ "\001\011\000\033\133\045\160\061\045\144\123\000\173\000\002"
+ "\000\010\000\175\000\004\000\033\133\132\000\326\000\004\000"
+ "\033\133\104\000\212\000\004\000\033\133\102\000\351\000\004"
+ "\000\033\133\103\000\015\001\004\000\033\133\101\000\324\000"
+ "\004\000\033\133\114\000\102\001\005\000\033\133\064\151\000"
+ "\103\001\005\000\033\133\065\151\000\047\001\005\000\015\033"
+ "\133\123\000\107\001\024\000\045\160\061\045\143\033\133\045"
+ "\160\062\045\173\061\175\045\055\045\144\142\000\067\001\011"
+ "\000\033\133\045\160\061\045\144\124\000\125\001\004\000\033"
+ "\050\102\000\126\001\004\000\033\051\102\000\127\001\004\000"
+ "\033\052\102\000\130\001\004\000\033\053\102\000\017\000\005"
+ "\000\033\133\063\147\000\117\001\013\000\033\133\045\151\045"
+ "\160\061\045\144\144\000\003\000\002\000\007\000\002\000\004"
+ "\000\033\133\132\000\021\000\007\000\033\133\110\033\133\112"
+ "\000\007\000\002\000\015\000\036\000\004\000\033\133\104\000"
+ "\033\000\004\000\033\133\102\000\041\000\004\000\033\133\103"
+ "\000\032\000\021\000\033\133\045\151\045\160\061\045\144\073"
+ "\045\160\062\045\144\110\000\043\000\004\000\033\133\101\000"
+ "\047\000\004\000\033\133\120\000\050\000\004\000\033\133\115"
+ "\000\024\000\004\000\033\133\112\000\023\000\004\000\033\133"
+ "\113\000\034\000\004\000\033\133\110\000\153\001\003\000\033"
+ "\110\000\166\000\004\000\033\133\114\000\122\001\002\000\012"
+ "\000\323\000\004\000\033\133\110\000\020\000\002\000\003\000"
+ "\101\130\000\146\001\003\000\142\163\000\146\001",
+ 942
+ },
+ {
+ "dumb",
+ "\001\005\000\144\165\155\142\000\000\000\000\000\005\000\001"
+ "\000\001\000\001\006\000\001\000\004\000\120\000\032\000\004"
+ "\000\003\000\002\000\007\000\007\000\002\000\015\000\033\000"
+ "\002\000\012\000\122\001\002\000\012\000\000\000",
+ 57
+ },
+ {
+ "rxvt-unicode-256color",
+ "\001\026\000\162\170\166\164\055\165\156\151\143\157\144\145"
+ "\055\062\065\066\143\157\154\157\162\000\000\000\000\000\032"
+ "\000\010\000\003\000\001\001\000\001\002\000\001\012\000\001"
+ "\026\000\001\027\000\001\011\000\001\044\000\001\026\000\005"
+ "\000\016\000\000\001\021\000\377\177\004\000\120\000\007\000"
+ "\010\000\012\000\030\000\053\007\222\000\163\000\136\000\033"
+ "\135\064\073\045\160\061\045\144\073\162\147\142\072\045\160"
+ "\062\045\173\062\065\065\175\045\052\045\173\061\060\060\060"
+ "\175\045\057\045\062\056\062\130\057\045\160\063\045\173\062"
+ "\065\065\175\045\052\045\173\061\060\060\060\175\045\057\045"
+ "\062\056\062\130\057\045\160\064\045\173\062\065\065\175\045"
+ "\052\045\173\061\060\060\060\175\045\057\045\062\056\062\130"
+ "\033\134\000\051\001\007\000\033\135\061\060\064\007\000\132"
+ "\001\100\000\033\133\045\077\045\160\061\045\173\070\175\045"
+ "\074\045\164\064\045\160\061\045\144\045\145\045\160\061\045"
+ "\173\061\066\175\045\074\045\164\061\060\045\160\061\045\173"
+ "\070\175\045\055\045\144\045\145\064\070\073\065\073\045\160"
+ "\061\045\144\045\073\155\000\133\001\077\000\033\133\045\077"
+ "\045\160\061\045\173\070\175\045\074\045\164\063\045\160\061"
+ "\045\144\045\145\045\160\061\045\173\061\066\175\045\074\045"
+ "\164\071\045\160\061\045\173\070\175\045\055\045\144\045\145"
+ "\063\070\073\065\073\045\160\061\045\144\045\073\155\000\027"
+ "\000\013\000\033\133\045\151\045\160\061\045\144\107\000\221"
+ "\000\006\000\033\133\062\061\176\000\125\000\005\000\033\133"
+ "\155\017\000\117\001\013\000\033\133\045\151\045\160\061\045"
+ "\144\144\000\000\000\063\000\140\140\141\141\146\146\147\147"
+ "\152\152\153\153\154\154\155\155\156\156\157\157\160\160\161"
+ "\161\162\162\163\163\164\164\165\165\166\166\167\167\170\170"
+ "\171\171\172\172\173\173\174\174\175\175\176\176\000\003\000"
+ "\002\000\007\000\063\000\005\000\033\133\065\155\000\064\000"
+ "\005\000\033\133\061\155\000\035\000\007\000\033\133\077\062"
+ "\065\154\000\021\000\010\000\033\133\110\033\133\062\112\000"
+ "\040\000\007\000\033\133\077\062\065\150\000\007\000\002\000"
+ "\015\000\014\000\021\000\033\133\045\151\045\160\061\045\144"
+ "\073\045\160\062\045\144\162\000\063\001\011\000\033\133\045"
+ "\160\061\045\144\104\000\036\000\002\000\010\000\056\001\011"
+ "\000\033\133\045\160\061\045\144\102\000\033\000\002\000\012"
+ "\000\065\001\011\000\033\133\045\160\061\045\144\103\000\041"
+ "\000\004\000\033\133\103\000\032\000\021\000\033\133\045\151"
+ "\045\160\061\045\144\073\045\160\062\045\144\110\000\070\001"
+ "\011\000\033\133\045\160\061\045\144\101\000\043\000\004\000"
+ "\033\133\101\000\055\001\011\000\033\133\045\160\061\045\144"
+ "\115\000\050\000\004\000\033\133\115\000\024\000\004\000\033"
+ "\133\112\000\023\000\004\000\033\133\113\000\022\000\005\000"
+ "\033\133\061\113\000\057\000\007\000\033\050\102\033\051\060"
+ "\000\150\000\022\000\033\133\077\065\150\044\074\061\060\060"
+ "\057\076\033\133\077\065\154\000\034\000\004\000\033\133\110"
+ "\000\166\001\002\000\011\000\153\001\003\000\033\110\000\060"
+ "\001\011\000\033\133\045\160\061\045\144\100\000\165\000\004"
+ "\000\033\133\100\000\062\001\011\000\033\133\045\160\061\045"
+ "\144\114\000\166\000\004\000\033\133\114\000\122\001\002\000"
+ "\012\000\156\000\016\000\033\133\077\064\067\154\033\075\033"
+ "\133\077\061\154\000\157\000\042\000\033\133\162\033\133\155"
+ "\033\133\062\112\033\133\110\033\133\077\067\150\033\133\077"
+ "\061\073\063\073\064\073\066\154\033\133\064\154\000\173\000"
+ "\002\000\010\000\175\000\004\000\033\133\132\000\332\000\004"
+ "\000\033\133\115\000\116\001\003\000\033\070\000\105\000\005"
+ "\000\033\133\067\155\000\123\001\003\000\033\115\000\123\000"
+ "\002\000\017\000\126\000\015\000\033\133\062\112\033\133\077"
+ "\064\067\154\033\070\000\131\000\005\000\033\133\064\154\000"
+ "\016\001\003\000\033\076\000\140\000\006\000\033\133\062\067"
+ "\155\000\143\000\006\000\033\133\062\064\155\000\112\001\041"
+ "\000\033\076\033\133\061\073\063\073\064\073\065\073\066\154"
+ "\033\133\077\067\150\033\133\155\033\133\162\033\133\062\112"
+ "\033\133\110\000\113\001\062\000\033\133\162\033\133\155\033"
+ "\133\062\112\033\133\110\033\133\077\067\150\033\133\077\061"
+ "\073\063\073\064\073\066\154\033\133\064\154\033\076\033\133"
+ "\077\061\060\060\060\154\033\133\077\062\065\150\000\125\001"
+ "\004\000\033\050\102\000\126\001\004\000\033\050\060\000\120"
+ "\001\003\000\033\067\000\134\001\103\000\033\133\060\045\077"
+ "\045\160\066\045\164\073\061\045\073\045\077\045\160\062\045"
+ "\164\073\064\045\073\045\077\045\160\061\045\160\063\045\174"
+ "\045\164\073\067\045\073\045\077\045\160\064\045\164\073\065"
+ "\045\073\155\045\077\045\160\071\045\164\016\045\145\017\045"
+ "\073\000\061\000\002\000\016\000\065\000\011\000\033\067\033"
+ "\133\077\064\067\150\000\073\000\005\000\033\133\064\150\000"
+ "\017\001\003\000\033\075\000\112\000\005\000\033\133\067\155"
+ "\000\116\000\005\000\033\133\064\155\000\017\000\005\000\033"
+ "\133\063\147\000\202\001\010\000\033\133\077\061\073\062\143"
+ "\000\200\001\013\000\033\133\045\151\045\144\073\045\144\122"
+ "\000\201\001\005\000\033\133\066\156\000\203\001\004\000\033"
+ "\133\143\000\360\000\005\000\033\133\063\044\000\363\000\005"
+ "\000\033\133\070\044\000\371\000\005\000\033\133\067\044\000"
+ "\372\000\005\000\033\133\062\044\000\373\000\004\000\033\133"
+ "\144\000\376\000\005\000\033\133\066\044\000\000\001\005\000"
+ "\033\133\065\044\000\005\001\004\000\033\133\143\000\326\000"
+ "\004\000\033\133\104\000\212\000\004\000\033\133\102\000\351"
+ "\000\004\000\033\133\103\000\015\001\004\000\033\133\101\000"
+ "\210\000\005\000\033\133\063\176\000\216\000\005\000\033\133"
+ "\070\136\000\214\000\005\000\033\133\070\176\000\222\000\006"
+ "\000\033\133\061\061\176\000\233\000\006\000\033\133\062\061"
+ "\176\000\234\000\006\000\033\133\062\063\176\000\235\000\006"
+ "\000\033\133\062\064\176\000\236\000\006\000\033\133\062\065"
+ "\176\000\237\000\006\000\033\133\062\066\176\000\240\000\006"
+ "\000\033\133\062\070\176\000\241\000\006\000\033\133\062\071"
+ "\176\000\242\000\006\000\033\133\063\061\176\000\243\000\006"
+ "\000\033\133\063\062\176\000\244\000\006\000\033\133\063\063"
+ "\176\000\223\000\006\000\033\133\061\062\176\000\245\000\006"
+ "\000\033\133\063\064\176\000\246\000\006\000\033\133\062\063"
+ "\044\000\247\000\006\000\033\133\062\064\044\000\250\000\006"
+ "\000\033\133\061\061\136\000\251\000\006\000\033\133\061\062"
+ "\136\000\252\000\006\000\033\133\061\063\136\000\253\000\006"
+ "\000\033\133\061\064\136\000\254\000\006\000\033\133\061\065"
+ "\136\000\255\000\006\000\033\133\061\067\136\000\256\000\006"
+ "\000\033\133\061\070\136\000\224\000\006\000\033\133\061\063"
+ "\176\000\257\000\006\000\033\133\061\071\136\000\260\000\006"
+ "\000\033\133\062\060\136\000\261\000\006\000\033\133\062\061"
+ "\136\000\262\000\006\000\033\133\062\063\136\000\263\000\006"
+ "\000\033\133\062\064\136\000\264\000\006\000\033\133\062\065"
+ "\136\000\265\000\006\000\033\133\062\066\136\000\266\000\006"
+ "\000\033\133\062\070\136\000\267\000\006\000\033\133\062\071"
+ "\136\000\270\000\006\000\033\133\063\061\136\000\225\000\006"
+ "\000\033\133\061\064\176\000\271\000\006\000\033\133\063\062"
+ "\136\000\272\000\006\000\033\133\063\063\136\000\273\000\006"
+ "\000\033\133\063\064\136\000\274\000\006\000\033\133\062\063"
+ "\100\000\275\000\006\000\033\133\062\064\100\000\226\000\006"
+ "\000\033\133\061\065\176\000\227\000\006\000\033\133\061\067"
+ "\176\000\230\000\006\000\033\133\061\070\176\000\231\000\006"
+ "\000\033\133\061\071\176\000\232\000\006\000\033\133\062\060"
+ "\176\000\321\000\005\000\033\133\061\176\000\323\000\005\000"
+ "\033\133\067\176\000\324\000\005\000\033\133\062\176\000\366"
+ "\000\004\000\033\133\141\000\335\000\005\000\033\133\066\176"
+ "\000\340\000\005\000\033\133\065\176\000\002\001\004\000\033"
+ "\133\142\000\362\000\005\000\033\133\064\176\000\170\000\004"
+ "\000\033\117\167\000\171\000\004\000\033\117\171\000\172\000"
+ "\004\000\033\117\165\000\176\000\004\000\033\117\161\000\177"
+ "\000\004\000\033\117\163\000\215\000\004\000\033\117\115\000"
+ "\052\001\011\000\033\133\063\071\073\064\071\155\000\133\001"
+ "\031\000\003\000\142\163\000\146\001\003\000\130\124\000\146"
+ "\001\005\000\153\104\103\065\000\163\005\000\033\133\063\136"
+ "\000\005\000\153\104\103\066\000\163\005\000\033\133\063\100"
+ "\000\004\000\153\104\116\000\163\004\000\033\133\142\000\005"
+ "\000\153\104\116\065\000\163\004\000\033\117\142\000\006\000"
+ "\153\105\116\104\065\000\163\005\000\033\133\070\136\000\006"
+ "\000\153\105\116\104\066\000\163\005\000\033\133\070\100\000"
+ "\006\000\153\110\117\115\065\000\163\005\000\033\133\067\136"
+ "\000\006\000\153\110\117\115\066\000\163\005\000\033\133\067"
+ "\100\000\005\000\153\111\103\065\000\163\005\000\033\133\062"
+ "\136\000\005\000\153\111\103\066\000\163\005\000\033\133\062"
+ "\100\000\006\000\153\114\106\124\065\000\163\004\000\033\117"
+ "\144\000\006\000\153\116\130\124\065\000\163\005\000\033\133"
+ "\066\136\000\006\000\153\116\130\124\066\000\163\005\000\033"
+ "\133\066\100\000\006\000\153\120\122\126\065\000\163\005\000"
+ "\033\133\065\136\000\006\000\153\120\122\126\066\000\163\005"
+ "\000\033\133\065\100\000\006\000\153\122\111\124\065\000\163"
+ "\004\000\033\117\143\000\004\000\153\125\120\000\163\004\000"
+ "\033\133\141\000\005\000\153\125\120\065\000\163\004\000\033"
+ "\117\141\000\004\000\153\141\062\000\163\004\000\033\117\170"
+ "\000\004\000\153\142\061\000\163\004\000\033\117\164\000\004"
+ "\000\153\142\063\000\163\004\000\033\117\166\000\004\000\153"
+ "\143\062\000\163\004\000\033\117\162\000\003\000\101\130\000"
+ "\146\001",
+ 2267
+ },
+ {
+ "screen",
+ "\001\007\000\163\143\162\145\145\156\000\000\000\000\000\021"
+ "\000\005\000\001\000\001\016\000\001\026\000\001\027\000\001"
+ "\011\000\001\032\000\006\000\016\000\010\000\004\000\120\000"
+ "\007\000\010\000\012\000\030\000\025\000\376\377\021\000\100"
+ "\000\330\003\127\000\000\000\101\000\053\053\054\054\055\055"
+ "\056\056\060\060\140\140\141\141\146\146\147\147\150\150\151"
+ "\151\152\152\153\153\154\154\155\155\156\156\157\157\160\160"
+ "\161\161\162\162\163\163\164\164\165\165\166\166\167\167\170"
+ "\170\171\171\172\172\173\173\174\174\175\175\176\176\000\003"
+ "\000\002\000\007\000\063\000\005\000\033\133\065\155\000\064"
+ "\000\005\000\033\133\061\155\000\002\000\004\000\033\133\132"
+ "\000\035\000\007\000\033\133\077\062\065\154\000\021\000\007"
+ "\000\033\133\110\033\133\112\000\040\000\014\000\033\133\063"
+ "\064\150\033\133\077\062\065\150\000\007\000\002\000\015\000"
+ "\014\000\021\000\033\133\045\151\045\160\061\045\144\073\045"
+ "\160\062\045\144\162\000\063\001\011\000\033\133\045\160\061"
+ "\045\144\104\000\036\000\002\000\010\000\056\001\011\000\033"
+ "\133\045\160\061\045\144\102\000\033\000\002\000\012\000\065"
+ "\001\011\000\033\133\045\160\061\045\144\103\000\041\000\004"
+ "\000\033\133\103\000\032\000\021\000\033\133\045\151\045\160"
+ "\061\045\144\073\045\160\062\045\144\110\000\070\001\011\000"
+ "\033\133\045\160\061\045\144\101\000\043\000\003\000\033\115"
+ "\000\044\000\006\000\033\133\063\064\154\000\054\001\011\000"
+ "\033\133\045\160\061\045\144\120\000\047\000\004\000\033\133"
+ "\120\000\067\000\005\000\033\133\062\155\000\055\001\011\000"
+ "\033\133\045\160\061\045\144\115\000\050\000\004\000\033\133"
+ "\115\000\024\000\004\000\033\133\112\000\023\000\004\000\033"
+ "\133\113\000\022\000\005\000\033\133\061\113\000\057\000\007"
+ "\000\033\050\102\033\051\060\000\150\000\003\000\033\147\000"
+ "\034\000\004\000\033\133\110\000\166\001\002\000\011\000\153"
+ "\001\003\000\033\110\000\060\001\011\000\033\133\045\160\061"
+ "\045\144\100\000\062\001\011\000\033\133\045\160\061\045\144"
+ "\114\000\166\000\004\000\033\133\114\000\122\001\002\000\012"
+ "\000\157\000\004\000\033\051\060\000\173\000\002\000\010\000"
+ "\175\000\004\000\033\133\132\000\326\000\004\000\033\117\104"
+ "\000\212\000\004\000\033\117\102\000\351\000\004\000\033\117"
+ "\103\000\015\001\004\000\033\117\101\000\210\000\005\000\033"
+ "\133\063\176\000\214\000\005\000\033\133\064\176\000\222\000"
+ "\004\000\033\117\120\000\233\000\006\000\033\133\062\061\176"
+ "\000\234\000\006\000\033\133\062\063\176\000\235\000\006\000"
+ "\033\133\062\064\176\000\223\000\004\000\033\117\121\000\224"
+ "\000\004\000\033\117\122\000\225\000\004\000\033\117\123\000"
+ "\226\000\006\000\033\133\061\065\176\000\227\000\006\000\033"
+ "\133\061\067\176\000\230\000\006\000\033\133\061\070\176\000"
+ "\231\000\006\000\033\133\061\071\176\000\232\000\006\000\033"
+ "\133\062\060\176\000\323\000\005\000\033\133\061\176\000\324"
+ "\000\005\000\033\133\062\176\000\332\000\004\000\033\133\115"
+ "\000\335\000\005\000\033\133\066\176\000\340\000\005\000\033"
+ "\133\065\176\000\047\001\003\000\033\105\000\116\001\003\000"
+ "\033\070\000\105\000\005\000\033\133\067\155\000\123\001\003"
+ "\000\033\115\000\123\000\002\000\017\000\126\000\011\000\033"
+ "\133\077\061\060\064\071\154\000\131\000\005\000\033\133\064"
+ "\154\000\016\001\010\000\033\133\077\061\154\033\076\000\140"
+ "\000\006\000\033\133\062\063\155\000\143\000\006\000\033\133"
+ "\062\064\155\000\113\001\021\000\033\143\033\133\077\061\060"
+ "\060\060\154\033\133\077\062\065\150\000\120\001\003\000\033"
+ "\067\000\134\001\124\000\033\133\060\045\077\045\160\066\045"
+ "\164\073\061\045\073\045\077\045\160\061\045\164\073\063\045"
+ "\073\045\077\045\160\062\045\164\073\064\045\073\045\077\045"
+ "\160\063\045\164\073\067\045\073\045\077\045\160\064\045\164"
+ "\073\065\045\073\045\077\045\160\065\045\164\073\062\045\073"
+ "\155\045\077\045\160\071\045\164\016\045\145\017\045\073\000"
+ "\125\000\005\000\033\133\155\017\000\061\000\002\000\016\000"
+ "\065\000\011\000\033\133\077\061\060\064\071\150\000\073\000"
+ "\005\000\033\133\064\150\000\017\001\010\000\033\133\077\061"
+ "\150\033\075\000\112\000\005\000\033\133\063\155\000\116\000"
+ "\005\000\033\133\064\155\000\017\000\005\000\033\133\063\147"
+ "\000\052\001\011\000\033\133\063\071\073\064\071\155\000\132"
+ "\001\012\000\033\133\064\045\160\061\045\144\155\000\133\001"
+ "\012\000\033\133\063\045\160\061\045\144\155\000\102\000\007"
+ "\000\003\000\142\163\000\146\001\003\000\160\164\000\146\001"
+ "\003\000\107\060\000\146\001\003\000\125\070\000\156\001\000"
+ "\003\000\105\060\000\163\004\000\033\050\102\000\003\000\123"
+ "\060\000\163\010\000\033\050\045\160\061\045\143\000\003\000"
+ "\101\130\000\146\001",
+ 1115
+ },
+ {
+ "screen-256color",
+ "\001\020\000\163\143\162\145\145\156\055\062\065\066\143\157"
+ "\154\157\162\000\000\000\000\000\021\000\005\000\001\000\001"
+ "\016\000\001\026\000\001\027\000\001\011\000\001\026\000\005"
+ "\000\016\000\000\001\021\000\377\177\004\000\120\000\007\000"
+ "\010\000\012\000\030\000\103\004\127\000\132\001\100\000\033"
+ "\133\045\077\045\160\061\045\173\070\175\045\074\045\164\064"
+ "\045\160\061\045\144\045\145\045\160\061\045\173\061\066\175"
+ "\045\074\045\164\061\060\045\160\061\045\173\070\175\045\055"
+ "\045\144\045\145\064\070\073\065\073\045\160\061\045\144\045"
+ "\073\155\000\133\001\077\000\033\133\045\077\045\160\061\045"
+ "\173\070\175\045\074\045\164\063\045\160\061\045\144\045\145"
+ "\045\160\061\045\173\061\066\175\045\074\045\164\071\045\160"
+ "\061\045\173\070\175\045\055\045\144\045\145\063\070\073\065"
+ "\073\045\160\061\045\144\045\073\155\000\000\000\101\000\053"
+ "\053\054\054\055\055\056\056\060\060\140\140\141\141\146\146"
+ "\147\147\150\150\151\151\152\152\153\153\154\154\155\155\156"
+ "\156\157\157\160\160\161\161\162\162\163\163\164\164\165\165"
+ "\166\166\167\167\170\170\171\171\172\172\173\173\174\174\175"
+ "\175\176\176\000\003\000\002\000\007\000\063\000\005\000\033"
+ "\133\065\155\000\064\000\005\000\033\133\061\155\000\002\000"
+ "\004\000\033\133\132\000\035\000\007\000\033\133\077\062\065"
+ "\154\000\021\000\007\000\033\133\110\033\133\112\000\040\000"
+ "\014\000\033\133\063\064\150\033\133\077\062\065\150\000\007"
+ "\000\002\000\015\000\014\000\021\000\033\133\045\151\045\160"
+ "\061\045\144\073\045\160\062\045\144\162\000\063\001\011\000"
+ "\033\133\045\160\061\045\144\104\000\036\000\002\000\010\000"
+ "\056\001\011\000\033\133\045\160\061\045\144\102\000\033\000"
+ "\002\000\012\000\065\001\011\000\033\133\045\160\061\045\144"
+ "\103\000\041\000\004\000\033\133\103\000\032\000\021\000\033"
+ "\133\045\151\045\160\061\045\144\073\045\160\062\045\144\110"
+ "\000\070\001\011\000\033\133\045\160\061\045\144\101\000\043"
+ "\000\003\000\033\115\000\044\000\006\000\033\133\063\064\154"
+ "\000\054\001\011\000\033\133\045\160\061\045\144\120\000\047"
+ "\000\004\000\033\133\120\000\067\000\005\000\033\133\062\155"
+ "\000\055\001\011\000\033\133\045\160\061\045\144\115\000\050"
+ "\000\004\000\033\133\115\000\024\000\004\000\033\133\112\000"
+ "\023\000\004\000\033\133\113\000\022\000\005\000\033\133\061"
+ "\113\000\057\000\007\000\033\050\102\033\051\060\000\150\000"
+ "\003\000\033\147\000\034\000\004\000\033\133\110\000\166\001"
+ "\002\000\011\000\153\001\003\000\033\110\000\060\001\011\000"
+ "\033\133\045\160\061\045\144\100\000\062\001\011\000\033\133"
+ "\045\160\061\045\144\114\000\166\000\004\000\033\133\114\000"
+ "\122\001\002\000\012\000\157\000\004\000\033\051\060\000\173"
+ "\000\002\000\010\000\175\000\004\000\033\133\132\000\326\000"
+ "\004\000\033\117\104\000\212\000\004\000\033\117\102\000\351"
+ "\000\004\000\033\117\103\000\015\001\004\000\033\117\101\000"
+ "\210\000\005\000\033\133\063\176\000\214\000\005\000\033\133"
+ "\064\176\000\222\000\004\000\033\117\120\000\233\000\006\000"
+ "\033\133\062\061\176\000\234\000\006\000\033\133\062\063\176"
+ "\000\235\000\006\000\033\133\062\064\176\000\223\000\004\000"
+ "\033\117\121\000\224\000\004\000\033\117\122\000\225\000\004"
+ "\000\033\117\123\000\226\000\006\000\033\133\061\065\176\000"
+ "\227\000\006\000\033\133\061\067\176\000\230\000\006\000\033"
+ "\133\061\070\176\000\231\000\006\000\033\133\061\071\176\000"
+ "\232\000\006\000\033\133\062\060\176\000\323\000\005\000\033"
+ "\133\061\176\000\324\000\005\000\033\133\062\176\000\332\000"
+ "\004\000\033\133\115\000\335\000\005\000\033\133\066\176\000"
+ "\340\000\005\000\033\133\065\176\000\047\001\003\000\033\105"
+ "\000\116\001\003\000\033\070\000\105\000\005\000\033\133\067"
+ "\155\000\123\001\003\000\033\115\000\123\000\002\000\017\000"
+ "\126\000\011\000\033\133\077\061\060\064\071\154\000\131\000"
+ "\005\000\033\133\064\154\000\016\001\010\000\033\133\077\061"
+ "\154\033\076\000\140\000\006\000\033\133\062\063\155\000\143"
+ "\000\006\000\033\133\062\064\155\000\113\001\021\000\033\143"
+ "\033\133\077\061\060\060\060\154\033\133\077\062\065\150\000"
+ "\120\001\003\000\033\067\000\134\001\124\000\033\133\060\045"
+ "\077\045\160\066\045\164\073\061\045\073\045\077\045\160\061"
+ "\045\164\073\063\045\073\045\077\045\160\062\045\164\073\064"
+ "\045\073\045\077\045\160\063\045\164\073\067\045\073\045\077"
+ "\045\160\064\045\164\073\065\045\073\045\077\045\160\065\045"
+ "\164\073\062\045\073\155\045\077\045\160\071\045\164\016\045"
+ "\145\017\045\073\000\125\000\005\000\033\133\155\017\000\061"
+ "\000\002\000\016\000\065\000\011\000\033\133\077\061\060\064"
+ "\071\150\000\073\000\005\000\033\133\064\150\000\017\001\010"
+ "\000\033\133\077\061\150\033\075\000\112\000\005\000\033\133"
+ "\063\155\000\116\000\005\000\033\133\064\155\000\017\000\005"
+ "\000\033\133\063\147\000\052\001\011\000\033\133\063\071\073"
+ "\064\071\155\000\102\000\007\000\003\000\142\163\000\146\001"
+ "\003\000\160\164\000\146\001\003\000\107\060\000\146\001\003"
+ "\000\125\070\000\156\001\000\003\000\105\060\000\163\004\000"
+ "\033\050\102\000\003\000\123\060\000\163\010\000\033\050\045"
+ "\160\061\045\143\000\003\000\101\130\000\146\001",
+ 1227
+ },
+ {
+ "st",
+ "\001\003\000\163\164\000\000\000\000\000\027\000\007\000\001"
+ "\000\001\002\000\001\020\000\001\026\000\001\027\000\001\032"
+ "\000\001\011\000\001\026\000\005\000\016\000\010\000\004\000"
+ "\120\000\007\000\010\000\012\000\030\000\021\000\100\000\134"
+ "\010\261\000\000\000\101\000\053\103\054\104\055\101\056\102"
+ "\060\105\140\140\141\141\146\146\147\147\150\106\151\107\152"
+ "\152\153\153\154\154\155\155\156\156\157\157\160\160\161\161"
+ "\162\162\163\163\164\164\165\165\166\166\167\167\170\170\171"
+ "\171\172\172\173\173\174\174\175\175\176\176\000\003\000\002"
+ "\000\007\000\063\000\005\000\033\133\065\155\000\064\000\005"
+ "\000\033\133\061\155\000\002\000\004\000\033\133\132\000\035"
+ "\000\007\000\033\133\077\062\065\154\000\021\000\010\000\033"
+ "\133\110\033\133\062\112\000\040\000\015\000\033\133\077\061"
+ "\062\154\033\133\077\062\065\150\000\007\000\002\000\015\000"
+ "\014\000\021\000\033\133\045\151\045\160\061\045\144\073\045"
+ "\160\062\045\144\162\000\063\001\011\000\033\133\045\160\061"
+ "\045\144\104\000\036\000\002\000\010\000\056\001\011\000\033"
+ "\133\045\160\061\045\144\102\000\033\000\002\000\012\000\065"
+ "\001\011\000\033\133\045\160\061\045\144\103\000\041\000\004"
+ "\000\033\133\103\000\032\000\021\000\033\133\045\151\045\160"
+ "\061\045\144\073\045\160\062\045\144\110\000\070\001\011\000"
+ "\033\133\045\160\061\045\144\101\000\043\000\004\000\033\133"
+ "\101\000\044\000\007\000\033\133\077\062\065\150\000\054\001"
+ "\011\000\033\133\045\160\061\045\144\120\000\047\000\004\000"
+ "\033\133\120\000\055\001\011\000\033\133\045\160\061\045\144"
+ "\115\000\050\000\004\000\033\133\115\000\122\000\011\000\033"
+ "\133\045\160\061\045\144\130\000\024\000\004\000\033\133\112"
+ "\000\023\000\004\000\033\133\113\000\022\000\005\000\033\133"
+ "\061\113\000\057\000\004\000\033\051\060\000\150\000\022\000"
+ "\033\133\077\065\150\044\074\061\060\060\057\076\033\133\077"
+ "\065\154\000\152\000\002\000\007\000\034\000\004\000\033\133"
+ "\110\000\027\000\013\000\033\133\045\151\045\160\061\045\144"
+ "\107\000\166\001\002\000\011\000\153\001\003\000\033\110\000"
+ "\060\001\011\000\033\133\045\160\061\045\144\100\000\062\001"
+ "\011\000\033\133\045\160\061\045\144\114\000\166\000\004\000"
+ "\033\133\114\000\122\001\002\000\012\000\061\001\011\000\033"
+ "\133\045\160\061\045\144\123\000\110\000\005\000\033\133\070"
+ "\155\000\157\000\017\000\033\133\064\154\033\076\033\133\077"
+ "\061\060\063\064\154\000\360\000\007\000\033\133\063\073\062"
+ "\176\000\363\000\007\000\033\133\061\073\062\106\000\371\000"
+ "\007\000\033\133\061\073\062\110\000\372\000\007\000\033\133"
+ "\062\073\062\176\000\373\000\007\000\033\133\061\073\062\104"
+ "\000\376\000\007\000\033\133\066\073\062\176\000\000\001\007"
+ "\000\033\133\065\073\062\176\000\005\001\007\000\033\133\061"
+ "\073\062\103\000\170\000\005\000\033\133\061\176\000\171\000"
+ "\005\000\033\133\065\176\000\172\000\004\000\033\117\165\000"
+ "\173\000\002\000\177\000\176\000\005\000\033\133\064\176\000"
+ "\177\000\005\000\033\133\066\176\000\175\000\004\000\033\133"
+ "\132\000\202\000\007\000\033\133\063\073\065\176\000\326\000"
+ "\004\000\033\117\104\000\212\000\004\000\033\117\102\000\351"
+ "\000\004\000\033\117\103\000\015\001\004\000\033\117\101\000"
+ "\210\000\005\000\033\133\063\176\000\211\000\007\000\033\133"
+ "\063\073\062\176\000\217\000\007\000\033\133\061\073\065\106"
+ "\000\216\000\007\000\033\133\061\073\062\106\000\214\000\005"
+ "\000\033\133\064\176\000\215\000\004\000\033\117\115\000\222"
+ "\000\004\000\033\117\120\000\233\000\006\000\033\133\062\061"
+ "\176\000\234\000\006\000\033\133\062\063\176\000\235\000\006"
+ "\000\033\133\062\064\176\000\236\000\007\000\033\133\061\073"
+ "\062\120\000\237\000\007\000\033\133\061\073\062\121\000\240"
+ "\000\007\000\033\133\061\073\062\122\000\241\000\007\000\033"
+ "\133\061\073\062\123\000\242\000\010\000\033\133\061\065\073"
+ "\062\176\000\243\000\010\000\033\133\061\067\073\062\176\000"
+ "\244\000\010\000\033\133\061\070\073\062\176\000\223\000\004"
+ "\000\033\117\121\000\245\000\010\000\033\133\061\071\073\062"
+ "\176\000\246\000\010\000\033\133\062\060\073\062\176\000\247"
+ "\000\010\000\033\133\062\061\073\062\176\000\250\000\010\000"
+ "\033\133\062\063\073\062\176\000\251\000\010\000\033\133\062"
+ "\064\073\062\176\000\252\000\007\000\033\133\061\073\065\120"
+ "\000\253\000\007\000\033\133\061\073\065\121\000\254\000\007"
+ "\000\033\133\061\073\065\122\000\255\000\007\000\033\133\061"
+ "\073\065\123\000\256\000\010\000\033\133\061\065\073\065\176"
+ "\000\224\000\004\000\033\117\122\000\257\000\010\000\033\133"
+ "\061\067\073\065\176\000\260\000\010\000\033\133\061\070\073"
+ "\065\176\000\261\000\010\000\033\133\061\071\073\065\176\000"
+ "\262\000\010\000\033\133\062\060\073\065\176\000\263\000\010"
+ "\000\033\133\062\061\073\065\176\000\264\000\010\000\033\133"
+ "\062\063\073\065\176\000\265\000\010\000\033\133\062\064\073"
+ "\065\176\000\266\000\007\000\033\133\061\073\066\120\000\267"
+ "\000\007\000\033\133\061\073\066\121\000\270\000\007\000\033"
+ "\133\061\073\066\122\000\225\000\004\000\033\117\123\000\271"
+ "\000\007\000\033\133\061\073\066\123\000\272\000\010\000\033"
+ "\133\061\065\073\066\176\000\273\000\010\000\033\133\061\067"
+ "\073\066\176\000\274\000\010\000\033\133\061\070\073\066\176"
+ "\000\275\000\010\000\033\133\061\071\073\066\176\000\276\000"
+ "\010\000\033\133\062\060\073\066\176\000\277\000\010\000\033"
+ "\133\062\061\073\066\176\000\300\000\010\000\033\133\062\063"
+ "\073\066\176\000\301\000\010\000\033\133\062\064\073\066\176"
+ "\000\302\000\007\000\033\133\061\073\063\120\000\226\000\006"
+ "\000\033\133\061\065\176\000\303\000\007\000\033\133\061\073"
+ "\063\121\000\304\000\007\000\033\133\061\073\063\122\000\305"
+ "\000\007\000\033\133\061\073\063\123\000\306\000\010\000\033"
+ "\133\061\065\073\063\176\000\307\000\010\000\033\133\061\067"
+ "\073\063\176\000\310\000\010\000\033\133\061\070\073\063\176"
+ "\000\311\000\010\000\033\133\061\071\073\063\176\000\312\000"
+ "\010\000\033\133\062\060\073\063\176\000\313\000\010\000\033"
+ "\133\062\061\073\063\176\000\314\000\010\000\033\133\062\063"
+ "\073\063\176\000\227\000\006\000\033\133\061\067\176\000\315"
+ "\000\010\000\033\133\062\064\073\063\176\000\316\000\007\000"
+ "\033\133\061\073\064\120\000\317\000\007\000\033\133\061\073"
+ "\064\121\000\320\000\007\000\033\133\061\073\064\122\000\230"
+ "\000\006\000\033\133\061\070\176\000\231\000\006\000\033\133"
+ "\061\071\176\000\232\000\006\000\033\133\062\060\176\000\323"
+ "\000\005\000\033\133\061\176\000\324\000\005\000\033\133\062"
+ "\176\000\325\000\007\000\033\133\062\073\065\176\000\366\000"
+ "\007\000\033\133\061\073\062\102\000\332\000\004\000\033\133"
+ "\115\000\335\000\005\000\033\133\066\176\000\340\000\005\000"
+ "\033\133\065\176\000\002\001\007\000\033\133\061\073\062\101"
+ "\000\213\000\007\000\033\133\062\073\062\176\000\100\001\004"
+ "\000\033\133\151\000\102\001\005\000\033\133\064\151\000\103"
+ "\001\005\000\033\133\065\151\000\052\001\011\000\033\133\063"
+ "\071\073\064\071\155\000\116\001\003\000\033\070\000\105\000"
+ "\005\000\033\133\067\155\000\123\001\003\000\033\115\000\132"
+ "\000\006\000\033\133\062\063\155\000\123\000\004\000\033\050"
+ "\102\000\126\000\011\000\033\133\077\061\060\064\071\154\000"
+ "\131\000\005\000\033\133\064\154\000\016\001\010\000\033\133"
+ "\077\061\154\033\076\000\140\000\006\000\033\133\062\067\155"
+ "\000\143\000\006\000\033\133\062\064\155\000\112\001\003\000"
+ "\033\143\000\113\001\017\000\033\133\064\154\033\076\033\133"
+ "\077\061\060\063\064\154\000\120\001\003\000\033\067\000\132"
+ "\001\012\000\033\133\064\045\160\061\045\144\155\000\133\001"
+ "\012\000\033\133\063\045\160\061\045\144\155\000\135\001\106"
+ "\000\033\133\064\045\077\045\160\061\045\173\061\175\045\075"
+ "\045\164\064\045\145\045\160\061\045\173\063\175\045\075\045"
+ "\164\066\045\145\045\160\061\045\173\064\175\045\075\045\164"
+ "\061\045\145\045\160\061\045\173\066\175\045\075\045\164\063"
+ "\045\145\045\160\061\045\144\045\073\155\000\143\001\106\000"
+ "\033\133\063\045\077\045\160\061\045\173\061\175\045\075\045"
+ "\164\064\045\145\045\160\061\045\173\063\175\045\075\045\164"
+ "\066\045\145\045\160\061\045\173\064\175\045\075\045\164\061"
+ "\045\145\045\160\061\045\173\066\175\045\075\045\164\063\045"
+ "\145\045\160\061\045\144\045\073\155\000\134\001\122\000\045"
+ "\077\045\160\071\045\164\033\050\060\045\145\033\050\102\045"
+ "\073\033\133\060\045\077\045\160\066\045\164\073\061\045\073"
+ "\045\077\045\160\062\045\164\073\064\045\073\045\077\045\160"
+ "\061\045\160\063\045\174\045\164\073\067\045\073\045\077\045"
+ "\160\064\045\164\073\065\045\073\045\077\045\160\067\045\164"
+ "\073\070\045\073\155\000\125\000\005\000\033\133\060\155\000"
+ "\074\000\005\000\033\133\063\155\000\061\000\004\000\033\050"
+ "\060\000\065\000\011\000\033\133\077\061\060\064\071\150\000"
+ "\073\000\005\000\033\133\064\150\000\017\001\010\000\033\133"
+ "\077\061\150\033\075\000\112\000\005\000\033\133\067\155\000"
+ "\116\000\005\000\033\133\064\155\000\017\000\005\000\033\133"
+ "\063\147\000\170\001\005\000\033\135\060\073\000\200\001\013"
+ "\000\033\133\045\151\045\144\073\045\144\122\000\201\001\005"
+ "\000\033\133\066\156\000\202\001\010\000\033\133\077\061\073"
+ "\062\143\000\203\001\004\000\033\133\143\000\117\001\013\000"
+ "\033\133\045\151\045\160\061\045\144\144\000\060\000\004\000"
+ "\003\000\124\143\000\146\001\003\000\130\124\000\146\001\003"
+ "\000\123\145\000\163\006\000\033\133\062\040\161\000\003\000"
+ "\123\163\000\163\012\000\033\133\045\160\061\045\144\040\161"
+ "\000",
+ 2251
+ },
+ {
+ "tmux",
+ "\001\005\000\164\155\165\170\000\000\000\000\000\024\000\006"
+ "\000\020\000\001\001\000\001\016\000\001\026\000\001\027\000"
+ "\001\011\000\001\026\000\005\000\016\000\010\000\004\000\120"
+ "\000\007\000\010\000\012\000\030\000\021\000\100\000\303\006"
+ "\231\000\132\000\006\000\033\133\062\063\155\000\140\000\006"
+ "\000\033\133\062\067\155\000\074\000\005\000\033\133\063\155"
+ "\000\112\000\005\000\033\133\067\155\000\210\000\005\000\033"
+ "\133\063\176\000\324\000\005\000\033\133\062\176\000\335\000"
+ "\005\000\033\133\066\176\000\340\000\005\000\033\133\065\176"
+ "\000\214\000\005\000\033\133\064\176\000\323\000\005\000\033"
+ "\133\061\176\000\326\000\004\000\033\117\104\000\212\000\004"
+ "\000\033\117\102\000\351\000\004\000\033\117\103\000\015\001"
+ "\004\000\033\117\101\000\222\000\004\000\033\117\120\000\233"
+ "\000\006\000\033\133\062\061\176\000\234\000\006\000\033\133"
+ "\062\063\176\000\235\000\006\000\033\133\062\064\176\000\236"
+ "\000\007\000\033\133\061\073\062\120\000\237\000\007\000\033"
+ "\133\061\073\062\121\000\240\000\007\000\033\133\061\073\062"
+ "\122\000\241\000\007\000\033\133\061\073\062\123\000\242\000"
+ "\010\000\033\133\061\065\073\062\176\000\243\000\010\000\033"
+ "\133\061\067\073\062\176\000\244\000\010\000\033\133\061\070"
+ "\073\062\176\000\223\000\004\000\033\117\121\000\245\000\010"
+ "\000\033\133\061\071\073\062\176\000\246\000\010\000\033\133"
+ "\062\060\073\062\176\000\247\000\010\000\033\133\062\061\073"
+ "\062\176\000\250\000\010\000\033\133\062\063\073\062\176\000"
+ "\251\000\010\000\033\133\062\064\073\062\176\000\252\000\007"
+ "\000\033\133\061\073\065\120\000\253\000\007\000\033\133\061"
+ "\073\065\121\000\254\000\007\000\033\133\061\073\065\122\000"
+ "\255\000\007\000\033\133\061\073\065\123\000\256\000\010\000"
+ "\033\133\061\065\073\065\176\000\224\000\004\000\033\117\122"
+ "\000\257\000\010\000\033\133\061\067\073\065\176\000\260\000"
+ "\010\000\033\133\061\070\073\065\176\000\261\000\010\000\033"
+ "\133\061\071\073\065\176\000\262\000\010\000\033\133\062\060"
+ "\073\065\176\000\263\000\010\000\033\133\062\061\073\065\176"
+ "\000\264\000\010\000\033\133\062\063\073\065\176\000\265\000"
+ "\010\000\033\133\062\064\073\065\176\000\266\000\007\000\033"
+ "\133\061\073\066\120\000\267\000\007\000\033\133\061\073\066"
+ "\121\000\270\000\007\000\033\133\061\073\066\122\000\225\000"
+ "\004\000\033\117\123\000\271\000\007\000\033\133\061\073\066"
+ "\123\000\272\000\010\000\033\133\061\065\073\066\176\000\273"
+ "\000\010\000\033\133\061\067\073\066\176\000\274\000\010\000"
+ "\033\133\061\070\073\066\176\000\275\000\010\000\033\133\061"
+ "\071\073\066\176\000\276\000\010\000\033\133\062\060\073\066"
+ "\176\000\277\000\010\000\033\133\062\061\073\066\176\000\300"
+ "\000\010\000\033\133\062\063\073\066\176\000\301\000\010\000"
+ "\033\133\062\064\073\066\176\000\302\000\007\000\033\133\061"
+ "\073\063\120\000\226\000\006\000\033\133\061\065\176\000\303"
+ "\000\007\000\033\133\061\073\063\121\000\304\000\007\000\033"
+ "\133\061\073\063\122\000\305\000\007\000\033\133\061\073\063"
+ "\123\000\306\000\010\000\033\133\061\065\073\063\176\000\307"
+ "\000\010\000\033\133\061\067\073\063\176\000\310\000\010\000"
+ "\033\133\061\070\073\063\176\000\311\000\010\000\033\133\061"
+ "\071\073\063\176\000\312\000\010\000\033\133\062\060\073\063"
+ "\176\000\313\000\010\000\033\133\062\061\073\063\176\000\314"
+ "\000\010\000\033\133\062\063\073\063\176\000\227\000\006\000"
+ "\033\133\061\067\176\000\315\000\010\000\033\133\062\064\073"
+ "\063\176\000\316\000\007\000\033\133\061\073\064\120\000\317"
+ "\000\007\000\033\133\061\073\064\121\000\320\000\007\000\033"
+ "\133\061\073\064\122\000\230\000\006\000\033\133\061\070\176"
+ "\000\231\000\006\000\033\133\061\071\176\000\232\000\006\000"
+ "\033\133\062\060\176\000\373\000\007\000\033\133\061\073\062"
+ "\104\000\005\001\007\000\033\133\061\073\062\103\000\366\000"
+ "\007\000\033\133\061\073\062\102\000\002\001\007\000\033\133"
+ "\061\073\062\101\000\360\000\007\000\033\133\063\073\062\176"
+ "\000\363\000\007\000\033\133\061\073\062\106\000\371\000\007"
+ "\000\033\133\061\073\062\110\000\372\000\007\000\033\133\062"
+ "\073\062\176\000\376\000\007\000\033\133\066\073\062\176\000"
+ "\000\001\007\000\033\133\065\073\062\176\000\053\000\006\000"
+ "\033\135\060\073\007\000\152\000\002\000\007\000\170\001\005"
+ "\000\033\135\060\073\000\000\000\101\000\053\053\054\054\055"
+ "\055\056\056\060\060\140\140\141\141\146\146\147\147\150\150"
+ "\151\151\152\152\153\153\154\154\155\155\156\156\157\157\160"
+ "\160\161\161\162\162\163\163\164\164\165\165\166\166\167\167"
+ "\170\170\171\171\172\172\173\173\174\174\175\175\176\176\000"
+ "\003\000\002\000\007\000\063\000\005\000\033\133\065\155\000"
+ "\064\000\005\000\033\133\061\155\000\002\000\004\000\033\133"
+ "\132\000\035\000\007\000\033\133\077\062\065\154\000\021\000"
+ "\007\000\033\133\110\033\133\112\000\040\000\014\000\033\133"
+ "\063\064\150\033\133\077\062\065\150\000\007\000\002\000\015"
+ "\000\014\000\021\000\033\133\045\151\045\160\061\045\144\073"
+ "\045\160\062\045\144\162\000\063\001\011\000\033\133\045\160"
+ "\061\045\144\104\000\036\000\002\000\010\000\056\001\011\000"
+ "\033\133\045\160\061\045\144\102\000\033\000\002\000\012\000"
+ "\065\001\011\000\033\133\045\160\061\045\144\103\000\041\000"
+ "\004\000\033\133\103\000\032\000\021\000\033\133\045\151\045"
+ "\160\061\045\144\073\045\160\062\045\144\110\000\070\001\011"
+ "\000\033\133\045\160\061\045\144\101\000\043\000\003\000\033"
+ "\115\000\044\000\006\000\033\133\063\064\154\000\054\001\011"
+ "\000\033\133\045\160\061\045\144\120\000\047\000\004\000\033"
+ "\133\120\000\067\000\005\000\033\133\062\155\000\055\001\011"
+ "\000\033\133\045\160\061\045\144\115\000\050\000\004\000\033"
+ "\133\115\000\024\000\004\000\033\133\112\000\023\000\004\000"
+ "\033\133\113\000\022\000\005\000\033\133\061\113\000\057\000"
+ "\007\000\033\050\102\033\051\060\000\150\000\003\000\033\147"
+ "\000\034\000\004\000\033\133\110\000\166\001\002\000\011\000"
+ "\153\001\003\000\033\110\000\060\001\011\000\033\133\045\160"
+ "\061\045\144\100\000\062\001\011\000\033\133\045\160\061\045"
+ "\144\114\000\166\000\004\000\033\133\114\000\122\001\002\000"
+ "\012\000\157\000\004\000\033\051\060\000\173\000\002\000\010"
+ "\000\175\000\004\000\033\133\132\000\332\000\004\000\033\133"
+ "\115\000\047\001\003\000\033\105\000\116\001\003\000\033\070"
+ "\000\105\000\005\000\033\133\067\155\000\123\001\003\000\033"
+ "\115\000\123\000\002\000\017\000\126\000\011\000\033\133\077"
+ "\061\060\064\071\154\000\131\000\005\000\033\133\064\154\000"
+ "\016\001\010\000\033\133\077\061\154\033\076\000\143\000\006"
+ "\000\033\133\062\064\155\000\113\001\021\000\033\143\033\133"
+ "\077\061\060\060\060\154\033\133\077\062\065\150\000\120\001"
+ "\003\000\033\067\000\134\001\124\000\033\133\060\045\077\045"
+ "\160\066\045\164\073\061\045\073\045\077\045\160\061\045\164"
+ "\073\063\045\073\045\077\045\160\062\045\164\073\064\045\073"
+ "\045\077\045\160\063\045\164\073\067\045\073\045\077\045\160"
+ "\064\045\164\073\065\045\073\045\077\045\160\065\045\164\073"
+ "\062\045\073\155\045\077\045\160\071\045\164\016\045\145\017"
+ "\045\073\000\125\000\005\000\033\133\155\017\000\061\000\002"
+ "\000\016\000\065\000\011\000\033\133\077\061\060\064\071\150"
+ "\000\073\000\005\000\033\133\064\150\000\017\001\010\000\033"
+ "\133\077\061\150\033\075\000\116\000\005\000\033\133\064\155"
+ "\000\017\000\005\000\033\133\063\147\000\052\001\011\000\033"
+ "\133\063\071\073\064\071\155\000\132\001\012\000\033\133\064"
+ "\045\160\061\045\144\155\000\133\001\012\000\033\133\063\045"
+ "\160\061\045\144\155\000\074\004\101\000\004\000\153\104\116"
+ "\000\163\007\000\033\133\061\073\062\102\000\005\000\153\104"
+ "\116\063\000\163\007\000\033\133\061\073\063\102\000\005\000"
+ "\153\104\116\064\000\163\007\000\033\133\061\073\064\102\000"
+ "\005\000\153\104\116\065\000\163\007\000\033\133\061\073\065"
+ "\102\000\005\000\153\104\116\066\000\163\007\000\033\133\061"
+ "\073\066\102\000\005\000\153\104\116\067\000\163\007\000\033"
+ "\133\061\073\067\102\000\006\000\153\114\106\124\063\000\163"
+ "\007\000\033\133\061\073\063\104\000\006\000\153\114\106\124"
+ "\064\000\163\007\000\033\133\061\073\064\104\000\006\000\153"
+ "\114\106\124\065\000\163\007\000\033\133\061\073\065\104\000"
+ "\006\000\153\114\106\124\066\000\163\007\000\033\133\061\073"
+ "\066\104\000\006\000\153\114\106\124\067\000\163\007\000\033"
+ "\133\061\073\067\104\000\006\000\153\122\111\124\063\000\163"
+ "\007\000\033\133\061\073\063\103\000\006\000\153\122\111\124"
+ "\064\000\163\007\000\033\133\061\073\064\103\000\006\000\153"
+ "\122\111\124\065\000\163\007\000\033\133\061\073\065\103\000"
+ "\006\000\153\122\111\124\066\000\163\007\000\033\133\061\073"
+ "\066\103\000\006\000\153\122\111\124\067\000\163\007\000\033"
+ "\133\061\073\067\103\000\004\000\153\125\120\000\163\007\000"
+ "\033\133\061\073\062\101\000\005\000\153\125\120\063\000\163"
+ "\007\000\033\133\061\073\063\101\000\005\000\153\125\120\064"
+ "\000\163\007\000\033\133\061\073\064\101\000\005\000\153\125"
+ "\120\065\000\163\007\000\033\133\061\073\065\101\000\005\000"
+ "\153\125\120\066\000\163\007\000\033\133\061\073\066\101\000"
+ "\005\000\153\125\120\067\000\163\007\000\033\133\061\073\067"
+ "\101\000\005\000\153\104\103\063\000\163\007\000\033\133\063"
+ "\073\063\176\000\005\000\153\104\103\064\000\163\007\000\033"
+ "\133\063\073\064\176\000\005\000\153\104\103\065\000\163\007"
+ "\000\033\133\063\073\065\176\000\005\000\153\104\103\066\000"
+ "\163\007\000\033\133\063\073\066\176\000\005\000\153\104\103"
+ "\067\000\163\007\000\033\133\063\073\067\176\000\006\000\153"
+ "\105\116\104\063\000\163\007\000\033\133\061\073\063\106\000"
+ "\006\000\153\105\116\104\064\000\163\007\000\033\133\061\073"
+ "\064\106\000\006\000\153\105\116\104\065\000\163\007\000\033"
+ "\133\061\073\065\106\000\006\000\153\105\116\104\066\000\163"
+ "\007\000\033\133\061\073\066\106\000\006\000\153\105\116\104"
+ "\067\000\163\007\000\033\133\061\073\067\106\000\006\000\153"
+ "\110\117\115\063\000\163\007\000\033\133\061\073\063\110\000"
+ "\006\000\153\110\117\115\064\000\163\007\000\033\133\061\073"
+ "\064\110\000\006\000\153\110\117\115\065\000\163\007\000\033"
+ "\133\061\073\065\110\000\006\000\153\110\117\115\066\000\163"
+ "\007\000\033\133\061\073\066\110\000\006\000\153\110\117\115"
+ "\067\000\163\007\000\033\133\061\073\067\110\000\005\000\153"
+ "\111\103\063\000\163\007\000\033\133\062\073\063\176\000\005"
+ "\000\153\111\103\064\000\163\007\000\033\133\062\073\064\176"
+ "\000\005\000\153\111\103\065\000\163\007\000\033\133\062\073"
+ "\065\176\000\005\000\153\111\103\066\000\163\007\000\033\133"
+ "\062\073\066\176\000\005\000\153\111\103\067\000\163\007\000"
+ "\033\133\062\073\067\176\000\006\000\153\116\130\124\063\000"
+ "\163\007\000\033\133\066\073\063\176\000\006\000\153\116\130"
+ "\124\064\000\163\007\000\033\133\066\073\064\176\000\006\000"
+ "\153\116\130\124\065\000\163\007\000\033\133\066\073\065\176"
+ "\000\006\000\153\116\130\124\066\000\163\007\000\033\133\066"
+ "\073\066\176\000\006\000\153\116\130\124\067\000\163\007\000"
+ "\033\133\066\073\067\176\000\006\000\153\120\122\126\063\000"
+ "\163\007\000\033\133\065\073\063\176\000\006\000\153\120\122"
+ "\126\064\000\163\007\000\033\133\065\073\064\176\000\006\000"
+ "\153\120\122\126\065\000\163\007\000\033\133\065\073\065\176"
+ "\000\006\000\153\120\122\126\066\000\163\007\000\033\133\065"
+ "\073\066\176\000\006\000\153\120\122\126\067\000\163\007\000"
+ "\033\133\065\073\067\176\000\003\000\124\123\000\163\005\000"
+ "\033\135\060\073\000\003\000\103\162\000\163\007\000\033\135"
+ "\061\061\062\007\000\003\000\103\163\000\163\014\000\033\135"
+ "\061\062\073\045\160\061\045\163\007\000\003\000\115\163\000"
+ "\163\022\000\033\135\065\062\073\045\160\061\045\163\073\045"
+ "\160\062\045\163\007\000\003\000\123\145\000\163\006\000\033"
+ "\133\062\040\161\000\003\000\123\163\000\163\012\000\033\133"
+ "\045\160\061\045\144\040\161\000\003\000\142\163\000\146\001"
+ "\003\000\160\164\000\146\001\003\000\107\060\000\146\001\003"
+ "\000\125\070\000\156\001\000\003\000\105\060\000\163\004\000"
+ "\033\050\102\000\003\000\123\060\000\163\010\000\033\050\045"
+ "\160\061\045\143\000\003\000\101\130\000\146\001",
+ 2877
+ },
+ {
+ "tmux-256color",
+ "\001\016\000\164\155\165\170\055\062\065\066\143\157\154\157"
+ "\162\000\000\000\000\000\024\000\006\000\020\000\001\001\000"
+ "\001\016\000\001\026\000\001\027\000\001\011\000\001\026\000"
+ "\005\000\016\000\000\001\021\000\377\177\004\000\120\000\007"
+ "\000\010\000\012\000\030\000\056\007\231\000\132\001\100\000"
+ "\033\133\045\077\045\160\061\045\173\070\175\045\074\045\164"
+ "\064\045\160\061\045\144\045\145\045\160\061\045\173\061\066"
+ "\175\045\074\045\164\061\060\045\160\061\045\173\070\175\045"
+ "\055\045\144\045\145\064\070\073\065\073\045\160\061\045\144"
+ "\045\073\155\000\133\001\077\000\033\133\045\077\045\160\061"
+ "\045\173\070\175\045\074\045\164\063\045\160\061\045\144\045"
+ "\145\045\160\061\045\173\061\066\175\045\074\045\164\071\045"
+ "\160\061\045\173\070\175\045\055\045\144\045\145\063\070\073"
+ "\065\073\045\160\061\045\144\045\073\155\000\132\000\006\000"
+ "\033\133\062\063\155\000\140\000\006\000\033\133\062\067\155"
+ "\000\074\000\005\000\033\133\063\155\000\112\000\005\000\033"
+ "\133\067\155\000\210\000\005\000\033\133\063\176\000\324\000"
+ "\005\000\033\133\062\176\000\335\000\005\000\033\133\066\176"
+ "\000\340\000\005\000\033\133\065\176\000\214\000\005\000\033"
+ "\133\064\176\000\323\000\005\000\033\133\061\176\000\326\000"
+ "\004\000\033\117\104\000\212\000\004\000\033\117\102\000\351"
+ "\000\004\000\033\117\103\000\015\001\004\000\033\117\101\000"
+ "\222\000\004\000\033\117\120\000\233\000\006\000\033\133\062"
+ "\061\176\000\234\000\006\000\033\133\062\063\176\000\235\000"
+ "\006\000\033\133\062\064\176\000\236\000\007\000\033\133\061"
+ "\073\062\120\000\237\000\007\000\033\133\061\073\062\121\000"
+ "\240\000\007\000\033\133\061\073\062\122\000\241\000\007\000"
+ "\033\133\061\073\062\123\000\242\000\010\000\033\133\061\065"
+ "\073\062\176\000\243\000\010\000\033\133\061\067\073\062\176"
+ "\000\244\000\010\000\033\133\061\070\073\062\176\000\223\000"
+ "\004\000\033\117\121\000\245\000\010\000\033\133\061\071\073"
+ "\062\176\000\246\000\010\000\033\133\062\060\073\062\176\000"
+ "\247\000\010\000\033\133\062\061\073\062\176\000\250\000\010"
+ "\000\033\133\062\063\073\062\176\000\251\000\010\000\033\133"
+ "\062\064\073\062\176\000\252\000\007\000\033\133\061\073\065"
+ "\120\000\253\000\007\000\033\133\061\073\065\121\000\254\000"
+ "\007\000\033\133\061\073\065\122\000\255\000\007\000\033\133"
+ "\061\073\065\123\000\256\000\010\000\033\133\061\065\073\065"
+ "\176\000\224\000\004\000\033\117\122\000\257\000\010\000\033"
+ "\133\061\067\073\065\176\000\260\000\010\000\033\133\061\070"
+ "\073\065\176\000\261\000\010\000\033\133\061\071\073\065\176"
+ "\000\262\000\010\000\033\133\062\060\073\065\176\000\263\000"
+ "\010\000\033\133\062\061\073\065\176\000\264\000\010\000\033"
+ "\133\062\063\073\065\176\000\265\000\010\000\033\133\062\064"
+ "\073\065\176\000\266\000\007\000\033\133\061\073\066\120\000"
+ "\267\000\007\000\033\133\061\073\066\121\000\270\000\007\000"
+ "\033\133\061\073\066\122\000\225\000\004\000\033\117\123\000"
+ "\271\000\007\000\033\133\061\073\066\123\000\272\000\010\000"
+ "\033\133\061\065\073\066\176\000\273\000\010\000\033\133\061"
+ "\067\073\066\176\000\274\000\010\000\033\133\061\070\073\066"
+ "\176\000\275\000\010\000\033\133\061\071\073\066\176\000\276"
+ "\000\010\000\033\133\062\060\073\066\176\000\277\000\010\000"
+ "\033\133\062\061\073\066\176\000\300\000\010\000\033\133\062"
+ "\063\073\066\176\000\301\000\010\000\033\133\062\064\073\066"
+ "\176\000\302\000\007\000\033\133\061\073\063\120\000\226\000"
+ "\006\000\033\133\061\065\176\000\303\000\007\000\033\133\061"
+ "\073\063\121\000\304\000\007\000\033\133\061\073\063\122\000"
+ "\305\000\007\000\033\133\061\073\063\123\000\306\000\010\000"
+ "\033\133\061\065\073\063\176\000\307\000\010\000\033\133\061"
+ "\067\073\063\176\000\310\000\010\000\033\133\061\070\073\063"
+ "\176\000\311\000\010\000\033\133\061\071\073\063\176\000\312"
+ "\000\010\000\033\133\062\060\073\063\176\000\313\000\010\000"
+ "\033\133\062\061\073\063\176\000\314\000\010\000\033\133\062"
+ "\063\073\063\176\000\227\000\006\000\033\133\061\067\176\000"
+ "\315\000\010\000\033\133\062\064\073\063\176\000\316\000\007"
+ "\000\033\133\061\073\064\120\000\317\000\007\000\033\133\061"
+ "\073\064\121\000\320\000\007\000\033\133\061\073\064\122\000"
+ "\230\000\006\000\033\133\061\070\176\000\231\000\006\000\033"
+ "\133\061\071\176\000\232\000\006\000\033\133\062\060\176\000"
+ "\373\000\007\000\033\133\061\073\062\104\000\005\001\007\000"
+ "\033\133\061\073\062\103\000\366\000\007\000\033\133\061\073"
+ "\062\102\000\002\001\007\000\033\133\061\073\062\101\000\360"
+ "\000\007\000\033\133\063\073\062\176\000\363\000\007\000\033"
+ "\133\061\073\062\106\000\371\000\007\000\033\133\061\073\062"
+ "\110\000\372\000\007\000\033\133\062\073\062\176\000\376\000"
+ "\007\000\033\133\066\073\062\176\000\000\001\007\000\033\133"
+ "\065\073\062\176\000\053\000\006\000\033\135\060\073\007\000"
+ "\152\000\002\000\007\000\170\001\005\000\033\135\060\073\000"
+ "\000\000\101\000\053\053\054\054\055\055\056\056\060\060\140"
+ "\140\141\141\146\146\147\147\150\150\151\151\152\152\153\153"
+ "\154\154\155\155\156\156\157\157\160\160\161\161\162\162\163"
+ "\163\164\164\165\165\166\166\167\167\170\170\171\171\172\172"
+ "\173\173\174\174\175\175\176\176\000\003\000\002\000\007\000"
+ "\063\000\005\000\033\133\065\155\000\064\000\005\000\033\133"
+ "\061\155\000\002\000\004\000\033\133\132\000\035\000\007\000"
+ "\033\133\077\062\065\154\000\021\000\007\000\033\133\110\033"
+ "\133\112\000\040\000\014\000\033\133\063\064\150\033\133\077"
+ "\062\065\150\000\007\000\002\000\015\000\014\000\021\000\033"
+ "\133\045\151\045\160\061\045\144\073\045\160\062\045\144\162"
+ "\000\063\001\011\000\033\133\045\160\061\045\144\104\000\036"
+ "\000\002\000\010\000\056\001\011\000\033\133\045\160\061\045"
+ "\144\102\000\033\000\002\000\012\000\065\001\011\000\033\133"
+ "\045\160\061\045\144\103\000\041\000\004\000\033\133\103\000"
+ "\032\000\021\000\033\133\045\151\045\160\061\045\144\073\045"
+ "\160\062\045\144\110\000\070\001\011\000\033\133\045\160\061"
+ "\045\144\101\000\043\000\003\000\033\115\000\044\000\006\000"
+ "\033\133\063\064\154\000\054\001\011\000\033\133\045\160\061"
+ "\045\144\120\000\047\000\004\000\033\133\120\000\067\000\005"
+ "\000\033\133\062\155\000\055\001\011\000\033\133\045\160\061"
+ "\045\144\115\000\050\000\004\000\033\133\115\000\024\000\004"
+ "\000\033\133\112\000\023\000\004\000\033\133\113\000\022\000"
+ "\005\000\033\133\061\113\000\057\000\007\000\033\050\102\033"
+ "\051\060\000\150\000\003\000\033\147\000\034\000\004\000\033"
+ "\133\110\000\166\001\002\000\011\000\153\001\003\000\033\110"
+ "\000\060\001\011\000\033\133\045\160\061\045\144\100\000\062"
+ "\001\011\000\033\133\045\160\061\045\144\114\000\166\000\004"
+ "\000\033\133\114\000\122\001\002\000\012\000\157\000\004\000"
+ "\033\051\060\000\173\000\002\000\010\000\175\000\004\000\033"
+ "\133\132\000\332\000\004\000\033\133\115\000\047\001\003\000"
+ "\033\105\000\116\001\003\000\033\070\000\105\000\005\000\033"
+ "\133\067\155\000\123\001\003\000\033\115\000\123\000\002\000"
+ "\017\000\126\000\011\000\033\133\077\061\060\064\071\154\000"
+ "\131\000\005\000\033\133\064\154\000\016\001\010\000\033\133"
+ "\077\061\154\033\076\000\143\000\006\000\033\133\062\064\155"
+ "\000\113\001\021\000\033\143\033\133\077\061\060\060\060\154"
+ "\033\133\077\062\065\150\000\120\001\003\000\033\067\000\134"
+ "\001\124\000\033\133\060\045\077\045\160\066\045\164\073\061"
+ "\045\073\045\077\045\160\061\045\164\073\063\045\073\045\077"
+ "\045\160\062\045\164\073\064\045\073\045\077\045\160\063\045"
+ "\164\073\067\045\073\045\077\045\160\064\045\164\073\065\045"
+ "\073\045\077\045\160\065\045\164\073\062\045\073\155\045\077"
+ "\045\160\071\045\164\016\045\145\017\045\073\000\125\000\005"
+ "\000\033\133\155\017\000\061\000\002\000\016\000\065\000\011"
+ "\000\033\133\077\061\060\064\071\150\000\073\000\005\000\033"
+ "\133\064\150\000\017\001\010\000\033\133\077\061\150\033\075"
+ "\000\116\000\005\000\033\133\064\155\000\017\000\005\000\033"
+ "\133\063\147\000\052\001\011\000\033\133\063\071\073\064\071"
+ "\155\000\074\004\101\000\004\000\153\104\116\000\163\007\000"
+ "\033\133\061\073\062\102\000\005\000\153\104\116\063\000\163"
+ "\007\000\033\133\061\073\063\102\000\005\000\153\104\116\064"
+ "\000\163\007\000\033\133\061\073\064\102\000\005\000\153\104"
+ "\116\065\000\163\007\000\033\133\061\073\065\102\000\005\000"
+ "\153\104\116\066\000\163\007\000\033\133\061\073\066\102\000"
+ "\005\000\153\104\116\067\000\163\007\000\033\133\061\073\067"
+ "\102\000\006\000\153\114\106\124\063\000\163\007\000\033\133"
+ "\061\073\063\104\000\006\000\153\114\106\124\064\000\163\007"
+ "\000\033\133\061\073\064\104\000\006\000\153\114\106\124\065"
+ "\000\163\007\000\033\133\061\073\065\104\000\006\000\153\114"
+ "\106\124\066\000\163\007\000\033\133\061\073\066\104\000\006"
+ "\000\153\114\106\124\067\000\163\007\000\033\133\061\073\067"
+ "\104\000\006\000\153\122\111\124\063\000\163\007\000\033\133"
+ "\061\073\063\103\000\006\000\153\122\111\124\064\000\163\007"
+ "\000\033\133\061\073\064\103\000\006\000\153\122\111\124\065"
+ "\000\163\007\000\033\133\061\073\065\103\000\006\000\153\122"
+ "\111\124\066\000\163\007\000\033\133\061\073\066\103\000\006"
+ "\000\153\122\111\124\067\000\163\007\000\033\133\061\073\067"
+ "\103\000\004\000\153\125\120\000\163\007\000\033\133\061\073"
+ "\062\101\000\005\000\153\125\120\063\000\163\007\000\033\133"
+ "\061\073\063\101\000\005\000\153\125\120\064\000\163\007\000"
+ "\033\133\061\073\064\101\000\005\000\153\125\120\065\000\163"
+ "\007\000\033\133\061\073\065\101\000\005\000\153\125\120\066"
+ "\000\163\007\000\033\133\061\073\066\101\000\005\000\153\125"
+ "\120\067\000\163\007\000\033\133\061\073\067\101\000\005\000"
+ "\153\104\103\063\000\163\007\000\033\133\063\073\063\176\000"
+ "\005\000\153\104\103\064\000\163\007\000\033\133\063\073\064"
+ "\176\000\005\000\153\104\103\065\000\163\007\000\033\133\063"
+ "\073\065\176\000\005\000\153\104\103\066\000\163\007\000\033"
+ "\133\063\073\066\176\000\005\000\153\104\103\067\000\163\007"
+ "\000\033\133\063\073\067\176\000\006\000\153\105\116\104\063"
+ "\000\163\007\000\033\133\061\073\063\106\000\006\000\153\105"
+ "\116\104\064\000\163\007\000\033\133\061\073\064\106\000\006"
+ "\000\153\105\116\104\065\000\163\007\000\033\133\061\073\065"
+ "\106\000\006\000\153\105\116\104\066\000\163\007\000\033\133"
+ "\061\073\066\106\000\006\000\153\105\116\104\067\000\163\007"
+ "\000\033\133\061\073\067\106\000\006\000\153\110\117\115\063"
+ "\000\163\007\000\033\133\061\073\063\110\000\006\000\153\110"
+ "\117\115\064\000\163\007\000\033\133\061\073\064\110\000\006"
+ "\000\153\110\117\115\065\000\163\007\000\033\133\061\073\065"
+ "\110\000\006\000\153\110\117\115\066\000\163\007\000\033\133"
+ "\061\073\066\110\000\006\000\153\110\117\115\067\000\163\007"
+ "\000\033\133\061\073\067\110\000\005\000\153\111\103\063\000"
+ "\163\007\000\033\133\062\073\063\176\000\005\000\153\111\103"
+ "\064\000\163\007\000\033\133\062\073\064\176\000\005\000\153"
+ "\111\103\065\000\163\007\000\033\133\062\073\065\176\000\005"
+ "\000\153\111\103\066\000\163\007\000\033\133\062\073\066\176"
+ "\000\005\000\153\111\103\067\000\163\007\000\033\133\062\073"
+ "\067\176\000\006\000\153\116\130\124\063\000\163\007\000\033"
+ "\133\066\073\063\176\000\006\000\153\116\130\124\064\000\163"
+ "\007\000\033\133\066\073\064\176\000\006\000\153\116\130\124"
+ "\065\000\163\007\000\033\133\066\073\065\176\000\006\000\153"
+ "\116\130\124\066\000\163\007\000\033\133\066\073\066\176\000"
+ "\006\000\153\116\130\124\067\000\163\007\000\033\133\066\073"
+ "\067\176\000\006\000\153\120\122\126\063\000\163\007\000\033"
+ "\133\065\073\063\176\000\006\000\153\120\122\126\064\000\163"
+ "\007\000\033\133\065\073\064\176\000\006\000\153\120\122\126"
+ "\065\000\163\007\000\033\133\065\073\065\176\000\006\000\153"
+ "\120\122\126\066\000\163\007\000\033\133\065\073\066\176\000"
+ "\006\000\153\120\122\126\067\000\163\007\000\033\133\065\073"
+ "\067\176\000\003\000\124\123\000\163\005\000\033\135\060\073"
+ "\000\003\000\103\162\000\163\007\000\033\135\061\061\062\007"
+ "\000\003\000\103\163\000\163\014\000\033\135\061\062\073\045"
+ "\160\061\045\163\007\000\003\000\115\163\000\163\022\000\033"
+ "\135\065\062\073\045\160\061\045\163\073\045\160\062\045\163"
+ "\007\000\003\000\123\145\000\163\006\000\033\133\062\040\161"
+ "\000\003\000\123\163\000\163\012\000\033\133\045\160\061\045"
+ "\144\040\161\000\003\000\142\163\000\146\001\003\000\160\164"
+ "\000\146\001\003\000\107\060\000\146\001\003\000\125\070\000"
+ "\156\001\000\003\000\105\060\000\163\004\000\033\050\102\000"
+ "\003\000\123\060\000\163\010\000\033\050\045\160\061\045\143"
+ "\000\003\000\101\130\000\146\001",
+ 2993
+ },
+ {
+ "vt100",
+ "\001\006\000\166\164\061\060\060\000\000\000\000\000\021\000"
+ "\005\000\001\000\001\036\000\001\027\000\001\011\000\001\044"
+ "\000\001\022\000\004\000\004\000\120\000\007\000\010\000\012"
+ "\000\030\000\036\000\003\000\100\003\107\000\000\000\063\000"
+ "\140\140\141\141\146\146\147\147\152\152\153\153\154\154\155"
+ "\155\156\156\157\157\160\160\161\161\162\162\163\163\164\164"
+ "\165\165\166\166\167\167\170\170\171\171\172\172\173\173\174"
+ "\174\175\175\176\176\000\003\000\002\000\007\000\063\000\011"
+ "\000\033\133\065\155\044\074\062\076\000\064\000\011\000\033"
+ "\133\061\155\044\074\062\076\000\021\000\014\000\033\133\110"
+ "\033\133\112\044\074\065\060\076\000\007\000\002\000\015\000"
+ "\014\000\021\000\033\133\045\151\045\160\061\045\144\073\045"
+ "\160\062\045\144\162\000\063\001\011\000\033\133\045\160\061"
+ "\045\144\104\000\036\000\002\000\010\000\056\001\011\000\033"
+ "\133\045\160\061\045\144\102\000\033\000\002\000\012\000\065"
+ "\001\011\000\033\133\045\160\061\045\144\103\000\041\000\010"
+ "\000\033\133\103\044\074\062\076\000\032\000\025\000\033\133"
+ "\045\151\045\160\061\045\144\073\045\160\062\045\144\110\044"
+ "\074\065\076\000\070\001\011\000\033\133\045\160\061\045\144"
+ "\101\000\043\000\010\000\033\133\101\044\074\062\076\000\024"
+ "\000\011\000\033\133\112\044\074\065\060\076\000\023\000\010"
+ "\000\033\133\113\044\074\063\076\000\022\000\011\000\033\133"
+ "\061\113\044\074\063\076\000\057\000\007\000\033\050\102\033"
+ "\051\060\000\034\000\004\000\033\133\110\000\166\001\002\000"
+ "\011\000\153\001\003\000\033\110\000\122\001\002\000\012\000"
+ "\173\000\002\000\010\000\326\000\004\000\033\117\104\000\212"
+ "\000\004\000\033\117\102\000\351\000\004\000\033\117\103\000"
+ "\015\001\004\000\033\117\101\000\021\001\004\000\160\146\061"
+ "\000\022\001\004\000\160\146\062\000\023\001\004\000\160\146"
+ "\063\000\024\001\004\000\160\146\064\000\100\001\005\000\033"
+ "\133\060\151\000\102\001\005\000\033\133\064\151\000\103\001"
+ "\005\000\033\133\065\151\000\116\001\003\000\033\070\000\105"
+ "\000\011\000\033\133\067\155\044\074\062\076\000\123\001\007"
+ "\000\033\115\044\074\065\076\000\123\000\002\000\017\000\124"
+ "\000\006\000\033\133\077\067\154\000\016\001\010\000\033\133"
+ "\077\061\154\033\076\000\140\000\010\000\033\133\155\044\074"
+ "\062\076\000\143\000\010\000\033\133\155\044\074\062\076\000"
+ "\113\001\034\000\033\076\033\133\077\063\154\033\133\077\064"
+ "\154\033\133\077\065\154\033\133\077\067\150\033\133\077\070"
+ "\150\000\120\001\003\000\033\067\000\134\001\114\000\033\133"
+ "\060\045\077\045\160\061\045\160\066\045\174\045\164\073\061"
+ "\045\073\045\077\045\160\062\045\164\073\064\045\073\045\077"
+ "\045\160\061\045\160\063\045\174\045\164\073\067\045\073\045"
+ "\077\045\160\064\045\164\073\065\045\073\155\045\077\045\160"
+ "\071\045\164\016\045\145\017\045\073\044\074\062\076\000\125"
+ "\000\011\000\033\133\155\017\044\074\062\076\000\061\000\002"
+ "\000\016\000\062\000\006\000\033\133\077\067\150\000\017\001"
+ "\010\000\033\133\077\061\150\033\075\000\112\000\011\000\033"
+ "\133\067\155\044\074\062\076\000\116\000\011\000\033\133\064"
+ "\155\044\074\062\076\000\017\000\005\000\033\133\063\147\000"
+ "\221\000\004\000\033\117\171\000\233\000\004\000\033\117\170"
+ "\000\226\000\004\000\033\117\164\000\227\000\004\000\033\117"
+ "\165\000\230\000\004\000\033\117\166\000\231\000\004\000\033"
+ "\117\154\000\232\000\004\000\033\117\167\000\215\000\004\000"
+ "\033\117\115\000\222\000\004\000\033\117\120\000\223\000\004"
+ "\000\033\117\121\000\224\000\004\000\033\117\122\000\225\000"
+ "\004\000\033\117\123\000\170\000\004\000\033\117\161\000\171"
+ "\000\004\000\033\117\163\000\172\000\004\000\033\117\162\000"
+ "\176\000\004\000\033\117\160\000\177\000\004\000\033\117\156"
+ "\000\011\000\001\000\003\000\142\163\000\146\001",
+ 897
+ },
+ {
+ "vt220",
+ "\001\006\000\166\164\062\062\060\000\000\000\000\000\024\000"
+ "\006\000\001\000\001\026\000\001\027\000\001\011\000\001\044"
+ "\000\001\036\000\001\022\000\004\000\004\000\120\000\007\000"
+ "\010\000\012\000\030\000\036\000\003\000\067\004\137\000\000"
+ "\000\063\000\140\140\141\141\146\146\147\147\152\152\153\153"
+ "\154\154\155\155\156\156\157\157\160\160\161\161\162\162\163"
+ "\163\164\164\165\165\166\166\167\167\170\170\171\171\172\172"
+ "\173\173\174\174\175\175\176\176\000\003\000\002\000\007\000"
+ "\063\000\005\000\033\133\065\155\000\064\000\005\000\033\133"
+ "\061\155\000\021\000\007\000\033\133\110\033\133\112\000\007"
+ "\000\002\000\015\000\014\000\021\000\033\133\045\151\045\160"
+ "\061\045\144\073\045\160\062\045\144\162\000\063\001\011\000"
+ "\033\133\045\160\061\045\144\104\000\036\000\002\000\010\000"
+ "\056\001\011\000\033\133\045\160\061\045\144\102\000\033\000"
+ "\002\000\012\000\065\001\011\000\033\133\045\160\061\045\144"
+ "\103\000\041\000\004\000\033\133\103\000\032\000\021\000\033"
+ "\133\045\151\045\160\061\045\144\073\045\160\062\045\144\110"
+ "\000\070\001\011\000\033\133\045\160\061\045\144\101\000\043"
+ "\000\004\000\033\133\101\000\054\001\011\000\033\133\045\160"
+ "\061\045\144\120\000\047\000\004\000\033\133\120\000\055\001"
+ "\011\000\033\133\045\160\061\045\144\115\000\050\000\004\000"
+ "\033\133\115\000\122\000\011\000\033\133\045\160\061\045\144"
+ "\130\000\024\000\004\000\033\133\112\000\023\000\004\000\033"
+ "\133\113\000\022\000\005\000\033\133\061\113\000\057\000\004"
+ "\000\033\051\060\000\150\000\022\000\033\133\077\065\150\044"
+ "\074\062\060\060\057\076\033\133\077\065\154\000\034\000\004"
+ "\000\033\133\110\000\166\001\002\000\011\000\153\001\003\000"
+ "\033\110\000\060\001\011\000\033\133\045\160\061\045\144\100"
+ "\000\161\000\030\000\057\165\163\162\057\163\150\141\162\145"
+ "\057\164\141\142\163\145\164\057\166\164\061\060\060\000\062"
+ "\001\011\000\033\133\045\160\061\045\144\114\000\166\000\004"
+ "\000\033\133\114\000\122\001\003\000\033\104\000\157\000\026"
+ "\000\033\133\077\067\150\033\133\076\033\133\077\061\154\033"
+ "\040\106\033\133\077\064\154\000\173\000\002\000\010\000\326"
+ "\000\004\000\033\133\104\000\212\000\004\000\033\133\102\000"
+ "\351\000\004\000\033\133\103\000\015\001\004\000\033\133\101"
+ "\000\210\000\005\000\033\133\063\176\000\222\000\004\000\033"
+ "\117\120\000\233\000\006\000\033\133\062\061\176\000\234\000"
+ "\006\000\033\133\062\063\176\000\235\000\006\000\033\133\062"
+ "\064\176\000\236\000\006\000\033\133\062\065\176\000\237\000"
+ "\006\000\033\133\062\066\176\000\242\000\006\000\033\133\063"
+ "\061\176\000\243\000\006\000\033\133\063\062\176\000\244\000"
+ "\006\000\033\133\063\063\176\000\223\000\004\000\033\117\121"
+ "\000\245\000\006\000\033\133\063\064\176\000\224\000\004\000"
+ "\033\117\122\000\225\000\004\000\033\117\123\000\227\000\006"
+ "\000\033\133\061\067\176\000\230\000\006\000\033\133\061\070"
+ "\176\000\231\000\006\000\033\133\061\071\176\000\232\000\006"
+ "\000\033\133\062\060\176\000\321\000\005\000\033\133\061\176"
+ "\000\322\000\006\000\033\133\062\070\176\000\324\000\005\000"
+ "\033\133\062\176\000\335\000\005\000\033\133\066\176\000\340"
+ "\000\005\000\033\133\065\176\000\343\000\006\000\033\133\062"
+ "\071\176\000\362\000\005\000\033\133\064\176\000\021\001\004"
+ "\000\160\146\061\000\022\001\004\000\160\146\062\000\023\001"
+ "\004\000\160\146\063\000\024\001\004\000\160\146\064\000\047"
+ "\001\003\000\033\105\000\116\001\003\000\033\070\000\105\000"
+ "\005\000\033\133\067\155\000\123\001\003\000\033\115\000\123"
+ "\000\010\000\033\050\102\044\074\064\076\000\124\000\006\000"
+ "\033\133\077\067\154\000\131\000\005\000\033\133\064\154\000"
+ "\140\000\006\000\033\133\062\067\155\000\143\000\006\000\033"
+ "\133\062\064\155\000\112\001\006\000\033\133\077\063\154\000"
+ "\120\001\003\000\033\067\000\134\001\113\000\033\133\060\045"
+ "\077\045\160\066\045\164\073\061\045\073\045\077\045\160\062"
+ "\045\164\073\064\045\073\045\077\045\160\064\045\164\073\065"
+ "\045\073\045\077\045\160\061\045\160\063\045\174\045\164\073"
+ "\067\045\073\155\045\077\045\160\071\045\164\033\050\060\045"
+ "\145\033\050\102\045\073\044\074\062\076\000\125\000\007\000"
+ "\033\133\155\033\050\102\000\061\000\010\000\033\050\060\044"
+ "\074\062\076\000\062\000\006\000\033\133\077\067\150\000\073"
+ "\000\005\000\033\133\064\150\000\112\000\005\000\033\133\067"
+ "\155\000\116\000\005\000\033\133\064\155\000\017\000\005\000"
+ "\033\133\063\147\000\100\001\004\000\033\133\151\000\102\001"
+ "\005\000\033\133\064\151\000\103\001\005\000\033\133\065\151"
+ "\000\200\001\013\000\033\133\045\151\045\144\073\045\144\122"
+ "\000\201\001\005\000\033\133\066\156\000\202\001\023\000\033"
+ "\133\077\045\133\073\060\061\062\063\064\065\066\067\070\071"
+ "\135\143\000\203\001\004\000\033\133\143\000\011\000\001\000"
+ "\003\000\142\163\000\146\001",
+ 1147
+ },
+ {
+ "wsvt25",
+ "\001\007\000\167\163\166\164\062\065\000\000\000\000\000\027"
+ "\000\007\000\002\000\001\027\000\001\001\000\001\026\000\001"
+ "\011\000\001\044\000\001\036\000\001\032\000\006\000\016\000"
+ "\010\000\004\000\120\000\007\000\010\000\012\000\031\000\021"
+ "\000\100\000\036\000\003\000\207\004\147\000\035\000\007\000"
+ "\033\133\077\062\065\154\000\040\000\007\000\033\133\077\062"
+ "\065\150\000\157\000\013\000\033\133\162\033\133\062\065\073"
+ "\061\110\000\210\000\005\000\033\133\063\176\000\214\000\005"
+ "\000\033\133\070\176\000\222\000\006\000\033\133\061\061\176"
+ "\000\233\000\006\000\033\133\062\061\176\000\234\000\006\000"
+ "\033\133\062\063\176\000\235\000\006\000\033\133\062\064\176"
+ "\000\223\000\006\000\033\133\061\062\176\000\224\000\006\000"
+ "\033\133\061\063\176\000\225\000\006\000\033\133\061\064\176"
+ "\000\226\000\006\000\033\133\061\065\176\000\227\000\006\000"
+ "\033\133\061\067\176\000\230\000\006\000\033\133\061\070\176"
+ "\000\231\000\006\000\033\133\061\071\176\000\232\000\006\000"
+ "\033\133\062\060\176\000\323\000\005\000\033\133\067\176\000"
+ "\052\001\004\000\033\133\155\000\112\001\003\000\033\143\000"
+ "\132\001\012\000\033\133\064\045\160\061\045\144\155\000\133"
+ "\001\012\000\033\133\063\045\160\061\045\144\155\000\000\000"
+ "\063\000\140\140\141\141\146\146\147\147\152\152\153\153\154"
+ "\154\155\155\156\156\157\157\160\160\161\161\162\162\163\163"
+ "\164\164\165\165\166\166\167\167\170\170\171\171\172\172\173"
+ "\173\174\174\175\175\176\176\000\003\000\002\000\007\000\063"
+ "\000\005\000\033\133\065\155\000\064\000\005\000\033\133\061"
+ "\155\000\021\000\007\000\033\133\110\033\133\112\000\007\000"
+ "\002\000\015\000\014\000\021\000\033\133\045\151\045\160\061"
+ "\045\144\073\045\160\062\045\144\162\000\063\001\011\000\033"
+ "\133\045\160\061\045\144\104\000\036\000\002\000\010\000\056"
+ "\001\011\000\033\133\045\160\061\045\144\102\000\033\000\002"
+ "\000\012\000\065\001\011\000\033\133\045\160\061\045\144\103"
+ "\000\041\000\004\000\033\133\103\000\032\000\021\000\033\133"
+ "\045\151\045\160\061\045\144\073\045\160\062\045\144\110\000"
+ "\070\001\011\000\033\133\045\160\061\045\144\101\000\043\000"
+ "\004\000\033\133\101\000\054\001\011\000\033\133\045\160\061"
+ "\045\144\120\000\047\000\004\000\033\133\120\000\055\001\011"
+ "\000\033\133\045\160\061\045\144\115\000\050\000\004\000\033"
+ "\133\115\000\122\000\011\000\033\133\045\160\061\045\144\130"
+ "\000\024\000\004\000\033\133\112\000\023\000\004\000\033\133"
+ "\113\000\022\000\005\000\033\133\061\113\000\057\000\004\000"
+ "\033\051\060\000\150\000\022\000\033\133\077\065\150\044\074"
+ "\062\060\060\057\076\033\133\077\065\154\000\034\000\004\000"
+ "\033\133\110\000\166\001\002\000\011\000\153\001\003\000\033"
+ "\110\000\060\001\011\000\033\133\045\160\061\045\144\100\000"
+ "\161\000\030\000\057\165\163\162\057\163\150\141\162\145\057"
+ "\164\141\142\163\145\164\057\166\164\061\060\060\000\062\001"
+ "\011\000\033\133\045\160\061\045\144\114\000\166\000\004\000"
+ "\033\133\114\000\122\001\003\000\033\104\000\173\000\002\000"
+ "\010\000\326\000\004\000\033\133\104\000\212\000\004\000\033"
+ "\133\102\000\351\000\004\000\033\133\103\000\015\001\004\000"
+ "\033\133\101\000\236\000\006\000\033\133\062\065\176\000\237"
+ "\000\006\000\033\133\062\066\176\000\242\000\006\000\033\133"
+ "\063\061\176\000\243\000\006\000\033\133\063\062\176\000\244"
+ "\000\006\000\033\133\063\063\176\000\245\000\006\000\033\133"
+ "\063\064\176\000\321\000\005\000\033\133\061\176\000\322\000"
+ "\006\000\033\133\062\070\176\000\324\000\005\000\033\133\062"
+ "\176\000\335\000\005\000\033\133\066\176\000\340\000\005\000"
+ "\033\133\065\176\000\343\000\006\000\033\133\062\071\176\000"
+ "\362\000\005\000\033\133\064\176\000\021\001\004\000\160\146"
+ "\061\000\022\001\004\000\160\146\062\000\023\001\004\000\160"
+ "\146\063\000\024\001\004\000\160\146\064\000\047\001\003\000"
+ "\033\105\000\116\001\003\000\033\070\000\105\000\005\000\033"
+ "\133\067\155\000\123\001\003\000\033\115\000\123\000\010\000"
+ "\033\050\102\044\074\064\076\000\124\000\006\000\033\133\077"
+ "\067\154\000\131\000\005\000\033\133\064\154\000\140\000\006"
+ "\000\033\133\062\067\155\000\143\000\006\000\033\133\062\064"
+ "\155\000\120\001\003\000\033\067\000\134\001\113\000\033\133"
+ "\060\045\077\045\160\066\045\164\073\061\045\073\045\077\045"
+ "\160\062\045\164\073\064\045\073\045\077\045\160\064\045\164"
+ "\073\065\045\073\045\077\045\160\061\045\160\063\045\174\045"
+ "\164\073\067\045\073\155\045\077\045\160\071\045\164\033\050"
+ "\060\045\145\033\050\102\045\073\044\074\062\076\000\125\000"
+ "\007\000\033\133\155\033\050\102\000\061\000\010\000\033\050"
+ "\060\044\074\062\076\000\062\000\006\000\033\133\077\067\150"
+ "\000\073\000\005\000\033\133\064\150\000\112\000\005\000\033"
+ "\133\067\155\000\116\000\005\000\033\133\064\155\000\017\000"
+ "\005\000\033\133\063\147\000\100\001\004\000\033\133\151\000"
+ "\102\001\005\000\033\133\064\151\000\103\001\005\000\033\133"
+ "\065\151\000\200\001\013\000\033\133\045\151\045\144\073\045"
+ "\144\122\000\201\001\005\000\033\133\066\156\000\202\001\023"
+ "\000\033\133\077\045\133\073\060\061\062\063\064\065\066\067"
+ "\070\071\135\143\000\203\001\004\000\033\133\143\000\011\000"
+ "\001\000\003\000\142\163\000\146\001",
+ 1239
+ },
+ {
+ "xterm",
+ "\001\006\000\170\164\145\162\155\000\000\000\000\000\032\000"
+ "\010\000\032\000\001\001\000\001\002\000\001\016\000\001\026"
+ "\000\001\027\000\001\011\000\001\036\000\001\026\000\005\000"
+ "\016\000\010\000\004\000\120\000\007\000\010\000\012\000\030"
+ "\000\021\000\100\000\066\010\252\000\061\001\011\000\033\133"
+ "\045\160\061\045\144\123\000\172\000\004\000\033\117\105\000"
+ "\175\000\004\000\033\133\132\000\215\000\004\000\033\117\115"
+ "\000\067\001\011\000\033\133\045\160\061\045\144\124\000\200"
+ "\001\013\000\033\133\045\151\045\144\073\045\144\122\000\201"
+ "\001\005\000\033\133\066\156\000\202\001\023\000\033\133\077"
+ "\045\133\073\060\061\062\063\064\065\066\067\070\071\135\143"
+ "\000\203\001\004\000\033\133\143\000\326\000\004\000\033\117"
+ "\104\000\212\000\004\000\033\117\102\000\351\000\004\000\033"
+ "\117\103\000\015\001\004\000\033\117\101\000\214\000\004\000"
+ "\033\117\106\000\323\000\004\000\033\117\110\000\222\000\004"
+ "\000\033\117\120\000\233\000\006\000\033\133\062\061\176\000"
+ "\234\000\006\000\033\133\062\063\176\000\235\000\006\000\033"
+ "\133\062\064\176\000\236\000\007\000\033\133\061\073\062\120"
+ "\000\237\000\007\000\033\133\061\073\062\121\000\240\000\007"
+ "\000\033\133\061\073\062\122\000\241\000\007\000\033\133\061"
+ "\073\062\123\000\242\000\010\000\033\133\061\065\073\062\176"
+ "\000\243\000\010\000\033\133\061\067\073\062\176\000\244\000"
+ "\010\000\033\133\061\070\073\062\176\000\223\000\004\000\033"
+ "\117\121\000\245\000\010\000\033\133\061\071\073\062\176\000"
+ "\246\000\010\000\033\133\062\060\073\062\176\000\247\000\010"
+ "\000\033\133\062\061\073\062\176\000\250\000\010\000\033\133"
+ "\062\063\073\062\176\000\251\000\010\000\033\133\062\064\073"
+ "\062\176\000\252\000\007\000\033\133\061\073\065\120\000\253"
+ "\000\007\000\033\133\061\073\065\121\000\254\000\007\000\033"
+ "\133\061\073\065\122\000\255\000\007\000\033\133\061\073\065"
+ "\123\000\256\000\010\000\033\133\061\065\073\065\176\000\224"
+ "\000\004\000\033\117\122\000\257\000\010\000\033\133\061\067"
+ "\073\065\176\000\260\000\010\000\033\133\061\070\073\065\176"
+ "\000\261\000\010\000\033\133\061\071\073\065\176\000\262\000"
+ "\010\000\033\133\062\060\073\065\176\000\263\000\010\000\033"
+ "\133\062\061\073\065\176\000\264\000\010\000\033\133\062\063"
+ "\073\065\176\000\265\000\010\000\033\133\062\064\073\065\176"
+ "\000\266\000\007\000\033\133\061\073\066\120\000\267\000\007"
+ "\000\033\133\061\073\066\121\000\270\000\007\000\033\133\061"
+ "\073\066\122\000\225\000\004\000\033\117\123\000\271\000\007"
+ "\000\033\133\061\073\066\123\000\272\000\010\000\033\133\061"
+ "\065\073\066\176\000\273\000\010\000\033\133\061\067\073\066"
+ "\176\000\274\000\010\000\033\133\061\070\073\066\176\000\275"
+ "\000\010\000\033\133\061\071\073\066\176\000\276\000\010\000"
+ "\033\133\062\060\073\066\176\000\277\000\010\000\033\133\062"
+ "\061\073\066\176\000\300\000\010\000\033\133\062\063\073\066"
+ "\176\000\301\000\010\000\033\133\062\064\073\066\176\000\302"
+ "\000\007\000\033\133\061\073\063\120\000\226\000\006\000\033"
+ "\133\061\065\176\000\303\000\007\000\033\133\061\073\063\121"
+ "\000\304\000\007\000\033\133\061\073\063\122\000\305\000\007"
+ "\000\033\133\061\073\063\123\000\306\000\010\000\033\133\061"
+ "\065\073\063\176\000\307\000\010\000\033\133\061\067\073\063"
+ "\176\000\310\000\010\000\033\133\061\070\073\063\176\000\311"
+ "\000\010\000\033\133\061\071\073\063\176\000\312\000\010\000"
+ "\033\133\062\060\073\063\176\000\313\000\010\000\033\133\062"
+ "\061\073\063\176\000\314\000\010\000\033\133\062\063\073\063"
+ "\176\000\227\000\006\000\033\133\061\067\176\000\315\000\010"
+ "\000\033\133\062\064\073\063\176\000\316\000\007\000\033\133"
+ "\061\073\064\120\000\317\000\007\000\033\133\061\073\064\121"
+ "\000\320\000\007\000\033\133\061\073\064\122\000\230\000\006"
+ "\000\033\133\061\070\176\000\231\000\006\000\033\133\061\071"
+ "\176\000\232\000\006\000\033\133\062\060\176\000\373\000\007"
+ "\000\033\133\061\073\062\104\000\005\001\007\000\033\133\061"
+ "\073\062\103\000\366\000\007\000\033\133\061\073\062\102\000"
+ "\002\001\007\000\033\133\061\073\062\101\000\360\000\007\000"
+ "\033\133\063\073\062\176\000\363\000\007\000\033\133\061\073"
+ "\062\106\000\371\000\007\000\033\133\061\073\062\110\000\372"
+ "\000\007\000\033\133\062\073\062\176\000\376\000\007\000\033"
+ "\133\066\073\062\176\000\000\001\007\000\033\133\065\073\062"
+ "\176\000\324\000\005\000\033\133\062\176\000\335\000\005\000"
+ "\033\133\066\176\000\340\000\005\000\033\133\065\176\000\210"
+ "\000\005\000\033\133\063\176\000\000\000\065\000\140\140\141"
+ "\141\146\146\147\147\151\151\152\152\153\153\154\154\155\155"
+ "\156\156\157\157\160\160\161\161\162\162\163\163\164\164\165"
+ "\165\166\166\167\167\170\170\171\171\172\172\173\173\174\174"
+ "\175\175\176\176\000\003\000\002\000\007\000\063\000\005\000"
+ "\033\133\065\155\000\064\000\005\000\033\133\061\155\000\002"
+ "\000\004\000\033\133\132\000\035\000\007\000\033\133\077\062"
+ "\065\154\000\021\000\010\000\033\133\110\033\133\062\112\000"
+ "\040\000\015\000\033\133\077\061\062\154\033\133\077\062\065"
+ "\150\000\007\000\002\000\015\000\014\000\021\000\033\133\045"
+ "\151\045\160\061\045\144\073\045\160\062\045\144\162\000\063"
+ "\001\011\000\033\133\045\160\061\045\144\104\000\036\000\002"
+ "\000\010\000\056\001\011\000\033\133\045\160\061\045\144\102"
+ "\000\033\000\002\000\012\000\065\001\011\000\033\133\045\160"
+ "\061\045\144\103\000\041\000\004\000\033\133\103\000\032\000"
+ "\021\000\033\133\045\151\045\160\061\045\144\073\045\160\062"
+ "\045\144\110\000\070\001\011\000\033\133\045\160\061\045\144"
+ "\101\000\043\000\004\000\033\133\101\000\044\000\012\000\033"
+ "\133\077\061\062\073\062\065\150\000\054\001\011\000\033\133"
+ "\045\160\061\045\144\120\000\047\000\004\000\033\133\120\000"
+ "\067\000\005\000\033\133\062\155\000\055\001\011\000\033\133"
+ "\045\160\061\045\144\115\000\050\000\004\000\033\133\115\000"
+ "\122\000\011\000\033\133\045\160\061\045\144\130\000\024\000"
+ "\004\000\033\133\112\000\023\000\004\000\033\133\113\000\022"
+ "\000\005\000\033\133\061\113\000\150\000\022\000\033\133\077"
+ "\065\150\044\074\061\060\060\057\076\033\133\077\065\154\000"
+ "\034\000\004\000\033\133\110\000\027\000\013\000\033\133\045"
+ "\151\045\160\061\045\144\107\000\166\001\002\000\011\000\153"
+ "\001\003\000\033\110\000\060\001\011\000\033\133\045\160\061"
+ "\045\144\100\000\062\001\011\000\033\133\045\160\061\045\144"
+ "\114\000\166\000\004\000\033\133\114\000\122\001\002\000\012"
+ "\000\110\000\005\000\033\133\070\155\000\157\000\022\000\033"
+ "\133\041\160\033\133\077\063\073\064\154\033\133\064\154\033"
+ "\076\000\332\000\004\000\033\133\115\000\052\001\011\000\033"
+ "\133\063\071\073\064\071\155\000\116\001\003\000\033\070\000"
+ "\105\000\005\000\033\133\067\155\000\123\001\003\000\033\115"
+ "\000\132\000\006\000\033\133\062\063\155\000\123\000\004\000"
+ "\033\050\102\000\124\000\006\000\033\133\077\067\154\000\126"
+ "\000\011\000\033\133\077\061\060\064\071\154\000\131\000\005"
+ "\000\033\133\064\154\000\016\001\010\000\033\133\077\061\154"
+ "\033\076\000\036\001\011\000\033\133\077\061\060\063\064\154"
+ "\000\140\000\006\000\033\133\062\067\155\000\143\000\006\000"
+ "\033\133\062\064\155\000\112\001\003\000\033\143\000\113\001"
+ "\022\000\033\133\041\160\033\133\077\063\073\064\154\033\133"
+ "\064\154\033\076\000\120\001\003\000\033\067\000\132\001\012"
+ "\000\033\133\064\045\160\061\045\144\155\000\133\001\012\000"
+ "\033\133\063\045\160\061\045\144\155\000\135\001\106\000\033"
+ "\133\064\045\077\045\160\061\045\173\061\175\045\075\045\164"
+ "\064\045\145\045\160\061\045\173\063\175\045\075\045\164\066"
+ "\045\145\045\160\061\045\173\064\175\045\075\045\164\061\045"
+ "\145\045\160\061\045\173\066\175\045\075\045\164\063\045\145"
+ "\045\160\061\045\144\045\073\155\000\143\001\106\000\033\133"
+ "\063\045\077\045\160\061\045\173\061\175\045\075\045\164\064"
+ "\045\145\045\160\061\045\173\063\175\045\075\045\164\066\045"
+ "\145\045\160\061\045\173\064\175\045\075\045\164\061\045\145"
+ "\045\160\061\045\173\066\175\045\075\045\164\063\045\145\045"
+ "\160\061\045\144\045\073\155\000\134\001\135\000\045\077\045"
+ "\160\071\045\164\033\050\060\045\145\033\050\102\045\073\033"
+ "\133\060\045\077\045\160\066\045\164\073\061\045\073\045\077"
+ "\045\160\065\045\164\073\062\045\073\045\077\045\160\062\045"
+ "\164\073\064\045\073\045\077\045\160\061\045\160\063\045\174"
+ "\045\164\073\067\045\073\045\077\045\160\064\045\164\073\065"
+ "\045\073\045\077\045\160\067\045\164\073\070\045\073\155\000"
+ "\125\000\007\000\033\050\102\033\133\155\000\074\000\005\000"
+ "\033\133\063\155\000\061\000\004\000\033\050\060\000\062\000"
+ "\006\000\033\133\077\067\150\000\065\000\011\000\033\133\077"
+ "\061\060\064\071\150\000\073\000\005\000\033\133\064\150\000"
+ "\017\001\010\000\033\133\077\061\150\033\075\000\037\001\011"
+ "\000\033\133\077\061\060\063\064\150\000\112\000\005\000\033"
+ "\133\067\155\000\116\000\005\000\033\133\064\155\000\017\000"
+ "\005\000\033\133\063\147\000\117\001\013\000\033\133\045\151"
+ "\045\160\061\045\144\144\000\100\001\004\000\033\133\151\000"
+ "\102\001\005\000\033\133\064\151\000\103\001\005\000\033\133"
+ "\065\151\000\173\000\002\000\010\000\053\004\077\000\004\000"
+ "\153\104\116\000\163\007\000\033\133\061\073\062\102\000\005"
+ "\000\153\104\116\063\000\163\007\000\033\133\061\073\063\102"
+ "\000\005\000\153\104\116\064\000\163\007\000\033\133\061\073"
+ "\064\102\000\005\000\153\104\116\065\000\163\007\000\033\133"
+ "\061\073\065\102\000\005\000\153\104\116\066\000\163\007\000"
+ "\033\133\061\073\066\102\000\005\000\153\104\116\067\000\163"
+ "\007\000\033\133\061\073\067\102\000\006\000\153\114\106\124"
+ "\063\000\163\007\000\033\133\061\073\063\104\000\006\000\153"
+ "\114\106\124\064\000\163\007\000\033\133\061\073\064\104\000"
+ "\006\000\153\114\106\124\065\000\163\007\000\033\133\061\073"
+ "\065\104\000\006\000\153\114\106\124\066\000\163\007\000\033"
+ "\133\061\073\066\104\000\006\000\153\114\106\124\067\000\163"
+ "\007\000\033\133\061\073\067\104\000\006\000\153\122\111\124"
+ "\063\000\163\007\000\033\133\061\073\063\103\000\006\000\153"
+ "\122\111\124\064\000\163\007\000\033\133\061\073\064\103\000"
+ "\006\000\153\122\111\124\065\000\163\007\000\033\133\061\073"
+ "\065\103\000\006\000\153\122\111\124\066\000\163\007\000\033"
+ "\133\061\073\066\103\000\006\000\153\122\111\124\067\000\163"
+ "\007\000\033\133\061\073\067\103\000\004\000\153\125\120\000"
+ "\163\007\000\033\133\061\073\062\101\000\005\000\153\125\120"
+ "\063\000\163\007\000\033\133\061\073\063\101\000\005\000\153"
+ "\125\120\064\000\163\007\000\033\133\061\073\064\101\000\005"
+ "\000\153\125\120\065\000\163\007\000\033\133\061\073\065\101"
+ "\000\005\000\153\125\120\066\000\163\007\000\033\133\061\073"
+ "\066\101\000\005\000\153\125\120\067\000\163\007\000\033\133"
+ "\061\073\067\101\000\005\000\153\104\103\063\000\163\007\000"
+ "\033\133\063\073\063\176\000\005\000\153\104\103\064\000\163"
+ "\007\000\033\133\063\073\064\176\000\005\000\153\104\103\065"
+ "\000\163\007\000\033\133\063\073\065\176\000\005\000\153\104"
+ "\103\066\000\163\007\000\033\133\063\073\066\176\000\005\000"
+ "\153\104\103\067\000\163\007\000\033\133\063\073\067\176\000"
+ "\006\000\153\105\116\104\063\000\163\007\000\033\133\061\073"
+ "\063\106\000\006\000\153\105\116\104\064\000\163\007\000\033"
+ "\133\061\073\064\106\000\006\000\153\105\116\104\065\000\163"
+ "\007\000\033\133\061\073\065\106\000\006\000\153\105\116\104"
+ "\066\000\163\007\000\033\133\061\073\066\106\000\006\000\153"
+ "\105\116\104\067\000\163\007\000\033\133\061\073\067\106\000"
+ "\006\000\153\110\117\115\063\000\163\007\000\033\133\061\073"
+ "\063\110\000\006\000\153\110\117\115\064\000\163\007\000\033"
+ "\133\061\073\064\110\000\006\000\153\110\117\115\065\000\163"
+ "\007\000\033\133\061\073\065\110\000\006\000\153\110\117\115"
+ "\066\000\163\007\000\033\133\061\073\066\110\000\006\000\153"
+ "\110\117\115\067\000\163\007\000\033\133\061\073\067\110\000"
+ "\005\000\153\111\103\063\000\163\007\000\033\133\062\073\063"
+ "\176\000\005\000\153\111\103\064\000\163\007\000\033\133\062"
+ "\073\064\176\000\005\000\153\111\103\065\000\163\007\000\033"
+ "\133\062\073\065\176\000\005\000\153\111\103\066\000\163\007"
+ "\000\033\133\062\073\066\176\000\005\000\153\111\103\067\000"
+ "\163\007\000\033\133\062\073\067\176\000\006\000\153\116\130"
+ "\124\063\000\163\007\000\033\133\066\073\063\176\000\006\000"
+ "\153\116\130\124\064\000\163\007\000\033\133\066\073\064\176"
+ "\000\006\000\153\116\130\124\065\000\163\007\000\033\133\066"
+ "\073\065\176\000\006\000\153\116\130\124\066\000\163\007\000"
+ "\033\133\066\073\066\176\000\006\000\153\116\130\124\067\000"
+ "\163\007\000\033\133\066\073\067\176\000\006\000\153\120\122"
+ "\126\063\000\163\007\000\033\133\065\073\063\176\000\006\000"
+ "\153\120\122\126\064\000\163\007\000\033\133\065\073\064\176"
+ "\000\006\000\153\120\122\126\065\000\163\007\000\033\133\065"
+ "\073\065\176\000\006\000\153\120\122\126\066\000\163\007\000"
+ "\033\133\065\073\066\176\000\006\000\153\120\122\126\067\000"
+ "\163\007\000\033\133\065\073\067\176\000\003\000\103\162\000"
+ "\163\007\000\033\135\061\061\062\007\000\003\000\103\163\000"
+ "\163\014\000\033\135\061\062\073\045\160\061\045\163\007\000"
+ "\003\000\115\163\000\163\022\000\033\135\065\062\073\045\160"
+ "\061\045\163\073\045\160\062\045\163\007\000\003\000\123\145"
+ "\000\163\006\000\033\133\062\040\161\000\003\000\123\163\000"
+ "\163\012\000\033\133\045\160\061\045\144\040\161\000\003\000"
+ "\142\163\000\146\001\003\000\101\130\000\146\001\003\000\130"
+ "\124\000\146\001\005\000\155\145\155\154\000\163\003\000\033"
+ "\154\000\005\000\155\145\155\165\000\163\003\000\033\155\000"
+ "\003\000\105\063\000\163\005\000\033\133\063\112\000",
+ 3238
+ },
+ {
+ "xterm-256color",
+ "\001\017\000\170\164\145\162\155\055\062\065\066\143\157\154"
+ "\157\162\000\000\000\000\000\035\000\011\000\003\000\001\032"
+ "\000\001\001\000\001\002\000\001\016\000\001\026\000\001\027"
+ "\000\001\011\000\001\036\000\001\026\000\005\000\016\000\000"
+ "\001\021\000\377\177\004\000\120\000\007\000\010\000\012\000"
+ "\030\000\024\011\254\000\112\001\011\000\033\143\033\135\061"
+ "\060\064\007\000\163\000\136\000\033\135\064\073\045\160\061"
+ "\045\144\073\162\147\142\072\045\160\062\045\173\062\065\065"
+ "\175\045\052\045\173\061\060\060\060\175\045\057\045\062\056"
+ "\062\130\057\045\160\063\045\173\062\065\065\175\045\052\045"
+ "\173\061\060\060\060\175\045\057\045\062\056\062\130\057\045"
+ "\160\064\045\173\062\065\065\175\045\052\045\173\061\060\060"
+ "\060\175\045\057\045\062\056\062\130\033\134\000\051\001\007"
+ "\000\033\135\061\060\064\007\000\132\001\100\000\033\133\045"
+ "\077\045\160\061\045\173\070\175\045\074\045\164\064\045\160"
+ "\061\045\144\045\145\045\160\061\045\173\061\066\175\045\074"
+ "\045\164\061\060\045\160\061\045\173\070\175\045\055\045\144"
+ "\045\145\064\070\073\065\073\045\160\061\045\144\045\073\155"
+ "\000\133\001\077\000\033\133\045\077\045\160\061\045\173\070"
+ "\175\045\074\045\164\063\045\160\061\045\144\045\145\045\160"
+ "\061\045\173\061\066\175\045\074\045\164\071\045\160\061\045"
+ "\173\070\175\045\055\045\144\045\145\063\070\073\065\073\045"
+ "\160\061\045\144\045\073\155\000\061\001\011\000\033\133\045"
+ "\160\061\045\144\123\000\172\000\004\000\033\117\105\000\175"
+ "\000\004\000\033\133\132\000\215\000\004\000\033\117\115\000"
+ "\067\001\011\000\033\133\045\160\061\045\144\124\000\200\001"
+ "\013\000\033\133\045\151\045\144\073\045\144\122\000\201\001"
+ "\005\000\033\133\066\156\000\202\001\023\000\033\133\077\045"
+ "\133\073\060\061\062\063\064\065\066\067\070\071\135\143\000"
+ "\203\001\004\000\033\133\143\000\326\000\004\000\033\117\104"
+ "\000\212\000\004\000\033\117\102\000\351\000\004\000\033\117"
+ "\103\000\015\001\004\000\033\117\101\000\214\000\004\000\033"
+ "\117\106\000\323\000\004\000\033\117\110\000\222\000\004\000"
+ "\033\117\120\000\233\000\006\000\033\133\062\061\176\000\234"
+ "\000\006\000\033\133\062\063\176\000\235\000\006\000\033\133"
+ "\062\064\176\000\236\000\007\000\033\133\061\073\062\120\000"
+ "\237\000\007\000\033\133\061\073\062\121\000\240\000\007\000"
+ "\033\133\061\073\062\122\000\241\000\007\000\033\133\061\073"
+ "\062\123\000\242\000\010\000\033\133\061\065\073\062\176\000"
+ "\243\000\010\000\033\133\061\067\073\062\176\000\244\000\010"
+ "\000\033\133\061\070\073\062\176\000\223\000\004\000\033\117"
+ "\121\000\245\000\010\000\033\133\061\071\073\062\176\000\246"
+ "\000\010\000\033\133\062\060\073\062\176\000\247\000\010\000"
+ "\033\133\062\061\073\062\176\000\250\000\010\000\033\133\062"
+ "\063\073\062\176\000\251\000\010\000\033\133\062\064\073\062"
+ "\176\000\252\000\007\000\033\133\061\073\065\120\000\253\000"
+ "\007\000\033\133\061\073\065\121\000\254\000\007\000\033\133"
+ "\061\073\065\122\000\255\000\007\000\033\133\061\073\065\123"
+ "\000\256\000\010\000\033\133\061\065\073\065\176\000\224\000"
+ "\004\000\033\117\122\000\257\000\010\000\033\133\061\067\073"
+ "\065\176\000\260\000\010\000\033\133\061\070\073\065\176\000"
+ "\261\000\010\000\033\133\061\071\073\065\176\000\262\000\010"
+ "\000\033\133\062\060\073\065\176\000\263\000\010\000\033\133"
+ "\062\061\073\065\176\000\264\000\010\000\033\133\062\063\073"
+ "\065\176\000\265\000\010\000\033\133\062\064\073\065\176\000"
+ "\266\000\007\000\033\133\061\073\066\120\000\267\000\007\000"
+ "\033\133\061\073\066\121\000\270\000\007\000\033\133\061\073"
+ "\066\122\000\225\000\004\000\033\117\123\000\271\000\007\000"
+ "\033\133\061\073\066\123\000\272\000\010\000\033\133\061\065"
+ "\073\066\176\000\273\000\010\000\033\133\061\067\073\066\176"
+ "\000\274\000\010\000\033\133\061\070\073\066\176\000\275\000"
+ "\010\000\033\133\061\071\073\066\176\000\276\000\010\000\033"
+ "\133\062\060\073\066\176\000\277\000\010\000\033\133\062\061"
+ "\073\066\176\000\300\000\010\000\033\133\062\063\073\066\176"
+ "\000\301\000\010\000\033\133\062\064\073\066\176\000\302\000"
+ "\007\000\033\133\061\073\063\120\000\226\000\006\000\033\133"
+ "\061\065\176\000\303\000\007\000\033\133\061\073\063\121\000"
+ "\304\000\007\000\033\133\061\073\063\122\000\305\000\007\000"
+ "\033\133\061\073\063\123\000\306\000\010\000\033\133\061\065"
+ "\073\063\176\000\307\000\010\000\033\133\061\067\073\063\176"
+ "\000\310\000\010\000\033\133\061\070\073\063\176\000\311\000"
+ "\010\000\033\133\061\071\073\063\176\000\312\000\010\000\033"
+ "\133\062\060\073\063\176\000\313\000\010\000\033\133\062\061"
+ "\073\063\176\000\314\000\010\000\033\133\062\063\073\063\176"
+ "\000\227\000\006\000\033\133\061\067\176\000\315\000\010\000"
+ "\033\133\062\064\073\063\176\000\316\000\007\000\033\133\061"
+ "\073\064\120\000\317\000\007\000\033\133\061\073\064\121\000"
+ "\320\000\007\000\033\133\061\073\064\122\000\230\000\006\000"
+ "\033\133\061\070\176\000\231\000\006\000\033\133\061\071\176"
+ "\000\232\000\006\000\033\133\062\060\176\000\373\000\007\000"
+ "\033\133\061\073\062\104\000\005\001\007\000\033\133\061\073"
+ "\062\103\000\366\000\007\000\033\133\061\073\062\102\000\002"
+ "\001\007\000\033\133\061\073\062\101\000\360\000\007\000\033"
+ "\133\063\073\062\176\000\363\000\007\000\033\133\061\073\062"
+ "\106\000\371\000\007\000\033\133\061\073\062\110\000\372\000"
+ "\007\000\033\133\062\073\062\176\000\376\000\007\000\033\133"
+ "\066\073\062\176\000\000\001\007\000\033\133\065\073\062\176"
+ "\000\324\000\005\000\033\133\062\176\000\335\000\005\000\033"
+ "\133\066\176\000\340\000\005\000\033\133\065\176\000\210\000"
+ "\005\000\033\133\063\176\000\000\000\065\000\140\140\141\141"
+ "\146\146\147\147\151\151\152\152\153\153\154\154\155\155\156"
+ "\156\157\157\160\160\161\161\162\162\163\163\164\164\165\165"
+ "\166\166\167\167\170\170\171\171\172\172\173\173\174\174\175"
+ "\175\176\176\000\003\000\002\000\007\000\063\000\005\000\033"
+ "\133\065\155\000\064\000\005\000\033\133\061\155\000\002\000"
+ "\004\000\033\133\132\000\035\000\007\000\033\133\077\062\065"
+ "\154\000\021\000\010\000\033\133\110\033\133\062\112\000\040"
+ "\000\015\000\033\133\077\061\062\154\033\133\077\062\065\150"
+ "\000\007\000\002\000\015\000\014\000\021\000\033\133\045\151"
+ "\045\160\061\045\144\073\045\160\062\045\144\162\000\063\001"
+ "\011\000\033\133\045\160\061\045\144\104\000\036\000\002\000"
+ "\010\000\056\001\011\000\033\133\045\160\061\045\144\102\000"
+ "\033\000\002\000\012\000\065\001\011\000\033\133\045\160\061"
+ "\045\144\103\000\041\000\004\000\033\133\103\000\032\000\021"
+ "\000\033\133\045\151\045\160\061\045\144\073\045\160\062\045"
+ "\144\110\000\070\001\011\000\033\133\045\160\061\045\144\101"
+ "\000\043\000\004\000\033\133\101\000\044\000\012\000\033\133"
+ "\077\061\062\073\062\065\150\000\054\001\011\000\033\133\045"
+ "\160\061\045\144\120\000\047\000\004\000\033\133\120\000\067"
+ "\000\005\000\033\133\062\155\000\055\001\011\000\033\133\045"
+ "\160\061\045\144\115\000\050\000\004\000\033\133\115\000\122"
+ "\000\011\000\033\133\045\160\061\045\144\130\000\024\000\004"
+ "\000\033\133\112\000\023\000\004\000\033\133\113\000\022\000"
+ "\005\000\033\133\061\113\000\150\000\022\000\033\133\077\065"
+ "\150\044\074\061\060\060\057\076\033\133\077\065\154\000\034"
+ "\000\004\000\033\133\110\000\027\000\013\000\033\133\045\151"
+ "\045\160\061\045\144\107\000\166\001\002\000\011\000\153\001"
+ "\003\000\033\110\000\060\001\011\000\033\133\045\160\061\045"
+ "\144\100\000\062\001\011\000\033\133\045\160\061\045\144\114"
+ "\000\166\000\004\000\033\133\114\000\122\001\002\000\012\000"
+ "\110\000\005\000\033\133\070\155\000\157\000\022\000\033\133"
+ "\041\160\033\133\077\063\073\064\154\033\133\064\154\033\076"
+ "\000\332\000\004\000\033\133\115\000\052\001\011\000\033\133"
+ "\063\071\073\064\071\155\000\116\001\003\000\033\070\000\105"
+ "\000\005\000\033\133\067\155\000\123\001\003\000\033\115\000"
+ "\132\000\006\000\033\133\062\063\155\000\123\000\004\000\033"
+ "\050\102\000\124\000\006\000\033\133\077\067\154\000\126\000"
+ "\011\000\033\133\077\061\060\064\071\154\000\131\000\005\000"
+ "\033\133\064\154\000\016\001\010\000\033\133\077\061\154\033"
+ "\076\000\036\001\011\000\033\133\077\061\060\063\064\154\000"
+ "\140\000\006\000\033\133\062\067\155\000\143\000\006\000\033"
+ "\133\062\064\155\000\113\001\022\000\033\133\041\160\033\133"
+ "\077\063\073\064\154\033\133\064\154\033\076\000\120\001\003"
+ "\000\033\067\000\135\001\106\000\033\133\064\045\077\045\160"
+ "\061\045\173\061\175\045\075\045\164\064\045\145\045\160\061"
+ "\045\173\063\175\045\075\045\164\066\045\145\045\160\061\045"
+ "\173\064\175\045\075\045\164\061\045\145\045\160\061\045\173"
+ "\066\175\045\075\045\164\063\045\145\045\160\061\045\144\045"
+ "\073\155\000\143\001\106\000\033\133\063\045\077\045\160\061"
+ "\045\173\061\175\045\075\045\164\064\045\145\045\160\061\045"
+ "\173\063\175\045\075\045\164\066\045\145\045\160\061\045\173"
+ "\064\175\045\075\045\164\061\045\145\045\160\061\045\173\066"
+ "\175\045\075\045\164\063\045\145\045\160\061\045\144\045\073"
+ "\155\000\134\001\135\000\045\077\045\160\071\045\164\033\050"
+ "\060\045\145\033\050\102\045\073\033\133\060\045\077\045\160"
+ "\066\045\164\073\061\045\073\045\077\045\160\065\045\164\073"
+ "\062\045\073\045\077\045\160\062\045\164\073\064\045\073\045"
+ "\077\045\160\061\045\160\063\045\174\045\164\073\067\045\073"
+ "\045\077\045\160\064\045\164\073\065\045\073\045\077\045\160"
+ "\067\045\164\073\070\045\073\155\000\125\000\007\000\033\050"
+ "\102\033\133\155\000\074\000\005\000\033\133\063\155\000\061"
+ "\000\004\000\033\050\060\000\062\000\006\000\033\133\077\067"
+ "\150\000\065\000\011\000\033\133\077\061\060\064\071\150\000"
+ "\073\000\005\000\033\133\064\150\000\017\001\010\000\033\133"
+ "\077\061\150\033\075\000\037\001\011\000\033\133\077\061\060"
+ "\063\064\150\000\112\000\005\000\033\133\067\155\000\116\000"
+ "\005\000\033\133\064\155\000\017\000\005\000\033\133\063\147"
+ "\000\117\001\013\000\033\133\045\151\045\160\061\045\144\144"
+ "\000\100\001\004\000\033\133\151\000\102\001\005\000\033\133"
+ "\064\151\000\103\001\005\000\033\133\065\151\000\173\000\002"
+ "\000\010\000\053\004\077\000\004\000\153\104\116\000\163\007"
+ "\000\033\133\061\073\062\102\000\005\000\153\104\116\063\000"
+ "\163\007\000\033\133\061\073\063\102\000\005\000\153\104\116"
+ "\064\000\163\007\000\033\133\061\073\064\102\000\005\000\153"
+ "\104\116\065\000\163\007\000\033\133\061\073\065\102\000\005"
+ "\000\153\104\116\066\000\163\007\000\033\133\061\073\066\102"
+ "\000\005\000\153\104\116\067\000\163\007\000\033\133\061\073"
+ "\067\102\000\006\000\153\114\106\124\063\000\163\007\000\033"
+ "\133\061\073\063\104\000\006\000\153\114\106\124\064\000\163"
+ "\007\000\033\133\061\073\064\104\000\006\000\153\114\106\124"
+ "\065\000\163\007\000\033\133\061\073\065\104\000\006\000\153"
+ "\114\106\124\066\000\163\007\000\033\133\061\073\066\104\000"
+ "\006\000\153\114\106\124\067\000\163\007\000\033\133\061\073"
+ "\067\104\000\006\000\153\122\111\124\063\000\163\007\000\033"
+ "\133\061\073\063\103\000\006\000\153\122\111\124\064\000\163"
+ "\007\000\033\133\061\073\064\103\000\006\000\153\122\111\124"
+ "\065\000\163\007\000\033\133\061\073\065\103\000\006\000\153"
+ "\122\111\124\066\000\163\007\000\033\133\061\073\066\103\000"
+ "\006\000\153\122\111\124\067\000\163\007\000\033\133\061\073"
+ "\067\103\000\004\000\153\125\120\000\163\007\000\033\133\061"
+ "\073\062\101\000\005\000\153\125\120\063\000\163\007\000\033"
+ "\133\061\073\063\101\000\005\000\153\125\120\064\000\163\007"
+ "\000\033\133\061\073\064\101\000\005\000\153\125\120\065\000"
+ "\163\007\000\033\133\061\073\065\101\000\005\000\153\125\120"
+ "\066\000\163\007\000\033\133\061\073\066\101\000\005\000\153"
+ "\125\120\067\000\163\007\000\033\133\061\073\067\101\000\005"
+ "\000\153\104\103\063\000\163\007\000\033\133\063\073\063\176"
+ "\000\005\000\153\104\103\064\000\163\007\000\033\133\063\073"
+ "\064\176\000\005\000\153\104\103\065\000\163\007\000\033\133"
+ "\063\073\065\176\000\005\000\153\104\103\066\000\163\007\000"
+ "\033\133\063\073\066\176\000\005\000\153\104\103\067\000\163"
+ "\007\000\033\133\063\073\067\176\000\006\000\153\105\116\104"
+ "\063\000\163\007\000\033\133\061\073\063\106\000\006\000\153"
+ "\105\116\104\064\000\163\007\000\033\133\061\073\064\106\000"
+ "\006\000\153\105\116\104\065\000\163\007\000\033\133\061\073"
+ "\065\106\000\006\000\153\105\116\104\066\000\163\007\000\033"
+ "\133\061\073\066\106\000\006\000\153\105\116\104\067\000\163"
+ "\007\000\033\133\061\073\067\106\000\006\000\153\110\117\115"
+ "\063\000\163\007\000\033\133\061\073\063\110\000\006\000\153"
+ "\110\117\115\064\000\163\007\000\033\133\061\073\064\110\000"
+ "\006\000\153\110\117\115\065\000\163\007\000\033\133\061\073"
+ "\065\110\000\006\000\153\110\117\115\066\000\163\007\000\033"
+ "\133\061\073\066\110\000\006\000\153\110\117\115\067\000\163"
+ "\007\000\033\133\061\073\067\110\000\005\000\153\111\103\063"
+ "\000\163\007\000\033\133\062\073\063\176\000\005\000\153\111"
+ "\103\064\000\163\007\000\033\133\062\073\064\176\000\005\000"
+ "\153\111\103\065\000\163\007\000\033\133\062\073\065\176\000"
+ "\005\000\153\111\103\066\000\163\007\000\033\133\062\073\066"
+ "\176\000\005\000\153\111\103\067\000\163\007\000\033\133\062"
+ "\073\067\176\000\006\000\153\116\130\124\063\000\163\007\000"
+ "\033\133\066\073\063\176\000\006\000\153\116\130\124\064\000"
+ "\163\007\000\033\133\066\073\064\176\000\006\000\153\116\130"
+ "\124\065\000\163\007\000\033\133\066\073\065\176\000\006\000"
+ "\153\116\130\124\066\000\163\007\000\033\133\066\073\066\176"
+ "\000\006\000\153\116\130\124\067\000\163\007\000\033\133\066"
+ "\073\067\176\000\006\000\153\120\122\126\063\000\163\007\000"
+ "\033\133\065\073\063\176\000\006\000\153\120\122\126\064\000"
+ "\163\007\000\033\133\065\073\064\176\000\006\000\153\120\122"
+ "\126\065\000\163\007\000\033\133\065\073\065\176\000\006\000"
+ "\153\120\122\126\066\000\163\007\000\033\133\065\073\066\176"
+ "\000\006\000\153\120\122\126\067\000\163\007\000\033\133\065"
+ "\073\067\176\000\003\000\103\162\000\163\007\000\033\135\061"
+ "\061\062\007\000\003\000\103\163\000\163\014\000\033\135\061"
+ "\062\073\045\160\061\045\163\007\000\003\000\115\163\000\163"
+ "\022\000\033\135\065\062\073\045\160\061\045\163\073\045\160"
+ "\062\045\163\007\000\003\000\123\145\000\163\006\000\033\133"
+ "\062\040\161\000\003\000\123\163\000\163\012\000\033\133\045"
+ "\160\061\045\144\040\161\000\003\000\142\163\000\146\001\003"
+ "\000\101\130\000\146\001\003\000\130\124\000\146\001\005\000"
+ "\155\145\155\154\000\163\003\000\033\154\000\005\000\155\145"
+ "\155\165\000\163\003\000\033\155\000\003\000\105\063\000\163"
+ "\005\000\033\133\063\112\000",
+ 3472
+ }
+};
Index: contrib/mg/configure
===================================================================
--- /dev/null
+++ contrib/mg/configure
@@ -0,0 +1,594 @@
+#!/bin/sh
+
+# This configure script written by Brian Callahan <bcallah@openbsd.org>
+# and released into the Public Domain.
+
+cccheck() {
+ if [ ! -z "$CC" ] ; then
+cat << EOF > conftest.c
+int main(void){return 0;}
+EOF
+ $CC -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ ./conftest
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ cc="$CC"
+ return 0
+ else
+ echo "could not build working executables"
+ echo "Please ensure your C compiler is a native compiler"
+ exit 1
+ fi
+ else
+ rm -f conftest conftest.c
+ fi
+ fi
+
+ for compiler in cc clang pcc xlc gcc ; do
+cat << EOF > conftest.c
+int main(void){return 0;}
+EOF
+
+ $compiler -o conftest conftest.c > /dev/null 2>&1
+
+ if [ $? -eq 0 ] ; then
+ ./conftest
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ cc="$compiler"
+ return 0
+ else
+ echo "could not build working executables"
+ echo "Please ensure your C compiler is a native compiler"
+ exit 1
+ fi
+ else
+ rm -f conftest conftest.c
+ fi
+ done
+ return 1
+}
+
+defaultcflagscheck() {
+ cat << EOF > conftest.c
+int main(void){return 0;}
+EOF
+ $cc $cflags -g -O2 -o conftest.o -c conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.o conftest.c
+ return 1
+ else
+ rm -f conftest conftest.o conftest.c
+ return 0
+ fi
+}
+
+fparselncheck() {
+ cat << EOF > conftest.c
+#include <stdio.h>
+#if defined(__APPLE__)
+#include "apple.h"
+#elif defined(__linux__)
+#include "util.h"
+#elif defined(__FreeBSD__)
+#include <libutil.h>
+#else
+#include <util.h>
+#endif
+int main(void){fparseln(NULL,NULL,NULL,NULL,0);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c $libs > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+fstatatcheck() {
+ cat << EOF > conftest.c
+#undef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+int main(void){fstatat(0, NULL, NULL, 0);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+futimenscheck() {
+ cat << EOF > conftest.c
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+int main(void){futimens(0, NULL);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+getlinecheck() {
+ cat << EOF > conftest.c
+#include <stdio.h>
+int main(void){getline(NULL,NULL,NULL);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+pledgecheck() {
+ cat << EOF > conftest.c
+#include <unistd.h>
+int main(void){pledge(NULL,NULL);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+reallocarraycheck() {
+ cat << EOF > conftest.c
+#include <stdlib.h>
+int main(void){reallocarray(NULL, 0, 0);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+strlcatcheck() {
+ cat << EOF > conftest.c
+#include <string.h>
+int main(void){strlcat(NULL,NULL,0);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+strlcpycheck() {
+ cat << EOF > conftest.c
+#include <string.h>
+int main(void){strlcpy(NULL,NULL,0);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+strndupcheck() {
+ cat << EOF > conftest.c
+#include <string.h>
+int main(void){strndup(NULL,0);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+strtonumcheck() {
+ cat << EOF > conftest.c
+#include <stdlib.h>
+int main(void){strtonum(NULL, 0, 0, NULL);return 0;}
+EOF
+ $cc $tflags -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.c
+ return 0
+ else
+ rm -f conftest conftest.c
+ return 1
+ fi
+}
+
+wflagcheck() {
+ cat << EOF > conftest.c
+int main(void){return 0;}
+EOF
+ $cc $cflags -w -o conftest conftest.c > /dev/null 2>&1
+ if [ $? -eq 0 ] ; then
+ rm -f conftest conftest.o conftest.c
+ return 1
+ else
+ rm -f conftest conftest.o conftest.c
+ return 0
+ fi
+}
+
+# Option variables
+if [ ! -z "$PREFIX" ] ; then
+ prefix="$PREFIX"
+else
+ prefix="/usr/local"
+fi
+
+mandir="$prefix/man"
+
+prog="mg"
+log=0
+static=0
+terminfo=0
+
+# Options
+for opt
+do
+ case "$opt" in
+ --prefix=*)
+ prefix=`echo $opt | cut -d '=' -f 2`
+ ;;
+ --mandir=*)
+ mandir=`echo $opt | cut -d '=' -f 2`
+ ;;
+ --disable-logging|--enable-logging)
+ if [ "x$opt" = "x--enable-logging" ] ; then
+ log=1
+ else
+ log=0
+ fi
+ ;;
+ --disable-static|--enable-static)
+ if [ "x$opt" = "x--enable-static" ] ; then
+ static=1
+ else
+ static=0
+ fi
+ ;;
+ --with-builtin-curses|--without-builtin-curses)
+ if [ "x$opt" = "x--with-builtin-curses" ] ; then
+ terminfo=1
+ else
+ terminfo=0
+ fi
+ ;;
+ --help|-h)
+ echo "Usage: configure [options]"
+ echo ""
+ echo "Options:"
+ printf " --help or -h "
+ echo "Display this help message"
+ printf " --prefix=PREFIX "
+ echo "Top level install directory is PREFIX [$prefix]"
+ printf " --mandir=MANDIR "
+ echo "Manual pages are installed to MANDIR [$mandir]"
+ printf " --enable-logging "
+ echo "Enable run-time debugging [default=no]"
+ printf " --enable-static "
+ echo "Statically link executables [default=no]"
+ printf " --with-builtin-curses "
+ echo "Compile with builtin curses library [default=no]"
+ exit 1
+ ;;
+ *)
+ ;;
+ esac
+done
+
+if [ ! -z "$CFLAGS" ] ; then
+ cflags="$CFLAGS -DREGEX"
+else
+ cflags="-DREGEX"
+fi
+
+if [ ! -z "$LDFLAGS" ] ; then
+ ldflags="$LDFLAGS "
+else
+ ldflags=""
+fi
+
+if [ $static -ne 0 ] ; then
+ ldflags="${ldflags}-static"
+fi
+
+printf "checking for C compiler... "
+cccheck
+if [ $? -ne 0 ] ; then
+ echo "not found"
+ echo "Please install a C compiler and re-run configure."
+ exit 1
+else
+ echo "$cc"
+fi
+
+if [ "x$cflags" = "x-DREGEX" ] ; then
+ printf "checking if the compiler accepts -g -O2... "
+ defaultcflagscheck
+ if [ $? -eq 0 ] ; then
+ echo "no"
+ else
+ cflags="-g -O2 $cflags"
+ echo "yes"
+ fi
+fi
+
+printf "checking for -w compiler flag... "
+wflagcheck
+if [ $? -eq 0 ] ; then
+ echo "no"
+else
+ cflags="$cflags -w"
+ echo "yes"
+fi
+
+printf "checking for OS... "
+
+if [ $terminfo -eq 0 ] ; then
+ libs="-lncursesw"
+else
+ libs=""
+fi
+
+os=`uname -s`
+echo "$os"
+
+case "x$os" in
+ "xLinux"|"xCYGWIN"*)
+ cflags="$cflags -D_GNU_SOURCE -D__dead=\"__attribute__((__noreturn__))\" -Dst_mtimespec=st_mtim"
+ libs="$libs -lutil"
+ ;;
+ "xDarwin")
+ cflags="$cflags -DMSG_NOSIGNAL=SO_NOSIGPIPE -DLOGIN_NAME_MAX=MAXLOGNAME"
+ if [ $terminfo -eq 0 ] ; then
+ libs="-lncurses -lutil"
+ else
+ libs="-lutil"
+ fi
+ ;;
+ "xFreeBSD")
+ cflags="$cflags -D__dead=__dead2 -DLOGIN_NAME_MAX=MAXLOGNAME"
+ libs="$libs -lutil"
+ ;;
+ "xOpenBSD")
+ libs="$libs -lutil"
+ ;;
+ "xNetBSD")
+ cflags="$cflags -D_OPENBSD_SOURCE"
+ if [ $terminfo -eq 0 ] ; then
+ libs="-lcurses -lutil"
+ else
+ libs="-lutil"
+ fi
+ ;;
+ "xDragonFly")
+ cflags="$cflags -D__dead=__dead2 -DLOGIN_NAME_MAX=MAXLOGNAME"
+ libs="$libs -lutil"
+ ;;
+esac
+
+cat << EOF > config.h
+/* This file generated automatically by configure. */
+
+#include "common.h"
+#if defined(__linux__) || defined(__CYGWIN__)
+#include "linux.h"
+#elif defined(__APPLE__)
+#include "apple.h"
+#elif defined(__FreeBSD__)
+#include "freebsd.h"
+#elif defined(__NetBSD__)
+#include "netbsd.h"
+#elif defined(__DragonFly__)
+#include "dragonfly.h"
+#endif
+
+EOF
+
+printf "checking for fparseln... "
+fparselncheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_FPARSELN" >> config.h
+ echo "yes"
+else
+ echo "extern char *fparseln(FILE *, size_t *, size_t *, const char[3], int);" >> config.h
+ echo "no"
+fi
+
+printf "checking for fstatat... "
+fstatatcheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_FSTATAT" >> config.h
+ echo "yes"
+else
+ echo "extern int fstatat(int, const char *, struct stat *, int);" >> config.h
+ echo "no"
+fi
+
+printf "checking for futimens... "
+futimenscheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_FUTIMENS" >> config.h
+ echo "yes"
+else
+ echo "extern int futimens(int, const struct timespec[2]);" >> config.h
+ echo "no"
+fi
+
+printf "checking for getline... "
+getlinecheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_GETLINE" >> config.h
+ echo "yes"
+else
+ echo "extern ssize_t getline(char **, size_t *, FILE *);" >> config.h
+ echo "no"
+fi
+
+printf "checking for pledge... "
+pledgecheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_PLEDGE" >> config.h
+ echo "yes"
+else
+ echo "no"
+fi
+
+printf "checking for reallocarray... "
+reallocarraycheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_REALLOCARRAY" >> config.h
+ echo "yes"
+else
+ echo "extern void *reallocarray(void *, size_t, size_t);" >> config.h
+ echo "no"
+fi
+
+printf "checking for strlcat... "
+strlcatcheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_STRLCAT" >> config.h
+ echo "yes"
+else
+ echo "extern size_t strlcat(char *, const char *, size_t);" >> config.h
+ echo "no"
+fi
+
+printf "checking for strlcpy... "
+strlcpycheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_STRLCPY" >> config.h
+ echo "yes"
+else
+ echo "extern size_t strlcpy(char *, const char *, size_t);" >> config.h
+ echo "no"
+fi
+
+printf "checking for strndup... "
+strndupcheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_STRNDUP" >> config.h
+ echo "yes"
+else
+ echo "extern char *strndup(const char *, size_t);" >> config.h
+ echo "no"
+fi
+
+printf "checking for strtonum... "
+strtonumcheck
+if [ $? -eq 0 ] ; then
+ echo "#define HAVE_STRTONUM" >> config.h
+ echo "yes"
+else
+ echo "extern long long strtonum(const char *, long long, long long, const char **);" >> config.h
+ echo "no"
+fi
+
+printf "creating Makefile... "
+cat << EOF > Makefile
+# This Makefile automatically generated by configure.
+.POSIX:
+
+CC = $cc
+CFLAGS = $cflags
+EOF
+
+if [ ! -z "$ldflags" ] ; then
+cat << EOF >> Makefile
+LDFLAGS = $ldflags
+EOF
+fi
+
+cat << EOF >> Makefile
+PREFIX = $prefix
+MANDIR = $mandir
+
+PROG = $prog
+OBJS = autoexec.o basic.o bell.o buffer.o cinfo.o dir.o display.o \\
+ echo.o extend.o file.o fileio.o funmap.o help.o kbd.o keymap.o \\
+ line.o macro.o main.o match.o modes.o paragraph.o re_search.o \\
+ region.o search.o spawn.o tty.o ttyio.o ttykbd.o undo.o util.o \\
+ version.o window.o word.o yank.o cmode.o cscope.o dired.o \\
+ grep.o tags.o fparseln.o fstatat.o futimens.o getline.o \\
+ reallocarray.o strlcat.o strlcpy.o strndup.o strtonum.o \\
+ interpreter.o extensions.o
+
+EOF
+
+if [ $terminfo -ne 0 ] ; then
+cat << EOF >> Makefile
+OBJS += term.o ti.o setupterm.o curterm.o tparm.o tputs.o \\
+ compile.o hash.o termcap.o mi_vector_hash.o cdbr.o
+
+EOF
+sed -i.bak 's,<term.h>,"terminfo_term.h",g' display.c echo.c spawn.c tty.c \
+ ttyio.c ttykbd.c
+else
+sed -i.bak 's,"terminfo_term.h",<term.h>,g' display.c echo.c spawn.c tty.c \
+ ttyio.c ttykbd.c
+fi
+
+if [ $log -ne 0 ] ; then
+cat << EOF >> Makefile
+OBJS += log.o
+
+EOF
+fi
+
+cat << EOF >> Makefile
+all: \${PROG}
+
+\${PROG}: \${OBJS}
+ \${CC} \${LDFLAGS} -o \${PROG} \${OBJS} $libs
+
+install:
+ install -d \${DESTDIR}\${PREFIX}/bin
+ install -d \${DESTDIR}\${MANDIR}/man1
+ install -c -s -m 755 \${PROG} \${DESTDIR}\${PREFIX}/bin
+ install -c -m 644 mg.1 \${DESTDIR}\${MANDIR}/man1/\${PROG}.1
+
+test:
+ echo "No tests"
+
+clean:
+ rm -f \${PROG} \${OBJS}
+
+distclean: clean
+ rm -f Makefile config.h *.bak
+EOF
+echo "done"
Index: contrib/mg/cscope.c
===================================================================
--- /dev/null
+++ contrib/mg/cscope.c
@@ -0,0 +1,611 @@
+/* $OpenBSD: cscope.c,v 1.20 2021/03/01 10:51:14 lum Exp $ */
+
+/*
+ * This file is in the public domain.
+ *
+ * Author: Sunil Nimmagadda <sunil@openbsd.org>
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+
+#define CSSYMBOL 0
+#define CSDEFINITION 1
+#define CSCALLEDFUNCS 2
+#define CSCALLERFUNCS 3
+#define CSTEXT 4
+#define CSEGREP 6
+#define CSFINDFILE 7
+#define CSINCLUDES 8
+
+struct cstokens {
+ const char *fname;
+ const char *function;
+ const char *lineno;
+ const char *pattern;
+};
+
+struct csmatch {
+ TAILQ_ENTRY(csmatch) entry;
+ int lineno;
+};
+
+struct csrecord {
+ TAILQ_ENTRY(csrecord) entry;
+ char *filename;
+ TAILQ_HEAD(matches, csmatch) matches;
+};
+
+static TAILQ_HEAD(csrecords, csrecord) csrecords = TAILQ_HEAD_INITIALIZER(csrecords);
+static struct csrecord *addentryr;
+static struct csrecord *currecord;
+static struct csmatch *curmatch;
+static const char *addentryfn;
+static const char *csprompt[] = {
+ "Find this symbol: ",
+ "Find this global definition: ",
+ "Find functions called by this function: ",
+ "Find functions calling this function: ",
+ "Find this text string: ",
+ "Change this text string: ",
+ "Find this egrep pattern: ",
+ "Find this file: ",
+ "Find files #including this file: "
+};
+
+static int addentry(struct buffer *, char *);
+static void csflush(void);
+static int do_cscope(int);
+static int csexists(const char *);
+static int getattr(char *, struct cstokens *);
+static int jumptomatch(void);
+static void prettyprint(struct buffer *, struct cstokens *);
+static const char *ltrim(const char *);
+
+/*
+ * Find this symbol. Bound to C-c s s
+ */
+/* ARGSUSED */
+int
+cssymbol(int f, int n)
+{
+ return (do_cscope(CSSYMBOL));
+}
+
+/*
+ * Find this global definition. Bound to C-c s d
+ */
+/* ARGSUSED */int
+csdefinition(int f, int n)
+{
+ return (do_cscope(CSDEFINITION));
+}
+
+/*
+ * Find functions called by this function. Bound to C-c s l
+ */
+/* ARGSUSED */
+int
+csfuncalled(int f, int n)
+{
+ return (do_cscope(CSCALLEDFUNCS));
+}
+
+/*
+ * Find functions calling this function. Bound to C-c s c
+ */
+/* ARGSUSED */
+int
+cscallerfuncs(int f, int n)
+{
+ return (do_cscope(CSCALLERFUNCS));
+}
+
+/*
+ * Find this text. Bound to C-c s t
+ */
+/* ARGSUSED */
+int
+csfindtext(int f, int n)
+{
+ return (do_cscope(CSTEXT));
+}
+
+/*
+ * Find this egrep pattern. Bound to C-c s e
+ */
+/* ARGSUSED */
+int
+csegrep(int f, int n)
+{
+ return (do_cscope(CSEGREP));
+}
+
+/*
+ * Find this file. Bound to C-c s f
+ */
+/* ARGSUSED */
+int
+csfindfile(int f, int n)
+{
+ return (do_cscope(CSFINDFILE));
+}
+
+/*
+ * Find files #including this file. Bound to C-c s i
+ */
+/* ARGSUSED */
+int
+csfindinc(int f, int n)
+{
+ return (do_cscope(CSINCLUDES));
+}
+
+/*
+ * Create list of files to index in the given directory
+ * using cscope-indexer.
+ */
+/* ARGSUSED */
+int
+cscreatelist(int f, int n)
+{
+ struct buffer *bp;
+ struct stat sb;
+ FILE *fpipe;
+ char dir[NFILEN], cmd[BUFSIZ], title[BUFSIZ], *line, *bufp;
+ size_t sz;
+ ssize_t len;
+ int clen;
+
+ line = NULL;
+ sz = 0;
+
+ if (getbufcwd(dir, sizeof(dir)) == FALSE)
+ dir[0] = '\0';
+
+ bufp = eread("Index files in directory: ", dir,
+ sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL);
+
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+
+ if (stat(dir, &sb) == -1)
+ return(dobeep_msgs("stat: %s", strerror(errno)));
+ else if (S_ISDIR(sb.st_mode) == 0)
+ return(dobeep_msgs("%s: Not a directory", dir));
+
+ if (csexists("cscope-indexer") == FALSE)
+ return(dobeep_msg("no such file or directory, cscope-indexer"));
+
+ clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir);
+ if (clen < 0 || clen >= sizeof(cmd))
+ return (FALSE);
+
+ if ((fpipe = popen(cmd, "r")) == NULL)
+ return(dobeep_msg("problem opening pipe"));
+
+ bp = bfind("*cscope*", TRUE);
+ if (bclear(bp) != TRUE) {
+ pclose(fpipe);
+ return (FALSE);
+ }
+ bp->b_flag |= BFREADONLY;
+
+ clen = snprintf(title, sizeof(title), "%s%s",
+ "Creating cscope file list 'cscope.files' in: ", dir);
+ if (clen < 0 || clen >= sizeof(title)) {
+ pclose(fpipe);
+ return (FALSE);
+ }
+ addline(bp, title);
+ addline(bp, "");
+ while ((len = getline(&line, &sz, fpipe)) != -1) {
+ if (line[len - 1] == *bp->b_nlchr)
+ line[len - 1] = '\0';
+ addline(bp, line);
+ }
+ free(line);
+ if (ferror(fpipe))
+ ewprintf("Problem reading pipe");
+ pclose(fpipe);
+ return (popbuftop(bp, WNONE));
+}
+
+/*
+ * Next Symbol. Bound to C-c s n
+ */
+/* ARGSUSED */
+int
+csnextmatch(int f, int n)
+{
+ struct csrecord *r;
+ struct csmatch *m;
+
+ if (curmatch == NULL) {
+ if ((r = TAILQ_FIRST(&csrecords)) == NULL)
+ return(dobeep_msg("The *cscope* buffer does "
+ "not exist yet"));
+
+ currecord = r;
+ curmatch = TAILQ_FIRST(&r->matches);
+ } else {
+ m = TAILQ_NEXT(curmatch, entry);
+ if (m == NULL) {
+ r = TAILQ_NEXT(currecord, entry);
+ if (r == NULL) {
+ return(dobeep_msg("The end of *cscope* buffer "
+ "has been reached"));
+ } else {
+ currecord = r;
+ curmatch = TAILQ_FIRST(&currecord->matches);
+ }
+ } else
+ curmatch = m;
+ }
+ return (jumptomatch());
+}
+
+/*
+ * Previous Symbol. Bound to C-c s p
+ */
+/* ARGSUSED */
+int
+csprevmatch(int f, int n)
+{
+ struct csmatch *m;
+ struct csrecord *r;
+
+ if (curmatch == NULL)
+ return (FALSE);
+ else {
+ m = TAILQ_PREV(curmatch, matches, entry);
+ if (m)
+ curmatch = m;
+ else {
+ r = TAILQ_PREV(currecord, csrecords, entry);
+ if (r == NULL) {
+ return(dobeep_msg("The beginning of *cscope* "
+ "buffer has been reached"));
+ } else {
+ currecord = r;
+ curmatch = TAILQ_LAST(&currecord->matches,
+ matches);
+ }
+ }
+ }
+ return (jumptomatch());
+}
+
+/*
+ * Next file.
+ */
+int
+csnextfile(int f, int n)
+{
+ struct csrecord *r;
+
+ if (curmatch == NULL) {
+ if ((r = TAILQ_FIRST(&csrecords)) == NULL)
+ return(dobeep_msg("The *cscope* buffer does not "
+ "exist yet"));
+ } else {
+ if ((r = TAILQ_NEXT(currecord, entry)) == NULL)
+ return(dobeep_msg("The end of *cscope* buffer has "
+ "been reached"));
+ }
+ currecord = r;
+ curmatch = TAILQ_FIRST(&currecord->matches);
+ return (jumptomatch());
+}
+
+/*
+ * Previous file.
+ */
+int
+csprevfile(int f, int n)
+{
+ struct csrecord *r;
+
+ if (curmatch == NULL) {
+ if ((r = TAILQ_FIRST(&csrecords)) == NULL)
+ return(dobeep_msg("The *cscope* buffer does not"
+ "exist yet"));
+ } else {
+ if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL)
+ return(dobeep_msg("The beginning of *cscope* buffer "
+ "has been reached"));
+ }
+ currecord = r;
+ curmatch = TAILQ_FIRST(&currecord->matches);
+ return (jumptomatch());
+}
+
+/*
+ * The current symbol location is extracted from currecord->filename and
+ * curmatch->lineno. Load the file similar to filevisit and goto the
+ * lineno recorded.
+ */
+int
+jumptomatch(void)
+{
+ struct buffer *bp;
+ char *adjf;
+
+ if (curmatch == NULL || currecord == NULL)
+ return (FALSE);
+ adjf = adjustname(currecord->filename, TRUE);
+ if (adjf == NULL)
+ return (FALSE);
+ if ((bp = findbuffer(adjf)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFFULL) != TRUE)
+ return (FALSE);
+ if (bp->b_fname[0] == '\0') {
+ if (readin(adjf) != TRUE)
+ killbuffer(bp);
+ }
+ gotoline(FFARG, curmatch->lineno);
+ return (TRUE);
+}
+
+/*
+ * Ask for the symbol, construct cscope commandline with the symbol
+ * and passed in index. Popen cscope, read the output into *cscope*
+ * buffer and pop it.
+ */
+int
+do_cscope(int i)
+{
+ struct buffer *bp;
+ FILE *fpipe;
+ char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ];
+ char *p, *buf;
+ int clen, nores = 0;
+ size_t sz;
+ ssize_t len;
+
+ buf = NULL;
+ sz = 0;
+
+ /* If current buffer isn't a source file just return */
+ if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0)
+ return(dobeep_msg("C-c s not defined"));
+
+ if (curtoken(0, 1, pattern) == FALSE)
+ return (FALSE);
+ p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]);
+ if (p == NULL)
+ return (ABORT);
+ else if (p[0] == '\0')
+ return (FALSE);
+
+ if (csexists("cscope") == FALSE)
+ return(dobeep_msg("no such file or directory, cscope"));
+
+ csflush();
+ clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null",
+ i, pattern);
+ if (clen < 0 || clen >= sizeof(cmd))
+ return (FALSE);
+
+ if ((fpipe = popen(cmd, "r")) == NULL)
+ return(dobeep_msg("problem opening pipe"));
+
+ bp = bfind("*cscope*", TRUE);
+ if (bclear(bp) != TRUE) {
+ pclose(fpipe);
+ return (FALSE);
+ }
+ bp->b_flag |= BFREADONLY;
+
+ clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern);
+ if (clen < 0 || clen >= sizeof(title)) {
+ pclose(fpipe);
+ return (FALSE);
+ }
+ addline(bp, title);
+ addline(bp, "");
+ addline(bp, "-------------------------------------------------------------------------------");
+ while ((len = getline(&buf, &sz, fpipe)) != -1) {
+ if (buf[len - 1] == *bp->b_nlchr)
+ buf[len - 1] = '\0';
+ if (addentry(bp, buf) != TRUE) {
+ free(buf);
+ return (FALSE);
+ }
+ nores = 1;
+ }
+ free(buf);
+ if (ferror(fpipe))
+ ewprintf("Problem reading pipe");
+ pclose(fpipe);
+ addline(bp, "-------------------------------------------------------------------------------");
+ if (nores == 0)
+ ewprintf("No matches were found.");
+ return (popbuftop(bp, WNONE));
+}
+
+/*
+ * For each line read from cscope output, extract the tokens,
+ * add them to list and pretty print a line in *cscope* buffer.
+ */
+int
+addentry(struct buffer *bp, char *csline)
+{
+ struct csrecord *r;
+ struct csmatch *m;
+ struct cstokens t;
+ int lineno;
+ char buf[BUFSIZ];
+ const char *errstr;
+
+ r = NULL;
+ if (getattr(csline, &t) == FALSE)
+ return (FALSE);
+
+ lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr);
+ if (errstr)
+ return (FALSE);
+
+ if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) {
+ if ((r = malloc(sizeof(struct csrecord))) == NULL)
+ return (FALSE);
+ addentryr = r;
+ if ((r->filename = strndup(t.fname, NFILEN)) == NULL)
+ goto cleanup;
+ addentryfn = r->filename;
+ TAILQ_INIT(&r->matches);
+ if ((m = malloc(sizeof(struct csmatch))) == NULL)
+ goto cleanup;
+ m->lineno = lineno;
+ TAILQ_INSERT_TAIL(&r->matches, m, entry);
+ TAILQ_INSERT_TAIL(&csrecords, r, entry);
+ addline(bp, "");
+ if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0)
+ goto cleanup;
+ addline(bp, buf);
+ } else {
+ if ((m = malloc(sizeof(struct csmatch))) == NULL)
+ goto cleanup;
+ m->lineno = lineno;
+ TAILQ_INSERT_TAIL(&addentryr->matches, m, entry);
+ }
+ prettyprint(bp, &t);
+ return (TRUE);
+cleanup:
+ free(r);
+ return (FALSE);
+}
+
+/*
+ * Cscope line: <filename> <function> <lineno> <pattern>
+ */
+int
+getattr(char *line, struct cstokens *t)
+{
+ char *p;
+
+ if ((p = strchr(line, ' ')) == NULL)
+ return (FALSE);
+ *p++ = '\0';
+ t->fname = line;
+ line = p;
+
+ if ((p = strchr(line, ' ')) == NULL)
+ return (FALSE);
+ *p++ = '\0';
+ t->function = line;
+ line = p;
+
+ if ((p = strchr(line, ' ')) == NULL)
+ return (FALSE);
+ *p++ = '\0';
+ t->lineno = line;
+
+ if (*p == '\0')
+ return (FALSE);
+ t->pattern = p;
+
+ return (TRUE);
+}
+
+void
+prettyprint(struct buffer *bp, struct cstokens *t)
+{
+ char buf[BUFSIZ];
+
+ if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s",
+ t->function, t->lineno, ltrim(t->pattern)) < 0)
+ return;
+ addline(bp, buf);
+}
+
+const char *
+ltrim(const char *s)
+{
+ while (isblank((unsigned char)*s))
+ s++;
+ return s;
+}
+
+void
+csflush(void)
+{
+ struct csrecord *r;
+ struct csmatch *m;
+
+ while ((r = TAILQ_FIRST(&csrecords)) != NULL) {
+ free(r->filename);
+ while ((m = TAILQ_FIRST(&r->matches)) != NULL) {
+ TAILQ_REMOVE(&r->matches, m, entry);
+ free(m);
+ }
+ TAILQ_REMOVE(&csrecords, r, entry);
+ free(r);
+ }
+ addentryr = NULL;
+ addentryfn = NULL;
+ currecord = NULL;
+ curmatch = NULL;
+}
+
+/*
+ * Check if the cmd exists in $PATH. Split on ":" and iterate through
+ * all paths in $PATH.
+ */
+int
+csexists(const char *cmd)
+{
+ char fname[NFILEN], *dir, *path, *pathc, *tmp;
+ int len, dlen;
+
+ /* Special case if prog contains '/' */
+ if (strchr(cmd, '/')) {
+ if (access(cmd, F_OK) == -1)
+ return (FALSE);
+ else
+ return (TRUE);
+ }
+ if ((tmp = getenv("PATH")) == NULL)
+ return (FALSE);
+ if ((pathc = path = strndup(tmp, NFILEN)) == NULL)
+ return(dobeep_msg("out of memory"));
+
+ while ((dir = strsep(&path, ":")) != NULL) {
+ if (*dir == '\0')
+ continue;
+
+ dlen = strlen(dir);
+ while (dlen > 0 && dir[dlen-1] == '/')
+ dir[--dlen] = '\0'; /* strip trailing '/' */
+
+ len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd);
+ if (len < 0 || len >= sizeof(fname)) {
+ (void)dobeep_msg("path too long");
+ goto cleanup;
+ }
+ if(access(fname, F_OK) == 0) {
+ free(pathc);
+ return (TRUE);
+ }
+ }
+cleanup:
+ free(pathc);
+ return (FALSE);
+}
Index: contrib/mg/curterm.c
===================================================================
--- /dev/null
+++ contrib/mg/curterm.c
@@ -0,0 +1,170 @@
+/* $NetBSD: curterm.c,v 1.13 2017/05/04 09:42:23 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <stdio.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#ifndef __arraycount
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+TERMINAL *cur_term;
+
+/*
+ * There is no standard way of getting a list of aliases for the
+ * terminal. However, some applications such as telnet want to know this.
+ * ncurses dumps the terminfo header into an undefined variable ttytype
+ * and these applications then parse it to work out the aliases.
+ * We should do the same for now, until a standard mechanism for getting
+ * the information is available or the need for it goes away.
+ */
+#define NAMESIZE 256
+char ttytype[NAMESIZE];
+
+static const speed_t bauds[] = {
+ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 2400, 4800, 9600,
+ 19200, 38400, 57600, 115200, 230400, 460800, 921600
+};
+
+void
+_ti_setospeed(TERMINAL *term)
+{
+ struct termios termios;
+ speed_t os;
+ size_t i;
+
+ term->_ospeed = 0;
+ if (tcgetattr(term->fildes, &termios) == 0) {
+ os = cfgetospeed(&termios);
+ for (i = 0; i < __arraycount(bauds); i++)
+ if (bauds[i] == os) {
+ term->_ospeed = (short)i;
+ break;
+ }
+ }
+}
+
+TERMINAL *
+set_curterm(TERMINAL *nterm)
+{
+ TERMINAL *oterm;
+ size_t l, n;
+ char *p;
+
+ oterm = cur_term;
+ cur_term = nterm;
+
+ ospeed = 0;
+ if (cur_term == NULL)
+ PC = '\0';
+ else {
+ if (pad_char == NULL)
+ PC = '\0';
+ else
+ PC = *pad_char;
+ _ti_setospeed(nterm);
+ ospeed = nterm->_ospeed;
+
+ p = ttytype;
+ l = sizeof(ttytype);
+ if ((n = strlcpy(p, nterm->name, l)) == strlen(p)) {
+ p += n;
+ l -= n;
+ *p++ = '|';
+ l--;
+ if (nterm->_alias &&
+ (n = strlcpy(p, nterm->_alias, l)) == strlen(p))
+ {
+ p += n;
+ l -= n;
+ *p++ = '|';
+ l--;
+ }
+ if (nterm->desc &&
+ (n = strlcpy(p, nterm->desc, l)) == strlen(p))
+ {
+ p += n;
+ l -= n;
+ *p++ = '|';
+ l--;
+ }
+ p--;
+ }
+ *p = '\0';
+ }
+
+ return oterm;
+}
+
+int
+del_curterm(TERMINAL *oterm)
+{
+
+ if (oterm == NULL)
+ return ERR;
+ free(oterm->_area);
+ free(oterm->strs);
+ free(oterm->nums);
+ free(oterm->flags);
+ free(oterm->_userdefs);
+ free(oterm->_buf);
+ free(oterm);
+ if (oterm == cur_term)
+ cur_term = NULL;
+ return OK;
+}
+
+char *
+termname(void)
+{
+
+ return __UNCONST(cur_term->name);
+}
+
+static const char * nullname = "";
+
+char *
+longname(void)
+{
+
+ if (cur_term->desc == NULL)
+ return __UNCONST(nullname);
+ return __UNCONST(cur_term->desc);
+}
Index: contrib/mg/def.h
===================================================================
--- /dev/null
+++ contrib/mg/def.h
@@ -0,0 +1,788 @@
+/* $OpenBSD: def.h,v 1.176 2021/05/06 14:16:12 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * This file is the general header file for all parts
+ * of the Mg display editor. It contains all of the
+ * general definitions and macros. It also contains some
+ * conditional compilation flags. All of the per-system and
+ * per-terminal definitions are in special header files.
+ */
+
+/*
+ * We open with Mg portable things.
+ */
+#include "config.h"
+
+#include "chrdef.h"
+
+typedef int (*PF)(int, int); /* generally useful type */
+
+/*
+ * Table sizes, etc.
+ */
+#define NFILEN 1024 /* Length, file name. */
+#define NBUFN NFILEN /* Length, buffer name. */
+#define NLINE 256 /* Length, line. */
+#define PBMODES 4 /* modes per buffer */
+#define NPAT 80 /* Length, pattern. */
+#define HUGE 1000 /* A rather large number. */
+#define NSRCH 128 /* Undoable search commands. */
+#define NXNAME 64 /* Length, extended command. */
+#define NKNAME 20 /* Length, key names. */
+#define NTIME 50 /* Length, timestamp string. */
+
+/*
+ * Universal.
+ */
+#define FALSE 0 /* False, no, bad, etc. */
+#define TRUE 1 /* True, yes, good, etc. */
+#define ABORT 2 /* Death, ^G, abort, etc. */
+#define UERROR 3 /* User Error. */
+#define REVERT 4 /* Revert the buffer */
+
+#define KCLEAR 2 /* clear echo area */
+
+/*
+ * These flag bits keep track of
+ * some aspects of the last command. The CFCPCN
+ * flag controls goal column setting. The CFKILL
+ * flag controls the clearing versus appending
+ * of data in the kill buffer.
+ */
+#define CFCPCN 0x0001 /* Last command was C-p or C-n */
+#define CFKILL 0x0002 /* Last command was a kill */
+#define CFINS 0x0004 /* Last command was self-insert */
+
+/*
+ * File I/O.
+ */
+#define FIOSUC 0 /* Success. */
+#define FIOFNF 1 /* File not found. */
+#define FIOEOF 2 /* End of file. */
+#define FIOERR 3 /* Error. */
+#define FIOLONG 4 /* long line partially read */
+#define FIODIR 5 /* File is a directory */
+
+/*
+ * Display colors.
+ */
+#define CNONE 0 /* Unknown color. */
+#define CTEXT 1 /* Text color. */
+#define CMODE 2 /* Mode line color. */
+
+/*
+ * Flags for keyboard invoked functions.
+ */
+#define FFUNIV 1 /* universal argument */
+#define FFNEGARG 2 /* negative only argument */
+#define FFOTHARG 4 /* other argument */
+#define FFARG 7 /* any argument */
+#define FFRAND 8 /* Called by other function */
+
+/*
+ * Flags for "eread".
+ */
+#define EFFUNC 0x0001 /* Autocomplete functions. */
+#define EFBUF 0x0002 /* Autocomplete buffers. */
+#define EFFILE 0x0004 /* " files (maybe someday) */
+#define EFAUTO 0x0007 /* Some autocompletion on */
+#define EFNEW 0x0008 /* New prompt. */
+#define EFCR 0x0010 /* Echo CR at end; last read. */
+#define EFDEF 0x0020 /* buffer contains default args */
+#define EFNUL 0x0040 /* Null Minibuffer OK */
+
+/*
+ * Direction of insert into kill ring
+ */
+#define KNONE 0x00
+#define KFORW 0x01 /* forward insert into kill ring */
+#define KBACK 0x02 /* Backwards insert into kill ring */
+#define KREG 0x04 /* This is a region-based kill */
+
+#define MAX_TOKEN 64
+
+#define BUFSIZE 128 /* Size of line contents in extend.c */
+/*
+ * Previously from sysdef.h
+ */
+typedef int RSIZE; /* Type for file/region sizes */
+typedef short KCHAR; /* Type for internal keystrokes */
+
+/*
+ * This structure holds the starting position
+ * (as a line/offset pair) and the number of characters in a
+ * region of a buffer. This makes passing the specification
+ * of a region around a little bit easier.
+ */
+struct region {
+ struct line *r_linep; /* Origin line address. */
+ int r_offset; /* Origin line offset. */
+ int r_lineno; /* Origin line number */
+ RSIZE r_size; /* Length in characters. */
+};
+
+
+/*
+ * All text is kept in circularly linked
+ * lists of "line" structures. These begin at the
+ * header line (which is the blank line beyond the
+ * end of the buffer). This line is pointed to by
+ * the "buffer" structure. Each line contains the number of
+ * bytes in the line (the "used" size), the size
+ * of the text array, and the text. The end of line
+ * is not stored as a byte; it's implied. Future
+ * additions will include update hints, and a
+ * list of marks into the line.
+ */
+struct line {
+ struct line *l_fp; /* Link to the next line */
+ struct line *l_bp; /* Link to the previous line */
+ int l_size; /* Allocated size */
+ int l_used; /* Used size */
+ char *l_text; /* Content of the line */
+};
+
+/*
+ * The rationale behind these macros is that you
+ * could (with some editing, like changing the type of a line
+ * link from a "struct line *" to a "REFLINE", and fixing the commands
+ * like file reading that break the rules) change the actual
+ * storage representation of lines to use something fancy on
+ * machines with small address spaces.
+ */
+#define lforw(lp) ((lp)->l_fp)
+#define lback(lp) ((lp)->l_bp)
+#define lgetc(lp, n) (CHARMASK((lp)->l_text[(n)]))
+#define lputc(lp, n, c) ((lp)->l_text[(n)]=(c))
+#define llength(lp) ((lp)->l_used)
+#define ltext(lp) ((lp)->l_text)
+
+/*
+ * All repeated structures are kept as linked lists of structures.
+ * All of these start with a LIST structure (except lines, which
+ * have their own abstraction). This will allow for
+ * later conversion to generic list manipulation routines should
+ * I decide to do that. It does mean that there are four extra
+ * bytes per window. I feel that this is an acceptable price,
+ * considering that there are usually only one or two windows.
+ */
+struct list {
+ union {
+ struct mgwin *l_wp;
+ struct buffer *x_bp; /* l_bp is used by struct line */
+ struct list *l_nxt;
+ } l_p;
+ char *l_name;
+};
+
+/*
+ * Usual hack - to keep from uglifying the code with lotsa
+ * references through the union, we #define something for it.
+ */
+#define l_next l_p.l_nxt
+
+/*
+ * There is a window structure allocated for
+ * every active display window. The windows are kept in a
+ * big list, in top to bottom screen order, with the listhead at
+ * "wheadp". Each window contains its own values of dot and mark.
+ * The flag field contains some bits that are set by commands
+ * to guide redisplay; although this is a bit of a compromise in
+ * terms of decoupling, the full blown redisplay is just too
+ * expensive to run for every input character.
+ */
+struct mgwin {
+ struct list w_list; /* List header */
+ struct buffer *w_bufp; /* Buffer displayed in window */
+ struct line *w_linep; /* Top line in the window */
+ struct line *w_dotp; /* Line containing "." */
+ struct line *w_markp; /* Line containing "mark" */
+ int w_doto; /* Byte offset for "." */
+ int w_marko; /* Byte offset for "mark" */
+ int w_toprow; /* Origin 0 top row of window */
+ int w_ntrows; /* # of rows of text in window */
+ int w_frame; /* #lines to reframe by. */
+ char w_rflag; /* Redisplay Flags. */
+ char w_flag; /* Flags. */
+ struct line *w_wrapline;
+ int w_dotline; /* current line number of dot */
+ int w_markline; /* current line number of mark */
+};
+#define w_wndp w_list.l_p.l_wp
+#define w_name w_list.l_name
+
+/*
+ * Window redisplay flags are set by command processors to
+ * tell the display system what has happened to the buffer
+ * mapped by the window. Setting "WFFULL" is always a safe thing
+ * to do, but it may do more work than is necessary. Always try
+ * to set the simplest action that achieves the required update.
+ * Because commands set bits in the "w_flag", update will see
+ * all change flags, and do the most general one.
+ */
+#define WFFRAME 0x01 /* Force reframe. */
+#define WFMOVE 0x02 /* Movement from line to line. */
+#define WFEDIT 0x04 /* Editing within a line. */
+#define WFFULL 0x08 /* Do a full display. */
+#define WFMODE 0x10 /* Update mode line. */
+
+/*
+ * Window flags
+ */
+#define WNONE 0x00 /* No special window options. */
+#define WEPHEM 0x01 /* Window is ephemeral. */
+
+struct undo_rec;
+TAILQ_HEAD(undoq, undo_rec);
+
+/*
+ * Previously from sysdef.h
+ * Only used in struct buffer.
+ */
+struct fileinfo {
+ uid_t fi_uid;
+ gid_t fi_gid;
+ mode_t fi_mode;
+ struct timespec fi_mtime; /* Last modified time */
+};
+
+/*
+ * Text is kept in buffers. A buffer header, described
+ * below, exists for every buffer in the system. The buffers are
+ * kept in a big list, so that commands that search for a buffer by
+ * name can find the buffer header. There is a safe store for the
+ * dot and mark in the header, but this is only valid if the buffer
+ * is not being displayed (that is, if "b_nwnd" is 0). The text for
+ * the buffer is kept in a circularly linked list of lines, with
+ * a pointer to the header line in "b_headp".
+ */
+struct buffer {
+ struct list b_list; /* buffer list pointer */
+ struct buffer *b_altb; /* Link to alternate buffer */
+ struct line *b_dotp; /* Link to "." line structure */
+ struct line *b_markp; /* ditto for mark */
+ struct line *b_headp; /* Link to the header line */
+ struct maps_s *b_modes[PBMODES]; /* buffer modes */
+ int b_doto; /* Offset of "." in above line */
+ int b_marko; /* ditto for the "mark" */
+ short b_nmodes; /* number of non-fundamental modes */
+ char b_nwnd; /* Count of windows on buffer */
+ char b_flag; /* Flags */
+ char b_fname[NFILEN]; /* File name */
+ char b_cwd[NFILEN]; /* working directory */
+ char *b_nlseq; /* Newline sequence of chars */
+ char *b_nlchr; /* 1st newline character */
+ struct fileinfo b_fi; /* File attributes */
+ struct undoq b_undo; /* Undo actions list */
+ struct undo_rec *b_undoptr;
+ int b_dotline; /* Line number of dot */
+ int b_markline; /* Line number of mark */
+ int b_lines; /* Number of lines in file */
+};
+#define b_bufp b_list.l_p.x_bp
+#define b_bname b_list.l_name
+
+/* Some helper macros, in case they ever change to functions */
+#define bfirstlp(buf) (lforw((buf)->b_headp))
+#define blastlp(buf) (lback((buf)->b_headp))
+
+#define BFCHG 0x01 /* Changed. */
+#define BFBAK 0x02 /* Need to make a backup. */
+#ifdef NOTAB
+#define BFNOTAB 0x04 /* no tab mode */
+#endif
+#define BFOVERWRITE 0x08 /* overwrite mode */
+#define BFREADONLY 0x10 /* read only mode */
+#define BFDIRTY 0x20 /* Buffer was modified elsewhere */
+#define BFIGNDIRTY 0x40 /* Ignore modifications */
+#define BFDIREDDEL 0x80 /* Dired has a deleted 'D' file */
+/*
+ * This structure holds information about recent actions for the Undo command.
+ */
+struct undo_rec {
+ TAILQ_ENTRY(undo_rec) next;
+ enum {
+ INSERT = 1,
+ DELETE,
+ BOUNDARY,
+ MODIFIED,
+ DELREG
+ } type;
+ struct region region;
+ int pos;
+ char *content;
+};
+
+/*
+ * Variable structure.
+ */
+struct varentry {
+ SLIST_ENTRY(varentry) entry;
+ char v_buf[BUFSIZE];
+ char *v_name;
+ char *v_vals;
+ int v_count;
+};
+SLIST_HEAD(vhead, varentry);
+
+/*
+ * Previously from ttydef.h
+ */
+#define STANDOUT_GLITCH /* possible standout glitch */
+
+#define putpad(str, num) tputs(str, num, ttputc)
+
+#define KFIRST K00
+#define KLAST K00
+
+/*
+ * Prototypes.
+ */
+
+/* tty.c X */
+void ttinit(void);
+void ttreinit(void);
+void tttidy(void);
+void ttmove(int, int);
+void tteeol(void);
+void tteeop(void);
+void ttbeep(void);
+void ttinsl(int, int, int);
+void ttdell(int, int, int);
+void ttwindow(int, int);
+void ttnowindow(void);
+void ttcolor(int);
+void ttresize(void);
+
+extern volatile sig_atomic_t winch_flag;
+
+/* ttyio.c */
+void ttopen(void);
+int ttraw(void);
+void ttclose(void);
+int ttcooked(void);
+int ttputc(int);
+void ttflush(void);
+int ttgetc(void);
+int ttwait(int);
+int charswaiting(void);
+
+/* dir.c */
+void dirinit(void);
+int changedir(int, int);
+int showcwdir(int, int);
+int getcwdir(char *, size_t);
+int makedir(int, int);
+int do_makedir(char *);
+int ask_makedir(void);
+
+/* dired.c */
+struct buffer *dired_(char *);
+int dired_jump(int, int);
+int do_dired(char *);
+
+/* file.c X */
+int fileinsert(int, int);
+int filevisit(int, int);
+int filevisitalt(int, int);
+int filevisitro(int, int);
+int poptofile(int, int);
+int readin(char *);
+int insertfile(char *, char *, int);
+int filewrite(int, int);
+int filesave(int, int);
+int buffsave(struct buffer *);
+int makebkfile(int, int);
+int writeout(FILE **, struct buffer *, char *);
+void upmodes(struct buffer *);
+size_t xbasename(char *, const char *, size_t);
+int do_filevisitalt(char *);
+
+/* line.c X */
+struct line *lalloc(int);
+int lrealloc(struct line *, int);
+void lfree(struct line *);
+void lchange(int);
+int linsert(int, int);
+int lnewline_at(struct line *, int);
+int lnewline(void);
+int ldelete(RSIZE, int);
+int ldelnewline(void);
+int lreplace(RSIZE, char *);
+char * linetostr(const struct line *);
+int setcasereplace(int, int);
+
+/* yank.c X */
+
+void kdelete(void);
+int kinsert(int, int);
+int kremove(int);
+int kchunk(char *, RSIZE, int);
+int killline(int, int);
+int yank(int, int);
+
+/* window.c X */
+struct mgwin *new_window(struct buffer *);
+int reposition(int, int);
+int redraw(int, int);
+int do_redraw(int, int, int);
+int nextwind(int, int);
+int prevwind(int, int);
+int onlywind(int, int);
+int splitwind(int, int);
+int enlargewind(int, int);
+int shrinkwind(int, int);
+int delwind(int, int);
+
+/* buffer.c */
+int togglereadonly(int, int);
+int togglereadonlyall(int, int);
+struct buffer *bfind(const char *, int);
+int poptobuffer(int, int);
+int killbuffer(struct buffer *);
+int killbuffer_cmd(int, int);
+int savebuffers(int, int);
+int listbuffers(int, int);
+int addlinef(struct buffer *, char *, ...);
+#define addline(bp, text) addlinef(bp, "%s", text)
+int anycb(int);
+int bclear(struct buffer *);
+int showbuffer(struct buffer *, struct mgwin *, int);
+int augbname(char *, const char *, size_t);
+struct mgwin *popbuf(struct buffer *, int);
+int bufferinsert(int, int);
+int usebuffer(int, int);
+int notmodified(int, int);
+int popbuftop(struct buffer *, int);
+int getbufcwd(char *, size_t);
+int checkdirty(struct buffer *);
+int revertbuffer(int, int);
+int dorevert(void);
+int diffbuffer(int, int);
+struct buffer *findbuffer(char *);
+
+/* display.c */
+int vtresize(int, int, int);
+void vtinit(void);
+void vttidy(void);
+void update(int);
+int linenotoggle(int, int);
+int colnotoggle(int, int);
+
+/* echo.c X */
+void eerase(void);
+int eyorn(const char *);
+int eynorr(const char *);
+int eyesno(const char *);
+void ewprintf(const char *fmt, ...);
+char *eread(const char *, char *, size_t, int, ...)
+ __attribute__((__format__ (printf, 1, 5)));
+int getxtra(struct list *, struct list *, int, int);
+void free_file_list(struct list *);
+
+/* fileio.c */
+int ffropen(FILE **, const char *, struct buffer *);
+void ffstat(FILE *, struct buffer *);
+int ffwopen(FILE **, const char *, struct buffer *);
+int ffclose(FILE *, struct buffer *);
+int ffputbuf(FILE *, struct buffer *, int);
+int ffgetline(FILE *, char *, int, int *);
+int fbackupfile(const char *);
+char *adjustname(const char *, int);
+char *startupfile(char *, char *);
+int copy(char *, char *);
+struct list *make_file_list(char *);
+int fisdir(const char *);
+int fchecktime(struct buffer *);
+int fupdstat(struct buffer *);
+int backuptohomedir(int, int);
+int toggleleavetmp(int, int);
+char *expandtilde(const char *);
+
+/* kbd.c X */
+int do_meta(int, int);
+int bsmap(int, int);
+void ungetkey(int);
+int getkey(int);
+int doin(void);
+int rescan(int, int);
+int universal_argument(int, int);
+int digit_argument(int, int);
+int negative_argument(int, int);
+int ask_selfinsert(int, int);
+int selfinsert(int, int);
+int quote(int, int);
+
+/* main.c */
+int ctrlg(int, int);
+int quit(int, int);
+
+/* ttyio.c */
+void panic(char *);
+
+/* cinfo.c */
+char *getkeyname(char *, size_t, int);
+
+/* basic.c */
+int gotobol(int, int);
+int backchar(int, int);
+int gotoeol(int, int);
+int forwchar(int, int);
+int gotobob(int, int);
+int gotoeob(int, int);
+int forwline(int, int);
+int backline(int, int);
+void setgoal(void);
+int getgoal(struct line *);
+int forwpage(int, int);
+int backpage(int, int);
+int forw1page(int, int);
+int back1page(int, int);
+int pagenext(int, int);
+void isetmark(void);
+int setmark(int, int);
+int clearmark(int, int);
+int swapmark(int, int);
+int gotoline(int, int);
+int setlineno(int);
+
+/* util.c X */
+int showcpos(int, int);
+int getcolpos(struct mgwin *);
+int twiddle(int, int);
+int openline(int, int);
+int enewline(int, int);
+int deblank(int, int);
+int justone(int, int);
+int delwhite(int, int);
+int delleadwhite(int, int);
+int deltrailwhite(int, int);
+int lfindent(int, int);
+int indent(int, int);
+int forwdel(int, int);
+int backdel(int, int);
+int space_to_tabstop(int, int);
+int backtoindent(int, int);
+int joinline(int, int);
+
+/* tags.c X */
+int findtag(int, int);
+int poptag(int, int);
+int tagsvisit(int, int);
+int curtoken(int, int, char *);
+
+/* cscope.c */
+int cssymbol(int, int);
+int csdefinition(int, int);
+int csfuncalled(int, int);
+int cscallerfuncs(int, int);
+int csfindtext(int, int);
+int csegrep(int, int);
+int csfindfile(int, int);
+int csfindinc(int, int);
+int csnextfile(int, int);
+int csnextmatch(int, int);
+int csprevfile(int, int);
+int csprevmatch(int, int);
+int cscreatelist(int, int);
+
+/* extend.c X */
+int insert(int, int);
+int bindtokey(int, int);
+int localbind(int, int);
+int redefine_key(int, int);
+int unbindtokey(int, int);
+int localunbind(int, int);
+int extend(int, int);
+int evalexpr(int, int);
+int evalbuffer(int, int);
+int evalfile(int, int);
+int load(const char *);
+int excline(char *, int, int);
+char *skipwhite(char *);
+
+/* help.c X */
+int desckey(int, int);
+int wallchart(int, int);
+int help_help(int, int);
+int apropos_command(int, int);
+
+/* paragraph.c X */
+int gotobop(int, int);
+int gotoeop(int, int);
+int fillpara(int, int);
+int killpara(int, int);
+int fillword(int, int);
+int setfillcol(int, int);
+int markpara(int, int);
+int transposepara(int, int);
+int sentencespace(int, int);
+
+/* word.c X */
+int backword(int, int);
+int forwword(int, int);
+int upperword(int, int);
+int lowerword(int, int);
+int capword(int, int);
+int delfword(int, int);
+int delbword(int, int);
+int inword(void);
+int transposeword(int, int);
+
+/* region.c X */
+int killregion(int, int);
+int copyregion(int, int);
+int lowerregion(int, int);
+int upperregion(int, int);
+int prefixregion(int, int);
+int setprefix(int, int);
+int region_get_data(struct region *, char *, int);
+void region_put_data(const char *, int);
+int markbuffer(int, int);
+int piperegion(int, int);
+int shellcommand(int, int);
+int pipeio(const char * const, char * const[], char * const, int,
+ struct buffer *);
+
+/* search.c X */
+int forwsearch(int, int);
+int backsearch(int, int);
+int searchagain(int, int);
+int forwisearch(int, int);
+int backisearch(int, int);
+int queryrepl(int, int);
+int forwsrch(void);
+int backsrch(void);
+int readpattern(char *);
+
+/* spawn.c X */
+int spawncli(int, int);
+
+/* ttykbd.c X */
+void ttykeymapinit(void);
+void ttykeymaptidy(void);
+
+/* match.c X */
+int showmatch(int, int);
+
+/* version.c X */
+int showversion(int, int);
+
+/* macro.c X */
+int definemacro(int, int);
+int finishmacro(int, int);
+int executemacro(int, int);
+
+/* modes.c X */
+int indentmode(int, int);
+int fillmode(int, int);
+#ifdef NOTAB
+int notabmode(int, int);
+#endif /* NOTAB */
+int overwrite_mode(int, int);
+int set_default_mode(int,int);
+
+#ifdef REGEX
+/* re_search.c X */
+int re_forwsearch(int, int);
+int re_backsearch(int, int);
+int re_searchagain(int, int);
+int re_queryrepl(int, int);
+int re_repl(int, int);
+int replstr(int, int);
+int setcasefold(int, int);
+int delmatchlines(int, int);
+int delnonmatchlines(int, int);
+int cntmatchlines(int, int);
+int cntnonmatchlines(int, int);
+#endif /* REGEX */
+
+/* undo.c X */
+void free_undo_record(struct undo_rec *);
+int undo_dump(int, int);
+int undo_enabled(void);
+int undo_enable(int, int);
+int undo_add_boundary(int, int);
+void undo_add_modified(void);
+int undo_add_insert(struct line *, int, int);
+int undo_add_delete(struct line *, int, int, int);
+int undo_boundary_enable(int, int);
+int undo_add_change(struct line *, int, int);
+int undo(int, int);
+
+/* autoexec.c X */
+int auto_execute(int, int);
+PF *find_autoexec(const char *);
+int add_autoexec(const char *, const char *);
+
+/* cmode.c X */
+int cmode(int, int);
+int cc_brace(int, int);
+int cc_char(int, int);
+int cc_tab(int, int);
+int cc_indent(int, int);
+int cc_lfindent(int, int);
+
+/* grep.c X */
+int next_error(int, int);
+int globalwdtoggle(int, int);
+int compile(int, int);
+
+/* bell.c */
+void bellinit(void);
+int toggleaudiblebell(int, int);
+int togglevisiblebell(int, int);
+int dobeep_num(const char *, int);
+int dobeep_msgs(const char *, const char *);
+int dobeep_msg(const char *);
+void dobeep(void);
+
+/* interpreter.c */
+int foundparen(char *, int, int);
+void cleanup(void);
+
+/*
+ * Externals.
+ */
+extern struct buffer *bheadp;
+extern struct buffer *curbp;
+extern struct mgwin *curwp;
+extern struct mgwin *wheadp;
+extern struct vhead varhead;
+extern int thisflag;
+extern int lastflag;
+extern int curgoal;
+extern int startrow;
+extern int epresf;
+extern int sgarbf;
+extern int mode;
+extern int nrow;
+extern int ncol;
+extern int ttrow;
+extern int ttcol;
+extern int tttop;
+extern int ttbot;
+extern int tthue;
+extern int defb_nmodes;
+extern int defb_flag;
+extern int doaudiblebell;
+extern int dovisiblebell;
+extern int dblspace;
+extern int allbro;
+extern int batch;
+extern char cinfo[];
+extern char *keystrings[];
+extern char pat[NPAT];
+extern char prompt[];
+extern int tceeol;
+extern int tcinsl;
+extern int tcdell;
+extern int rptcount; /* successive invocation count */
+
+/*
+ * Extensions.
+ */
+extern int shownlprompt;
+int togglenewlineprompt(int, int);
Index: contrib/mg/dir.c
===================================================================
--- /dev/null
+++ contrib/mg/dir.c
@@ -0,0 +1,183 @@
+/* $OpenBSD: dir.c,v 1.31 2019/06/28 13:35:02 deraadt Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Name: MG 2a
+ * Directory management functions
+ * Created: Ron Flax (ron@vsedev.vse.com)
+ * Modified for MG 2a by Mic Kaczmarczik 03-Aug-1987
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+
+static char mgcwd[NFILEN];
+
+/*
+ * Initialize anything the directory management routines need.
+ */
+void
+dirinit(void)
+{
+ mgcwd[0] = '\0';
+ if (getcwd(mgcwd, sizeof(mgcwd)) == NULL)
+ ewprintf("Can't get current directory!");
+ if (mgcwd[0] != '\0' && !(mgcwd[0] == '/' && mgcwd[1] == '\0'))
+ (void)strlcat(mgcwd, "/", sizeof(mgcwd));
+}
+
+/*
+ * Change current working directory.
+ */
+/* ARGSUSED */
+int
+changedir(int f, int n)
+{
+ char bufc[NFILEN], *bufp;
+
+ (void)strlcpy(bufc, mgcwd, sizeof(bufc));
+ if ((bufp = eread("Change default directory: ", bufc, NFILEN,
+ EFDEF | EFNEW | EFCR | EFFILE)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ /* Append trailing slash */
+ if (chdir(bufc) == -1) {
+ dobeep();
+ ewprintf("Can't change dir to %s", bufc);
+ return (FALSE);
+ }
+ if ((bufp = getcwd(mgcwd, sizeof(mgcwd))) == NULL) {
+ if (bufc[0] == '/')
+ (void)strlcpy(mgcwd, bufc, sizeof(mgcwd));
+ else
+ (void)strlcat(mgcwd, bufc, sizeof(mgcwd));
+ }
+ if (mgcwd[strlen(mgcwd) - 1] != '/')
+ (void)strlcat(mgcwd, "/", sizeof(mgcwd));
+ ewprintf("Current directory is now %s", mgcwd);
+ return (TRUE);
+}
+
+/*
+ * Show current directory.
+ */
+/* ARGSUSED */
+int
+showcwdir(int f, int n)
+{
+ ewprintf("Current directory: %s", mgcwd);
+ return (TRUE);
+}
+
+int
+getcwdir(char *buf, size_t len)
+{
+ if (strlcpy(buf, mgcwd, len) >= len)
+ return (FALSE);
+
+ return (TRUE);
+}
+
+/* Create the directory and it's parents. */
+/* ARGSUSED */
+int
+makedir(int f, int n)
+{
+ return (ask_makedir());
+}
+
+int
+ask_makedir(void)
+{
+
+ char bufc[NFILEN];
+ char *path;
+
+ if (getbufcwd(bufc, sizeof(bufc)) != TRUE)
+ return (ABORT);
+ if ((path = eread("Make directory: ", bufc, NFILEN,
+ EFDEF | EFNEW | EFCR | EFFILE)) == NULL)
+ return (ABORT);
+ else if (path[0] == '\0')
+ return (FALSE);
+
+ return (do_makedir(path));
+}
+
+int
+do_makedir(char *path)
+{
+ struct stat sb;
+ int finished, ishere;
+ mode_t dir_mode, f_mode, oumask;
+ char *slash;
+
+ if ((path = adjustname(path, TRUE)) == NULL)
+ return (FALSE);
+
+ /* Remove trailing slashes */
+ slash = strrchr(path, '\0');
+ while (--slash > path && *slash == '/')
+ *slash = '\0';
+
+ slash = path;
+
+ oumask = umask(0);
+ f_mode = 0777 & ~oumask;
+ dir_mode = f_mode | S_IWUSR | S_IXUSR;
+
+ for (;;) {
+ slash += strspn(slash, "/");
+ slash += strcspn(slash, "/");
+
+ finished = (*slash == '\0');
+ *slash = '\0';
+
+ ishere = !stat(path, &sb);
+ if (finished && ishere) {
+ dobeep();
+ ewprintf("Cannot create directory %s: file exists",
+ path);
+ return(FALSE);
+ } else if (!finished && ishere && S_ISDIR(sb.st_mode)) {
+ *slash = '/';
+ continue;
+ }
+
+ if (mkdir(path, finished ? f_mode : dir_mode) == 0) {
+ if (f_mode > 0777 && chmod(path, f_mode) == -1) {
+ umask(oumask);
+ return (ABORT);
+ }
+ } else {
+ if (!ishere || !S_ISDIR(sb.st_mode)) {
+ if (!ishere) {
+ dobeep();
+ ewprintf("Creating directory: "
+ "permission denied, %s", path);
+ } else
+ eerase();
+
+ umask(oumask);
+ return (FALSE);
+ }
+ }
+
+ if (finished)
+ break;
+
+ *slash = '/';
+ }
+
+ eerase();
+ umask(oumask);
+ return (TRUE);
+}
Index: contrib/mg/dired.c
===================================================================
--- /dev/null
+++ contrib/mg/dired.c
@@ -0,0 +1,1206 @@
+/* $OpenBSD: dired.c,v 1.100 2021/05/02 14:13:17 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/* dired module for mg 2a
+ * by Robert A. Larson
+ */
+
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+#include "funmap.h"
+#include "kbd.h"
+
+void dired_init(void);
+static int dired(int, int);
+static int d_otherwindow(int, int);
+static int d_undel(int, int);
+static int d_undelbak(int, int);
+static int d_findfile(int, int);
+static int d_ffotherwindow(int, int);
+static int d_expunge(int, int);
+static int d_copy(int, int);
+static int d_del(int, int);
+static int d_rename(int, int);
+static int d_exec(int, struct buffer *, const char *, const char *, ...);
+static int d_shell_command(int, int);
+static int d_create_directory(int, int);
+static int d_makename(struct line *, char *, size_t);
+static int d_warpdot(struct line *, int *);
+static int d_forwpage(int, int);
+static int d_backpage(int, int);
+static int d_forwline(int, int);
+static int d_backline(int, int);
+static int d_killbuffer_cmd(int, int);
+static int d_refreshbuffer(int, int);
+static int d_filevisitalt(int, int);
+static int d_gotofile(int, int);
+static void reaper(int);
+static int gotofile(char*);
+static struct buffer *refreshbuffer(struct buffer *);
+static int createlist(struct buffer *);
+static void redelete(struct buffer *);
+static char *findfname(struct line *, char *);
+
+extern struct keymap_s helpmap, cXmap, metamap;
+
+const char DDELCHAR = 'D';
+
+/*
+ * Structure which holds a linked list of file names marked for
+ * deletion. Used to maintain dired buffer 'state' between refreshes.
+ */
+struct delentry {
+ SLIST_ENTRY(delentry) entry;
+ char *fn;
+};
+SLIST_HEAD(slisthead, delentry) delhead = SLIST_HEAD_INITIALIZER(delhead);
+
+static PF dirednul[] = {
+ setmark, /* ^@ */
+ gotobol, /* ^A */
+ backchar, /* ^B */
+ rescan, /* ^C */
+ d_del, /* ^D */
+ gotoeol, /* ^E */
+ forwchar, /* ^F */
+ ctrlg, /* ^G */
+ NULL, /* ^H */
+};
+
+static PF diredcl[] = {
+ reposition, /* ^L */
+ d_findfile, /* ^M */
+ d_forwline, /* ^N */
+ rescan, /* ^O */
+ d_backline, /* ^P */
+ rescan, /* ^Q */
+ backisearch, /* ^R */
+ forwisearch, /* ^S */
+ rescan, /* ^T */
+ universal_argument, /* ^U */
+ d_forwpage, /* ^V */
+ rescan, /* ^W */
+ NULL /* ^X */
+};
+
+static PF diredcz[] = {
+ spawncli, /* ^Z */
+ NULL, /* esc */
+ rescan, /* ^\ */
+ rescan, /* ^] */
+ rescan, /* ^^ */
+ rescan, /* ^_ */
+ d_forwline, /* SP */
+ d_shell_command, /* ! */
+ rescan, /* " */
+ rescan, /* # */
+ rescan, /* $ */
+ rescan, /* % */
+ rescan, /* & */
+ rescan, /* ' */
+ rescan, /* ( */
+ rescan, /* ) */
+ rescan, /* * */
+ d_create_directory /* + */
+};
+
+static PF direda[] = {
+ d_filevisitalt, /* a */
+ rescan, /* b */
+ d_copy, /* c */
+ d_del, /* d */
+ d_findfile, /* e */
+ d_findfile, /* f */
+ d_refreshbuffer, /* g */
+ rescan, /* h */
+ rescan, /* i */
+ d_gotofile /* j */
+};
+
+static PF diredn[] = {
+ d_forwline, /* n */
+ d_ffotherwindow, /* o */
+ d_backline, /* p */
+ d_killbuffer_cmd, /* q */
+ d_rename, /* r */
+ rescan, /* s */
+ rescan, /* t */
+ d_undel, /* u */
+ rescan, /* v */
+ rescan, /* w */
+ d_expunge /* x */
+};
+
+static PF direddl[] = {
+ d_undelbak /* del */
+};
+
+static PF diredbp[] = {
+ d_backpage /* v */
+};
+
+static PF dirednull[] = {
+ NULL
+};
+
+static struct KEYMAPE (1) d_backpagemap = {
+ 1,
+ 1,
+ rescan,
+ {
+ {
+ 'v', 'v', diredbp, NULL
+ }
+ }
+};
+
+static struct KEYMAPE (7) diredmap = {
+ 7,
+ 7,
+ rescan,
+ {
+ {
+ CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap
+ },
+ {
+ CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap
+ },
+ {
+ CCHR('['), CCHR('['), dirednull, (KEYMAP *) &
+ d_backpagemap
+ },
+ {
+ CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap
+ },
+ {
+ 'a', 'j', direda, NULL
+ },
+ {
+ 'n', 'x', diredn, NULL
+ },
+ {
+ CCHR('?'), CCHR('?'), direddl, NULL
+ },
+ }
+};
+
+void
+dired_init(void)
+{
+ funmap_add(dired, "dired", 1);
+ funmap_add(d_create_directory, "dired-create-directory", 1);
+ funmap_add(d_copy, "dired-do-copy", 1);
+ funmap_add(d_expunge, "dired-do-flagged-delete", 0);
+ funmap_add(d_rename, "dired-do-rename", 1);
+ funmap_add(d_findfile, "dired-find-file", 1);
+ funmap_add(d_ffotherwindow, "dired-find-file-other-window", 1);
+ funmap_add(d_del, "dired-flag-file-deletion", 0);
+ funmap_add(d_gotofile, "dired-goto-file", 1);
+ funmap_add(d_forwline, "dired-next-line", 0);
+ funmap_add(d_otherwindow, "dired-other-window", 0);
+ funmap_add(d_backline, "dired-previous-line", 0);
+ funmap_add(d_refreshbuffer, "dired-revert", 0);
+ funmap_add(d_backpage, "dired-scroll-down", 0);
+ funmap_add(d_forwpage, "dired-scroll-up", 0);
+ funmap_add(d_shell_command, "dired-shell-command", 1);
+ funmap_add(d_undel, "dired-unmark", 0);
+ funmap_add(d_undelbak, "dired-unmark-backward", 0);
+ funmap_add(d_killbuffer_cmd, "quit-window", 0);
+ maps_add((KEYMAP *)&diredmap, "dired");
+ dobindkey(fundamental_map, "dired", "^Xd");
+}
+
+/* ARGSUSED */
+int
+dired(int f, int n)
+{
+ char dname[NFILEN], *bufp, *slash;
+ struct buffer *bp;
+
+ if (curbp->b_fname[0] != '\0') {
+ (void)strlcpy(dname, curbp->b_fname, sizeof(dname));
+ if ((slash = strrchr(dname, '/')) != NULL) {
+ *(slash + 1) = '\0';
+ }
+ } else {
+ if (getcwd(dname, sizeof(dname)) == NULL)
+ dname[0] = '\0';
+ }
+
+ if ((bufp = eread("Dired (directory): ", dname, NFILEN,
+ EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ if (bufp[0] == '\0')
+ return (FALSE);
+ if ((bp = dired_(bufp)) == NULL)
+ return (FALSE);
+
+ curbp = bp;
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+int
+d_otherwindow(int f, int n)
+{
+ char dname[NFILEN], *bufp, *slash;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ if (curbp->b_fname[0] != '\0') {
+ (void)strlcpy(dname, curbp->b_fname, sizeof(dname));
+ if ((slash = strrchr(dname, '/')) != NULL) {
+ *(slash + 1) = '\0';
+ }
+ } else {
+ if (getcwd(dname, sizeof(dname)) == NULL)
+ dname[0] = '\0';
+ }
+
+ if ((bufp = eread("Dired other window: ", dname, NFILEN,
+ EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if ((bp = dired_(bufp)) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_del(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ if (d_warpdot(curwp->w_dotp, &curwp->w_doto) == TRUE) {
+ lputc(curwp->w_dotp, 0, DDELCHAR);
+ curbp->b_flag |= BFDIREDDEL;
+ }
+ if (lforw(curwp->w_dotp) != curbp->b_headp) {
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_dotline++;
+ }
+ }
+ curwp->w_rflag |= WFEDIT | WFMOVE;
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+/* ARGSUSED */
+int
+d_undel(int f, int n)
+{
+ if (n < 0)
+ return (d_undelbak(f, -n));
+ while (n--) {
+ if (llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, ' ');
+ if (lforw(curwp->w_dotp) != curbp->b_headp) {
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_dotline++;
+ }
+ }
+ curwp->w_rflag |= WFEDIT | WFMOVE;
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+/* ARGSUSED */
+int
+d_undelbak(int f, int n)
+{
+ if (n < 0)
+ return (d_undel(f, -n));
+ while (n--) {
+ if (lback(curwp->w_dotp) != curbp->b_headp) {
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_dotline--;
+ }
+ if (llength(curwp->w_dotp) > 0)
+ lputc(curwp->w_dotp, 0, ' ');
+ }
+ curwp->w_rflag |= WFEDIT | WFMOVE;
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+/* ARGSUSED */
+int
+d_findfile(int f, int n)
+{
+ struct buffer *bp;
+ int s;
+ char fname[NFILEN];
+
+ if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
+ return (FALSE);
+ if (s == TRUE)
+ bp = dired_(fname);
+ else
+ bp = findbuffer(fname);
+ if (bp == NULL)
+ return (FALSE);
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFFULL) != TRUE)
+ return (FALSE);
+ if (bp->b_fname[0] != 0)
+ return (TRUE);
+ return (readin(fname));
+}
+
+/* ARGSUSED */
+int
+d_ffotherwindow(int f, int n)
+{
+ char fname[NFILEN];
+ int s;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT)
+ return (FALSE);
+ if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ if (bp->b_fname[0] != 0)
+ return (TRUE); /* never true for dired buffers */
+ return (readin(fname));
+}
+
+/* ARGSUSED */
+int
+d_expunge(int f, int n)
+{
+ struct line *lp, *nlp;
+ char fname[NFILEN], sname[NFILEN];
+ int tmp;
+
+ tmp = curwp->w_dotline;
+ curwp->w_dotline = 0;
+
+ for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
+ curwp->w_dotline++;
+ nlp = lforw(lp);
+ if (llength(lp) && lgetc(lp, 0) == 'D') {
+ switch (d_makename(lp, fname, sizeof(fname))) {
+ case ABORT:
+ dobeep();
+ ewprintf("Bad line in dired buffer");
+ curwp->w_dotline = tmp;
+ return (FALSE);
+ case FALSE:
+ if (unlink(fname) == -1) {
+ (void)xbasename(sname, fname, NFILEN);
+ dobeep();
+ ewprintf("Could not delete '%s'", sname);
+ curwp->w_dotline = tmp;
+ return (FALSE);
+ }
+ break;
+ case TRUE:
+ if (rmdir(fname) == -1) {
+ (void)xbasename(sname, fname, NFILEN);
+ dobeep();
+ ewprintf("Could not delete directory "
+ "'%s'", sname);
+ curwp->w_dotline = tmp;
+ return (FALSE);
+ }
+ break;
+ }
+ lfree(lp);
+ curwp->w_bufp->b_lines--;
+ if (tmp > curwp->w_dotline)
+ tmp--;
+ curwp->w_rflag |= WFFULL;
+ }
+ }
+ curwp->w_dotline = tmp;
+ d_warpdot(curwp->w_dotp, &curwp->w_doto);
+
+ /* we have deleted all items successfully, remove del flag */
+ curbp->b_flag &= ~BFDIREDDEL;
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+d_copy(int f, int n)
+{
+ struct stat statbuf;
+ char frname[NFILEN], toname[NFILEN], sname[NFILEN];
+ char *topath, *bufp;
+ int ret;
+ size_t off;
+ struct buffer *bp;
+
+ if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
+ dobeep();
+ ewprintf("Not a file");
+ return (FALSE);
+ }
+ off = strlcpy(toname, curbp->b_fname, sizeof(toname));
+ if (off >= sizeof(toname) - 1) { /* can't happen, really */
+ dobeep();
+ ewprintf("Directory name too long");
+ return (FALSE);
+ }
+ (void)xbasename(sname, frname, NFILEN);
+ bufp = eread("Copy %s to: ", toname, sizeof(toname),
+ EFDEF | EFNEW | EFCR, sname);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+
+ topath = adjustname(toname, TRUE);
+ if (stat(topath, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ ret = snprintf(toname, sizeof(toname), "%s/%s",
+ topath, sname);
+ if (ret < 0 || ret >= sizeof(toname) - 1) {
+ dobeep();
+ ewprintf("Directory name too long");
+ return (FALSE);
+ }
+ topath = adjustname(toname, TRUE);
+ }
+ }
+ if (topath == NULL)
+ return (FALSE);
+ if (strcmp(frname, topath) == 0) {
+ ewprintf("Cannot copy to same file: %s", frname);
+ return (TRUE);
+ }
+ ret = (copy(frname, topath) >= 0) ? TRUE : FALSE;
+ if (ret != TRUE)
+ return (ret);
+ if ((bp = refreshbuffer(curbp)) == NULL)
+ return (FALSE);
+
+ ewprintf("Copy: 1 file");
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+int
+d_rename(int f, int n)
+{
+ struct stat statbuf;
+ char frname[NFILEN], toname[NFILEN];
+ char *topath, *bufp;
+ int ret;
+ size_t off;
+ struct buffer *bp;
+ char sname[NFILEN];
+
+ if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) {
+ dobeep();
+ ewprintf("Not a file");
+ return (FALSE);
+ }
+ off = strlcpy(toname, curbp->b_fname, sizeof(toname));
+ if (off >= sizeof(toname) - 1) { /* can't happen, really */
+ dobeep();
+ ewprintf("Name too long");
+ return (FALSE);
+ }
+ (void)xbasename(sname, frname, NFILEN);
+ bufp = eread("Rename %s to: ", toname,
+ sizeof(toname), EFDEF | EFNEW | EFCR, sname);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+
+ topath = adjustname(toname, TRUE);
+ if (stat(topath, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ ret = snprintf(toname, sizeof(toname), "%s/%s",
+ topath, sname);
+ if (ret < 0 || ret >= sizeof(toname) - 1) {
+ dobeep();
+ ewprintf("Directory name too long");
+ return (FALSE);
+ }
+ topath = adjustname(toname, TRUE);
+ }
+ }
+ if (topath == NULL)
+ return (FALSE);
+ if (strcmp(frname, topath) == 0) {
+ ewprintf("Cannot move to same file: %s", frname);
+ return (TRUE);
+ }
+ ret = (rename(frname, topath) >= 0) ? TRUE : FALSE;
+ if (ret != TRUE)
+ return (ret);
+ if ((bp = refreshbuffer(curbp)) == NULL)
+ return (FALSE);
+
+ ewprintf("Move: 1 file");
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+void
+reaper(int signo __attribute__((unused)))
+{
+ int save_errno = errno, status;
+
+ while (waitpid(-1, &status, WNOHANG) >= 0)
+ ;
+ errno = save_errno;
+}
+
+/*
+ * Pipe the currently selected file through a shell command.
+ */
+/* ARGSUSED */
+int
+d_shell_command(int f, int n)
+{
+ char command[512], fname[PATH_MAX], *bufp;
+ struct buffer *bp;
+ struct mgwin *wp;
+ char sname[NFILEN];
+
+ bp = bfind("*Shell Command Output*", TRUE);
+ if (bclear(bp) != TRUE)
+ return (ABORT);
+
+ if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) {
+ dobeep();
+ ewprintf("bad line");
+ return (ABORT);
+ }
+
+ command[0] = '\0';
+ (void)xbasename(sname, fname, NFILEN);
+ bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname);
+ if (bufp == NULL)
+ return (ABORT);
+
+ if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE)
+ return (ABORT);
+
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (ABORT); /* XXX - free the buffer?? */
+ curwp = wp;
+ curbp = wp->w_bufp;
+ return (TRUE);
+}
+
+/*
+ * Pipe input file to cmd and insert the command's output in the
+ * given buffer. Each line will be prefixed with the given
+ * number of spaces.
+ */
+static int
+d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...)
+{
+ char buf[BUFSIZ];
+ va_list ap;
+ struct sigaction olda, newa;
+ char **argv = NULL, *cp;
+ FILE *fin;
+ int fds[2] = { -1, -1 };
+ int infd = -1;
+ int ret = (ABORT), n;
+ pid_t pid;
+
+ if (sigaction(SIGCHLD, NULL, &olda) == -1)
+ return (ABORT);
+
+ /* Find the number of arguments. */
+ va_start(ap, cmd);
+ for (n = 2; va_arg(ap, char *) != NULL; n++)
+ ;
+ va_end(ap);
+
+ /* Allocate and build the argv. */
+ if ((argv = calloc(n, sizeof(*argv))) == NULL) {
+ dobeep();
+ ewprintf("Can't allocate argv : %s", strerror(errno));
+ goto out;
+ }
+
+ n = 1;
+ argv[0] = (char *)cmd;
+ va_start(ap, cmd);
+ while ((argv[n] = va_arg(ap, char *)) != NULL)
+ n++;
+ va_end(ap);
+
+ if (input == NULL)
+ input = "/dev/null";
+
+ if ((infd = open(input, O_RDONLY)) == -1) {
+ dobeep();
+ ewprintf("Can't open input file : %s", strerror(errno));
+ goto out;
+ }
+
+ if (pipe(fds) == -1) {
+ dobeep();
+ ewprintf("Can't create pipe : %s", strerror(errno));
+ goto out;
+ }
+
+ newa.sa_handler = reaper;
+ newa.sa_flags = 0;
+ if (sigaction(SIGCHLD, &newa, NULL) == -1)
+ goto out;
+
+ if ((pid = fork()) == -1) {
+ dobeep();
+ ewprintf("Can't fork");
+ goto out;
+ }
+
+ switch (pid) {
+ case 0: /* Child */
+ close(fds[0]);
+ dup2(infd, STDIN_FILENO);
+ dup2(fds[1], STDOUT_FILENO);
+ dup2(fds[1], STDERR_FILENO);
+ if (execvp(argv[0], argv) == -1)
+ ewprintf("Can't exec %s: %s", argv[0], strerror(errno));
+ exit(1);
+ break;
+ default: /* Parent */
+ close(infd);
+ close(fds[1]);
+ infd = fds[1] = -1;
+ if ((fin = fdopen(fds[0], "r")) == NULL)
+ goto out;
+ while (fgets(buf, sizeof(buf), fin) != NULL) {
+ cp = strrchr(buf, *bp->b_nlchr);
+ if (cp == NULL && !feof(fin)) { /* too long a line */
+ int c;
+ addlinef(bp, "%*s%s...", space, "", buf);
+ while ((c = getc(fin)) != EOF &&
+ c != *bp->b_nlchr)
+ ;
+ continue;
+ } else if (cp)
+ *cp = '\0';
+ addlinef(bp, "%*s%s", space, "", buf);
+ }
+ fclose(fin);
+ break;
+ }
+ ret = (TRUE);
+
+out:
+ if (sigaction(SIGCHLD, &olda, NULL) == -1)
+ ewprintf("Warning, couldn't reset previous signal handler");
+ if (fds[0] != -1)
+ close(fds[0]);
+ if (fds[1] != -1)
+ close(fds[1]);
+ if (infd != -1)
+ close(infd);
+ free(argv);
+ return ret;
+}
+
+/* ARGSUSED */
+int
+d_create_directory(int f, int n)
+{
+ int ret;
+ struct buffer *bp;
+
+ ret = ask_makedir();
+ if (ret != TRUE)
+ return(ret);
+
+ if ((bp = refreshbuffer(curbp)) == NULL)
+ return (FALSE);
+
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/* ARGSUSED */
+int
+d_killbuffer_cmd(int f, int n)
+{
+ return(killbuffer_cmd(FFRAND, 0));
+}
+
+int
+d_refreshbuffer(int f, int n)
+{
+ struct buffer *bp;
+
+ if ((bp = refreshbuffer(curbp)) == NULL)
+ return (FALSE);
+
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
+
+/*
+ * Kill then re-open the requested dired buffer.
+ * If required, take a note of any files marked for deletion. Then once
+ * the buffer has been re-opened, remark the same files as deleted.
+ */
+struct buffer *
+refreshbuffer(struct buffer *bp)
+{
+ char *tmp_b_fname;
+ int i, tmp_w_dotline, ddel = 0;
+
+ /* remember directory path to open later */
+ tmp_b_fname = strdup(bp->b_fname);
+ if (tmp_b_fname == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ return (NULL);
+ }
+ tmp_w_dotline = curwp->w_dotline;
+
+ /* create a list of files for deletion */
+ if (bp->b_flag & BFDIREDDEL)
+ ddel = createlist(bp);
+
+ killbuffer(bp);
+
+ /* dired_() uses findbuffer() to create new buffer */
+ if ((bp = dired_(tmp_b_fname)) == NULL) {
+ free(tmp_b_fname);
+ return (NULL);
+ }
+ free(tmp_b_fname);
+
+ /* remark any previously deleted files with a 'D' */
+ if (ddel)
+ redelete(bp);
+
+ /* find dot line */
+ bp->b_dotp = bfirstlp(bp);
+ if (tmp_w_dotline > bp->b_lines)
+ tmp_w_dotline = bp->b_lines - 1;
+ for (i = 1; i < tmp_w_dotline; i++)
+ bp->b_dotp = lforw(bp->b_dotp);
+
+ bp->b_dotline = i;
+ bp->b_doto = 0;
+ d_warpdot(bp->b_dotp, &bp->b_doto);
+
+ curbp = bp;
+
+ return (bp);
+}
+
+static int
+d_makename(struct line *lp, char *fn, size_t len)
+{
+ int start, nlen, ret;
+ char *namep;
+
+ if (d_warpdot(lp, &start) == FALSE)
+ return (ABORT);
+ namep = &lp->l_text[start];
+ nlen = llength(lp) - start;
+
+ ret = snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep);
+ if (ret < 0 || ret >= (int)len)
+ return (ABORT); /* Name is too long. */
+
+ /* Return TRUE if the entry is a directory. */
+ return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE);
+}
+
+#define NAME_FIELD 9
+
+static int
+d_warpdot(struct line *dotp, int *doto)
+{
+ char *tp = dotp->l_text;
+ int off = 0, field = 0, len;
+
+ /*
+ * Find the byte offset to the (space-delimited) filename
+ * field in formatted ls output.
+ */
+ len = llength(dotp);
+ while (off < len) {
+ if (tp[off++] == ' ') {
+ if (++field == NAME_FIELD) {
+ *doto = off;
+ return (TRUE);
+ }
+ /* Skip the space. */
+ while (off < len && tp[off] == ' ')
+ off++;
+ }
+ }
+ /* We didn't find the field. */
+ *doto = 0;
+ return (FALSE);
+}
+
+static int
+d_forwpage(int f, int n)
+{
+ forwpage(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+static int
+d_backpage (int f, int n)
+{
+ backpage(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+static int
+d_forwline (int f, int n)
+{
+ forwline(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+static int
+d_backline (int f, int n)
+{
+ backline(f | FFRAND, n);
+ return (d_warpdot(curwp->w_dotp, &curwp->w_doto));
+}
+
+int
+d_filevisitalt (int f, int n)
+{
+ char fname[NFILEN];
+
+ if (d_makename(curwp->w_dotp, fname, sizeof(fname)) == ABORT)
+ return (FALSE);
+
+ return(do_filevisitalt(fname));
+}
+
+/*
+ * XXX dname needs to have enough place to store an additional '/'.
+ */
+struct buffer *
+dired_(char *dname)
+{
+ struct buffer *bp;
+ int i;
+ size_t len;
+
+ if ((dname = adjustname(dname, TRUE)) == NULL) {
+ dobeep();
+ ewprintf("Bad directory name");
+ return (NULL);
+ }
+ /* this should not be done, instead adjustname() should get a flag */
+ len = strlen(dname);
+ if (dname[len - 1] != '/') {
+ dname[len++] = '/';
+ dname[len] = '\0';
+ }
+ if ((access(dname, R_OK | X_OK)) == -1) {
+ if (errno == EACCES) {
+ dobeep();
+ ewprintf("Permission denied: %s", dname);
+ } else {
+ dobeep();
+ ewprintf("Error opening: %s", dname);
+ }
+ return (NULL);
+ }
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ if (strcmp(bp->b_fname, dname) == 0) {
+ if (fchecktime(bp) != TRUE)
+ ewprintf("Directory has changed on disk;"
+ " type g to update Dired");
+ return (bp);
+ }
+
+ }
+ bp = bfind(dname, TRUE);
+ bp->b_flag |= BFREADONLY | BFIGNDIRTY;
+
+ if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE)
+ return (NULL);
+
+ /* Find the line with ".." on it. */
+ bp->b_dotp = bfirstlp(bp);
+ bp->b_dotline = 1;
+ for (i = 0; i < bp->b_lines; i++) {
+ bp->b_dotp = lforw(bp->b_dotp);
+ bp->b_dotline++;
+ if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE)
+ continue;
+ if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0)
+ break;
+ }
+
+ /* We want dot on the entry right after "..", if possible. */
+ if (++i < bp->b_lines - 2) {
+ bp->b_dotp = lforw(bp->b_dotp);
+ bp->b_dotline++;
+ }
+ d_warpdot(bp->b_dotp, &bp->b_doto);
+
+ (void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname));
+ (void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd));
+ if ((bp->b_modes[1] = name_mode("dired")) == NULL) {
+ bp->b_modes[0] = name_mode("fundamental");
+ dobeep();
+ ewprintf("Could not find mode dired");
+ return (NULL);
+ }
+ (void)fupdstat(bp);
+ bp->b_nmodes = 1;
+ return (bp);
+}
+
+/*
+ * Iterate through the lines of the dired buffer looking for files
+ * collected in the linked list made in createlist(). If a line is found
+ * replace 'D' as first char in a line. As lines are found, remove the
+ * corresponding item from the linked list. Iterate for as long as there
+ * are items in the linked list or until end of buffer is found.
+ */
+void
+redelete(struct buffer *bp)
+{
+ struct delentry *dt, *d1 = NULL;
+ struct line *lp, *nlp;
+ char fname[NFILEN];
+ char *p = fname;
+ size_t plen, fnlen;
+ int finished = 0;
+
+ /* reset the deleted file buffer flag until a deleted file is found */
+ bp->b_flag &= ~BFDIREDDEL;
+
+ for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
+ bp->b_dotp = lp;
+ if ((p = findfname(lp, p)) == NULL) {
+ nlp = lforw(lp);
+ continue;
+ }
+ plen = strlen(p);
+ SLIST_FOREACH_SAFE(d1, &delhead, entry, dt) {
+ fnlen = strlen(d1->fn);
+ if ((plen == fnlen) &&
+ (strncmp(p, d1->fn, plen) == 0)) {
+ lputc(bp->b_dotp, 0, DDELCHAR);
+ bp->b_flag |= BFDIREDDEL;
+ SLIST_REMOVE(&delhead, d1, delentry, entry);
+ if (SLIST_EMPTY(&delhead)) {
+ finished = 1;
+ break;
+ }
+ }
+ }
+ if (finished)
+ break;
+ nlp = lforw(lp);
+ }
+ while (!SLIST_EMPTY(&delhead)) {
+ d1 = SLIST_FIRST(&delhead);
+ SLIST_REMOVE_HEAD(&delhead, entry);
+ free(d1->fn);
+ free(d1);
+ }
+ return;
+}
+
+/*
+ * Create a list of files marked for deletion.
+ */
+int
+createlist(struct buffer *bp)
+{
+ struct delentry *d1 = NULL, *d2;
+ struct line *lp, *nlp;
+ char fname[NFILEN];
+ char *p = fname;
+ int ret = FALSE;
+
+ for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) {
+ /*
+ * Check if the line has 'D' on the first char and if a valid
+ * filename can be extracted from it.
+ */
+ if (((lp->l_text[0] != DDELCHAR)) ||
+ ((p = findfname(lp, p)) == NULL)) {
+ nlp = lforw(lp);
+ continue;
+ }
+ if (SLIST_EMPTY(&delhead)) {
+ if ((d1 = malloc(sizeof(struct delentry)))
+ == NULL)
+ return (ABORT);
+ if ((d1->fn = strdup(p)) == NULL) {
+ free(d1);
+ return (ABORT);
+ }
+ SLIST_INSERT_HEAD(&delhead, d1, entry);
+ } else {
+ if ((d2 = malloc(sizeof(struct delentry)))
+ == NULL) {
+ free(d1->fn);
+ free(d1);
+ return (ABORT);
+ }
+ if ((d2->fn = strdup(p)) == NULL) {
+ free(d1->fn);
+ free(d1);
+ free(d2);
+ return (ABORT);
+ }
+ if (!d1)
+ SLIST_INSERT_HEAD(&delhead, d2, entry);
+ else
+ SLIST_INSERT_AFTER(d1, d2, entry);
+ d1 = d2;
+ }
+ ret = TRUE;
+ nlp = lforw(lp);
+ }
+ return (ret);
+}
+
+int
+dired_jump(int f, int n)
+{
+ struct buffer *bp;
+ const char *modename;
+ char dname[NFILEN], *fname;
+ int ret, i;
+
+ /*
+ * We use fundamental mode in dired, so just check we aren't in
+ * dired mode for this specific function. Seems like a corner
+ * case at the moment.
+ */
+ for (i = 0; i <= curbp->b_nmodes; i++) {
+ modename = curbp->b_modes[i]->p_name;
+ if (strncmp(modename, "dired", 5) == 0)
+ return (dobeep_msg("In dired mode already"));
+ }
+
+ if (getbufcwd(dname, sizeof(dname)) != TRUE)
+ return (FALSE);
+
+ fname = curbp->b_fname;
+
+ if ((bp = dired_(dname)) == NULL)
+ return (FALSE);
+ curbp = bp;
+
+ ret = showbuffer(bp, curwp, WFFULL | WFMODE);
+ if (ret != TRUE)
+ return ret;
+
+ fname = adjustname(fname, TRUE);
+ if (fname != NULL)
+ gotofile(fname);
+
+ return (TRUE);
+}
+
+int
+d_gotofile(int f, int n)
+{
+ size_t lenfpath;
+ char fpath[NFILEN];
+ char *fpth, *fnp = NULL;
+
+ if (getbufcwd(fpath, sizeof(fpath)) != TRUE)
+ fpath[0] = '\0';
+ lenfpath = strlen(fpath);
+ fnp = eread("Goto file: ", fpath, NFILEN,
+ EFNEW | EFCR | EFFILE | EFDEF);
+ if (fnp == NULL)
+ return (ABORT);
+ else if (fnp[0] == '\0')
+ return (FALSE);
+
+ fpth = adjustname(fpath, TRUE); /* Removes last '/' if dir... */
+ if (fpth == NULL || strlen(fpth) == lenfpath - 1) { /* ...hence -1. */
+ ewprintf("No file to find"); /* Current directory given so */
+ return (TRUE); /* return at present location. */
+ }
+ return gotofile(fpth);
+}
+
+int
+gotofile(char *fpth)
+{
+ struct line *lp, *nlp;
+ char fname[NFILEN];
+ char *p;
+ int tmp;
+
+ (void)xbasename(fname, fpth, NFILEN);
+ tmp = 0;
+ for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) {
+ tmp++;
+ if ((p = findfname(lp, p)) == NULL) {
+ nlp = lforw(lp);
+ continue;
+ }
+ if (strcmp(fname, p) == 0) {
+ curwp->w_dotp = lp;
+ curwp->w_dotline = tmp;
+ (void)d_warpdot(curwp->w_dotp, &curwp->w_doto);
+ tmp--;
+ break;
+ }
+ nlp = lforw(lp);
+ }
+ if (tmp == curbp->b_lines - 1) {
+ ewprintf("File not found %s", fname);
+ return (FALSE);
+ } else {
+ ewprintf("");
+ return (TRUE);
+ }
+}
+
+/*
+ * Look for and extract a file name on a dired buffer line.
+ */
+char *
+findfname(struct line *lp, char *fn)
+{
+ int start;
+
+ (void)d_warpdot(lp, &start);
+ if (start < 1)
+ return NULL;
+ fn = &lp->l_text[start];
+ return fn;
+}
Index: contrib/mg/display.c
===================================================================
--- /dev/null
+++ contrib/mg/display.c
@@ -0,0 +1,1081 @@
+/* $OpenBSD: display.c,v 1.48 2017/07/06 19:27:37 schwarze Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * The functions in this file handle redisplay. The
+ * redisplay system knows almost nothing about the editing
+ * process; the editing functions do, however, set some
+ * hints to eliminate a lot of the grinding. There is more
+ * that can be done; the "vtputc" interface is a real
+ * pig.
+ */
+
+#include <sys/queue.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+
+#include "def.h"
+#include "kbd.h"
+
+/*
+ * A video structure always holds
+ * an array of characters whose length is equal to
+ * the longest line possible. v_text is allocated
+ * dynamically to fit the screen width.
+ */
+struct video {
+ short v_hash; /* Hash code, for compares. */
+ short v_flag; /* Flag word. */
+ short v_color; /* Color of the line. */
+ int v_cost; /* Cost of display. */
+ char *v_text; /* The actual characters. */
+};
+
+#define VFCHG 0x0001 /* Changed. */
+#define VFHBAD 0x0002 /* Hash and cost are bad. */
+#define VFEXT 0x0004 /* extended line (beond ncol) */
+
+/*
+ * SCORE structures hold the optimal
+ * trace trajectory, and the cost of redisplay, when
+ * the dynamic programming redisplay code is used.
+ */
+struct score {
+ int s_itrace; /* "i" index for track back. */
+ int s_jtrace; /* "j" index for trace back. */
+ int s_cost; /* Display cost. */
+};
+
+void vtmove(int, int);
+void vtputc(int);
+void vtpute(int);
+int vtputs(const char *);
+void vteeol(void);
+void updext(int, int);
+void modeline(struct mgwin *, int);
+void setscores(int, int);
+void traceback(int, int, int, int);
+void ucopy(struct video *, struct video *);
+void uline(int, struct video *, struct video *);
+void hash(struct video *);
+
+
+int sgarbf = TRUE; /* TRUE if screen is garbage. */
+int vtrow = HUGE; /* Virtual cursor row. */
+int vtcol = HUGE; /* Virtual cursor column. */
+int tthue = CNONE; /* Current color. */
+int ttrow = HUGE; /* Physical cursor row. */
+int ttcol = HUGE; /* Physical cursor column. */
+int tttop = HUGE; /* Top of scroll region. */
+int ttbot = HUGE; /* Bottom of scroll region. */
+int lbound = 0; /* leftmost bound of the current */
+ /* line being displayed */
+
+struct video **vscreen; /* Edge vector, virtual. */
+struct video **pscreen; /* Edge vector, physical. */
+struct video *video; /* Actual screen data. */
+struct video blanks; /* Blank line image. */
+
+/*
+ * This matrix is written as an array because
+ * we do funny things in the "setscores" routine, which
+ * is very compute intensive, to make the subscripts go away.
+ * It would be "SCORE score[NROW][NROW]" in old speak.
+ * Look at "setscores" to understand what is up.
+ */
+struct score *score; /* [NROW * NROW] */
+
+static int linenos = TRUE;
+static int colnos = FALSE;
+
+/* Is macro recording enabled? */
+extern int macrodef;
+/* Is working directory global? */
+extern int globalwd;
+
+/*
+ * Since we don't have variables (we probably should) these are command
+ * processors for changing the values of mode flags.
+ */
+/* ARGSUSED */
+int
+linenotoggle(int f, int n)
+{
+ if (f & FFARG)
+ linenos = n > 0;
+ else
+ linenos = !linenos;
+
+ sgarbf = TRUE;
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+colnotoggle(int f, int n)
+{
+ if (f & FFARG)
+ colnos = n > 0;
+ else
+ colnos = !colnos;
+
+ sgarbf = TRUE;
+
+ return (TRUE);
+}
+
+/*
+ * Reinit the display data structures, this is called when the terminal
+ * size changes.
+ */
+int
+vtresize(int force, int newrow, int newcol)
+{
+ int i;
+ int rowchanged, colchanged;
+ static int first_run = 1;
+ struct video *vp;
+
+ if (newrow < 1 || newcol < 1)
+ return (FALSE);
+
+ rowchanged = (newrow != nrow);
+ colchanged = (newcol != ncol);
+
+#define TRYREALLOC(a, n) do { \
+ void *tmp; \
+ if ((tmp = realloc((a), (n))) == NULL) { \
+ panic("out of memory in display code"); \
+ } \
+ (a) = tmp; \
+ } while (0)
+
+#define TRYREALLOCARRAY(a, n, m) do { \
+ void *tmp; \
+ if ((tmp = reallocarray((a), (n), (m))) == NULL) {\
+ panic("out of memory in display code"); \
+ } \
+ (a) = tmp; \
+ } while (0)
+
+ /* No update needed */
+ if (!first_run && !force && !rowchanged && !colchanged)
+ return (TRUE);
+
+ if (first_run)
+ memset(&blanks, 0, sizeof(blanks));
+
+ if (rowchanged || first_run) {
+ int vidstart;
+
+ /*
+ * This is not pretty.
+ */
+ if (nrow == 0)
+ vidstart = 0;
+ else
+ vidstart = 2 * (nrow - 1);
+
+ /*
+ * We're shrinking, free some internal data.
+ */
+ if (newrow < nrow) {
+ for (i = 2 * (newrow - 1); i < 2 * (nrow - 1); i++) {
+ free(video[i].v_text);
+ video[i].v_text = NULL;
+ }
+ }
+
+ TRYREALLOCARRAY(score, newrow, newrow * sizeof(struct score));
+ TRYREALLOCARRAY(vscreen, (newrow - 1), sizeof(struct video *));
+ TRYREALLOCARRAY(pscreen, (newrow - 1), sizeof(struct video *));
+ TRYREALLOCARRAY(video, (newrow - 1), 2 * sizeof(struct video));
+
+ /*
+ * Zero-out the entries we just allocated.
+ */
+ for (i = vidstart; i < 2 * (newrow - 1); i++)
+ memset(&video[i], 0, sizeof(struct video));
+
+ /*
+ * Reinitialize vscreen and pscreen arrays completely.
+ */
+ vp = &video[0];
+ for (i = 0; i < newrow - 1; ++i) {
+ vscreen[i] = vp;
+ ++vp;
+ pscreen[i] = vp;
+ ++vp;
+ }
+ }
+ if (rowchanged || colchanged || first_run) {
+ for (i = 0; i < 2 * (newrow - 1); i++)
+ TRYREALLOC(video[i].v_text, newcol);
+ TRYREALLOC(blanks.v_text, newcol);
+ }
+
+ nrow = newrow;
+ ncol = newcol;
+
+ if (ttrow > nrow)
+ ttrow = nrow;
+ if (ttcol > ncol)
+ ttcol = ncol;
+
+ first_run = 0;
+ return (TRUE);
+}
+
+#undef TRYREALLOC
+#undef TRYREALLOCARRAY
+
+/*
+ * Initialize the data structures used
+ * by the display code. The edge vectors used
+ * to access the screens are set up. The operating
+ * system's terminal I/O channel is set up. Fill the
+ * "blanks" array with ASCII blanks. The rest is done
+ * at compile time. The original window is marked
+ * as needing full update, and the physical screen
+ * is marked as garbage, so all the right stuff happens
+ * on the first call to redisplay.
+ */
+void
+vtinit(void)
+{
+ int i;
+
+ ttopen();
+ ttinit();
+
+ /*
+ * ttinit called ttresize(), which called vtresize(), so our data
+ * structures are setup correctly.
+ */
+
+ blanks.v_color = CTEXT;
+ for (i = 0; i < ncol; ++i)
+ blanks.v_text[i] = ' ';
+}
+
+/*
+ * Tidy up the virtual display system
+ * in anticipation of a return back to the host
+ * operating system. Right now all we do is position
+ * the cursor to the last line, erase the line, and
+ * close the terminal channel.
+ */
+void
+vttidy(void)
+{
+ ttcolor(CTEXT);
+ ttnowindow(); /* No scroll window. */
+ ttmove(nrow - 1, 0); /* Echo line. */
+ tteeol();
+ tttidy();
+ ttflush();
+ ttclose();
+}
+
+/*
+ * Move the virtual cursor to an origin
+ * 0 spot on the virtual display screen. I could
+ * store the column as a character pointer to the spot
+ * on the line, which would make "vtputc" a little bit
+ * more efficient. No checking for errors.
+ */
+void
+vtmove(int row, int col)
+{
+ vtrow = row;
+ vtcol = col;
+}
+
+/*
+ * Write a character to the virtual display,
+ * dealing with long lines and the display of unprintable
+ * things like control characters. Also expand tabs every 8
+ * columns. This code only puts printing characters into
+ * the virtual display image. Special care must be taken when
+ * expanding tabs. On a screen whose width is not a multiple
+ * of 8, it is possible for the virtual cursor to hit the
+ * right margin before the next tab stop is reached. This
+ * makes the tab code loop if you are not careful.
+ * Three guesses how we found this.
+ */
+void
+vtputc(int c)
+{
+ struct video *vp;
+
+ c &= 0xff;
+
+ vp = vscreen[vtrow];
+ if (vtcol >= ncol)
+ vp->v_text[ncol - 1] = '$';
+ else if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ ) {
+ do {
+ vtputc(' ');
+ } while (vtcol < ncol && (vtcol & 0x07) != 0);
+ } else if (ISCTRL(c)) {
+ vtputc('^');
+ vtputc(CCHR(c));
+ } else if (isprint(c))
+ vp->v_text[vtcol++] = c;
+ else {
+ char bf[5];
+
+ snprintf(bf, sizeof(bf), "\\%o", c);
+ vtputs(bf);
+ }
+}
+
+/*
+ * Put a character to the virtual screen in an extended line. If we are not
+ * yet on left edge, don't print it yet. Check for overflow on the right
+ * margin.
+ */
+void
+vtpute(int c)
+{
+ struct video *vp;
+
+ c &= 0xff;
+
+ vp = vscreen[vtrow];
+ if (vtcol >= ncol)
+ vp->v_text[ncol - 1] = '$';
+ else if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ ) {
+ do {
+ vtpute(' ');
+ } while (((vtcol + lbound) & 0x07) != 0 && vtcol < ncol);
+ } else if (ISCTRL(c) != FALSE) {
+ vtpute('^');
+ vtpute(CCHR(c));
+ } else if (isprint(c)) {
+ if (vtcol >= 0)
+ vp->v_text[vtcol] = c;
+ ++vtcol;
+ } else {
+ char bf[5], *cp;
+
+ snprintf(bf, sizeof(bf), "\\%o", c);
+ for (cp = bf; *cp != '\0'; cp++)
+ vtpute(*cp);
+ }
+}
+
+/*
+ * Erase from the end of the software cursor to the end of the line on which
+ * the software cursor is located. The display routines will decide if a
+ * hardware erase to end of line command should be used to display this.
+ */
+void
+vteeol(void)
+{
+ struct video *vp;
+
+ vp = vscreen[vtrow];
+ while (vtcol < ncol)
+ vp->v_text[vtcol++] = ' ';
+}
+
+/*
+ * Make sure that the display is
+ * right. This is a three part process. First,
+ * scan through all of the windows looking for dirty
+ * ones. Check the framing, and refresh the screen.
+ * Second, make sure that "currow" and "curcol" are
+ * correct for the current window. Third, make the
+ * virtual and physical screens the same.
+ */
+void
+update(int modelinecolor)
+{
+ struct line *lp;
+ struct mgwin *wp;
+ struct video *vp1;
+ struct video *vp2;
+ int c, i, j;
+ int hflag;
+ int currow, curcol;
+ int offs, size;
+
+ if (charswaiting())
+ return;
+ if (sgarbf) { /* must update everything */
+ wp = wheadp;
+ while (wp != NULL) {
+ wp->w_rflag |= WFMODE | WFFULL;
+ wp = wp->w_wndp;
+ }
+ }
+ if (linenos || colnos) {
+ wp = wheadp;
+ while (wp != NULL) {
+ wp->w_rflag |= WFMODE;
+ wp = wp->w_wndp;
+ }
+ }
+ hflag = FALSE; /* Not hard. */
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ /*
+ * Nothing to be done.
+ */
+ if (wp->w_rflag == 0)
+ continue;
+
+ if ((wp->w_rflag & WFFRAME) == 0) {
+ lp = wp->w_linep;
+ for (i = 0; i < wp->w_ntrows; ++i) {
+ if (lp == wp->w_dotp)
+ goto out;
+ if (lp == wp->w_bufp->b_headp)
+ break;
+ lp = lforw(lp);
+ }
+ }
+ /*
+ * Put the middle-line in place.
+ */
+ i = wp->w_frame;
+ if (i > 0) {
+ --i;
+ if (i >= wp->w_ntrows)
+ i = wp->w_ntrows - 1;
+ } else if (i < 0) {
+ i += wp->w_ntrows;
+ if (i < 0)
+ i = 0;
+ } else
+ i = wp->w_ntrows / 2; /* current center, no change */
+
+ /*
+ * Find the line.
+ */
+ lp = wp->w_dotp;
+ while (i != 0 && lback(lp) != wp->w_bufp->b_headp) {
+ --i;
+ lp = lback(lp);
+ }
+ wp->w_linep = lp;
+ wp->w_rflag |= WFFULL; /* Force full. */
+ out:
+ lp = wp->w_linep; /* Try reduced update. */
+ i = wp->w_toprow;
+ if ((wp->w_rflag & ~WFMODE) == WFEDIT) {
+ while (lp != wp->w_dotp) {
+ ++i;
+ lp = lforw(lp);
+ }
+ vscreen[i]->v_color = CTEXT;
+ vscreen[i]->v_flag |= (VFCHG | VFHBAD);
+ vtmove(i, 0);
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+ vteeol();
+ } else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) {
+ hflag = TRUE;
+ while (i < wp->w_toprow + wp->w_ntrows) {
+ vscreen[i]->v_color = CTEXT;
+ vscreen[i]->v_flag |= (VFCHG | VFHBAD);
+ vtmove(i, 0);
+ if (lp != wp->w_bufp->b_headp) {
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+ lp = lforw(lp);
+ }
+ vteeol();
+ ++i;
+ }
+ }
+ if ((wp->w_rflag & WFMODE) != 0)
+ modeline(wp, modelinecolor);
+ wp->w_rflag = 0;
+ wp->w_frame = 0;
+ }
+ lp = curwp->w_linep; /* Cursor location. */
+ currow = curwp->w_toprow;
+ while (lp != curwp->w_dotp) {
+ ++currow;
+ lp = lforw(lp);
+ }
+ curcol = 0;
+ i = 0;
+ while (i < curwp->w_doto) {
+ c = lgetc(lp, i++);
+ if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ ) {
+ curcol |= 0x07;
+ curcol++;
+ } else if (ISCTRL(c) != FALSE)
+ curcol += 2;
+ else if (isprint(c))
+ curcol++;
+ else {
+ char bf[5];
+
+ snprintf(bf, sizeof(bf), "\\%o", c);
+ curcol += strlen(bf);
+ }
+ }
+ if (curcol >= ncol - 1) { /* extended line. */
+ /* flag we are extended and changed */
+ vscreen[currow]->v_flag |= VFEXT | VFCHG;
+ updext(currow, curcol); /* and output extended line */
+ } else
+ lbound = 0; /* not extended line */
+
+ /*
+ * Make sure no lines need to be de-extended because the cursor is no
+ * longer on them.
+ */
+ wp = wheadp;
+ while (wp != NULL) {
+ lp = wp->w_linep;
+ i = wp->w_toprow;
+ while (i < wp->w_toprow + wp->w_ntrows) {
+ if (vscreen[i]->v_flag & VFEXT) {
+ /* always flag extended lines as changed */
+ vscreen[i]->v_flag |= VFCHG;
+ if ((wp != curwp) || (lp != wp->w_dotp) ||
+ (curcol < ncol - 1)) {
+ vtmove(i, 0);
+ for (j = 0; j < llength(lp); ++j)
+ vtputc(lgetc(lp, j));
+ vteeol();
+ /* this line no longer is extended */
+ vscreen[i]->v_flag &= ~VFEXT;
+ }
+ }
+ lp = lforw(lp);
+ ++i;
+ }
+ /* if garbaged then fix up mode lines */
+ if (sgarbf != FALSE)
+ vscreen[i]->v_flag |= VFCHG;
+ /* and onward to the next window */
+ wp = wp->w_wndp;
+ }
+
+ if (sgarbf != FALSE) { /* Screen is garbage. */
+ sgarbf = FALSE; /* Erase-page clears. */
+ epresf = FALSE; /* The message area. */
+ tttop = HUGE; /* Forget where you set. */
+ ttbot = HUGE; /* scroll region. */
+ tthue = CNONE; /* Color unknown. */
+ ttmove(0, 0);
+ tteeop();
+ for (i = 0; i < nrow - 1; ++i) {
+ uline(i, vscreen[i], &blanks);
+ ucopy(vscreen[i], pscreen[i]);
+ }
+ ttmove(currow, curcol - lbound);
+ ttflush();
+ return;
+ }
+ if (hflag != FALSE) { /* Hard update? */
+ for (i = 0; i < nrow - 1; ++i) {/* Compute hash data. */
+ hash(vscreen[i]);
+ hash(pscreen[i]);
+ }
+ offs = 0; /* Get top match. */
+ while (offs != nrow - 1) {
+ vp1 = vscreen[offs];
+ vp2 = pscreen[offs];
+ if (vp1->v_color != vp2->v_color
+ || vp1->v_hash != vp2->v_hash)
+ break;
+ uline(offs, vp1, vp2);
+ ucopy(vp1, vp2);
+ ++offs;
+ }
+ if (offs == nrow - 1) { /* Might get it all. */
+ ttmove(currow, curcol - lbound);
+ ttflush();
+ return;
+ }
+ size = nrow - 1; /* Get bottom match. */
+ while (size != offs) {
+ vp1 = vscreen[size - 1];
+ vp2 = pscreen[size - 1];
+ if (vp1->v_color != vp2->v_color
+ || vp1->v_hash != vp2->v_hash)
+ break;
+ uline(size - 1, vp1, vp2);
+ ucopy(vp1, vp2);
+ --size;
+ }
+ if ((size -= offs) == 0) /* Get screen size. */
+ panic("Illegal screen size in update");
+ setscores(offs, size); /* Do hard update. */
+ traceback(offs, size, size, size);
+ for (i = 0; i < size; ++i)
+ ucopy(vscreen[offs + i], pscreen[offs + i]);
+ ttmove(currow, curcol - lbound);
+ ttflush();
+ return;
+ }
+ for (i = 0; i < nrow - 1; ++i) { /* Easy update. */
+ vp1 = vscreen[i];
+ vp2 = pscreen[i];
+ if ((vp1->v_flag & VFCHG) != 0) {
+ uline(i, vp1, vp2);
+ ucopy(vp1, vp2);
+ }
+ }
+ ttmove(currow, curcol - lbound);
+ ttflush();
+}
+
+/*
+ * Update a saved copy of a line,
+ * kept in a video structure. The "vvp" is
+ * the one in the "vscreen". The "pvp" is the one
+ * in the "pscreen". This is called to make the
+ * virtual and physical screens the same when
+ * display has done an update.
+ */
+void
+ucopy(struct video *vvp, struct video *pvp)
+{
+ vvp->v_flag &= ~VFCHG; /* Changes done. */
+ pvp->v_flag = vvp->v_flag; /* Update model. */
+ pvp->v_hash = vvp->v_hash;
+ pvp->v_cost = vvp->v_cost;
+ pvp->v_color = vvp->v_color;
+ bcopy(vvp->v_text, pvp->v_text, ncol);
+}
+
+/*
+ * updext: update the extended line which the cursor is currently on at a
+ * column greater than the terminal width. The line will be scrolled right or
+ * left to let the user see where the cursor is.
+ */
+void
+updext(int currow, int curcol)
+{
+ struct line *lp; /* pointer to current line */
+ int j; /* index into line */
+
+ if (ncol < 2)
+ return;
+
+ /*
+ * calculate what column the left bound should be
+ * (force cursor into middle half of screen)
+ */
+ lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2);
+
+ /*
+ * scan through the line outputing characters to the virtual screen
+ * once we reach the left edge
+ */
+ vtmove(currow, -lbound); /* start scanning offscreen */
+ lp = curwp->w_dotp; /* line to output */
+ for (j = 0; j < llength(lp); ++j) /* until the end-of-line */
+ vtpute(lgetc(lp, j));
+ vteeol(); /* truncate the virtual line */
+ vscreen[currow]->v_text[0] = '$'; /* and put a '$' in column 1 */
+}
+
+/*
+ * Update a single line. This routine only
+ * uses basic functionality (no insert and delete character,
+ * but erase to end of line). The "vvp" points at the video
+ * structure for the line on the virtual screen, and the "pvp"
+ * is the same for the physical screen. Avoid erase to end of
+ * line when updating CMODE color lines, because of the way that
+ * reverse video works on most terminals.
+ */
+void
+uline(int row, struct video *vvp, struct video *pvp)
+{
+ char *cp1;
+ char *cp2;
+ char *cp3;
+ char *cp4;
+ char *cp5;
+ int nbflag;
+
+ if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */
+ ttmove(row, 0); /* full redraw. */
+#ifdef STANDOUT_GLITCH
+ if (pvp->v_color != CTEXT && magic_cookie_glitch >= 0)
+ tteeol();
+#endif
+ ttcolor(vvp->v_color);
+#ifdef STANDOUT_GLITCH
+ cp1 = &vvp->v_text[magic_cookie_glitch > 0 ? magic_cookie_glitch : 0];
+ /*
+ * The odd code for magic_cookie_glitch==0 is to avoid
+ * putting the invisible glitch character on the next line.
+ * (Hazeltine executive 80 model 30)
+ */
+ cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ?
+ (magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)];
+#else
+ cp1 = &vvp->v_text[0];
+ cp2 = &vvp->v_text[ncol];
+#endif
+ while (cp1 != cp2) {
+ ttputc(*cp1++);
+ ++ttcol;
+ }
+ ttcolor(CTEXT);
+ return;
+ }
+ cp1 = &vvp->v_text[0]; /* Compute left match. */
+ cp2 = &pvp->v_text[0];
+ while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) {
+ ++cp1;
+ ++cp2;
+ }
+ if (cp1 == &vvp->v_text[ncol]) /* All equal. */
+ return;
+ nbflag = FALSE;
+ cp3 = &vvp->v_text[ncol]; /* Compute right match. */
+ cp4 = &pvp->v_text[ncol];
+ while (cp3[-1] == cp4[-1]) {
+ --cp3;
+ --cp4;
+ if (cp3[0] != ' ') /* Note non-blanks in */
+ nbflag = TRUE; /* the right match. */
+ }
+ cp5 = cp3; /* Is erase good? */
+ if (nbflag == FALSE && vvp->v_color == CTEXT) {
+ while (cp5 != cp1 && cp5[-1] == ' ')
+ --cp5;
+ /* Alcyon hack */
+ if ((int) (cp3 - cp5) <= tceeol)
+ cp5 = cp3;
+ }
+ /* Alcyon hack */
+ ttmove(row, (int) (cp1 - &vvp->v_text[0]));
+#ifdef STANDOUT_GLITCH
+ if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) {
+ if (cp1 < &vvp->v_text[magic_cookie_glitch])
+ cp1 = &vvp->v_text[magic_cookie_glitch];
+ if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch])
+ cp5 = &vvp->v_text[ncol - magic_cookie_glitch];
+ } else if (magic_cookie_glitch < 0)
+#endif
+ ttcolor(vvp->v_color);
+ while (cp1 != cp5) {
+ ttputc(*cp1++);
+ ++ttcol;
+ }
+ if (cp5 != cp3) /* Do erase. */
+ tteeol();
+}
+
+/*
+ * Redisplay the mode line for the window pointed to by the "wp".
+ * This is the only routine that has any idea of how the mode line is
+ * formatted. You can change the modeline format by hacking at this
+ * routine. Called by "update" any time there is a dirty window. Note
+ * that if STANDOUT_GLITCH is defined, first and last magic_cookie_glitch
+ * characters may never be seen.
+ */
+void
+modeline(struct mgwin *wp, int modelinecolor)
+{
+ int n, md;
+ struct buffer *bp;
+ char sl[21]; /* Overkill. Space for 2^64 in base 10. */
+ int len;
+
+ n = wp->w_toprow + wp->w_ntrows; /* Location. */
+ vscreen[n]->v_color = modelinecolor; /* Mode line color. */
+ vscreen[n]->v_flag |= (VFCHG | VFHBAD); /* Recompute, display. */
+ vtmove(n, 0); /* Seek to right line. */
+ bp = wp->w_bufp;
+ vtputc('-');
+ vtputc('-');
+ if ((bp->b_flag & BFREADONLY) != 0) {
+ vtputc('%');
+ if ((bp->b_flag & BFCHG) != 0)
+ vtputc('*');
+ else
+ vtputc('%');
+ } else if ((bp->b_flag & BFCHG) != 0) { /* "*" if changed. */
+ vtputc('*');
+ vtputc('*');
+ } else {
+ vtputc('-');
+ vtputc('-');
+ }
+ vtputc('-');
+ n = 5;
+ n += vtputs("Mg: ");
+ if (bp->b_bname[0] != '\0')
+ n += vtputs(&(bp->b_bname[0]));
+ while (n < 42) { /* Pad out with blanks. */
+ vtputc(' ');
+ ++n;
+ }
+ vtputc('(');
+ ++n;
+ for (md = 0; ; ) {
+ n += vtputs(bp->b_modes[md]->p_name);
+ if (++md > bp->b_nmodes)
+ break;
+ vtputc('-');
+ ++n;
+ }
+ /* XXX These should eventually move to a real mode */
+ if (macrodef == TRUE)
+ n += vtputs("-def");
+ if (globalwd == TRUE)
+ n += vtputs("-gwd");
+ vtputc(')');
+ ++n;
+
+ if (linenos && colnos)
+ len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline,
+ getcolpos(wp));
+ else if (linenos)
+ len = snprintf(sl, sizeof(sl), "--L%d", wp->w_dotline);
+ else if (colnos)
+ len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp));
+ if ((linenos || colnos) && len < sizeof(sl) && len != -1)
+ n += vtputs(sl);
+
+ while (n < ncol) { /* Pad out. */
+ vtputc('-');
+ ++n;
+ }
+}
+
+/*
+ * Output a string to the mode line, report how long it was.
+ */
+int
+vtputs(const char *s)
+{
+ int n = 0;
+
+ while (*s != '\0') {
+ vtputc(*s++);
+ ++n;
+ }
+ return (n);
+}
+
+/*
+ * Compute the hash code for the line pointed to by the "vp".
+ * Recompute it if necessary. Also set the approximate redisplay
+ * cost. The validity of the hash code is marked by a flag bit.
+ * The cost understand the advantages of erase to end of line.
+ * Tuned for the VAX by Bob McNamara; better than it used to be on
+ * just about any machine.
+ */
+void
+hash(struct video *vp)
+{
+ int i, n;
+ char *s;
+
+ if ((vp->v_flag & VFHBAD) != 0) { /* Hash bad. */
+ s = &vp->v_text[ncol - 1];
+ for (i = ncol; i != 0; --i, --s)
+ if (*s != ' ')
+ break;
+ n = ncol - i; /* Erase cheaper? */
+ if (n > tceeol)
+ n = tceeol;
+ vp->v_cost = i + n; /* Bytes + blanks. */
+ for (n = 0; i != 0; --i, --s)
+ n = (n << 5) + n + *s;
+ vp->v_hash = n; /* Hash code. */
+ vp->v_flag &= ~VFHBAD; /* Flag as all done. */
+ }
+}
+
+/*
+ * Compute the Insert-Delete
+ * cost matrix. The dynamic programming algorithm
+ * described by James Gosling is used. This code assumes
+ * that the line above the echo line is the last line involved
+ * in the scroll region. This is easy to arrange on the VT100
+ * because of the scrolling region. The "offs" is the origin 0
+ * offset of the first row in the virtual/physical screen that
+ * is being updated; the "size" is the length of the chunk of
+ * screen being updated. For a full screen update, use offs=0
+ * and size=nrow-1.
+ *
+ * Older versions of this code implemented the score matrix by
+ * a two dimensional array of SCORE nodes. This put all kinds of
+ * multiply instructions in the code! This version is written to
+ * use a linear array and pointers, and contains no multiplication
+ * at all. The code has been carefully looked at on the VAX, with
+ * only marginal checking on other machines for efficiency. In
+ * fact, this has been tuned twice! Bob McNamara tuned it even
+ * more for the VAX, which is a big issue for him because of
+ * the 66 line X displays.
+ *
+ * On some machines, replacing the "for (i=1; i<=size; ++i)" with
+ * i = 1; do { } while (++i <=size)" will make the code quite a
+ * bit better; but it looks ugly.
+ */
+void
+setscores(int offs, int size)
+{
+ struct score *sp;
+ struct score *sp1;
+ struct video **vp, **pp;
+ struct video **vbase, **pbase;
+ int tempcost;
+ int bestcost;
+ int j, i;
+
+ vbase = &vscreen[offs - 1]; /* By hand CSE's. */
+ pbase = &pscreen[offs - 1];
+ score[0].s_itrace = 0; /* [0, 0] */
+ score[0].s_jtrace = 0;
+ score[0].s_cost = 0;
+ sp = &score[1]; /* Row 0, inserts. */
+ tempcost = 0;
+ vp = &vbase[1];
+ for (j = 1; j <= size; ++j) {
+ sp->s_itrace = 0;
+ sp->s_jtrace = j - 1;
+ tempcost += tcinsl;
+ tempcost += (*vp)->v_cost;
+ sp->s_cost = tempcost;
+ ++vp;
+ ++sp;
+ }
+ sp = &score[nrow]; /* Column 0, deletes. */
+ tempcost = 0;
+ for (i = 1; i <= size; ++i) {
+ sp->s_itrace = i - 1;
+ sp->s_jtrace = 0;
+ tempcost += tcdell;
+ sp->s_cost = tempcost;
+ sp += nrow;
+ }
+ sp1 = &score[nrow + 1]; /* [1, 1]. */
+ pp = &pbase[1];
+ for (i = 1; i <= size; ++i) {
+ sp = sp1;
+ vp = &vbase[1];
+ for (j = 1; j <= size; ++j) {
+ sp->s_itrace = i - 1;
+ sp->s_jtrace = j;
+ bestcost = (sp - nrow)->s_cost;
+ if (j != size) /* Cd(A[i])=0 @ Dis. */
+ bestcost += tcdell;
+ tempcost = (sp - 1)->s_cost;
+ tempcost += (*vp)->v_cost;
+ if (i != size) /* Ci(B[j])=0 @ Dsj. */
+ tempcost += tcinsl;
+ if (tempcost < bestcost) {
+ sp->s_itrace = i;
+ sp->s_jtrace = j - 1;
+ bestcost = tempcost;
+ }
+ tempcost = (sp - nrow - 1)->s_cost;
+ if ((*pp)->v_color != (*vp)->v_color
+ || (*pp)->v_hash != (*vp)->v_hash)
+ tempcost += (*vp)->v_cost;
+ if (tempcost < bestcost) {
+ sp->s_itrace = i - 1;
+ sp->s_jtrace = j - 1;
+ bestcost = tempcost;
+ }
+ sp->s_cost = bestcost;
+ ++sp; /* Next column. */
+ ++vp;
+ }
+ ++pp;
+ sp1 += nrow; /* Next row. */
+ }
+}
+
+/*
+ * Trace back through the dynamic programming cost
+ * matrix, and update the screen using an optimal sequence
+ * of redraws, insert lines, and delete lines. The "offs" is
+ * the origin 0 offset of the chunk of the screen we are about to
+ * update. The "i" and "j" are always started in the lower right
+ * corner of the matrix, and imply the size of the screen.
+ * A full screen traceback is called with offs=0 and i=j=nrow-1.
+ * There is some do-it-yourself double subscripting here,
+ * which is acceptable because this routine is much less compute
+ * intensive then the code that builds the score matrix!
+ */
+void
+traceback(int offs, int size, int i, int j)
+{
+ int itrace, jtrace;
+ int k;
+ int ninsl, ndraw, ndell;
+
+ if (i == 0 && j == 0) /* End of update. */
+ return;
+ itrace = score[(nrow * i) + j].s_itrace;
+ jtrace = score[(nrow * i) + j].s_jtrace;
+ if (itrace == i) { /* [i, j-1] */
+ ninsl = 0; /* Collect inserts. */
+ if (i != size)
+ ninsl = 1;
+ ndraw = 1;
+ while (itrace != 0 || jtrace != 0) {
+ if (score[(nrow * itrace) + jtrace].s_itrace != itrace)
+ break;
+ jtrace = score[(nrow * itrace) + jtrace].s_jtrace;
+ if (i != size)
+ ++ninsl;
+ ++ndraw;
+ }
+ traceback(offs, size, itrace, jtrace);
+ if (ninsl != 0) {
+ ttcolor(CTEXT);
+ ttinsl(offs + j - ninsl, offs + size - 1, ninsl);
+ }
+ do { /* B[j], A[j] blank. */
+ k = offs + j - ndraw;
+ uline(k, vscreen[k], &blanks);
+ } while (--ndraw);
+ return;
+ }
+ if (jtrace == j) { /* [i-1, j] */
+ ndell = 0; /* Collect deletes. */
+ if (j != size)
+ ndell = 1;
+ while (itrace != 0 || jtrace != 0) {
+ if (score[(nrow * itrace) + jtrace].s_jtrace != jtrace)
+ break;
+ itrace = score[(nrow * itrace) + jtrace].s_itrace;
+ if (j != size)
+ ++ndell;
+ }
+ if (ndell != 0) {
+ ttcolor(CTEXT);
+ ttdell(offs + i - ndell, offs + size - 1, ndell);
+ }
+ traceback(offs, size, itrace, jtrace);
+ return;
+ }
+ traceback(offs, size, itrace, jtrace);
+ k = offs + j - 1;
+ uline(k, vscreen[k], pscreen[offs + i - 1]);
+}
Index: contrib/mg/echo.c
===================================================================
--- /dev/null
+++ contrib/mg/echo.c
@@ -0,0 +1,1020 @@
+/* $OpenBSD: echo.c,v 1.68 2021/03/02 15:03:35 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Echo line reading and writing.
+ *
+ * Common routines for reading and writing characters in the echo line area
+ * of the display screen. Used by the entire known universe.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+
+#include "def.h"
+#include "funmap.h"
+#include "key.h"
+#include "macro.h"
+
+static char *veread(const char *, char *, size_t, int, va_list)
+ __attribute__((__format__ (printf, 1, 0)));
+static int complt(int, int, char *, size_t, int, int *);
+static int complt_list(int, char *, int);
+static void eformat(const char *, va_list)
+ __attribute__((__format__ (printf, 1, 0)));
+static void eputi(int, int);
+static void eputl(long, int);
+static void eputs(const char *);
+static void eputc(char);
+static struct list *copy_list(struct list *);
+
+int epresf = FALSE; /* stuff in echo line flag */
+
+/*
+ * Erase the echo line.
+ */
+void
+eerase(void)
+{
+ ttcolor(CTEXT);
+ ttmove(nrow - 1, 0);
+ tteeol();
+ ttflush();
+ epresf = FALSE;
+}
+
+/*
+ * Ask a "yes" or "no" question. Return ABORT if the user answers the
+ * question with the abort ("^G") character. Return FALSE for "no" and
+ * TRUE for "yes". No formatting services are available. No newline
+ * required.
+ */
+int
+eyorn(const char *sp)
+{
+ int s;
+
+ if (inmacro)
+ return (TRUE);
+
+ ewprintf("%s? (y or n) ", sp);
+ for (;;) {
+ s = getkey(FALSE);
+ if (s == 'y' || s == 'Y' || s == ' ') {
+ ewprintf("");
+ return (TRUE);
+ }
+ if (s == 'n' || s == 'N' || s == CCHR('M')) {
+ ewprintf("");
+ return (FALSE);
+ }
+ if (s == CCHR('G')) {
+ ewprintf("");
+ return (ctrlg(FFRAND, 1));
+ }
+ ewprintf("Please answer y or n. %s? (y or n) ", sp);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Ask a "yes", "no" or "revert" question. Return ABORT if the user answers
+ * the question with the abort ("^G") character. Return FALSE for "no",
+ * TRUE for "yes" and REVERT for "revert". No formatting services are
+ * available. No newline required.
+ */
+int
+eynorr(const char *sp)
+{
+ int s;
+
+ if (inmacro)
+ return (TRUE);
+
+ ewprintf("%s? (y, n or r) ", sp);
+ for (;;) {
+ s = getkey(FALSE);
+ if (s == 'y' || s == 'Y' || s == ' ') {
+ ewprintf("");
+ return (TRUE);
+ }
+ if (s == 'n' || s == 'N' || s == CCHR('M')) {
+ ewprintf("");
+ return (FALSE);
+ }
+ if (s == 'r' || s == 'R') {
+ ewprintf("");
+ return (REVERT);
+ }
+ if (s == CCHR('G')) {
+ ewprintf("");
+ return (ctrlg(FFRAND, 1));
+ }
+ ewprintf("Please answer y, n or r.");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Like eyorn, but for more important questions. User must type all of
+ * "yes" or "no" and the trailing newline.
+ */
+int
+eyesno(const char *sp)
+{
+ char buf[64], *rep;
+
+ if (inmacro)
+ return (TRUE);
+
+ rep = eread("%s? (yes or no) ", buf, sizeof(buf),
+ EFNUL | EFNEW | EFCR, sp);
+ for (;;) {
+ if (rep == NULL) {
+ ewprintf("");
+ return (ABORT);
+ }
+ if (rep[0] != '\0') {
+ if (macrodef) {
+ struct line *lp = maclcur;
+
+ maclcur = lp->l_bp;
+ maclcur->l_fp = lp->l_fp;
+ free(lp);
+ }
+ if (strcasecmp(rep, "yes") == 0) {
+ ewprintf("");
+ return (TRUE);
+ }
+ if (strcasecmp(rep, "no") == 0) {
+ ewprintf("");
+ return (FALSE);
+ }
+ }
+ rep = eread("Please answer yes or no. %s? (yes or no) ",
+ buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * This is the general "read input from the echo line" routine. The basic
+ * idea is that the prompt string "prompt" is written to the echo line, and
+ * a one line reply is read back into the supplied "buf" (with maximum
+ * length "len").
+ * XXX: When checking for an empty return value, always check rep, *not* buf
+ * as buf may be freed in pathological cases.
+ */
+char *
+eread(const char *fmt, char *buf, size_t nbuf, int flag, ...)
+{
+ va_list ap;
+ char *rep;
+
+ va_start(ap, flag);
+ rep = veread(fmt, buf, nbuf, flag, ap);
+ va_end(ap);
+ return (rep);
+}
+
+static char *
+veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap)
+{
+ int dynbuf = (buf == NULL);
+ int cpos, epos; /* cursor, end position in buf */
+ int c, i, y;
+ int cplflag; /* display completion list */
+ int cwin = FALSE; /* completion list created */
+ int mr, ml; /* match left/right arrows */
+ int esc; /* position in esc pattern */
+ struct buffer *bp; /* completion list buffer */
+ struct mgwin *wp; /* window for compl list */
+ int match; /* esc match found */
+ int cc, rr; /* saved ttcol, ttrow */
+ char *ret; /* return value */
+
+ static char emptyval[] = ""; /* XXX hackish way to return err msg*/
+
+ if (inmacro) {
+ if (dynbuf) {
+ if ((buf = malloc(maclcur->l_used + 1)) == NULL)
+ return (NULL);
+ } else if (maclcur->l_used >= nbuf)
+ return (NULL);
+ bcopy(maclcur->l_text, buf, maclcur->l_used);
+ buf[maclcur->l_used] = '\0';
+ maclcur = maclcur->l_fp;
+ return (buf);
+ }
+ epos = cpos = 0;
+ ml = mr = esc = 0;
+ cplflag = FALSE;
+
+ if ((flag & EFNEW) != 0 || ttrow != nrow - 1) {
+ ttcolor(CTEXT);
+ ttmove(nrow - 1, 0);
+ epresf = TRUE;
+ } else
+ eputc(' ');
+ eformat(fp, ap);
+ if ((flag & EFDEF) != 0) {
+ if (buf == NULL)
+ return (NULL);
+ eputs(buf);
+ epos = cpos += strlen(buf);
+ }
+ tteeol();
+ ttflush();
+ for (;;) {
+ c = getkey(FALSE);
+ if ((flag & EFAUTO) != 0 && c == CCHR('I')) {
+ if (cplflag == TRUE) {
+ complt_list(flag, buf, cpos);
+ cwin = TRUE;
+ } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) {
+ cplflag = TRUE;
+ epos += i;
+ cpos = epos;
+ }
+ continue;
+ }
+ cplflag = FALSE;
+
+ if (esc > 0) { /* ESC sequence started */
+ match = 0;
+ if (ml == esc && key_left[ml] && c == key_left[ml]) {
+ match++;
+ if (key_left[++ml] == '\0') {
+ c = CCHR('B');
+ esc = 0;
+ }
+ }
+ if (mr == esc && key_right[mr] && c == key_right[mr]) {
+ match++;
+ if (key_right[++mr] == '\0') {
+ c = CCHR('F');
+ esc = 0;
+ }
+ }
+ if (match == 0) {
+ esc = 0;
+ continue;
+ /* hack. how do we know esc pattern is done? */
+ }
+ if (esc > 0) {
+ esc++;
+ continue;
+ }
+ }
+ switch (c) {
+ case CCHR('A'): /* start of line */
+ while (cpos > 0) {
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ --ttcol;
+ }
+ ttputc('\b');
+ --ttcol;
+ }
+ ttflush();
+ break;
+ case CCHR('D'):
+ if (cpos != epos) {
+ tteeol();
+ epos--;
+ rr = ttrow;
+ cc = ttcol;
+ for (i = cpos; i < epos; i++) {
+ buf[i] = buf[i + 1];
+ eputc(buf[i]);
+ }
+ ttmove(rr, cc);
+ ttflush();
+ }
+ break;
+ case CCHR('E'): /* end of line */
+ while (cpos < epos) {
+ eputc(buf[cpos++]);
+ }
+ ttflush();
+ break;
+ case CCHR('B'): /* back */
+ if (cpos > 0) {
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ --ttcol;
+ }
+ ttputc('\b');
+ --ttcol;
+ ttflush();
+ }
+ break;
+ case CCHR('F'): /* forw */
+ if (cpos < epos) {
+ eputc(buf[cpos++]);
+ ttflush();
+ }
+ break;
+ case CCHR('Y'): /* yank from kill buffer */
+ i = 0;
+ while ((y = kremove(i++)) >= 0 && y != *curbp->b_nlchr) {
+ int t;
+ if (dynbuf && epos + 1 >= nbuf) {
+ void *newp;
+ size_t newsize = epos + epos + 16;
+ if ((newp = realloc(buf, newsize))
+ == NULL)
+ goto memfail;
+ buf = newp;
+ nbuf = newsize;
+ }
+ if (!dynbuf && epos + 1 >= nbuf) {
+ dobeep();
+ ewprintf("Line too long. Press Control-g to escape.");
+ goto skipkey;
+ }
+ for (t = epos; t > cpos; t--)
+ buf[t] = buf[t - 1];
+ buf[cpos++] = (char)y;
+ epos++;
+ eputc((char)y);
+ cc = ttcol;
+ rr = ttrow;
+ for (t = cpos; t < epos; t++)
+ eputc(buf[t]);
+ ttmove(rr, cc);
+ }
+ ttflush();
+ break;
+ case CCHR('K'): /* copy here-EOL to kill buffer */
+ kdelete();
+ for (i = cpos; i < epos; i++)
+ kinsert(buf[i], KFORW);
+ tteeol();
+ epos = cpos;
+ ttflush();
+ break;
+ case CCHR('['):
+ ml = mr = esc = 1;
+ break;
+ case CCHR('J'):
+ c = CCHR('M');
+ /* FALLTHROUGH */
+ case CCHR('M'): /* return, done */
+ /* if there's nothing in the minibuffer, abort */
+ if (epos == 0 && !(flag & EFNUL)) {
+ (void)ctrlg(FFRAND, 0);
+ ttflush();
+ return (NULL);
+ }
+ if ((flag & EFFUNC) != 0) {
+ if (complt(flag, c, buf, nbuf, epos, &i)
+ == FALSE)
+ continue;
+ if (i > 0)
+ epos += i;
+ }
+ buf[epos] = '\0';
+ if ((flag & EFCR) != 0) {
+ ttputc(CCHR('M'));
+ ttflush();
+ }
+ if (macrodef) {
+ struct line *lp;
+
+ if ((lp = lalloc(cpos)) == NULL)
+ goto memfail;
+ lp->l_fp = maclcur->l_fp;
+ maclcur->l_fp = lp;
+ lp->l_bp = maclcur;
+ maclcur = lp;
+ bcopy(buf, lp->l_text, cpos);
+ }
+ ret = buf;
+ goto done;
+ case CCHR('G'): /* bell, abort */
+ eputc(CCHR('G'));
+ (void)ctrlg(FFRAND, 0);
+ ttflush();
+ ret = NULL;
+ goto done;
+ case CCHR('H'): /* rubout, erase */
+ case CCHR('?'):
+ if (cpos != 0) {
+ y = buf[--cpos];
+ epos--;
+ ttputc('\b');
+ ttcol--;
+ if (ISCTRL(y) != FALSE) {
+ ttputc('\b');
+ ttcol--;
+ }
+ rr = ttrow;
+ cc = ttcol;
+ for (i = cpos; i < epos; i++) {
+ buf[i] = buf[i + 1];
+ eputc(buf[i]);
+ }
+ ttputc(' ');
+ if (ISCTRL(y) != FALSE) {
+ ttputc(' ');
+ ttputc('\b');
+ }
+ ttputc('\b');
+ ttmove(rr, cc);
+ ttflush();
+ }
+ break;
+ case CCHR('X'): /* kill line */
+ case CCHR('U'):
+ while (cpos != 0) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ epos--;
+ }
+ ttflush();
+ break;
+ case CCHR('W'): /* kill to beginning of word */
+ while ((cpos > 0) && !ISWORD(buf[cpos - 1])) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ epos--;
+ }
+ while ((cpos > 0) && ISWORD(buf[cpos - 1])) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ if (ISCTRL(buf[--cpos]) != FALSE) {
+ ttputc('\b');
+ ttputc(' ');
+ ttputc('\b');
+ --ttcol;
+ }
+ epos--;
+ }
+ ttflush();
+ break;
+ case CCHR('\\'):
+ case CCHR('Q'): /* quote next */
+ c = getkey(FALSE);
+ /* FALLTHROUGH */
+ default:
+ if (dynbuf && epos + 1 >= nbuf) {
+ void *newp;
+ size_t newsize = epos + epos + 16;
+ if ((newp = realloc(buf, newsize)) == NULL)
+ goto memfail;
+ buf = newp;
+ nbuf = newsize;
+ }
+ if (!dynbuf && epos + 1 >= nbuf) {
+ dobeep();
+ ewprintf("Line too long. Press Control-g to escape.");
+ goto skipkey;
+ }
+ for (i = epos; i > cpos; i--)
+ buf[i] = buf[i - 1];
+ buf[cpos++] = (char)c;
+ epos++;
+ eputc((char)c);
+ cc = ttcol;
+ rr = ttrow;
+ for (i = cpos; i < epos; i++)
+ eputc(buf[i]);
+ ttmove(rr, cc);
+ ttflush();
+ }
+
+skipkey: /* ignore key press */
+;
+ }
+done:
+ if (cwin == TRUE) {
+ /* blow away cpltion window */
+ bp = bfind("*Completions*", TRUE);
+ if ((wp = popbuf(bp, WEPHEM)) != NULL) {
+ if (wp->w_flag & WEPHEM) {
+ curwp = wp;
+ delwind(FFRAND, 1);
+ } else {
+ killbuffer(bp);
+ }
+ }
+ }
+ return (ret);
+memfail:
+ if (dynbuf && buf)
+ free(buf);
+ dobeep();
+ ewprintf("Out of memory");
+ return (emptyval);
+}
+
+/*
+ * Do completion on a list of objects.
+ * c is SPACE, TAB, or CR
+ * return TRUE if matched (or partially matched)
+ * FALSE is result is ambiguous,
+ * ABORT on error.
+ */
+static int
+complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx)
+{
+ struct list *lh, *lh2;
+ struct list *wholelist = NULL;
+ int i, nxtra, nhits, bxtra, msglen, nshown;
+ int wflag = FALSE;
+ char *msg;
+
+ lh = lh2 = NULL;
+
+ if ((flags & EFFUNC) != 0) {
+ buf[cpos] = '\0';
+ wholelist = lh = complete_function_list(buf);
+ } else if ((flags & EFBUF) != 0) {
+ lh = &(bheadp->b_list);
+ } else if ((flags & EFFILE) != 0) {
+ buf[cpos] = '\0';
+ wholelist = lh = make_file_list(buf);
+ } else
+ panic("broken complt call: flags");
+
+ if (c == ' ')
+ wflag = TRUE;
+ else if (c != '\t' && c != CCHR('M'))
+ panic("broken complt call: c");
+
+ nhits = 0;
+ nxtra = HUGE;
+
+ for (; lh != NULL; lh = lh->l_next) {
+ if (memcmp(buf, lh->l_name, cpos) != 0)
+ continue;
+ if (nhits == 0)
+ lh2 = lh;
+ ++nhits;
+ if (lh->l_name[cpos] == '\0')
+ nxtra = -1; /* exact match */
+ else {
+ bxtra = getxtra(lh, lh2, cpos, wflag);
+ if (bxtra < nxtra)
+ nxtra = bxtra;
+ lh2 = lh;
+ }
+ }
+ if (nhits == 0)
+ msg = " [No match]";
+ else if (nhits > 1 && nxtra == 0)
+ msg = " [Ambiguous. Ctrl-G to cancel]";
+ else {
+ /*
+ * Being lazy - ought to check length, but all things
+ * autocompleted have known types/lengths.
+ */
+ if (nxtra < 0 && nhits > 1 && c == ' ')
+ nxtra = 1; /* ??? */
+ for (i = 0; i < nxtra && cpos < nbuf; ++i) {
+ buf[cpos] = lh2->l_name[cpos];
+ eputc(buf[cpos++]);
+ }
+ /* XXX should grow nbuf */
+ ttflush();
+ free_file_list(wholelist);
+ *nx = nxtra;
+ if (nxtra < 0 && c != CCHR('M')) /* exact */
+ *nx = 0;
+ return (TRUE);
+ }
+
+ /*
+ * wholelist is NULL if we are doing buffers. Want to free lists
+ * that were created for us, but not the buffer list!
+ */
+ free_file_list(wholelist);
+
+ /* Set up backspaces, etc., being mindful of echo line limit. */
+ msglen = strlen(msg);
+ nshown = (ttcol + msglen + 2 > ncol) ?
+ ncol - ttcol - 2 : msglen;
+ eputs(msg);
+ ttcol -= (i = nshown); /* update ttcol! */
+ while (i--) /* move back before msg */
+ ttputc('\b');
+ ttflush(); /* display to user */
+ i = nshown;
+ while (i--) /* blank out on next flush */
+ eputc(' ');
+ ttcol -= (i = nshown); /* update ttcol on BS's */
+ while (i--)
+ ttputc('\b'); /* update ttcol again! */
+ *nx = nxtra;
+ return ((nhits > 0) ? TRUE : FALSE);
+}
+
+/*
+ * Do completion on a list of objects, listing instead of completing.
+ */
+static int
+complt_list(int flags, char *buf, int cpos)
+{
+ struct list *lh, *lh2, *lh3;
+ struct list *wholelist = NULL;
+ struct buffer *bp;
+ int i, maxwidth, width;
+ int preflen = 0;
+ int oldrow = ttrow;
+ int oldcol = ttcol;
+ int oldhue = tthue;
+ char *linebuf;
+ size_t linesize, len;
+ char *cp;
+
+ lh = NULL;
+
+ ttflush();
+
+ /* The results are put into a completion buffer. */
+ bp = bfind("*Completions*", TRUE);
+ if (bclear(bp) == FALSE)
+ return (FALSE);
+ bp->b_flag |= BFREADONLY;
+
+ /*
+ * First get the list of objects. This list may contain only
+ * the ones that complete what has been typed, or may be the
+ * whole list of all objects of this type. They are filtered
+ * later in any case. Set wholelist if the list has been
+ * cons'ed up just for us, so we can free it later. We have
+ * to copy the buffer list for this function even though we
+ * didn't for complt. The sorting code does destructive
+ * changes to the list, which we don't want to happen to the
+ * main buffer list!
+ */
+ if ((flags & EFBUF) != 0)
+ wholelist = lh = copy_list(&(bheadp->b_list));
+ else if ((flags & EFFUNC) != 0) {
+ buf[cpos] = '\0';
+ wholelist = lh = complete_function_list(buf);
+ } else if ((flags & EFFILE) != 0) {
+ buf[cpos] = '\0';
+ wholelist = lh = make_file_list(buf);
+ /*
+ * We don't want to display stuff up to the / for file
+ * names preflen is the list of a prefix of what the
+ * user typed that should not be displayed.
+ */
+ cp = strrchr(buf, '/');
+ if (cp)
+ preflen = cp - buf + 1;
+ } else
+ panic("broken complt call: flags");
+
+ /*
+ * Sort the list, since users expect to see it in alphabetic
+ * order.
+ */
+ lh2 = lh;
+ while (lh2 != NULL) {
+ lh3 = lh2->l_next;
+ while (lh3 != NULL) {
+ if (strcmp(lh2->l_name, lh3->l_name) > 0) {
+ cp = lh2->l_name;
+ lh2->l_name = lh3->l_name;
+ lh3->l_name = cp;
+ }
+ lh3 = lh3->l_next;
+ }
+ lh2 = lh2->l_next;
+ }
+
+ /*
+ * First find max width of object to be displayed, so we can
+ * put several on a line.
+ */
+ maxwidth = 0;
+ lh2 = lh;
+ while (lh2 != NULL) {
+ for (i = 0; i < cpos; ++i) {
+ if (buf[i] != lh2->l_name[i])
+ break;
+ }
+ if (i == cpos) {
+ width = strlen(lh2->l_name);
+ if (width > maxwidth)
+ maxwidth = width;
+ }
+ lh2 = lh2->l_next;
+ }
+ maxwidth += 1 - preflen;
+
+ /*
+ * Now do the display. Objects are written into linebuf until
+ * it fills, and then put into the help buffer.
+ */
+ linesize = (ncol > maxwidth ? ncol : maxwidth) + 1;
+ if ((linebuf = malloc(linesize)) == NULL) {
+ free_file_list(wholelist);
+ return (FALSE);
+ }
+ width = 0;
+
+ /*
+ * We're going to strlcat() into the buffer, so it has to be
+ * NUL terminated.
+ */
+ linebuf[0] = '\0';
+ for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) {
+ for (i = 0; i < cpos; ++i) {
+ if (buf[i] != lh2->l_name[i])
+ break;
+ }
+ /* if we have a match */
+ if (i == cpos) {
+ /* if it wraps */
+ if ((width + maxwidth) > ncol) {
+ addline(bp, linebuf);
+ linebuf[0] = '\0';
+ width = 0;
+ }
+ len = strlcat(linebuf, lh2->l_name + preflen,
+ linesize);
+ width += maxwidth;
+ if (len < width && width < linesize) {
+ /* pad so the objects nicely line up */
+ memset(linebuf + len, ' ',
+ maxwidth - strlen(lh2->l_name + preflen));
+ linebuf[width] = '\0';
+ }
+ }
+ }
+ if (width > 0)
+ addline(bp, linebuf);
+ free(linebuf);
+
+ /*
+ * Note that we free lists only if they are put in wholelist lists
+ * that were built just for us should be freed. However when we use
+ * the buffer list, obviously we don't want it freed.
+ */
+ free_file_list(wholelist);
+ popbuftop(bp, WEPHEM); /* split the screen and put up the help
+ * buffer */
+ update(CMODE); /* needed to make the new stuff actually
+ * appear */
+ ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */
+ ttcolor(oldhue); /* with arbitrary color */
+ ttflush();
+ return (0);
+}
+
+/*
+ * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal
+ * position in the name. Return the longest block of characters that can be
+ * autocompleted at this point. Sometimes the two symbols are the same, but
+ * this is normal.
+ */
+int
+getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag)
+{
+ int i;
+
+ i = cpos;
+ for (;;) {
+ if (lp1->l_name[i] != lp2->l_name[i])
+ break;
+ if (lp1->l_name[i] == '\0')
+ break;
+ ++i;
+ if (wflag && !ISWORD(lp1->l_name[i - 1]))
+ break;
+ }
+ return (i - cpos);
+}
+
+/*
+ * Special "printf" for the echo line. Each call to "ewprintf" starts a
+ * new line in the echo area, and ends with an erase to end of the echo
+ * line. The formatting is done by a call to the standard formatting
+ * routine.
+ */
+void
+ewprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (inmacro)
+ return;
+
+ va_start(ap, fmt);
+ ttcolor(CTEXT);
+ ttmove(nrow - 1, 0);
+ eformat(fmt, ap);
+ va_end(ap);
+ tteeol();
+ ttflush();
+ epresf = TRUE;
+}
+
+/*
+ * Printf style formatting. This is called by "ewprintf" to provide
+ * formatting services to its clients. The move to the start of the
+ * echo line, and the erase to the end of the echo line, is done by
+ * the caller.
+ * %c prints the "name" of the supplied character.
+ * %k prints the name of the current key (and takes no arguments).
+ * %d prints a decimal integer
+ * %o prints an octal integer
+ * %p prints a pointer
+ * %s prints a string
+ * %ld prints a long word
+ * Anything else is echoed verbatim
+ */
+static void
+eformat(const char *fp, va_list ap)
+{
+ char kname[NKNAME], tmp[100], *cp;
+ int c;
+
+ while ((c = *fp++) != '\0') {
+ if (c != '%')
+ eputc(c);
+ else {
+ c = *fp++;
+ switch (c) {
+ case 'c':
+ getkeyname(kname, sizeof(kname),
+ va_arg(ap, int));
+ eputs(kname);
+ break;
+
+ case 'k':
+ for (cp = kname, c = 0; c < key.k_count; c++) {
+ if (c)
+ *cp++ = ' ';
+ cp = getkeyname(cp, sizeof(kname) -
+ (cp - kname) - 1, key.k_chars[c]);
+ }
+ eputs(kname);
+ break;
+
+ case 'd':
+ eputi(va_arg(ap, int), 10);
+ break;
+
+ case 'o':
+ eputi(va_arg(ap, int), 8);
+ break;
+
+ case 'p':
+ snprintf(tmp, sizeof(tmp), "%p",
+ va_arg(ap, void *));
+ eputs(tmp);
+ break;
+
+ case 's':
+ eputs(va_arg(ap, char *));
+ break;
+
+ case 'l':
+ /* explicit longword */
+ c = *fp++;
+ switch (c) {
+ case 'd':
+ eputl(va_arg(ap, long), 10);
+ break;
+ default:
+ eputc(c);
+ break;
+ }
+ break;
+
+ default:
+ eputc(c);
+ }
+ }
+ }
+}
+
+/*
+ * Put integer, in radix "r".
+ */
+static void
+eputi(int i, int r)
+{
+ int q;
+
+ if (i < 0) {
+ eputc('-');
+ i = -i;
+ }
+ if ((q = i / r) != 0)
+ eputi(q, r);
+ eputc(i % r + '0');
+}
+
+/*
+ * Put long, in radix "r".
+ */
+static void
+eputl(long l, int r)
+{
+ long q;
+
+ if (l < 0) {
+ eputc('-');
+ l = -l;
+ }
+ if ((q = l / r) != 0)
+ eputl(q, r);
+ eputc((int)(l % r) + '0');
+}
+
+/*
+ * Put string.
+ */
+static void
+eputs(const char *s)
+{
+ int c;
+
+ while ((c = *s++) != '\0')
+ eputc(c);
+}
+
+/*
+ * Put character. Watch for control characters, and for the line getting
+ * too long.
+ */
+static void
+eputc(char c)
+{
+ if (ttcol + 2 < ncol) {
+ if (ISCTRL(c)) {
+ eputc('^');
+ c = CCHR(c);
+ }
+ ttputc(c);
+ ++ttcol;
+ }
+}
+
+void
+free_file_list(struct list *lp)
+{
+ struct list *next;
+
+ while (lp) {
+ next = lp->l_next;
+ free(lp->l_name);
+ free(lp);
+ lp = next;
+ }
+}
+
+static struct list *
+copy_list(struct list *lp)
+{
+ struct list *current, *last, *nxt;
+
+ last = NULL;
+ while (lp) {
+ current = malloc(sizeof(struct list));
+ if (current == NULL) {
+ /* Free what we have allocated so far */
+ for (current = last; current; current = nxt) {
+ nxt = current->l_next;
+ free(current->l_name);
+ free(current);
+ }
+ return (NULL);
+ }
+ current->l_next = last;
+ current->l_name = strdup(lp->l_name);
+ last = current;
+ lp = lp->l_next;
+ }
+ return (last);
+}
Index: contrib/mg/extend.c
===================================================================
--- /dev/null
+++ contrib/mg/extend.c
@@ -0,0 +1,942 @@
+/* $OpenBSD: extend.c,v 1.75 2021/05/06 14:16:12 lum Exp $ */
+/* This file is in the public domain. */
+
+/*
+ * Extended (M-x) commands, rebinding, and startup file processing.
+ */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "chrdef.h"
+#include "def.h"
+#include "funmap.h"
+#include "kbd.h"
+#include "key.h"
+#include "macro.h"
+
+static int remap(KEYMAP *, int, PF, KEYMAP *);
+static KEYMAP *reallocmap(KEYMAP *);
+static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *);
+static int dobind(KEYMAP *, const char *, int);
+static char *parsetoken(char *);
+static int bindkey(KEYMAP **, const char *, KCHAR *, int);
+
+/*
+ * Insert a string, mainly for use from macros (created by selfinsert).
+ */
+/* ARGSUSED */
+int
+insert(int f, int n)
+{
+ char buf[BUFSIZE], *bufp, *cp;
+ int count, c;
+
+ if (inmacro) {
+ while (--n >= 0) {
+ for (count = 0; count < maclcur->l_used; count++) {
+ if ((((c = maclcur->l_text[count]) ==
+ *curbp->b_nlchr)
+ ? lnewline() : linsert(1, c)) != TRUE)
+ return (FALSE);
+ }
+ }
+ maclcur = maclcur->l_fp;
+ return (TRUE);
+ }
+ if (n == 1)
+ /* CFINS means selfinsert can tack on the end */
+ thisflag |= CFINS;
+
+ if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ while (--n >= 0) {
+ cp = buf;
+ while (*cp) {
+ if (((*cp == *curbp->b_nlchr) ?
+ lnewline() : linsert(1, *cp))
+ != TRUE)
+ return (FALSE);
+ cp++;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Bind a key to a function. Cases range from the trivial (replacing an
+ * existing binding) to the extremely complex (creating a new prefix in a
+ * map_element that already has one, so the map_element must be split,
+ * but the keymap doesn't have enough room for another map_element, so
+ * the keymap is reallocated). No attempt is made to reclaim space no
+ * longer used, if this is a problem flags must be added to indicate
+ * malloced versus static storage in both keymaps and map_elements.
+ * Structure assignments would come in real handy, but K&R based compilers
+ * don't have them. Care is taken so running out of memory will leave
+ * the keymap in a usable state.
+ * Parameters are:
+ * curmap: pointer to the map being changed
+ * c: character being changed
+ * funct: function being changed to
+ * pref_map: if funct==NULL, map to bind to or NULL for new
+ */
+static int
+remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
+{
+ int i, n1, n2, nold;
+ KEYMAP *mp, *newmap;
+ PF *pfp;
+ struct map_element *mep;
+
+ if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
+ if (ele > &curmap->map_element[0] && (funct != NULL ||
+ (ele - 1)->k_prefmap == NULL))
+ n1 = c - (ele - 1)->k_num;
+ else
+ n1 = HUGE;
+ if (ele < &curmap->map_element[curmap->map_num] &&
+ (funct != NULL || ele->k_prefmap == NULL))
+ n2 = ele->k_base - c;
+ else
+ n2 = HUGE;
+ if (n1 <= MAPELEDEF && n1 <= n2) {
+ ele--;
+ if ((pfp = calloc(c - ele->k_base + 1,
+ sizeof(PF))) == NULL)
+ return (dobeep_msg("Out of memory"));
+
+ nold = ele->k_num - ele->k_base + 1;
+ for (i = 0; i < nold; i++)
+ pfp[i] = ele->k_funcp[i];
+ while (--n1)
+ pfp[i++] = curmap->map_default;
+ pfp[i] = funct;
+ ele->k_num = c;
+ ele->k_funcp = pfp;
+ } else if (n2 <= MAPELEDEF) {
+ if ((pfp = calloc(ele->k_num - c + 1,
+ sizeof(PF))) == NULL)
+ return (dobeep_msg("Out of memory"));
+
+ nold = ele->k_num - ele->k_base + 1;
+ for (i = 0; i < nold; i++)
+ pfp[i + n2] = ele->k_funcp[i];
+ while (--n2)
+ pfp[n2] = curmap->map_default;
+ pfp[0] = funct;
+ ele->k_base = c;
+ ele->k_funcp = pfp;
+ } else {
+ if (curmap->map_num >= curmap->map_max) {
+ if ((newmap = reallocmap(curmap)) == NULL)
+ return (FALSE);
+ curmap = newmap;
+ }
+ if ((pfp = malloc(sizeof(PF))) == NULL)
+ return (dobeep_msg("Out of memory"));
+
+ pfp[0] = funct;
+ for (mep = &curmap->map_element[curmap->map_num];
+ mep > ele; mep--) {
+ mep->k_base = (mep - 1)->k_base;
+ mep->k_num = (mep - 1)->k_num;
+ mep->k_funcp = (mep - 1)->k_funcp;
+ mep->k_prefmap = (mep - 1)->k_prefmap;
+ }
+ ele->k_base = c;
+ ele->k_num = c;
+ ele->k_funcp = pfp;
+ ele->k_prefmap = NULL;
+ curmap->map_num++;
+ }
+ if (funct == NULL) {
+ if (pref_map != NULL)
+ ele->k_prefmap = pref_map;
+ else {
+ if ((mp = malloc(sizeof(KEYMAP) +
+ (MAPINIT - 1) * sizeof(struct map_element))) == NULL) {
+ (void)dobeep_msg("Out of memory");
+ ele->k_funcp[c - ele->k_base] =
+ curmap->map_default;
+ return (FALSE);
+ }
+ mp->map_num = 0;
+ mp->map_max = MAPINIT;
+ mp->map_default = rescan;
+ ele->k_prefmap = mp;
+ }
+ }
+ } else {
+ n1 = c - ele->k_base;
+ if (ele->k_funcp[n1] == funct && (funct != NULL ||
+ pref_map == NULL || pref_map == ele->k_prefmap))
+ /* no change */
+ return (TRUE);
+ if (funct != NULL || ele->k_prefmap == NULL) {
+ if (ele->k_funcp[n1] == NULL)
+ ele->k_prefmap = NULL;
+ /* easy case */
+ ele->k_funcp[n1] = funct;
+ if (funct == NULL) {
+ if (pref_map != NULL)
+ ele->k_prefmap = pref_map;
+ else {
+ if ((mp = malloc(sizeof(KEYMAP) +
+ (MAPINIT - 1) *
+ sizeof(struct map_element))) == NULL) {
+ (void)dobeep_msg("Out of memory");
+ ele->k_funcp[c - ele->k_base] =
+ curmap->map_default;
+ return (FALSE);
+ }
+ mp->map_num = 0;
+ mp->map_max = MAPINIT;
+ mp->map_default = rescan;
+ ele->k_prefmap = mp;
+ }
+ }
+ } else {
+ /*
+ * This case is the splits.
+ * Determine which side of the break c goes on
+ * 0 = after break; 1 = before break
+ */
+ n2 = 1;
+ for (i = 0; n2 && i < n1; i++)
+ n2 &= ele->k_funcp[i] != NULL;
+ if (curmap->map_num >= curmap->map_max) {
+ if ((newmap = reallocmap(curmap)) == NULL)
+ return (FALSE);
+ curmap = newmap;
+ }
+ if ((pfp = calloc(ele->k_num - c + !n2,
+ sizeof(PF))) == NULL)
+ return (dobeep_msg("Out of memory"));
+
+ ele->k_funcp[n1] = NULL;
+ for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++)
+ pfp[i - n1 - n2] = ele->k_funcp[i];
+ for (mep = &curmap->map_element[curmap->map_num];
+ mep > ele; mep--) {
+ mep->k_base = (mep - 1)->k_base;
+ mep->k_num = (mep - 1)->k_num;
+ mep->k_funcp = (mep - 1)->k_funcp;
+ mep->k_prefmap = (mep - 1)->k_prefmap;
+ }
+ ele->k_num = c - !n2;
+ (ele + 1)->k_base = c + n2;
+ (ele + 1)->k_funcp = pfp;
+ ele += !n2;
+ ele->k_prefmap = NULL;
+ curmap->map_num++;
+ if (pref_map == NULL) {
+ if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1)
+ * sizeof(struct map_element))) == NULL) {
+ (void)dobeep_msg("Out of memory");
+ ele->k_funcp[c - ele->k_base] =
+ curmap->map_default;
+ return (FALSE);
+ }
+ mp->map_num = 0;
+ mp->map_max = MAPINIT;
+ mp->map_default = rescan;
+ ele->k_prefmap = mp;
+ } else
+ ele->k_prefmap = pref_map;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Reallocate a keymap. Returns NULL (without trashing the current map)
+ * on failure.
+ */
+static KEYMAP *
+reallocmap(KEYMAP *curmap)
+{
+ struct maps_s *mps;
+ KEYMAP *mp;
+ int i;
+
+ if (curmap->map_max > SHRT_MAX - MAPGROW) {
+ (void)dobeep_msg("keymap too large");
+ return (NULL);
+ }
+ if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) *
+ sizeof(struct map_element))) == NULL) {
+ (void)dobeep_msg("Out of memory");
+ return (NULL);
+ }
+ mp->map_num = curmap->map_num;
+ mp->map_max = curmap->map_max + MAPGROW;
+ mp->map_default = curmap->map_default;
+ for (i = curmap->map_num; i--;) {
+ mp->map_element[i].k_base = curmap->map_element[i].k_base;
+ mp->map_element[i].k_num = curmap->map_element[i].k_num;
+ mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp;
+ mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap;
+ }
+ for (mps = maps; mps != NULL; mps = mps->p_next) {
+ if (mps->p_map == curmap)
+ mps->p_map = mp;
+ else
+ fixmap(curmap, mp, mps->p_map);
+ }
+ ele = &mp->map_element[ele - &curmap->map_element[0]];
+ return (mp);
+}
+
+/*
+ * Fix references to a reallocated keymap (recursive).
+ */
+static void
+fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt)
+{
+ int i;
+
+ for (i = mt->map_num; i--;) {
+ if (mt->map_element[i].k_prefmap != NULL) {
+ if (mt->map_element[i].k_prefmap == curmap)
+ mt->map_element[i].k_prefmap = mp;
+ else
+ fixmap(curmap, mp, mt->map_element[i].k_prefmap);
+ }
+ }
+}
+
+/*
+ * Do the input for local-set-key, global-set-key and define-key
+ * then call remap to do the work.
+ */
+static int
+dobind(KEYMAP *curmap, const char *p, int unbind)
+{
+ KEYMAP *pref_map = NULL;
+ PF funct;
+ char bprompt[80], *bufp, *pep;
+ int c, s, n;
+
+ if (macrodef) {
+ /*
+ * Keystrokes aren't collected. Not hard, but pretty useless.
+ * Would not work for function keys in any case.
+ */
+ return (dobeep_msg("Can't rebind key in macro"));
+ }
+ if (inmacro) {
+ for (s = 0; s < maclcur->l_used - 1; s++) {
+ if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap)
+ != NULL) {
+ if (remap(curmap, c, NULL, NULL)
+ != TRUE)
+ return (FALSE);
+ }
+ }
+ (void)doscan(curmap, c = maclcur->l_text[s], NULL);
+ maclcur = maclcur->l_fp;
+ } else {
+ n = strlcpy(bprompt, p, sizeof(bprompt));
+ if (n >= sizeof(bprompt))
+ n = sizeof(bprompt) - 1;
+ pep = bprompt + n;
+ for (;;) {
+ ewprintf("%s", bprompt);
+ pep[-1] = ' ';
+ pep = getkeyname(pep, sizeof(bprompt) -
+ (pep - bprompt), c = getkey(FALSE));
+ if (doscan(curmap, c, &curmap) != NULL)
+ break;
+ *pep++ = '-';
+ *pep = '\0';
+ }
+ }
+ if (unbind)
+ funct = rescan;
+ else {
+ if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt),
+ EFFUNC | EFNEW, bprompt)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if (((funct = name_function(bprompt)) == NULL) ?
+ (pref_map = name_map(bprompt)) == NULL : funct == NULL)
+ return (dobeep_msg("[No match]"));
+
+ }
+ return (remap(curmap, c, funct, pref_map));
+}
+
+/*
+ * bindkey: bind key sequence to a function in the specified map. Used by
+ * excline so it can bind function keys. To close to release to change
+ * calling sequence, should just pass KEYMAP *curmap rather than
+ * KEYMAP **mapp.
+ */
+static int
+bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount)
+{
+ KEYMAP *curmap = *mapp;
+ KEYMAP *pref_map = NULL;
+ PF funct;
+ int c;
+
+ if (fname == NULL)
+ funct = rescan;
+ else if (((funct = name_function(fname)) == NULL) ?
+ (pref_map = name_map(fname)) == NULL : funct == NULL) {
+ dobeep();
+ ewprintf("[No match: %s]", fname);
+ return (FALSE);
+ }
+ while (--kcount) {
+ if (doscan(curmap, c = *keys++, &curmap) != NULL) {
+ if (remap(curmap, c, NULL, NULL) != TRUE)
+ return (FALSE);
+ /*
+ * XXX - Bizzarreness. remap creates an empty KEYMAP
+ * that the last key is supposed to point to.
+ */
+ curmap = ele->k_prefmap;
+ }
+ }
+ (void)doscan(curmap, c = *keys, NULL);
+ return (remap(curmap, c, funct, pref_map));
+}
+
+/*
+ * Wrapper for bindkey() that converts escapes.
+ */
+int
+dobindkey(KEYMAP *map, const char *func, const char *str)
+{
+ int i;
+
+ for (i = 0; *str && i < MAXKEY; i++) {
+ /* XXX - convert numbers w/ strol()? */
+ if (*str == '^' && *(str + 1) != '\0') {
+ key.k_chars[i] = CCHR(toupper((unsigned char)*++str));
+ } else if (*str == '\\' && *(str + 1) != '\0') {
+ switch (*++str) {
+ case '^':
+ key.k_chars[i] = '^';
+ break;
+ case 't':
+ case 'T':
+ key.k_chars[i] = '\t';
+ break;
+ case 'n':
+ case 'N':
+ key.k_chars[i] = *curbp->b_nlchr;
+ break;
+ case 'r':
+ case 'R':
+ key.k_chars[i] = '\r';
+ break;
+ case 'e':
+ case 'E':
+ key.k_chars[i] = CCHR('[');
+ break;
+ case '\\':
+ key.k_chars[i] = '\\';
+ break;
+ }
+ } else
+ key.k_chars[i] = *str;
+ str++;
+ }
+ key.k_count = i;
+ return (bindkey(&map, func, key.k_chars, key.k_count));
+}
+
+/*
+ * This function modifies the fundamental keyboard map.
+ */
+/* ARGSUSED */
+int
+bindtokey(int f, int n)
+{
+ return (dobind(fundamental_map, "Global set key: ", FALSE));
+}
+
+/*
+ * This function modifies the current mode's keyboard map.
+ */
+/* ARGSUSED */
+int
+localbind(int f, int n)
+{
+ return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
+ "Local set key: ", FALSE));
+}
+
+/*
+ * This function redefines a key in any keymap.
+ */
+/* ARGSUSED */
+int
+redefine_key(int f, int n)
+{
+ static char buf[48];
+ char tmp[32], *bufp;
+ KEYMAP *mp;
+
+ (void)strlcpy(buf, "Define key map: ", sizeof(buf));
+ if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ (void)strlcat(buf, tmp, sizeof(buf));
+ if ((mp = name_map(tmp)) == NULL)
+ return (dobeep_msgs("Unknown map ", tmp));
+
+ if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
+ return (FALSE);
+
+ return (dobind(mp, buf, FALSE));
+}
+
+/* ARGSUSED */
+int
+unbindtokey(int f, int n)
+{
+ return (dobind(fundamental_map, "Global unset key: ", TRUE));
+}
+
+/* ARGSUSED */
+int
+localunbind(int f, int n)
+{
+ return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
+ "Local unset key: ", TRUE));
+}
+
+/*
+ * Extended command. Call the message line routine to read in the command
+ * name and apply autocompletion to it. When it comes back, look the name
+ * up in the symbol table and run the command if it is found. Print an
+ * error if there is anything wrong.
+ */
+int
+extend(int f, int n)
+{
+ PF funct;
+ char xname[NXNAME], *bufp;
+
+ if (!(f & FFARG))
+ bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC);
+ else
+ bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if ((funct = name_function(bufp)) != NULL) {
+ if (macrodef) {
+ struct line *lp = maclcur;
+ macro[macrocount - 1].m_funct = funct;
+ maclcur = lp->l_bp;
+ maclcur->l_fp = lp->l_fp;
+ free(lp);
+ }
+ return ((*funct)(f, n));
+ }
+ return (dobeep_msg("[No match]"));
+}
+
+/*
+ * Define the commands needed to do startup-file processing.
+ * This code is mostly a kludge just so we can get startup-file processing.
+ *
+ * If you're serious about having this code, you should rewrite it.
+ * To wit:
+ * It has lots of funny things in it to make the startup-file look
+ * like a GNU startup file; mostly dealing with parens and semicolons.
+ * This should all vanish.
+ *
+ * We define eval-expression because it's easy. It can make
+ * *-set-key or define-key set an arbitrary key sequence, so it isn't
+ * useless.
+ */
+
+/*
+ * evalexpr - get one line from the user, and run it.
+ * Use strlen for length of line, assume user is not typing in a '\0' in the
+ * modeline. llen only used for foundparen() so old-school will be ok.
+ */
+/* ARGSUSED */
+int
+evalexpr(int f, int n)
+{
+ char exbuf[BUFSIZE], *bufp;
+ int llen;
+
+ if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
+ EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ llen = strlen(bufp);
+
+ return (excline(exbuf, llen, 1));
+}
+
+/*
+ * evalbuffer - evaluate the current buffer as line commands. Useful for
+ * testing startup files.
+ */
+/* ARGSUSED */
+int
+evalbuffer(int f, int n)
+{
+ struct line *lp;
+ struct buffer *bp = curbp;
+ int s, llen, lnum = 0;
+ static char excbuf[BUFSIZE];
+
+ for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
+ lnum++;
+ llen = llength(lp);
+ if (llen >= BUFSIZE)
+ return (FALSE);
+ (void)strncpy(excbuf, ltext(lp), llen);
+
+ /* make sure the line is terminated */
+ excbuf[llen] = '\0';
+ if ((s = excline(excbuf, llen, lnum)) != TRUE) {
+ cleanup();
+ return (s);
+ }
+ }
+ cleanup();
+ return (TRUE);
+}
+
+/*
+ * evalfile - go get a file and evaluate it as line commands. You can
+ * go get your own startup file if need be.
+ */
+/* ARGSUSED */
+int
+evalfile(int f, int n)
+{
+ char fname[NFILEN], *bufp;
+
+ if ((bufp = eread("Load file: ", fname, NFILEN,
+ EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ return (load(fname));
+}
+
+/*
+ * load - go load the file name we got passed.
+ */
+int
+load(const char *fname)
+{
+ int s = TRUE, line, ret;
+ int nbytes = 0;
+ char excbuf[BUFSIZE], fncpy[NFILEN];
+ FILE *ffp;
+
+ if ((fname = adjustname(fname, TRUE)) == NULL)
+ /* just to be careful */
+ return (FALSE);
+
+ ret = ffropen(&ffp, fname, NULL);
+ if (ret != FIOSUC) {
+ if (ret == FIODIR)
+ (void)ffclose(ffp, NULL);
+ return (FALSE);
+ }
+
+ /* keep a note of fname incase of errors in loaded file. */
+ (void)strlcpy(fncpy, fname, sizeof(fncpy));
+ line = 0;
+ while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
+ == FIOSUC) {
+ line++;
+ excbuf[nbytes] = '\0';
+ if (excline(excbuf, nbytes, line) != TRUE) {
+ s = FIOERR;
+ dobeep();
+ ewprintf("Error loading file %s at line %d", fncpy, line);
+ break;
+ }
+ }
+ (void)ffclose(ffp, NULL);
+ excbuf[nbytes] = '\0';
+ if (s != FIOEOF || (nbytes && excline(excbuf, nbytes, ++line) != TRUE))
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * excline - run a line from a load file or eval-expression.
+ */
+int
+excline(char *line, int llen, int lnum)
+{
+ PF fp;
+ struct line *lp, *np;
+ int status, c, f, n;
+ char *funcp, *tmp;
+ char *argp = NULL;
+ long nl;
+ int bind;
+ KEYMAP *curmap = NULL;
+#define BINDARG 0 /* this arg is key to bind (local/global set key) */
+#define BINDNO 1 /* not binding or non-quoted BINDARG */
+#define BINDNEXT 2 /* next arg " (define-key) */
+#define BINDDO 3 /* already found key to bind */
+#define BINDEXT 1 /* space for trailing \0 */
+
+ lp = NULL;
+
+ if (macrodef || inmacro)
+ return (dobeep_msg("Not now!"));
+
+ f = 0;
+ n = 1;
+ funcp = skipwhite(line);
+ if (*funcp == '\0')
+ return (TRUE); /* No error on blank lines */
+ if (*funcp == '(')
+ return (foundparen(funcp, llen, lnum));
+ line = parsetoken(funcp);
+ if (*line != '\0') {
+ *line++ = '\0';
+ line = skipwhite(line);
+ if (ISDIGIT(*line) || *line == '-') {
+ argp = line;
+ line = parsetoken(line);
+ }
+ }
+ if (argp != NULL) {
+ f = FFARG;
+ nl = strtol(argp, &tmp, 10);
+ if (*tmp != '\0')
+ return (FALSE);
+ if (nl >= INT_MAX || nl <= INT_MIN)
+ return (FALSE);
+ n = (int)nl;
+ }
+ if ((fp = name_function(funcp)) == NULL)
+ return (dobeep_msgs("Unknown function: ", funcp));
+
+ if (fp == bindtokey || fp == unbindtokey) {
+ bind = BINDARG;
+ curmap = fundamental_map;
+ } else if (fp == localbind || fp == localunbind) {
+ bind = BINDARG;
+ curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
+ } else if (fp == redefine_key)
+ bind = BINDNEXT;
+ else
+ bind = BINDNO;
+ /* Pack away all the args now... */
+ if ((np = lalloc(0)) == FALSE)
+ return (FALSE);
+ np->l_fp = np->l_bp = maclcur = np;
+ while (*line != '\0') {
+ argp = skipwhite(line);
+ if (*argp == '\0')
+ break;
+ line = parsetoken(argp);
+ if (*argp != '"') {
+ if (*argp == '\'')
+ ++argp;
+ if ((lp = lalloc((int) (line - argp) + BINDEXT)) ==
+ NULL) {
+ status = FALSE;
+ goto cleanup;
+ }
+ bcopy(argp, ltext(lp), (int)(line - argp));
+ /* don't count BINDEXT */
+ lp->l_used--;
+ if (bind == BINDARG)
+ bind = BINDNO;
+ } else {
+ /* quoted strings are special */
+ ++argp;
+ if (bind != BINDARG) {
+ lp = lalloc((int)(line - argp) + BINDEXT);
+ if (lp == NULL) {
+ status = FALSE;
+ goto cleanup;
+ }
+ lp->l_used = 0;
+ } else
+ key.k_count = 0;
+ while (*argp != '"' && *argp != '\0') {
+ if (*argp != '\\')
+ c = *argp++;
+ else {
+ switch (*++argp) {
+ case 't':
+ case 'T':
+ c = CCHR('I');
+ break;
+ case 'n':
+ case 'N':
+ c = CCHR('J');
+ break;
+ case 'r':
+ case 'R':
+ c = CCHR('M');
+ break;
+ case 'e':
+ case 'E':
+ c = CCHR('[');
+ break;
+ case '^':
+ /*
+ * split into two statements
+ * due to bug in OSK cpp
+ */
+ c = CHARMASK(*++argp);
+ c = ISLOWER(c) ?
+ CCHR(TOUPPER(c)) : CCHR(c);
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ c = *argp - '0';
+ if (argp[1] <= '7' &&
+ argp[1] >= '0') {
+ c <<= 3;
+ c += *++argp - '0';
+ if (argp[1] <= '7' &&
+ argp[1] >= '0') {
+ c <<= 3;
+ c += *++argp
+ - '0';
+ }
+ }
+ break;
+ case 'f':
+ case 'F':
+ c = *++argp - '0';
+ if (ISDIGIT(argp[1])) {
+ c *= 10;
+ c += *++argp - '0';
+ }
+ c += KFIRST;
+ break;
+ default:
+ c = CHARMASK(*argp);
+ break;
+ }
+ argp++;
+ }
+ if (bind == BINDARG)
+ key.k_chars[key.k_count++] = c;
+ else
+ lp->l_text[lp->l_used++] = c;
+ }
+ if (*line)
+ line++;
+ }
+ switch (bind) {
+ case BINDARG:
+ bind = BINDDO;
+ break;
+ case BINDNEXT:
+ lp->l_text[lp->l_used] = '\0';
+ if ((curmap = name_map(lp->l_text)) == NULL) {
+ (void)dobeep_msgs("No such mode: ", lp->l_text);
+ status = FALSE;
+ free(lp);
+ goto cleanup;
+ }
+ free(lp);
+ bind = BINDARG;
+ break;
+ default:
+ lp->l_fp = np->l_fp;
+ lp->l_bp = np;
+ np->l_fp = lp;
+ np = lp;
+ }
+ }
+ switch (bind) {
+ default:
+ (void)dobeep_msg("Bad args to set key");
+ status = FALSE;
+ break;
+ case BINDDO:
+ if (fp != unbindtokey && fp != localunbind) {
+ lp->l_text[lp->l_used] = '\0';
+ status = bindkey(&curmap, lp->l_text, key.k_chars,
+ key.k_count);
+ } else
+ status = bindkey(&curmap, NULL, key.k_chars,
+ key.k_count);
+ break;
+ case BINDNO:
+ inmacro = TRUE;
+ maclcur = maclcur->l_fp;
+ status = (*fp)(f, n);
+ inmacro = FALSE;
+ }
+cleanup:
+ lp = maclcur->l_fp;
+ while (lp != maclcur) {
+ np = lp->l_fp;
+ free(lp);
+ lp = np;
+ }
+ free(lp);
+ maclhead = NULL;
+ macrodef = FALSE;
+ return (status);
+}
+
+/*
+ * a pair of utility functions for the above
+ */
+char *
+skipwhite(char *s)
+{
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if ((*s == ';') || (*s == '#'))
+ *s = '\0';
+ return (s);
+}
+
+static char *
+parsetoken(char *s)
+{
+ if (*s != '"') {
+ while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(')
+ s++;
+ if (*s == ';')
+ *s = '\0';
+ } else
+ do {
+ /*
+ * Strings get special treatment.
+ * Beware: You can \ out the end of the string!
+ */
+ if (*s == '\\')
+ ++s;
+ } while (*++s != '"' && *s != '\0');
+ return (s);
+}
Index: contrib/mg/extensions.c
===================================================================
--- /dev/null
+++ contrib/mg/extensions.c
@@ -0,0 +1,33 @@
+/*
+ * This file is in the public domain.
+ *
+ * Authors:
+ * * Brian Callahan
+ * * Kyle Isom
+ */
+
+#include <sys/queue.h>
+
+#include <signal.h>
+#include <stdio.h>
+
+#include "def.h"
+
+/*
+ * Extensions unique to Mg portable.
+ */
+
+int shownlprompt = TRUE;
+
+/* Check for a newline at the end of a file? */
+int
+togglenewlineprompt(int f, int n)
+{
+
+ if (shownlprompt == TRUE)
+ shownlprompt = FALSE;
+ else
+ shownlprompt = TRUE;
+
+ return (TRUE);
+}
Index: contrib/mg/file.c
===================================================================
--- /dev/null
+++ contrib/mg/file.c
@@ -0,0 +1,778 @@
+/* $OpenBSD: file.c,v 1.102 2019/06/22 15:03:43 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * File commands.
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+
+size_t xdirname(char *, const char *, size_t);
+
+/*
+ * Insert a file into the current buffer. Real easy - just call the
+ * insertfile routine with the file name.
+ */
+/* ARGSUSED */
+int
+fileinsert(int f, int n)
+{
+ char fname[NFILEN], *bufp, *adjf;
+
+ if (getbufcwd(fname, sizeof(fname)) != TRUE)
+ fname[0] = '\0';
+ bufp = eread("Insert file: ", fname, NFILEN,
+ EFNEW | EFCR | EFFILE | EFDEF);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ adjf = adjustname(bufp, TRUE);
+ if (adjf == NULL)
+ return (FALSE);
+ return (insertfile(adjf, NULL, FALSE));
+}
+
+/*
+ * Select a file for editing. If the file is a directory, invoke dired.
+ * Otherwise, look around to see if you can find the file in another buffer;
+ * if you can find it, just switch to the buffer. If you cannot find the
+ * file, create a new buffer, read in the text, and switch to the new buffer.
+ */
+/* ARGSUSED */
+int
+filevisit(int f, int n)
+{
+ struct buffer *bp;
+ char fname[NFILEN], *bufp, *adjf;
+ int status;
+
+ if (getbufcwd(fname, sizeof(fname)) != TRUE)
+ fname[0] = '\0';
+ bufp = eread("Find file: ", fname, NFILEN,
+ EFNEW | EFCR | EFFILE | EFDEF);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ adjf = adjustname(fname, TRUE);
+ if (adjf == NULL)
+ return (FALSE);
+ if (fisdir(adjf) == TRUE)
+ return (do_dired(adjf));
+ if ((bp = findbuffer(adjf)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFFULL) != TRUE)
+ return (FALSE);
+ if (bp->b_fname[0] == '\0') {
+ if ((status = readin(adjf)) != TRUE)
+ killbuffer(bp);
+ return (status);
+ }
+ return (TRUE);
+}
+
+/*
+ * Replace the current file with an alternate one. Semantics for finding
+ * the replacement file are the same as 'filevisit', except the current
+ * buffer is killed before the switch. If the kill fails, or is aborted,
+ * revert to the original file.
+ */
+/* ARGSUSED */
+int
+filevisitalt(int f, int n)
+{
+ char fname[NFILEN], *bufp;
+
+ if (getbufcwd(fname, sizeof(fname)) != TRUE)
+ fname[0] = '\0';
+ bufp = eread("Find alternate file: ", fname, NFILEN,
+ EFNEW | EFCR | EFFILE | EFDEF);
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+
+ return (do_filevisitalt(fname));
+}
+
+int
+do_filevisitalt(char *fn)
+{
+ struct buffer *bp;
+ int status;
+ char *adjf;
+
+ status = killbuffer(curbp);
+ if (status == ABORT || status == FALSE)
+ return (ABORT);
+
+ adjf = adjustname(fn, TRUE);
+ if (adjf == NULL)
+ return (FALSE);
+ if (fisdir(adjf) == TRUE)
+ return (do_dired(adjf));
+ if ((bp = findbuffer(adjf)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ if (showbuffer(bp, curwp, WFFULL) != TRUE)
+ return (FALSE);
+ if (bp->b_fname[0] == '\0') {
+ if ((status = readin(adjf)) != TRUE)
+ killbuffer(bp);
+ return (status);
+ }
+ return (TRUE);
+}
+
+int
+filevisitro(int f, int n)
+{
+ int error;
+
+ error = filevisit(f, n);
+ if (error != TRUE)
+ return (error);
+ curbp->b_flag |= BFREADONLY;
+ return (TRUE);
+}
+
+/*
+ * Pop to a file in the other window. Same as the last function, but uses
+ * popbuf instead of showbuffer.
+ */
+/* ARGSUSED */
+int
+poptofile(int f, int n)
+{
+ struct buffer *bp;
+ struct mgwin *wp;
+ char fname[NFILEN], *adjf, *bufp;
+ int status;
+
+ if (getbufcwd(fname, sizeof(fname)) != TRUE)
+ fname[0] = '\0';
+ if ((bufp = eread("Find file in other window: ", fname, NFILEN,
+ EFNEW | EFCR | EFFILE | EFDEF)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ adjf = adjustname(fname, TRUE);
+ if (adjf == NULL)
+ return (FALSE);
+ if (fisdir(adjf) == TRUE)
+ return (do_dired(adjf));
+ if ((bp = findbuffer(adjf)) == NULL)
+ return (FALSE);
+ if (bp == curbp)
+ return (splitwind(f, n));
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ if (bp->b_fname[0] == '\0') {
+ if ((status = readin(adjf)) != TRUE)
+ killbuffer(bp);
+ return (status);
+ }
+ return (TRUE);
+}
+
+/*
+ * Read the file "fname" into the current buffer. Make all of the text
+ * in the buffer go away, after checking for unsaved changes. This is
+ * called by the "read" command, the "visit" command, and the mainline
+ * (for "mg file").
+ */
+int
+readin(char *fname)
+{
+ struct mgwin *wp;
+ struct stat statbuf;
+ int status, i, ro = FALSE;
+ PF *ael;
+ char dp[NFILEN];
+
+ /* might be old */
+ if (bclear(curbp) != TRUE)
+ return (TRUE);
+ /* Clear readonly. May be set by autoexec path */
+ curbp->b_flag &= ~BFREADONLY;
+ if ((status = insertfile(fname, fname, TRUE)) != TRUE) {
+ dobeep();
+ ewprintf("File is not readable: %s", fname);
+ return (FALSE);
+ }
+
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wp->w_dotp = wp->w_linep = bfirstlp(curbp);
+ wp->w_doto = 0;
+ wp->w_markp = NULL;
+ wp->w_marko = 0;
+ }
+ }
+
+ /*
+ * Call auto-executing function if we need to.
+ */
+ if ((ael = find_autoexec(fname)) != NULL) {
+ for (i = 0; ael[i] != NULL; i++)
+ (*ael[i])(0, 1);
+ free(ael);
+ }
+
+ /* no change */
+ curbp->b_flag &= ~BFCHG;
+
+ /*
+ * Set the buffer READONLY flag if any of following are true:
+ * 1. file is a directory.
+ * 2. file is read-only.
+ * 3. file doesn't exist and directory is read-only.
+ */
+ if (fisdir(fname) == TRUE) {
+ ro = TRUE;
+ } else if ((access(fname, W_OK) == -1)) {
+ if (errno != ENOENT) {
+ ro = TRUE;
+ } else if (errno == ENOENT) {
+ (void)xdirname(dp, fname, sizeof(dp));
+ (void)strlcat(dp, "/", sizeof(dp));
+
+ /* Missing directory; keep buffer rw, like emacs */
+ if (stat(dp, &statbuf) == -1 && errno == ENOENT) {
+ if (eyorn("Missing directory, create") == TRUE)
+ (void)do_makedir(dp);
+ } else if (access(dp, W_OK) == -1 && errno == EACCES) {
+ ewprintf("File not found and directory"
+ " write-protected");
+ ro = TRUE;
+ }
+ }
+ }
+ if (ro == TRUE)
+ curbp->b_flag |= BFREADONLY;
+
+ if (startrow) {
+ gotoline(FFARG, startrow);
+ startrow = 0;
+ }
+
+ undo_add_modified();
+ return (status);
+}
+
+/*
+ * NB, getting file attributes is done here under control of a flag
+ * rather than in readin, which would be cleaner. I was concerned
+ * that some operating system might require the file to be open
+ * in order to get the information. Similarly for writing.
+ */
+
+/*
+ * Insert a file in the current buffer, after dot. If file is a directory,
+ * and 'replacebuf' is TRUE, invoke dired mode, else die with an error.
+ * If file is a regular file, set mark at the end of the text inserted;
+ * point at the beginning. Return a standard status. Print a summary
+ * (lines read, error message) out as well. This routine also does the
+ * read end of backup processing. The BFBAK flag, if set in a buffer,
+ * says that a backup should be taken. It is set when a file is read in,
+ * but not on a new file. You don't need to make a backup copy of nothing.
+ */
+
+static char *line = NULL;
+static int linesize = 0;
+
+int
+insertfile(char *fname, char *newname, int replacebuf)
+{
+ struct buffer *bp;
+ struct line *lp1, *lp2;
+ struct line *olp; /* line we started at */
+ struct mgwin *wp;
+ int nbytes, s, nline = 0, siz, x, x2;
+ int opos; /* offset we started at */
+ int oline; /* original line number */
+ FILE *ffp;
+
+ if (replacebuf == TRUE)
+ x = undo_enable(FFRAND, 0);
+ else
+ x = undo_enabled();
+
+ lp1 = NULL;
+ if (line == NULL) {
+ line = malloc(NLINE);
+ if (line == NULL)
+ panic("out of memory");
+ linesize = NLINE;
+ }
+
+ /* cheap */
+ bp = curbp;
+ if (newname != NULL) {
+ (void)strlcpy(bp->b_fname, newname, sizeof(bp->b_fname));
+ (void)xdirname(bp->b_cwd, newname, sizeof(bp->b_cwd));
+ (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd));
+ }
+
+ /* hard file open */
+ if ((s = ffropen(&ffp, fname, (replacebuf == TRUE) ? bp : NULL))
+ == FIOERR)
+ goto out;
+ if (s == FIOFNF) {
+ /* file not found */
+ if (newname != NULL)
+ ewprintf("(New file)");
+ else
+ ewprintf("(File not found)");
+ goto out;
+ } else if (s == FIODIR) {
+ /* file was a directory */
+ if (replacebuf == FALSE) {
+ dobeep();
+ ewprintf("Cannot insert: file is a directory, %s",
+ fname);
+ goto cleanup;
+ }
+ killbuffer(bp);
+ bp = dired_(fname);
+ undo_enable(FFRAND, x);
+ if (bp == NULL)
+ return (FALSE);
+ curbp = bp;
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+ } else {
+ (void)xdirname(bp->b_cwd, fname, sizeof(bp->b_cwd));
+ (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd));
+ }
+ opos = curwp->w_doto;
+ oline = curwp->w_dotline;
+ /*
+ * Open a new line at dot and start inserting after it.
+ * We will delete this newline after insertion.
+ * Disable undo, as we create the undo record manually.
+ */
+ x2 = undo_enable(FFRAND, 0);
+ (void)lnewline();
+ olp = lback(curwp->w_dotp);
+ undo_enable(FFRAND, x2);
+
+ nline = 0;
+ siz = 0;
+ while ((s = ffgetline(ffp, line, linesize, &nbytes)) != FIOERR) {
+retry:
+ siz += nbytes + 1;
+ switch (s) {
+ case FIOSUC:
+ /* FALLTHRU */
+ case FIOEOF:
+ ++nline;
+ if ((lp1 = lalloc(nbytes)) == NULL) {
+ /* keep message on the display */
+ s = FIOERR;
+ undo_add_insert(olp, opos,
+ siz - nbytes - 1 - 1);
+ goto endoffile;
+ }
+ bcopy(line, &ltext(lp1)[0], nbytes);
+ lp2 = lback(curwp->w_dotp);
+ lp2->l_fp = lp1;
+ lp1->l_fp = curwp->w_dotp;
+ lp1->l_bp = lp2;
+ curwp->w_dotp->l_bp = lp1;
+ if (s == FIOEOF) {
+ undo_add_insert(olp, opos, siz - 1);
+ goto endoffile;
+ }
+ break;
+ case FIOLONG: {
+ /* a line too long to fit in our buffer */
+ char *cp;
+ int newsize;
+
+ newsize = linesize * 2;
+ if (newsize < 0 ||
+ (cp = malloc(newsize)) == NULL) {
+ dobeep();
+ ewprintf("Could not allocate %d bytes",
+ newsize);
+ s = FIOERR;
+ goto endoffile;
+ }
+ bcopy(line, cp, linesize);
+ free(line);
+ line = cp;
+ s = ffgetline(ffp, line + linesize, linesize,
+ &nbytes);
+ nbytes += linesize;
+ linesize = newsize;
+ if (s == FIOERR)
+ goto endoffile;
+ goto retry;
+ }
+ default:
+ dobeep();
+ ewprintf("Unknown code %d reading file", s);
+ s = FIOERR;
+ break;
+ }
+ }
+endoffile:
+ /* ignore errors */
+ (void)ffclose(ffp, NULL);
+ /* don't zap an error */
+ if (s == FIOEOF) {
+ if (nline == 1)
+ ewprintf("(Read 1 line)");
+ else
+ ewprintf("(Read %d lines)", nline);
+ }
+ /* set mark at the end of the text */
+ curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp);
+ curwp->w_marko = llength(curwp->w_markp);
+ curwp->w_markline = oline + nline + 1;
+ /*
+ * if we are at the end of the file, ldelnewline is a no-op,
+ * but we still need to decrement the line and markline counts
+ * as we've accounted for this fencepost in our arithmetic
+ */
+ if (lforw(curwp->w_dotp) == curwp->w_bufp->b_headp) {
+ curwp->w_bufp->b_lines--;
+ curwp->w_markline--;
+ } else
+ (void)ldelnewline();
+ curwp->w_dotp = olp;
+ curwp->w_doto = opos;
+ curwp->w_dotline = oline;
+ if (olp == curbp->b_headp)
+ curwp->w_dotp = lforw(olp);
+ if (newname != NULL)
+ bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */
+ else
+ bp->b_flag |= BFCHG;
+ /*
+ * If the insert was at the end of buffer, set lp1 to the end of
+ * buffer line, and lp2 to the beginning of the newly inserted text.
+ * (Otherwise lp2 is set to NULL.) This is used below to set
+ * pointers in other windows correctly if they are also at the end of
+ * buffer.
+ */
+ lp1 = bp->b_headp;
+ if (curwp->w_markp == lp1) {
+ lp2 = curwp->w_dotp;
+ } else {
+ /* delete extraneous newline */
+ (void)ldelnewline();
+out: lp2 = NULL;
+ }
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wp->w_rflag |= WFMODE | WFEDIT;
+ if (wp != curwp && lp2 != NULL) {
+ if (wp->w_dotp == lp1)
+ wp->w_dotp = lp2;
+ if (wp->w_markp == lp1)
+ wp->w_markp = lp2;
+ if (wp->w_linep == lp1)
+ wp->w_linep = lp2;
+ }
+ }
+ }
+ bp->b_lines += nline;
+cleanup:
+ undo_enable(FFRAND, x);
+
+ /* return FALSE if error */
+ return (s != FIOERR);
+}
+
+/*
+ * Ask for a file name and write the contents of the current buffer to that
+ * file. Update the remembered file name and clear the buffer changed flag.
+ * This handling of file names is different from the earlier versions and
+ * is more compatible with Gosling EMACS than with ITS EMACS.
+ */
+/* ARGSUSED */
+int
+filewrite(int f, int n)
+{
+ struct stat statbuf;
+ int s;
+ char fname[NFILEN], bn[NBUFN], tmp[NFILEN + 25];
+ char *adjfname, *bufp;
+ FILE *ffp;
+
+ if (getbufcwd(fname, sizeof(fname)) != TRUE)
+ fname[0] = '\0';
+ if ((bufp = eread("Write file: ", fname, NFILEN,
+ EFDEF | EFNEW | EFCR | EFFILE)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+
+ adjfname = adjustname(fname, TRUE);
+ if (adjfname == NULL)
+ return (FALSE);
+
+ /* Check if file exists; write checks done later */
+ if (stat(adjfname, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ dobeep();
+ ewprintf("%s is a directory", adjfname);
+ return (FALSE);
+ }
+ snprintf(tmp, sizeof(tmp), "File `%s' exists; overwrite",
+ adjfname);
+ if ((s = eyorn(tmp)) != TRUE)
+ return (s);
+ }
+
+ /* old attributes are no longer current */
+ bzero(&curbp->b_fi, sizeof(curbp->b_fi));
+ if ((s = writeout(&ffp, curbp, adjfname)) == TRUE) {
+ (void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname));
+ if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE)
+ (void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd));
+ if (augbname(bn, curbp->b_fname, sizeof(bn))
+ == FALSE)
+ return (FALSE);
+ free(curbp->b_bname);
+ if ((curbp->b_bname = strdup(bn)) == NULL)
+ return (FALSE);
+ (void)fupdstat(curbp);
+ curbp->b_flag &= ~(BFBAK | BFCHG);
+ upmodes(curbp);
+ undo_add_boundary(FFRAND, 1);
+ undo_add_modified();
+ }
+ return (s);
+}
+
+/*
+ * Save the contents of the current buffer back into its associated file.
+ */
+static int makebackup = TRUE;
+
+/* ARGSUSED */
+int
+filesave(int f, int n)
+{
+ if (curbp->b_fname[0] == '\0')
+ return (filewrite(f, n));
+ else
+ return (buffsave(curbp));
+}
+
+/*
+ * Save the contents of the buffer argument into its associated file. Do
+ * nothing if there have been no changes (is this a bug, or a feature?).
+ * Error if there is no remembered file name. If this is the first write
+ * since the read or visit, then a backup copy of the file is made.
+ * Allow user to select whether or not to make backup files by looking at
+ * the value of makebackup.
+ */
+int
+buffsave(struct buffer *bp)
+{
+ int s;
+ FILE *ffp;
+
+ /* return, no changes */
+ if ((bp->b_flag & BFCHG) == 0) {
+ ewprintf("(No changes need to be saved)");
+ return (TRUE);
+ }
+
+ /* must have a name */
+ if (bp->b_fname[0] == '\0') {
+ dobeep();
+ ewprintf("No file name");
+ return (FALSE);
+ }
+
+ /* Ensure file has not been modified elsewhere */
+ /* We don't use the ignore flag here */
+ if (fchecktime(bp) != TRUE) {
+ if ((s = eyesno("File has changed on disk since last save. "
+ "Save anyway")) != TRUE)
+ return (s);
+ }
+
+ if (makebackup && (bp->b_flag & BFBAK)) {
+ s = fbackupfile(bp->b_fname);
+ /* hard error */
+ if (s == ABORT)
+ return (FALSE);
+ /* softer error */
+ if (s == FALSE &&
+ (s = eyesno("Backup error, save anyway")) != TRUE)
+ return (s);
+ }
+ if ((s = writeout(&ffp, bp, bp->b_fname)) == TRUE) {
+ (void)fupdstat(bp);
+ bp->b_flag &= ~(BFCHG | BFBAK);
+ upmodes(bp);
+ undo_add_boundary(FFRAND, 1);
+ undo_add_modified();
+ }
+ return (s);
+}
+
+/*
+ * Since we don't have variables (we probably should) this is a command
+ * processor for changing the value of the make backup flag. If no argument
+ * is given, sets makebackup to true, so backups are made. If an argument is
+ * given, no backup files are made when saving a new version of a file.
+ */
+/* ARGSUSED */
+int
+makebkfile(int f, int n)
+{
+ if (f & FFARG)
+ makebackup = n > 0;
+ else
+ makebackup = !makebackup;
+ ewprintf("Backup files %sabled", makebackup ? "en" : "dis");
+ return (TRUE);
+}
+
+/*
+ * NB: bp is passed to both ffwopen and ffclose because some
+ * attribute information may need to be updated at open time
+ * and others after the close. This is OS-dependent. Note
+ * that the ff routines are assumed to be able to tell whether
+ * the attribute information has been set up in this buffer
+ * or not.
+ */
+
+/*
+ * This function performs the details of file writing; writing the file
+ * in buffer bp to file fn. Uses the file management routines in the
+ * "fileio.c" package. Most of the grief is checking of some sort.
+ * You may want to call fupdstat() after using this function.
+ */
+int
+writeout(FILE ** ffp, struct buffer *bp, char *fn)
+{
+ struct stat statbuf;
+ struct line *lpend;
+ int s, eobnl;
+ char dp[NFILEN];
+
+ if (stat(fn, &statbuf) == -1 && errno == ENOENT) {
+ errno = 0;
+ (void)xdirname(dp, fn, sizeof(dp));
+ (void)strlcat(dp, "/", sizeof(dp));
+ if (access(dp, W_OK) && errno == EACCES) {
+ dobeep();
+ ewprintf("Directory %s write-protected", dp);
+ return (FIOERR);
+ } else if (errno == ENOENT) {
+ dobeep();
+ ewprintf("%s: no such directory", dp);
+ return (FIOERR);
+ }
+ }
+ lpend = bp->b_headp;
+ eobnl = 0;
+ if (llength(lback(lpend)) != 0) {
+ eobnl = eyorn("No newline at end of file, add one");
+ if (eobnl != TRUE && eobnl != FALSE)
+ return (eobnl); /* abort */
+ }
+ /* open writes message */
+ if ((s = ffwopen(ffp, fn, bp)) != FIOSUC)
+ return (FALSE);
+ s = ffputbuf(*ffp, bp, eobnl);
+ if (s == FIOSUC) {
+ /* no write error */
+ s = ffclose(*ffp, bp);
+ if (s == FIOSUC)
+ ewprintf("Wrote %s", fn);
+ } else {
+ /* print a message indicating write error */
+ (void)ffclose(*ffp, bp);
+ dobeep();
+ ewprintf("Unable to write %s", fn);
+ }
+ return (s == FIOSUC);
+}
+
+/*
+ * Tag all windows for bp (all windows if bp == NULL) as needing their
+ * mode line updated.
+ */
+void
+upmodes(struct buffer *bp)
+{
+ struct mgwin *wp;
+
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp)
+ if (bp == NULL || curwp->w_bufp == bp)
+ wp->w_rflag |= WFMODE;
+}
+
+/*
+ * dirname using strlcpy semantic.
+ * Like dirname() except an empty string is returned in
+ * place of "/". This means we can always add a trailing
+ * slash and be correct.
+ * Address portability issues by copying argument
+ * before using. Some implementations modify the input string.
+ */
+size_t
+xdirname(char *dp, const char *path, size_t dplen)
+{
+ char ts[NFILEN];
+ size_t len;
+
+ (void)strlcpy(ts, path, NFILEN);
+ len = strlcpy(dp, dirname(ts), dplen);
+ if (dplen > 0 && dp[0] == '/' && dp[1] == '\0') {
+ dp[0] = '\0';
+ len = 0;
+ }
+ return (len);
+}
+
+/*
+ * basename using strlcpy/strlcat semantic.
+ * Address portability issue by copying argument
+ * before using: some implementations modify the input string.
+ */
+size_t
+xbasename(char *bp, const char *path, size_t bplen)
+{
+ char ts[NFILEN];
+
+ (void)strlcpy(ts, path, NFILEN);
+ return (strlcpy(bp, basename(ts), bplen));
+}
+
+/*
+ * The adjusted file name refers to a directory, so open dired mode.
+ */
+int
+do_dired(char *adjf)
+{
+ struct buffer *bp;
+
+ if ((bp = dired_(adjf)) == FALSE)
+ return (FALSE);
+ curbp = bp;
+ return (showbuffer(bp, curwp, WFFULL | WFMODE));
+}
Index: contrib/mg/fileio.c
===================================================================
--- /dev/null
+++ contrib/mg/fileio.c
@@ -0,0 +1,752 @@
+/* $OpenBSD: fileio.c,v 1.108 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * POSIX fileio.c
+ */
+
+#include <sys/queue.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+#include "kbd.h"
+#include "pathnames.h"
+
+static char *bkuplocation(const char *);
+static int bkupleavetmp(const char *);
+
+static char *bkupdir;
+static int leavetmp = 0; /* 1 = leave any '~' files in tmp dir */
+
+/*
+ * Open a file for reading.
+ */
+int
+ffropen(FILE ** ffp, const char *fn, struct buffer *bp)
+{
+ if ((*ffp = fopen(fn, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (FIOFNF);
+ return (FIOERR);
+ }
+
+ /* If 'fn' is a directory open it with dired. */
+ if (fisdir(fn) == TRUE)
+ return (FIODIR);
+
+ ffstat(*ffp, bp);
+
+ return (FIOSUC);
+}
+
+/*
+ * Update stat/dirty info
+ */
+void
+ffstat(FILE *ffp, struct buffer *bp)
+{
+ struct stat sb;
+
+ if (bp && fstat(fileno(ffp), &sb) == 0) {
+ /* set highorder bit to make sure this isn't all zero */
+ bp->b_fi.fi_mode = sb.st_mode | 0x8000;
+ bp->b_fi.fi_uid = sb.st_uid;
+ bp->b_fi.fi_gid = sb.st_gid;
+ bp->b_fi.fi_mtime = sb.st_mtimespec;
+ /* Clear the ignore flag */
+ bp->b_flag &= ~(BFIGNDIRTY | BFDIRTY);
+ }
+}
+
+/*
+ * Update the status/dirty info. If there is an error,
+ * there's not a lot we can do.
+ */
+int
+fupdstat(struct buffer *bp)
+{
+ FILE *ffp;
+
+ if ((ffp = fopen(bp->b_fname, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (FIOFNF);
+ return (FIOERR);
+ }
+ ffstat(ffp, bp);
+ (void)ffclose(ffp, bp);
+ return (FIOSUC);
+}
+
+/*
+ * Open a file for writing.
+ */
+int
+ffwopen(FILE ** ffp, const char *fn, struct buffer *bp)
+{
+ int fd;
+ mode_t fmode = DEFFILEMODE;
+
+ if (bp && bp->b_fi.fi_mode)
+ fmode = bp->b_fi.fi_mode & 07777;
+
+ fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, fmode);
+ if (fd == -1) {
+ ffp = NULL;
+ dobeep();
+ ewprintf("Cannot open file for writing : %s", strerror(errno));
+ return (FIOERR);
+ }
+
+ if ((*ffp = fdopen(fd, "w")) == NULL) {
+ dobeep();
+ ewprintf("Cannot open file for writing : %s", strerror(errno));
+ close(fd);
+ return (FIOERR);
+ }
+
+ /*
+ * If we have file information, use it. We don't bother to check for
+ * errors, because there's no a lot we can do about it. Certainly
+ * trying to change ownership will fail if we aren't root. That's
+ * probably OK. If we don't have info, no need to get it, since any
+ * future writes will do the same thing.
+ */
+ if (bp && bp->b_fi.fi_mode) {
+ fchmod(fd, bp->b_fi.fi_mode & 07777);
+ fchown(fd, bp->b_fi.fi_uid, bp->b_fi.fi_gid);
+ }
+ return (FIOSUC);
+}
+
+/*
+ * Close a file.
+ */
+/* ARGSUSED */
+int
+ffclose(FILE *ffp, struct buffer *bp)
+{
+ if (fclose(ffp) == 0)
+ return (FIOSUC);
+ return (FIOERR);
+}
+
+/*
+ * Write a buffer to the already opened file. bp points to the
+ * buffer. Return the status.
+ */
+int
+ffputbuf(FILE *ffp, struct buffer *bp, int eobnl)
+{
+ struct line *lp, *lpend;
+
+ lpend = bp->b_headp;
+
+ for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) {
+ if (fwrite(ltext(lp), 1, llength(lp), ffp) != llength(lp)) {
+ dobeep();
+ ewprintf("Write I/O error");
+ return (FIOERR);
+ }
+ if (lforw(lp) != lpend) /* no implied \n on last line */
+ putc(*bp->b_nlchr, ffp);
+ }
+ if (eobnl) {
+ lnewline_at(lback(lpend), llength(lback(lpend)));
+ putc(*bp->b_nlchr, ffp);
+ }
+ return (FIOSUC);
+}
+
+/*
+ * Read a line from a file, and store the bytes
+ * in the supplied buffer. Stop on end of file or end of
+ * line. When FIOEOF is returned, there is a valid line
+ * of data without the normally implied \n.
+ * If the line length exceeds nbuf, FIOLONG is returned.
+ */
+int
+ffgetline(FILE *ffp, char *buf, int nbuf, int *nbytes)
+{
+ int c, i;
+
+ i = 0;
+ while ((c = getc(ffp)) != EOF && c != *curbp->b_nlchr) {
+ buf[i++] = c;
+ if (i >= nbuf)
+ return (FIOLONG);
+ }
+ if (c == EOF && ferror(ffp) != FALSE) {
+ dobeep();
+ ewprintf("File read error");
+ return (FIOERR);
+ }
+ *nbytes = i;
+ return (c == EOF ? FIOEOF : FIOSUC);
+}
+
+/*
+ * Make a backup copy of "fname". On Unix the backup has the same
+ * name as the original file, with a "~" on the end; this seems to
+ * be newest of the new-speak. The error handling is all in "file.c".
+ * We do a copy instead of a rename since otherwise another process
+ * with an open fd will get the backup, not the new file. This is
+ * a problem when using mg with things like crontab and vipw.
+ */
+int
+fbackupfile(const char *fn)
+{
+ struct stat sb;
+ struct timespec new_times[2];
+ int from, to, serrno;
+ ssize_t nread;
+ char buf[BUFSIZ];
+ char *nname, *tname, *bkpth;
+
+ if (stat(fn, &sb) == -1) {
+ dobeep();
+ ewprintf("Can't stat %s : %s", fn, strerror(errno));
+ return (FALSE);
+ }
+
+ if ((bkpth = bkuplocation(fn)) == NULL)
+ return (FALSE);
+
+ if (asprintf(&nname, "%s~", bkpth) == -1) {
+ dobeep();
+ ewprintf("Can't allocate backup file name : %s", strerror(errno));
+ free(bkpth);
+ return (ABORT);
+ }
+ if (asprintf(&tname, "%s.XXXXXXXXXX", bkpth) == -1) {
+ dobeep();
+ ewprintf("Can't allocate temp file name : %s", strerror(errno));
+ free(bkpth);
+ free(nname);
+ return (ABORT);
+ }
+ free(bkpth);
+
+ if ((from = open(fn, O_RDONLY)) == -1) {
+ free(nname);
+ free(tname);
+ return (FALSE);
+ }
+ to = mkstemp(tname);
+ if (to == -1) {
+ serrno = errno;
+ close(from);
+ free(nname);
+ free(tname);
+ errno = serrno;
+ return (FALSE);
+ }
+ while ((nread = read(from, buf, sizeof(buf))) > 0) {
+ if (write(to, buf, (size_t)nread) != nread) {
+ nread = -1;
+ break;
+ }
+ }
+ serrno = errno;
+ (void) fchmod(to, (sb.st_mode & 0777));
+
+ /* copy the mtime to the backupfile */
+ new_times[0] = sb.st_atim;
+ new_times[1] = sb.st_mtim;
+ futimens(to, new_times);
+
+ close(from);
+ close(to);
+ if (nread == -1) {
+ if (unlink(tname) == -1)
+ ewprintf("Can't unlink temp : %s", strerror(errno));
+ } else {
+ if (rename(tname, nname) == -1) {
+ ewprintf("Can't rename temp : %s", strerror(errno));
+ (void) unlink(tname);
+ nread = -1;
+ }
+ }
+ free(nname);
+ free(tname);
+ errno = serrno;
+
+ return (nread == -1 ? FALSE : TRUE);
+}
+
+/*
+ * Convert "fn" to a canonicalized absolute filename, replacing
+ * a leading ~/ with the user's home dir, following symlinks, and
+ * remove all occurrences of /./ and /../
+ */
+char *
+adjustname(const char *fn, int slashslash)
+{
+ static char fnb[PATH_MAX];
+ const char *cp, *ep = NULL;
+ char *path;
+
+ if (slashslash == TRUE) {
+ cp = fn + strlen(fn) - 1;
+ for (; cp >= fn; cp--) {
+ if (ep && (*cp == '/')) {
+ fn = ep;
+ break;
+ }
+ if (*cp == '/' || *cp == '~')
+ ep = cp;
+ else
+ ep = NULL;
+ }
+ }
+ if ((path = expandtilde(fn)) == NULL)
+ return (NULL);
+
+ if (realpath(path, fnb) == NULL)
+ (void)strlcpy(fnb, path, sizeof(fnb));
+
+ free(path);
+ return (fnb);
+}
+
+/*
+ * Find a startup file for the user and return its name. As a service
+ * to other pieces of code that may want to find a startup file (like
+ * the terminal driver in particular), accepts a suffix to be appended
+ * to the startup file name.
+ */
+char *
+startupfile(char *suffix, char *conffile)
+{
+ static char file[NFILEN];
+ char *home;
+ int ret;
+
+ if ((home = getenv("HOME")) == NULL || *home == '\0')
+ goto nohome;
+
+ if (conffile != NULL) {
+ (void)strncpy(file, conffile, NFILEN);
+ } else if (suffix == NULL) {
+ ret = snprintf(file, sizeof(file), _PATH_MG_STARTUP, home);
+ if (ret < 0 || ret >= sizeof(file))
+ return (NULL);
+ } else {
+ ret = snprintf(file, sizeof(file), _PATH_MG_TERM, home, suffix);
+ if (ret < 0 || ret >= sizeof(file))
+ return (NULL);
+ }
+
+ if (access(file, R_OK) == 0)
+ return (file);
+nohome:
+#ifdef STARTUPFILE
+ if (suffix == NULL) {
+ ret = snprintf(file, sizeof(file), "%s", STARTUPFILE);
+ if (ret < 0 || ret >= sizeof(file))
+ return (NULL);
+ } else {
+ ret = snprintf(file, sizeof(file), "%s%s", STARTUPFILE,
+ suffix);
+ if (ret < 0 || ret >= sizeof(file))
+ return (NULL);
+ }
+
+ if (access(file, R_OK) == 0)
+ return (file);
+#endif /* STARTUPFILE */
+ return (NULL);
+}
+
+int
+copy(char *frname, char *toname)
+{
+ int ifd, ofd;
+ char buf[BUFSIZ];
+ mode_t fmode = DEFFILEMODE; /* XXX?? */
+ struct stat orig;
+ ssize_t sr;
+
+ if ((ifd = open(frname, O_RDONLY)) == -1)
+ return (FALSE);
+ if (fstat(ifd, &orig) == -1) {
+ dobeep();
+ ewprintf("fstat: %s", strerror(errno));
+ close(ifd);
+ return (FALSE);
+ }
+
+ if ((ofd = open(toname, O_WRONLY|O_CREAT|O_TRUNC, fmode)) == -1) {
+ close(ifd);
+ return (FALSE);
+ }
+ while ((sr = read(ifd, buf, sizeof(buf))) > 0) {
+ if (write(ofd, buf, (size_t)sr) != sr) {
+ ewprintf("write error : %s", strerror(errno));
+ break;
+ }
+ }
+ if (fchmod(ofd, orig.st_mode) == -1)
+ ewprintf("Cannot set original mode : %s", strerror(errno));
+
+ if (sr == -1) {
+ ewprintf("Read error : %s", strerror(errno));
+ close(ifd);
+ close(ofd);
+ return (FALSE);
+ }
+ /*
+ * It is "normal" for this to fail since we can't guarantee that
+ * we will be running as root.
+ */
+ if (fchown(ofd, orig.st_uid, orig.st_gid) && errno != EPERM)
+ ewprintf("Cannot set owner : %s", strerror(errno));
+
+ (void) close(ifd);
+ (void) close(ofd);
+
+ return (TRUE);
+}
+
+/*
+ * return list of file names that match the name in buf.
+ */
+struct list *
+make_file_list(char *buf)
+{
+ char *dir, *file, *cp;
+ size_t len, preflen;
+ int ret;
+ DIR *dirp;
+ struct dirent *dent;
+ struct list *last, *current;
+ char fl_name[NFILEN + 2];
+ char prefixx[NFILEN + 1];
+
+ /*
+ * We need three different strings:
+
+ * dir - the name of the directory containing what the user typed.
+ * Must be a real unix file name, e.g. no ~user, etc..
+ * Must not end in /.
+ * prefix - the portion of what the user typed that is before the
+ * names we are going to find in the directory. Must have a
+ * trailing / if the user typed it.
+ * names from the directory - We open dir, and return prefix
+ * concatenated with names.
+ */
+
+ /* first we get a directory name we can look up */
+ /*
+ * Names ending in . are potentially odd, because adjustname will
+ * treat foo/bar/.. as a foo/, whereas we are
+ * interested in names starting with ..
+ */
+ len = strlen(buf);
+ if (len && buf[len - 1] == '.') {
+ buf[len - 1] = 'x';
+ dir = adjustname(buf, TRUE);
+ buf[len - 1] = '.';
+ } else
+ dir = adjustname(buf, TRUE);
+ if (dir == NULL)
+ return (NULL);
+ /*
+ * If the user typed a trailing / or the empty string
+ * he wants us to use his file spec as a directory name.
+ */
+ if (len && buf[len - 1] != '/') {
+ file = strrchr(dir, '/');
+ if (file) {
+ *file = '\0';
+ if (*dir == '\0')
+ dir = "/";
+ } else
+ return (NULL);
+ }
+ /* Now we get the prefix of the name the user typed. */
+ if (strlcpy(prefixx, buf, sizeof(prefixx)) >= sizeof(prefixx))
+ return (NULL);
+ cp = strrchr(prefixx, '/');
+ if (cp == NULL)
+ prefixx[0] = '\0';
+ else
+ cp[1] = '\0';
+
+ preflen = strlen(prefixx);
+ /* cp is the tail of buf that really needs to be compared. */
+ cp = buf + preflen;
+ len = strlen(cp);
+
+ /*
+ * Now make sure that file names will fit in the buffers allocated.
+ * SV files are fairly short. For BSD, something more general would
+ * be required.
+ */
+ if (preflen > NFILEN - NAME_MAX)
+ return (NULL);
+
+ /* loop over the specified directory, making up the list of files */
+
+ /*
+ * Note that it is worth our time to filter out names that don't
+ * match, even though our caller is going to do so again, and to
+ * avoid doing the stat if completion is being done, because stat'ing
+ * every file in the directory is relatively expensive.
+ */
+
+ dirp = opendir(dir);
+ if (dirp == NULL)
+ return (NULL);
+ last = NULL;
+
+ while ((dent = readdir(dirp)) != NULL) {
+ int isdir;
+ if (strncmp(cp, dent->d_name, len) != 0)
+ continue;
+ isdir = 0;
+ if (dent->d_type == DT_DIR) {
+ isdir = 1;
+ } else if (dent->d_type == DT_LNK ||
+ dent->d_type == DT_UNKNOWN) {
+ struct stat statbuf;
+
+ if (fstatat(dirfd(dirp), dent->d_name, &statbuf, 0) < 0)
+ continue;
+ if (S_ISDIR(statbuf.st_mode))
+ isdir = 1;
+ }
+
+ if ((current = malloc(sizeof(struct list))) == NULL) {
+ free_file_list(last);
+ closedir(dirp);
+ return (NULL);
+ }
+ ret = snprintf(fl_name, sizeof(fl_name),
+ "%s%s%s", prefixx, dent->d_name, isdir ? "/" : "");
+ if (ret < 0 || ret >= sizeof(fl_name)) {
+ free(current);
+ continue;
+ }
+ current->l_next = last;
+ current->l_name = strdup(fl_name);
+ last = current;
+ }
+ closedir(dirp);
+
+ return (last);
+}
+
+/*
+ * Test if a supplied filename refers to a directory
+ * Returns ABORT on error, TRUE if directory. FALSE otherwise
+ */
+int
+fisdir(const char *fname)
+{
+ struct stat statbuf;
+
+ if (stat(fname, &statbuf) != 0)
+ return (ABORT);
+
+ if (S_ISDIR(statbuf.st_mode))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+/*
+ * Check the mtime of the supplied filename.
+ * Return TRUE if last mtime matches, FALSE if not,
+ * If the stat fails, return TRUE and try the save anyway
+ */
+int
+fchecktime(struct buffer *bp)
+{
+ struct stat sb;
+
+ if (stat(bp->b_fname, &sb) == -1)
+ return (TRUE);
+
+ if (bp->b_fi.fi_mtime.tv_sec != sb.st_mtimespec.tv_sec ||
+ bp->b_fi.fi_mtime.tv_nsec != sb.st_mtimespec.tv_nsec)
+ return (FALSE);
+
+ return (TRUE);
+
+}
+
+/*
+ * Location of backup file. This function creates the correct path.
+ */
+static char *
+bkuplocation(const char *fn)
+{
+ struct stat sb;
+ char *ret;
+
+ if (bkupdir != NULL && (stat(bkupdir, &sb) == 0) &&
+ S_ISDIR(sb.st_mode) && !bkupleavetmp(fn)) {
+ char fname[NFILEN];
+ const char *c;
+ int i = 0, len;
+
+ c = fn;
+ len = strlen(bkupdir);
+
+ while (*c != '\0') {
+ /* Make sure we don't go over combined:
+ * strlen(bkupdir + '/' + fname + '\0')
+ */
+ if (i >= NFILEN - len - 1)
+ return (NULL);
+ if (*c == '/') {
+ fname[i] = '!';
+ } else if (*c == '!') {
+ if (i >= NFILEN - len - 2)
+ return (NULL);
+ fname[i++] = '!';
+ fname[i] = '!';
+ } else
+ fname[i] = *c;
+ i++;
+ c++;
+ }
+ fname[i] = '\0';
+ if (asprintf(&ret, "%s/%s", bkupdir, fname) == -1)
+ return (NULL);
+
+ } else if ((ret = strndup(fn, NFILEN)) == NULL)
+ return (NULL);
+
+ return (ret);
+}
+
+int
+backuptohomedir(int f, int n)
+{
+ const char *c = _PATH_MG_DIR;
+ char *p;
+
+ if (bkupdir == NULL) {
+ p = adjustname(c, TRUE);
+ bkupdir = strndup(p, NFILEN);
+ if (bkupdir == NULL)
+ return(FALSE);
+
+ if (mkdir(bkupdir, 0700) == -1 && errno != EEXIST) {
+ free(bkupdir);
+ bkupdir = NULL;
+ }
+ } else {
+ free(bkupdir);
+ bkupdir = NULL;
+ }
+
+ return (TRUE);
+}
+
+/*
+ * For applications that use mg as the editor and have a desire to keep
+ * '~' files in /tmp, toggle the location: /tmp | ~/.mg.d
+ */
+int
+toggleleavetmp(int f, int n)
+{
+ leavetmp = !leavetmp;
+
+ return (TRUE);
+}
+
+/*
+ * Returns TRUE if fn is located in the temp directory and we want to save
+ * those backups there.
+ */
+int
+bkupleavetmp(const char *fn)
+{
+ if (!leavetmp)
+ return(FALSE);
+
+ if (strncmp(fn, "/tmp", 4) == 0)
+ return (TRUE);
+
+ return (FALSE);
+}
+
+/*
+ * Expand file names beginning with '~' if appropriate:
+ * 1, if ./~fn exists, continue without expanding tilde.
+ * 2, else, if username 'fn' exists, expand tilde with home directory path.
+ * 3, otherwise, continue and create new buffer called ~fn.
+ */
+char *
+expandtilde(const char *fn)
+{
+ struct passwd *pw;
+ struct stat statbuf;
+ const char *cp;
+ char user[LOGIN_NAME_MAX], path[NFILEN];
+ char *ret;
+ size_t ulen, plen;
+
+ path[0] = '\0';
+
+ if (fn[0] != '~' || stat(fn, &statbuf) == 0) {
+ if ((ret = strndup(fn, NFILEN)) == NULL)
+ return (NULL);
+ return(ret);
+ }
+ cp = strchr(fn, '/');
+ if (cp == NULL)
+ cp = fn + strlen(fn); /* point to the NUL byte */
+ ulen = cp - &fn[1];
+ if (ulen >= sizeof(user)) {
+ if ((ret = strndup(fn, NFILEN)) == NULL)
+ return (NULL);
+ return(ret);
+ }
+ if (ulen == 0) /* ~/ or ~ */
+ pw = getpwuid(geteuid());
+ else { /* ~user/ or ~user */
+ memcpy(user, &fn[1], ulen);
+ user[ulen] = '\0';
+ pw = getpwnam(user);
+ }
+ if (pw != NULL) {
+ plen = strlcpy(path, pw->pw_dir, sizeof(path));
+ if (plen == 0 || path[plen - 1] != '/') {
+ if (strlcat(path, "/", sizeof(path)) >= sizeof(path)) {
+ dobeep();
+ ewprintf("Path too long");
+ return (NULL);
+ }
+ }
+ fn = cp;
+ if (*fn == '/')
+ fn++;
+ }
+ if (strlcat(path, fn, sizeof(path)) >= sizeof(path)) {
+ dobeep();
+ ewprintf("Path too long");
+ return (NULL);
+ }
+ if ((ret = strndup(path, NFILEN)) == NULL)
+ return (NULL);
+
+ return (ret);
+}
Index: contrib/mg/fparseln.c
===================================================================
--- /dev/null
+++ contrib/mg/fparseln.c
@@ -0,0 +1,258 @@
+/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */
+
+/*-
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $OpenBSD: fparseln.c,v 1.7 2012/12/05 23:20:06 deraadt Exp $ */
+/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */
+
+/*
+ * Copyright (c) 1997 Christos Zoulas. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+#ifndef HAVE_FPARSELN
+
+#ifndef __APPLE__
+#include "util.h"
+#endif
+
+static char *
+efgetln(FILE *fp, size_t *len)
+{
+ static char *buf = NULL;
+ static size_t bufsiz = 0;
+ char *ptr;
+
+
+ if (buf == NULL) {
+ bufsiz = BUFSIZ;
+ if ((buf = malloc(bufsiz)) == NULL)
+ return NULL;
+ }
+
+ if (fgets(buf, bufsiz, fp) == NULL)
+ return NULL;
+
+ *len = 0;
+ while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
+ size_t nbufsiz = bufsiz + BUFSIZ;
+ char *nbuf = realloc(buf, nbufsiz);
+
+ if (nbuf == NULL) {
+ int oerrno = errno;
+ free(buf);
+ errno = oerrno;
+ buf = NULL;
+ return NULL;
+ } else
+ buf = nbuf;
+
+ if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) {
+ buf[bufsiz] = '\0';
+ *len = strlen(buf);
+ return buf;
+ }
+
+ *len = bufsiz;
+ bufsiz = nbufsiz;
+ }
+
+ *len = (ptr - buf) + 1;
+ return buf;
+}
+
+static int isescaped(const char *, const char *, int);
+
+/* isescaped():
+ * Return true if the character in *p that belongs to a string
+ * that starts in *sp, is escaped by the escape character esc.
+ */
+static int
+isescaped(const char *sp, const char *p, int esc)
+{
+ const char *cp;
+ size_t ne;
+
+ /* No escape character */
+ if (esc == '\0')
+ return 1;
+
+ /* Count the number of escape characters that precede ours */
+ for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
+ continue;
+
+ /* Return true if odd number of escape characters */
+ return (ne & 1) != 0;
+}
+
+
+/* fparseln():
+ * Read a line from a file parsing continuations ending in \
+ * and eliminating trailing newlines, or comments starting with
+ * the comment char.
+ */
+char *
+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3],
+ int flags)
+{
+ static const char dstr[3] = { '\\', '\\', '#' };
+ char *buf = NULL, *ptr, *cp, esc, con, nl, com;
+ size_t s, len = 0;
+ int cnt = 1;
+
+ if (str == NULL)
+ str = dstr;
+
+ esc = str[0];
+ con = str[1];
+ com = str[2];
+
+ /*
+ * XXX: it would be cool to be able to specify the newline character,
+ * but unfortunately, fgetln does not let us
+ */
+ nl = '\n';
+
+ while (cnt) {
+ cnt = 0;
+
+ if (lineno)
+ (*lineno)++;
+
+ if ((ptr = efgetln(fp, &s)) == NULL)
+ break;
+
+ if (s && com) { /* Check and eliminate comments */
+ for (cp = ptr; cp < ptr + s; cp++)
+ if (*cp == com && !isescaped(ptr, cp, esc)) {
+ s = cp - ptr;
+ cnt = s == 0 && buf == NULL;
+ break;
+ }
+ }
+
+ if (s && nl) { /* Check and eliminate newlines */
+ cp = &ptr[s - 1];
+
+ if (*cp == nl)
+ s--; /* forget newline */
+ }
+
+ if (s && con) { /* Check and eliminate continuations */
+ cp = &ptr[s - 1];
+
+ if (*cp == con && !isescaped(ptr, cp, esc)) {
+ s--; /* forget escape */
+ cnt = 1;
+ }
+ }
+
+ if (s == 0 && buf != NULL)
+ continue;
+
+ if ((cp = realloc(buf, len + s + 1)) == NULL) {
+ free(buf);
+ return NULL;
+ }
+ buf = cp;
+
+ (void) memcpy(buf + len, ptr, s);
+ len += s;
+ buf[len] = '\0';
+ }
+
+ if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
+ strchr(buf, esc) != NULL) {
+ ptr = cp = buf;
+ while (cp[0] != '\0') {
+ int skipesc;
+
+ while (cp[0] != '\0' && cp[0] != esc)
+ *ptr++ = *cp++;
+ if (cp[0] == '\0' || cp[1] == '\0')
+ break;
+
+ skipesc = 0;
+ if (cp[1] == com)
+ skipesc += (flags & FPARSELN_UNESCCOMM);
+ if (cp[1] == con)
+ skipesc += (flags & FPARSELN_UNESCCONT);
+ if (cp[1] == esc)
+ skipesc += (flags & FPARSELN_UNESCESC);
+ if (cp[1] != com && cp[1] != con && cp[1] != esc)
+ skipesc = (flags & FPARSELN_UNESCREST);
+
+ if (skipesc)
+ cp++;
+ else
+ *ptr++ = *cp++;
+ *ptr++ = *cp++;
+ }
+ *ptr = '\0';
+ len = strlen(buf);
+ }
+
+ if (size)
+ *size = len;
+ return buf;
+}
+
+#endif /* !HAVE_FPARSELN */
Index: contrib/mg/freebsd.h
===================================================================
--- /dev/null
+++ contrib/mg/freebsd.h
@@ -0,0 +1,6 @@
+/*
+ * FreeBSD-specific support.
+ */
+
+#include <sys/param.h>
+#include <time.h>
Index: contrib/mg/fstatat.c
===================================================================
--- /dev/null
+++ contrib/mg/fstatat.c
@@ -0,0 +1,22 @@
+/*
+ * fstatat(2) wrapper for systems that do not support it.
+ * Written by Brian Callahan <bcallah@openbsd.org> and released
+ * into the Public Domain.
+ */
+
+#include <sys/stat.h>
+
+#include <stdio.h>
+
+#include "config.h"
+
+#ifndef HAVE_FSTATAT
+
+int
+fstatat(int fd, const char *path, struct stat *buf, int flag)
+{
+
+ return stat(path, buf);
+}
+
+#endif /* !HAVE_FSTATAT */
Index: contrib/mg/funmap.h
===================================================================
--- /dev/null
+++ contrib/mg/funmap.h
@@ -0,0 +1,10 @@
+/* $OpenBSD: funmap.h,v 1.8 2019/07/11 18:20:18 lum Exp $ */
+
+/* This file is in the public domain */
+
+void funmap_init(void);
+PF name_function(const char *);
+const char *function_name(PF);
+struct list *complete_function_list(const char *);
+int funmap_add(PF, const char *, int);
+int numparams_function(PF);
Index: contrib/mg/funmap.c
===================================================================
--- /dev/null
+++ contrib/mg/funmap.c
@@ -0,0 +1,339 @@
+/* $OpenBSD: funmap.c,v 1.63 2021/04/22 19:50:55 lum Exp $ */
+
+/* This file is in the public domain */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+#include "funmap.h"
+#include "kbd.h"
+
+/*
+ * funmap structure: a list of functions and their command-names/#parameters.
+ *
+ * If the function is NULL, it must be listed with the same name in the
+ * map_table.
+ */
+struct funmap {
+ PF fn_funct;
+ const char *fn_name;
+ int fn_nparams;
+ struct funmap *fn_next;
+};
+static struct funmap *funs;
+
+/*
+ * 3rd column in the functnames structure indicates how many parameters the
+ * function takes in 'normal' usage. This column is only used to identify
+ * function profiles when lines of a buffer are being evaluated via excline().
+ *
+ * 0 = a toggle, non-modifiable insert/delete, region modifier, etc
+ * 1 = value can be string or number value (like: file/buf name, search string)
+ * 2 = multiple type value required, see auto-execute, or global-set-key, etc
+ * -1 = error: interactive commmand, unsuitable for interpreter
+ *
+ * Some functions when used interactively may ask for a 'y' or 'n' (or another
+ * character) to continue, in excline, a 'y' is assumed. Functions like this
+ * have '0' in the 3rd column below.
+ */
+static struct funmap functnames[] = {
+ {apropos_command, "apropos", 1},
+ {toggleaudiblebell, "audible-bell", 0},
+ {auto_execute, "auto-execute", 2},
+ {fillmode, "auto-fill-mode", 0},
+ {indentmode, "auto-indent-mode", 0},
+ {backtoindent, "back-to-indentation", 0},
+ {backuptohomedir, "backup-to-home-directory", 0},
+ {backchar, "backward-char", 1},
+ {delbword, "backward-kill-word", 1},
+ {gotobop, "backward-paragraph", 1},
+ {backword, "backward-word", 1},
+ {gotobob, "beginning-of-buffer", 0},
+ {gotobol, "beginning-of-line", 0},
+ {showmatch, "blink-and-insert", 1}, /* startup only */
+ {bsmap, "bsmap-mode", 0},
+ {NULL, "c-x 4 prefix", 0}, /* internal */
+ {NULL, "c-x prefix", 0}, /* internal */
+ {executemacro, "call-last-kbd-macro", 0},
+ {capword, "capitalize-word", 1},
+ {changedir, "cd", 1},
+ {clearmark, "clear-mark", 0},
+ {colnotoggle, "column-number-mode", 0},
+ {copyregion, "copy-region-as-kill", 0},
+#ifdef REGEX
+ {cntmatchlines, "count-matches", 1},
+ {cntnonmatchlines, "count-non-matches", 1},
+#endif /* REGEX */
+ {cscreatelist, "cscope-create-list-of-files-to-index", 1},
+ {csfuncalled, "cscope-find-called-functions", 1},
+ {csegrep, "cscope-find-egrep-pattern", 1},
+ {csfindinc, "cscope-find-files-including-file", 1},
+ {cscallerfuncs, "cscope-find-functions-calling-this-function", 1},
+ {csdefinition, "cscope-find-global-definition", 1},
+ {csfindfile, "cscope-find-this-file", 1},
+ {cssymbol, "cscope-find-this-symbol", 1},
+ {csfindtext, "cscope-find-this-text-string", 1},
+ {csnextfile, "cscope-next-file", 0},
+ {csnextmatch, "cscope-next-symbol", 0},
+ {csprevfile, "cscope-prev-file", 0},
+ {csprevmatch, "cscope-prev-symbol", 0},
+ {redefine_key, "define-key", 3},
+ {backdel, "delete-backward-char", 1},
+ {deblank, "delete-blank-lines", 0},
+ {forwdel, "delete-char", 1},
+ {delwhite, "delete-horizontal-space", 0},
+ {delleadwhite, "delete-leading-space", 0},
+#ifdef REGEX
+ {delmatchlines, "delete-matching-lines", 1},
+ {delnonmatchlines, "delete-non-matching-lines", 1},
+#endif /* REGEX */
+ {onlywind, "delete-other-windows", 0},
+ {deltrailwhite, "delete-trailing-space", 0},
+ {delwind, "delete-window", 0},
+ {wallchart, "describe-bindings", 0},
+ {desckey, "describe-key-briefly", 1},
+ {diffbuffer, "diff-buffer-with-file", 0},
+ {digit_argument, "digit-argument", 1},
+ {dired_jump, "dired-jump", 1},
+ {lowerregion, "downcase-region", 0},
+ {lowerword, "downcase-word", 1},
+ {showversion, "emacs-version", 0},
+ {finishmacro, "end-kbd-macro", 0},
+ {gotoeob, "end-of-buffer", 0},
+ {gotoeol, "end-of-line", 0},
+ {enlargewind, "enlarge-window", 0},
+ {NULL, "esc prefix", 0}, /* internal */
+ {evalbuffer, "eval-current-buffer", 0},
+ {evalexpr, "eval-expression", 0},
+ {swapmark, "exchange-point-and-mark", 0},
+ {extend, "execute-extended-command", 1},
+ {fillpara, "fill-paragraph", 0},
+ {filevisitalt, "find-alternate-file", 1},
+ {filevisit, "find-file", 1},
+ {poptofile, "find-file-other-window", 1},
+ {filevisitro, "find-file-read-only", 1},
+ {findtag, "find-tag", 1},
+ {forwchar, "forward-char", 1},
+ {gotoeop, "forward-paragraph", 1},
+ {forwword, "forward-word", 1},
+ {bindtokey, "global-set-key", 2},
+ {unbindtokey, "global-unset-key", 1},
+ {globalwdtoggle, "global-wd-mode", 0},
+ {gotoline, "goto-line", 1},
+ {help_help, "help-help", 0},
+ {indent, "indent-current-line", 0},
+ {insert, "insert", 1},
+ {bufferinsert, "insert-buffer", 1},
+ {fileinsert, "insert-file", 1},
+ {fillword, "insert-with-wrap", 1}, /* startup only */
+ {backisearch, "isearch-backward", 1},
+ {forwisearch, "isearch-forward", 1},
+ {joinline, "join-line", 0},
+ {justone, "just-one-space", 0},
+ {ctrlg, "keyboard-quit", 0},
+ {killbuffer_cmd, "kill-buffer", 1},
+ {killline, "kill-line", 1},
+ {killpara, "kill-paragraph", 1},
+ {killregion, "kill-region", 0},
+ {delfword, "kill-word", 1},
+ {toggleleavetmp, "leave-tmpdir-backups", 0},
+ {linenotoggle, "line-number-mode", 0},
+ {listbuffers, "list-buffers", 0},
+ {evalfile, "load", 1},
+ {localbind, "local-set-key", 1},
+ {localunbind, "local-unset-key", 1},
+ {makebkfile, "make-backup-files", 0},
+ {makedir, "make-directory", 1},
+ {markpara, "mark-paragraph", 1},
+ {markbuffer, "mark-whole-buffer", 0},
+ {do_meta, "meta-key-mode", 0}, /* better name, anyone? */
+ {negative_argument, "negative-argument", 1},
+ {enewline, "newline", 1},
+ {lfindent, "newline-and-indent", 1},
+ {forwline, "next-line", 1},
+#ifdef NOTAB
+ {notabmode, "no-tab-mode", 0},
+#endif /* NOTAB */
+ {notmodified, "not-modified", 0},
+ {openline, "open-line", 1},
+ {nextwind, "other-window", 0},
+ {overwrite_mode, "overwrite-mode", 0},
+ {poptag, "pop-tag-mark", 0},
+ {prefixregion, "prefix-region", 0},
+ {backline, "previous-line", 1},
+ {prevwind, "previous-window", 0},
+ {spawncli, "push-shell", 0},
+ {showcwdir, "pwd", 0},
+ {queryrepl, "query-replace", -1},
+#ifdef REGEX
+ {re_queryrepl, "query-replace-regexp", -1},
+#endif /* REGEX */
+ {quote, "quoted-insert", 1},
+#ifdef REGEX
+ {re_searchagain, "re-search-again", 0},
+ {re_backsearch, "re-search-backward", 0},
+ {re_forwsearch, "re-search-forward", 0},
+#endif /* REGEX */
+ {reposition, "recenter", 0},
+ {redraw, "redraw-display", 0},
+#ifdef REGEX
+ {re_repl, "replace-regexp", 2},
+ {replstr, "replace-string", 2},
+#endif /* REGEX */
+ {revertbuffer, "revert-buffer", 0},
+ {filesave, "save-buffer", 1},
+ {quit, "save-buffers-kill-emacs", 0},
+ {savebuffers, "save-some-buffers", 0},
+ {backpage, "scroll-down", 1},
+ {back1page, "scroll-one-line-down", 1},
+ {forw1page, "scroll-one-line-up", 1},
+ {pagenext, "scroll-other-window", 1},
+ {forwpage, "scroll-up", 1},
+ {searchagain, "search-again", 0},
+ {backsearch, "search-backward", 0},
+ {forwsearch, "search-forward", 0},
+ {ask_selfinsert, "self-insert-char", 1},
+ {selfinsert, "self-insert-command", 1}, /* startup only */
+ {sentencespace, "sentence-end-double-space", 0},
+#ifdef REGEX
+ {setcasefold, "set-case-fold-search", 0},
+#endif /* REGEX */
+ {setcasereplace, "set-case-replace", 0},
+ {set_default_mode, "set-default-mode", 1},
+ {setfillcol, "set-fill-column", 1},
+ {setmark, "set-mark-command", 0},
+ {setprefix, "set-prefix-string", 1},
+ {shellcommand, "shell-command", 1},
+ {piperegion, "shell-command-on-region", 1},
+ {shrinkwind, "shrink-window", 1},
+#ifdef NOTAB
+ {space_to_tabstop, "space-to-tabstop", 0},
+#endif /* NOTAB */
+ {splitwind, "split-window-vertically", 0},
+ {definemacro, "start-kbd-macro", 0},
+ {spawncli, "suspend-emacs", 0},
+ {usebuffer, "switch-to-buffer", 1},
+ {poptobuffer, "switch-to-buffer-other-window", 1},
+#ifndef NOEXTENSIONS
+ {togglenewlineprompt, "toggle-newline-prompt",},
+#endif /* NOEXTENSIONS */
+ {togglereadonly, "toggle-read-only", 0},
+ {togglereadonlyall, "toggle-read-only-all", 0},
+ {twiddle, "transpose-chars", 0},
+ {transposepara, "transpose-paragraphs", 0},
+ {transposeword, "transpose-words", 0},
+ {undo, "undo", 0},
+ {undo_add_boundary, "undo-boundary", 0},
+ {undo_boundary_enable, "undo-boundary-toggle", 0},
+ {undo_enable, "undo-enable", 0},
+ {undo_dump, "undo-list", 0},
+ {universal_argument, "universal-argument", 1},
+ {upperregion, "upcase-region", 0},
+ {upperword, "upcase-word", 1},
+ {togglevisiblebell, "visible-bell", 0},
+ {tagsvisit, "visit-tags-table", 0},
+ {showcpos, "what-cursor-position", 0},
+ {filewrite, "write-file", 1},
+ {yank, "yank", 1},
+ {NULL, NULL, 0}
+};
+
+void
+funmap_init(void)
+{
+ struct funmap *fn;
+
+ for (fn = functnames; fn->fn_name != NULL; fn++) {
+ fn->fn_next = funs;
+ funs = fn;
+ }
+}
+
+int
+funmap_add(PF fun, const char *fname, int fparams)
+{
+ struct funmap *fn;
+
+ if ((fn = malloc(sizeof(*fn))) == NULL)
+ return (FALSE);
+
+ fn->fn_funct = fun;
+ fn->fn_name = fname;
+ fn->fn_nparams = fparams;
+ fn->fn_next = funs;
+
+ funs = fn;
+ return (TRUE);
+}
+
+/*
+ * Translate from function name to function pointer.
+ */
+PF
+name_function(const char *fname)
+{
+ struct funmap *fn;
+
+ for (fn = funs; fn != NULL; fn = fn->fn_next) {
+ if (strcmp(fn->fn_name, fname) == 0)
+ return (fn->fn_funct);
+ }
+ return (NULL);
+}
+
+const char *
+function_name(PF fun)
+{
+ struct funmap *fn;
+
+ for (fn = funs; fn != NULL; fn = fn->fn_next) {
+ if (fn->fn_funct == fun)
+ return (fn->fn_name);
+ }
+ return (NULL);
+}
+
+/*
+ * List possible function name completions.
+ */
+struct list *
+complete_function_list(const char *fname)
+{
+ struct funmap *fn;
+ struct list *head, *el;
+ int len;
+
+ len = strlen(fname);
+ head = NULL;
+ for (fn = funs; fn != NULL; fn = fn->fn_next) {
+ if (memcmp(fname, fn->fn_name, len) == 0) {
+ if ((el = malloc(sizeof(*el))) == NULL) {
+ free_file_list(head);
+ return (NULL);
+ }
+ el->l_name = strdup(fn->fn_name);
+ el->l_next = head;
+ head = el;
+ }
+ }
+ return (head);
+}
+
+/*
+ * Find number of parameters for function name.
+ */
+int
+numparams_function(PF fun)
+{
+ struct funmap *fn;
+
+ for (fn = funs; fn != NULL; fn = fn->fn_next) {
+ if (fn->fn_funct == fun)
+ return (fn->fn_nparams);
+ }
+ return (FALSE);
+}
Index: contrib/mg/futimens.c
===================================================================
--- /dev/null
+++ contrib/mg/futimens.c
@@ -0,0 +1,20 @@
+/* This file is in the public domain. */
+
+#include <sys/time.h>
+
+#include <stdio.h>
+
+#include "config.h"
+
+#ifndef HAVE_FUTIMENS
+
+int
+futimens(int fildes, const struct timespec times[2])
+{
+ struct timeval timevals[2];
+ TIMESPEC_TO_TIMEVAL(&timevals[0], &times[0]);
+ TIMESPEC_TO_TIMEVAL(&timevals[1], &times[1]);
+ return futimes(fildes, timevals);
+}
+
+#endif /* !HAVE_FUTIMENS */
Index: contrib/mg/getline.c
===================================================================
--- /dev/null
+++ contrib/mg/getline.c
@@ -0,0 +1,89 @@
+/* $NetBSD: getdelim.c,v 1.2 2015/12/25 20:12:46 joerg Exp $ */
+/* NetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+#ifndef HAVE_GETLINE
+
+static ssize_t
+getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
+{
+ char *ptr, *eptr;
+
+
+ if (*buf == NULL || *bufsiz == 0) {
+ *bufsiz = BUFSIZ;
+ if ((*buf = malloc(*bufsiz)) == NULL)
+ return -1;
+ }
+
+ for (ptr = *buf, eptr = *buf + *bufsiz;;) {
+ int c = fgetc(fp);
+ if (c == -1) {
+ if (feof(fp)) {
+ ssize_t diff = (ssize_t)(ptr - *buf);
+ if (diff != 0) {
+ *ptr = '\0';
+ return diff;
+ }
+ }
+ return -1;
+ }
+ *ptr++ = c;
+ if (c == delimiter) {
+ *ptr = '\0';
+ return ptr - *buf;
+ }
+ if (ptr + 2 >= eptr) {
+ char *nbuf;
+ size_t nbufsiz = *bufsiz * 2;
+ ssize_t d = ptr - *buf;
+ if ((nbuf = realloc(*buf, nbufsiz)) == NULL)
+ return -1;
+ *buf = nbuf;
+ *bufsiz = nbufsiz;
+ eptr = nbuf + nbufsiz;
+ ptr = nbuf + d;
+ }
+ }
+}
+
+ssize_t
+getline(char **buf, size_t *bufsiz, FILE *fp)
+{
+ return getdelim(buf, bufsiz, '\n', fp);
+}
+
+#endif
Index: contrib/mg/graph2.h
===================================================================
--- /dev/null
+++ contrib/mg/graph2.h
@@ -0,0 +1,63 @@
+/* $NetBSD: graph2.h,v 1.1 2009/08/15 16:21:05 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Implementation of common 2-graph routines:
+ * - build a 2-graph with hash-pairs as edges
+ * - check a 2-graph for acyclicness and compute an output order
+ */
+
+struct vertex2 {
+ uint32_t l_edge, r_edge;
+};
+
+struct edge2 {
+ uint32_t left, right;
+ uint32_t l_prev, l_next;
+ uint32_t r_prev, r_next;
+};
+
+struct graph2 {
+ struct vertex2 *verts;
+ struct edge2 *edges;
+ uint32_t output_index;
+ uint32_t *output_order;
+ uint8_t *visited;
+ uint32_t e, v;
+};
+
+void graph2_setup(struct graph2 *, uint32_t, uint32_t);
+void graph2_free(struct graph2 *);
+
+int graph2_hash(struct nbperf *, struct graph2 *);
+int graph2_output_order(struct graph2 *graph);
Index: contrib/mg/graph2.c
===================================================================
--- /dev/null
+++ contrib/mg/graph2.c
@@ -0,0 +1,206 @@
+/* $NetBSD: graph2.c,v 1.4 2011/10/21 23:47:11 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nbperf.h"
+#include "graph2.h"
+
+static const uint32_t unused = 0xffffffffU;
+
+void
+graph2_setup(struct graph2 *graph, uint32_t v, uint32_t e)
+{
+ graph->v = v;
+ graph->e = e;
+
+ graph->verts = calloc(sizeof(struct vertex2), v);
+ graph->edges = calloc(sizeof(struct edge2), e);
+ graph->output_order = calloc(sizeof(uint32_t), e);
+
+ if (graph->verts == NULL || graph->edges == NULL ||
+ graph->output_order == NULL)
+ err(1, "malloc failed");
+}
+
+void
+graph2_free(struct graph2 *graph)
+{
+ free(graph->verts);
+ free(graph->edges);
+ free(graph->output_order);
+
+ graph->verts = NULL;
+ graph->edges = NULL;
+ graph->output_order = NULL;
+}
+
+static int
+graph2_check_duplicates(struct nbperf *nbperf, struct graph2 *graph)
+{
+ struct vertex2 *v;
+ struct edge2 *e, *e2;
+ uint32_t i, j;
+
+ for (i = 0; i < graph->e; ++i) {
+ e = &graph->edges[i];
+ v = &graph->verts[e->left];
+ j = v->l_edge;
+ e2 = &graph->edges[j];
+ for (;;) {
+ if (i < j && e->right == e2->right &&
+ nbperf->keylens[i] == nbperf->keylens[j] &&
+ memcmp(nbperf->keys[i], nbperf->keys[j],
+ nbperf->keylens[i]) == 0) {
+ nbperf->has_duplicates = 1;
+ return -1;
+ }
+ if (e2->l_next == unused)
+ break;
+ j = e2->l_next;
+ e2 = &graph->edges[j];
+ }
+ }
+ return 0;
+}
+
+int
+graph2_hash(struct nbperf *nbperf, struct graph2 *graph)
+{
+ struct vertex2 *v;
+ uint32_t hashes[NBPERF_MAX_HASH_SIZE];
+ size_t i;
+
+ for (i = 0; i < graph->e; ++i) {
+ (*nbperf->compute_hash)(nbperf,
+ nbperf->keys[i], nbperf->keylens[i], hashes);
+ graph->edges[i].left = hashes[0] % graph->v;
+ graph->edges[i].right = hashes[1] % graph->v;
+ if (graph->edges[i].left == graph->edges[i].right)
+ return -1;
+ }
+
+ for (i = 0; i < graph->v; ++i) {
+ graph->verts[i].l_edge = unused;
+ graph->verts[i].r_edge = unused;
+ }
+
+ for (i = 0; i < graph->e; ++i) {
+ v = &graph->verts[graph->edges[i].left];
+ if (v->l_edge != unused)
+ graph->edges[v->l_edge].l_prev = i;
+ graph->edges[i].l_next = v->l_edge;
+ graph->edges[i].l_prev = unused;
+ v->l_edge = i;
+
+ v = &graph->verts[graph->edges[i].right];
+ if (v->r_edge != unused)
+ graph->edges[v->r_edge].r_prev = i;
+ graph->edges[i].r_next = v->r_edge;
+ graph->edges[i].r_prev = unused;
+ v->r_edge = i;
+ }
+
+ if (nbperf->first_round) {
+ nbperf->first_round = 0;
+ return graph2_check_duplicates(nbperf, graph);
+ }
+
+ return 0;
+}
+
+static void
+graph2_remove_vertex(struct graph2 *graph, struct vertex2 *v)
+{
+ struct edge2 *e;
+ struct vertex2 *v2;
+
+ for (;;) {
+ if (v->l_edge != unused && v->r_edge != unused)
+ break;
+ if (v->l_edge == unused && v->r_edge == unused)
+ break;
+
+ if (v->l_edge != unused) {
+ e = &graph->edges[v->l_edge];
+ if (e->l_next != unused)
+ break;
+ v->l_edge = unused; /* No other elements possible! */
+ v2 = &graph->verts[e->right];
+ if (e->r_prev == unused)
+ v2->r_edge = e->r_next;
+ else
+ graph->edges[e->r_prev].r_next = e->r_next;
+ if (e->r_next != unused)
+ graph->edges[e->r_next].r_prev = e->r_prev;
+ v = v2;
+ } else {
+ e = &graph->edges[v->r_edge];
+ if (e->r_next != unused)
+ break;
+ v->r_edge = unused; /* No other elements possible! */
+ v2 = &graph->verts[e->left];
+ if (e->l_prev == unused)
+ v2->l_edge = e->l_next;
+ else
+ graph->edges[e->l_prev].l_next = e->l_next;
+ if (e->l_next != unused)
+ graph->edges[e->l_next].l_prev = e->l_prev;
+ v = v2;
+ }
+
+ graph->output_order[--graph->output_index] = e - graph->edges;
+ }
+}
+
+int
+graph2_output_order(struct graph2 *graph)
+{
+ size_t i;
+
+ graph->output_index = graph->e;
+
+ for (i = 0; i < graph->v; ++i)
+ graph2_remove_vertex(graph, &graph->verts[i]);
+
+ if (graph->output_index != 0)
+ return -1;
+
+ return 0;
+}
Index: contrib/mg/graph3.h
===================================================================
--- /dev/null
+++ contrib/mg/graph3.h
@@ -0,0 +1,62 @@
+/* $NetBSD: graph3.h,v 1.1 2009/08/15 16:21:05 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Implementation of common 3-graph routines:
+ * - build a 3-graph with hash-triple as edges
+ * - check a 3-graph for acyclicness and compute an output order
+ */
+
+struct vertex3 {
+ uint32_t l_edge, m_edge, r_edge;
+};
+
+struct edge3 {
+ uint32_t left, middle, right;
+ uint32_t l_prev, m_prev, l_next;
+ uint32_t r_prev, m_next, r_next;
+};
+
+struct graph3 {
+ struct vertex3 *verts;
+ struct edge3 *edges;
+ uint32_t output_index;
+ uint32_t *output_order;
+ uint32_t e, v;
+};
+
+void graph3_setup(struct graph3 *, uint32_t, uint32_t);
+void graph3_free(struct graph3 *);
+
+int graph3_hash(struct nbperf *, struct graph3 *);
+int graph3_output_order(struct graph3 *);
Index: contrib/mg/graph3.c
===================================================================
--- /dev/null
+++ contrib/mg/graph3.c
@@ -0,0 +1,245 @@
+/* $NetBSD: graph3.c,v 1.4 2011/10/21 23:47:11 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nbperf.h"
+#include "graph3.h"
+
+static const uint32_t unused = 0xffffffffU;
+
+void
+graph3_setup(struct graph3 *graph, uint32_t v, uint32_t e)
+{
+ graph->v = v;
+ graph->e = e;
+
+ graph->verts = calloc(sizeof(struct vertex3), v);
+ graph->edges = calloc(sizeof(struct edge3), e);
+ graph->output_order = calloc(sizeof(uint32_t), e);
+
+ if (graph->verts == NULL || graph->edges == NULL ||
+ graph->output_order == NULL)
+ err(1, "malloc failed");
+}
+
+void
+graph3_free(struct graph3 *graph)
+{
+ free(graph->verts);
+ free(graph->edges);
+ free(graph->output_order);
+
+ graph->verts = NULL;
+ graph->edges = NULL;
+ graph->output_order = NULL;
+}
+
+static int
+graph3_check_duplicates(struct nbperf *nbperf, struct graph3 *graph)
+{
+ struct vertex3 *v;
+ struct edge3 *e, *e2;
+ uint32_t i, j;
+
+ for (i = 0; i < graph->e; ++i) {
+ e = &graph->edges[i];
+ v = &graph->verts[e->left];
+ j = v->l_edge;
+ e2 = &graph->edges[j];
+ for (;;) {
+ if (i < j && e->middle == e2->middle &&
+ e->right == e2->right &&
+ nbperf->keylens[i] == nbperf->keylens[j] &&
+ memcmp(nbperf->keys[i], nbperf->keys[j],
+ nbperf->keylens[i]) == 0) {
+ nbperf->has_duplicates = 1;
+ return -1;
+ }
+ if (e2->l_next == unused)
+ break;
+ j = e2->l_next;
+ e2 = &graph->edges[j];
+ }
+ }
+ return 0;
+}
+
+int
+graph3_hash(struct nbperf *nbperf, struct graph3 *graph)
+{
+ struct vertex3 *v;
+ uint32_t hashes[NBPERF_MAX_HASH_SIZE];
+ size_t i;
+
+ for (i = 0; i < graph->e; ++i) {
+ (*nbperf->compute_hash)(nbperf,
+ nbperf->keys[i], nbperf->keylens[i], hashes);
+ graph->edges[i].left = hashes[0] % graph->v;
+ graph->edges[i].middle = hashes[1] % graph->v;
+ graph->edges[i].right = hashes[2] % graph->v;
+ if (graph->edges[i].left == graph->edges[i].middle)
+ return -1;
+ if (graph->edges[i].left == graph->edges[i].right)
+ return -1;
+ if (graph->edges[i].middle == graph->edges[i].right)
+ return -1;
+ }
+
+ for (i = 0; i < graph->v; ++i) {
+ graph->verts[i].l_edge = unused;
+ graph->verts[i].m_edge = unused;
+ graph->verts[i].r_edge = unused;
+ }
+
+ for (i = 0; i < graph->e; ++i) {
+ v = &graph->verts[graph->edges[i].left];
+ if (v->l_edge != unused)
+ graph->edges[v->l_edge].l_prev = i;
+ graph->edges[i].l_next = v->l_edge;
+ graph->edges[i].l_prev = unused;
+ v->l_edge = i;
+
+ v = &graph->verts[graph->edges[i].middle];
+ if (v->m_edge != unused)
+ graph->edges[v->m_edge].m_prev = i;
+ graph->edges[i].m_next = v->m_edge;
+ graph->edges[i].m_prev = unused;
+ v->m_edge = i;
+
+ v = &graph->verts[graph->edges[i].right];
+ if (v->r_edge != unused)
+ graph->edges[v->r_edge].r_prev = i;
+ graph->edges[i].r_next = v->r_edge;
+ graph->edges[i].r_prev = unused;
+ v->r_edge = i;
+ }
+
+ if (nbperf->first_round) {
+ nbperf->first_round = 0;
+ return graph3_check_duplicates(nbperf, graph);
+ }
+
+ return 0;
+}
+
+static void
+graph3_remove_vertex(struct graph3 *graph, struct vertex3 *v)
+{
+ struct edge3 *e;
+ struct vertex3 *vl, *vm, *vr;
+
+ if (v->l_edge != unused && v->m_edge != unused)
+ return;
+ if (v->l_edge != unused && v->r_edge != unused)
+ return;
+ if (v->m_edge != unused && v->r_edge != unused)
+ return;
+ if (v->l_edge == unused && v->m_edge == unused && v->r_edge == unused)
+ return;
+
+ if (v->l_edge != unused) {
+ e = &graph->edges[v->l_edge];
+ if (e->l_next != unused)
+ return;
+ } else if (v->m_edge != unused) {
+ e = &graph->edges[v->m_edge];
+ if (e->m_next != unused)
+ return;
+ } else {
+ if (v->r_edge == unused)
+ abort();
+ e = &graph->edges[v->r_edge];
+ if (e->r_next != unused)
+ return;
+ }
+
+ graph->output_order[--graph->output_index] = e - graph->edges;
+
+ vl = &graph->verts[e->left];
+ vm = &graph->verts[e->middle];
+ vr = &graph->verts[e->right];
+
+ if (e->l_prev == unused)
+ vl->l_edge = e->l_next;
+ else
+ graph->edges[e->l_prev].l_next = e->l_next;
+ if (e->l_next != unused)
+ graph->edges[e->l_next].l_prev = e->l_prev;
+
+ if (e->m_prev == unused)
+ vm->m_edge = e->m_next;
+ else
+ graph->edges[e->m_prev].m_next = e->m_next;
+ if (e->m_next != unused)
+ graph->edges[e->m_next].m_prev = e->m_prev;
+
+ if (e->r_prev == unused)
+ vr->r_edge = e->r_next;
+ else
+ graph->edges[e->r_prev].r_next = e->r_next;
+ if (e->r_next != unused)
+ graph->edges[e->r_next].r_prev = e->r_prev;
+}
+
+int
+graph3_output_order(struct graph3 *graph)
+{
+ struct edge3 *e;
+ size_t i;
+
+ graph->output_index = graph->e;
+
+ for (i = 0; i < graph->v; ++i)
+ graph3_remove_vertex(graph, &graph->verts[i]);
+
+ for (i = graph->e; i > 0 && i > graph->output_index;) {
+ --i;
+ e = &graph->edges[graph->output_order[i]];
+
+ graph3_remove_vertex(graph, &graph->verts[e->left]);
+ graph3_remove_vertex(graph, &graph->verts[e->middle]);
+ graph3_remove_vertex(graph, &graph->verts[e->right]);
+ }
+
+ if (graph->output_index != 0)
+ return -1;
+
+ return 0;
+}
Index: contrib/mg/grep.c
===================================================================
--- /dev/null
+++ contrib/mg/grep.c
@@ -0,0 +1,362 @@
+/* $OpenBSD: grep.c,v 1.49 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain */
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "def.h"
+#include "kbd.h"
+#include "funmap.h"
+
+int globalwd = FALSE;
+static int compile_goto_error(int, int);
+int next_error(int, int);
+static int grep(int, int);
+static int gid(int, int);
+static struct buffer *compile_mode(const char *, const char *);
+void grep_init(void);
+
+static char compile_last_command[NFILEN] = "make ";
+
+/*
+ * Hints for next-error
+ *
+ * XXX - need some kind of callback to find out when those get killed.
+ */
+struct mgwin *compile_win;
+struct buffer *compile_buffer;
+
+static PF compile_pf[] = {
+ compile_goto_error
+};
+
+static struct KEYMAPE (1) compilemap = {
+ 1,
+ 1,
+ rescan,
+ {
+ { CCHR('M'), CCHR('M'), compile_pf, NULL }
+ }
+};
+
+void
+grep_init(void)
+{
+ funmap_add(compile_goto_error, "compile-goto-error", 0);
+ funmap_add(next_error, "next-error", 0);
+ funmap_add(grep, "grep", 1);
+ funmap_add(compile, "compile", 0);
+ funmap_add(gid, "gid", 1);
+ maps_add((KEYMAP *)&compilemap, "compile");
+}
+
+/* ARGSUSED */
+static int
+grep(int f, int n)
+{
+ char cprompt[NFILEN], *bufp;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ (void)strlcpy(cprompt, "grep -n ", sizeof(cprompt));
+ if ((bufp = eread("Run grep: ", cprompt, NFILEN,
+ EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if (strlcat(cprompt, " /dev/null", sizeof(cprompt)) >= sizeof(cprompt))
+ return (FALSE);
+
+ if ((bp = compile_mode("*grep*", cprompt)) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ compile_win = curwp = wp;
+ return (TRUE);
+}
+
+/* ARGSUSED */
+int
+compile(int f, int n)
+{
+ char cprompt[NFILEN], *bufp;
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ (void)strlcpy(cprompt, compile_last_command, sizeof(cprompt));
+ if ((bufp = eread("Compile command: ", cprompt, NFILEN,
+ EFDEF | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if (savebuffers(f, n) == ABORT)
+ return (ABORT);
+ (void)strlcpy(compile_last_command, bufp, sizeof(compile_last_command));
+
+ if ((bp = compile_mode("*compile*", cprompt)) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ compile_win = curwp = wp;
+ gotoline(FFARG, 0);
+ return (TRUE);
+}
+
+/* id-utils foo. */
+/* ARGSUSED */
+static int
+gid(int f, int n)
+{
+ char command[NFILEN];
+ char cprompt[NFILEN], *bufp;
+ int c;
+ struct buffer *bp;
+ struct mgwin *wp;
+ int i, j, len;
+
+ /* catch ([^\s(){}]+)[\s(){}]* */
+
+ i = curwp->w_doto;
+ /* Skip backwards over delimiters we are currently on */
+ while (i > 0) {
+ c = lgetc(curwp->w_dotp, i);
+ if (isalnum(c) || c == '_')
+ break;
+
+ i--;
+ }
+
+ /* Skip the symbol itself */
+ for (; i > 0; i--) {
+ c = lgetc(curwp->w_dotp, i - 1);
+ if (!isalnum(c) && c != '_')
+ break;
+ }
+ /* Fill the symbol in cprompt[] */
+ for (j = 0; j < sizeof(cprompt) - 1 && i < llength(curwp->w_dotp);
+ j++, i++) {
+ c = lgetc(curwp->w_dotp, i);
+ if (!isalnum(c) && c != '_')
+ break;
+ cprompt[j] = c;
+ }
+ cprompt[j] = '\0';
+
+ if ((bufp = eread("Run gid (with args): ", cprompt, NFILEN,
+ (j ? EFDEF : 0) | EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ len = snprintf(command, sizeof(command), "gid %s", cprompt);
+ if (len < 0 || len >= sizeof(command))
+ return (FALSE);
+
+ if ((bp = compile_mode("*gid*", command)) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ compile_win = curwp = wp;
+ return (TRUE);
+}
+
+struct buffer *
+compile_mode(const char *name, const char *command)
+{
+ struct buffer *bp;
+ FILE *fpipe;
+ char *buf;
+ size_t sz;
+ ssize_t len;
+ int ret, n, status;
+ char cwd[NFILEN], qcmd[NFILEN];
+ char timestr[NTIME];
+ time_t t;
+
+ buf = NULL;
+ sz = 0;
+
+ n = snprintf(qcmd, sizeof(qcmd), "%s 2>&1", command);
+ if (n < 0 || n >= sizeof(qcmd))
+ return (NULL);
+
+ bp = bfind(name, TRUE);
+ if (bclear(bp) != TRUE)
+ return (NULL);
+
+ if (getbufcwd(bp->b_cwd, sizeof(bp->b_cwd)) != TRUE)
+ return (NULL);
+ addlinef(bp, "cd %s", bp->b_cwd);
+ addline(bp, qcmd);
+ addline(bp, "");
+
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ panic("Can't get current directory!");
+ if (chdir(bp->b_cwd) == -1) {
+ dobeep();
+ ewprintf("Can't change dir to %s", bp->b_cwd);
+ return (NULL);
+ }
+ if ((fpipe = popen(qcmd, "r")) == NULL) {
+ dobeep();
+ ewprintf("Problem opening pipe");
+ return (NULL);
+ }
+ while ((len = getline(&buf, &sz, fpipe)) != -1) {
+ if (buf[len - 1] == *bp->b_nlchr)
+ buf[len - 1] = '\0';
+ addline(bp, buf);
+ }
+ free(buf);
+ if (ferror(fpipe))
+ ewprintf("Problem reading pipe");
+ ret = pclose(fpipe);
+ t = time(NULL);
+ strftime(timestr, sizeof(timestr), "%a %b %e %T %Y", localtime(&t));
+ addline(bp, "");
+ if (WIFEXITED(ret)) {
+ status = WEXITSTATUS(ret);
+ if (status == 0)
+ addlinef(bp, "Command finished at %s", timestr);
+ else
+ addlinef(bp, "Command exited abnormally with code %d "
+ "at %s", status, timestr);
+ } else
+ addlinef(bp, "Subshell killed by signal %d at %s",
+ WTERMSIG(ret), timestr);
+
+ bp->b_dotp = bfirstlp(bp);
+ bp->b_modes[0] = name_mode("fundamental");
+ bp->b_modes[1] = name_mode("compile");
+ bp->b_nmodes = 1;
+
+ compile_buffer = bp;
+
+ if (chdir(cwd) == -1) {
+ dobeep();
+ ewprintf("Can't change dir back to %s", cwd);
+ return (NULL);
+ }
+ return (bp);
+}
+
+/* ARGSUSED */
+static int
+compile_goto_error(int f, int n)
+{
+ struct buffer *bp;
+ struct mgwin *wp;
+ char *fname, *line, *lp, *ln;
+ int lineno;
+ char *adjf, path[NFILEN];
+ const char *errstr;
+ struct line *last;
+
+ compile_win = curwp;
+ compile_buffer = curbp;
+ last = blastlp(compile_buffer);
+
+ retry:
+ /* last line is compilation result */
+ if (curwp->w_dotp == last)
+ return (FALSE);
+
+ if ((line = linetostr(curwp->w_dotp)) == NULL)
+ return (FALSE);
+ lp = line;
+ if ((fname = strsep(&lp, ":")) == NULL || *fname == '\0')
+ goto fail;
+ if ((ln = strsep(&lp, ":")) == NULL || *ln == '\0')
+ goto fail;
+ lineno = (int)strtonum(ln, INT_MIN, INT_MAX, &errstr);
+ if (errstr)
+ goto fail;
+
+ if (fname && fname[0] != '/') {
+ if (getbufcwd(path, sizeof(path)) == FALSE)
+ goto fail;
+ if (strlcat(path, fname, sizeof(path)) >= sizeof(path))
+ goto fail;
+ adjf = path;
+ } else {
+ adjf = adjustname(fname, TRUE);
+ }
+ free(line);
+
+ if (adjf == NULL)
+ return (FALSE);
+
+ if ((bp = findbuffer(adjf)) == NULL)
+ return (FALSE);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+ curbp = bp;
+ curwp = wp;
+ if (bp->b_fname[0] == '\0')
+ readin(adjf);
+ gotoline(FFARG, lineno);
+ return (TRUE);
+fail:
+ free(line);
+ if (curwp->w_dotp != blastlp(curbp)) {
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_rflag |= WFMOVE;
+ goto retry;
+ }
+ dobeep();
+ ewprintf("No more hits");
+ return (FALSE);
+}
+
+/* ARGSUSED */
+int
+next_error(int f, int n)
+{
+ if (compile_win == NULL || compile_buffer == NULL) {
+ dobeep();
+ ewprintf("No compilation active");
+ return (FALSE);
+ }
+ curwp = compile_win;
+ curbp = compile_buffer;
+ if (curwp->w_dotp == blastlp(curbp)) {
+ dobeep();
+ ewprintf("No more hits");
+ return (FALSE);
+ }
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_rflag |= WFMOVE;
+
+ return (compile_goto_error(f, n));
+}
+
+/*
+ * Since we don't have variables (we probably should) these are command
+ * processors for changing the values of mode flags.
+ */
+/* ARGSUSED */
+int
+globalwdtoggle(int f, int n)
+{
+ if (f & FFARG)
+ globalwd = n > 0;
+ else
+ globalwd = !globalwd;
+
+ sgarbf = TRUE;
+
+ return (TRUE);
+}
Index: contrib/mg/hash.c
===================================================================
--- /dev/null
+++ contrib/mg/hash.c
@@ -0,0 +1,709 @@
+/* DO NOT EDIT
+ * Automatically generated from term.h */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#ifndef __arraycount
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+extern void mi_vector_hash(const void * __restrict, size_t, uint32_t,
+ uint32_t[3]);
+
+static const char _ti_flagids[][6] = {
+ "bw",
+ "am",
+ "bce",
+ "ccc",
+ "xhp",
+ "xhpa",
+ "cpix",
+ "crxm",
+ "xt",
+ "xenl",
+ "eo",
+ "gn",
+ "hc",
+ "chts",
+ "km",
+ "daisy",
+ "hs",
+ "hls",
+ "in",
+ "lpix",
+ "da",
+ "db",
+ "mir",
+ "msgr",
+ "nxon",
+ "xsb",
+ "npc",
+ "ndscr",
+ "nrrmc",
+ "os",
+ "mc5i",
+ "xvpa",
+ "sam",
+ "eslok",
+ "hz",
+ "ul",
+ "xon",
+};
+
+#include <stdlib.h>
+
+static uint32_t
+_ti_flaghash(const void * __restrict key, size_t keylen)
+{
+ static const uint8_t g[75] = {
+ 0x13, 0x06, 0x00, 0x17, 0x1f, 0x20, 0x23, 0x00, 0x00, 0x12,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x0d, 0x22, 0x00,
+ 0x00, 0x00, 0x00, 0x0b, 0x00, 0x11, 0x02, 0x03, 0x18, 0x00,
+ 0x00, 0x00, 0x00, 0x15, 0x00, 0x22, 0x02, 0x09, 0x04, 0x08,
+ 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1f, 0x22, 0x24, 0x1b, 0x19,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x00, 0x16, 0x00, 0x18, 0x1d, 0x17, 0x00, 0x00, 0x0f, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x05,
+ };
+ uint32_t h[3];
+
+ mi_vector_hash(key, keylen, 0x00000000U, h);
+ return (g[h[0] % 75] + g[h[1] % 75]) % 37;
+}
+
+const char *
+_ti_flagid(ssize_t idx)
+{
+
+ if ((size_t)idx >= __arraycount(_ti_flagids))
+ return NULL;
+ return _ti_flagids[idx];
+}
+
+ssize_t
+_ti_flagindex(const char *key)
+{
+ uint32_t idx;
+
+ idx = _ti_flaghash((const unsigned char *)key, strlen(key));
+ if (idx >= __arraycount(_ti_flagids) ||
+ strcmp(key, _ti_flagids[idx]) != 0)
+ return -1;
+ return idx;
+}
+
+static const char _ti_numids[][7] = {
+ "bitwin",
+ "bitype",
+ "bufsz",
+ "btns",
+ "cols",
+ "spinh",
+ "spinv",
+ "it",
+ "lh",
+ "lw",
+ "lines",
+ "lm",
+ "ma",
+ "xmc",
+ "colors",
+ "maddr",
+ "mjump",
+ "pairs",
+ "wnum",
+ "mcs",
+ "mls",
+ "ncv",
+ "nlab",
+ "npins",
+ "orc",
+ "orl",
+ "orhi",
+ "orvi",
+ "pb",
+ "cps",
+ "vt",
+ "widcs",
+ "wsl",
+};
+
+#include <stdlib.h>
+
+static uint32_t
+_ti_numhash(const void * __restrict key, size_t keylen)
+{
+ static const uint8_t g[67] = {
+ 0x0b, 0x01, 0x16, 0x00, 0x00, 0x11, 0x1b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1b, 0x01, 0x00, 0x05,
+ 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x04, 0x02, 0x00, 0x0f, 0x00, 0x02, 0x1f, 0x06,
+ 0x15, 0x00, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x11, 0x0d, 0x1e, 0x19, 0x0a, 0x00, 0x06,
+ 0x00, 0x04, 0x1c, 0x00, 0x10, 0x00, 0x0c,
+ };
+ uint32_t h[3];
+
+ mi_vector_hash(key, keylen, 0x00000000U, h);
+ return (g[h[0] % 67] + g[h[1] % 67]) % 33;
+}
+
+const char *
+_ti_numid(ssize_t idx)
+{
+
+ if ((size_t)idx >= __arraycount(_ti_numids))
+ return NULL;
+ return _ti_numids[idx];
+}
+
+ssize_t
+_ti_numindex(const char *key)
+{
+ uint32_t idx;
+
+ idx = _ti_numhash((const unsigned char *)key, strlen(key));
+ if (idx >= __arraycount(_ti_numids) ||
+ strcmp(key, _ti_numids[idx]) != 0)
+ return -1;
+ return idx;
+}
+
+static const char _ti_strids[][9] = {
+ "acsc",
+ "scesa",
+ "cbt",
+ "bel",
+ "bicr",
+ "binel",
+ "birep",
+ "cr",
+ "cpi",
+ "lpi",
+ "chr",
+ "cvr",
+ "csr",
+ "rmp",
+ "csnm",
+ "tbc",
+ "mgc",
+ "clear",
+ "el1",
+ "el",
+ "ed",
+ "csin",
+ "colornm",
+ "hpa",
+ "cmdch",
+ "cwin",
+ "cup",
+ "cud1",
+ "home",
+ "civis",
+ "cub1",
+ "mrcup",
+ "cnorm",
+ "cuf1",
+ "ll",
+ "cuu1",
+ "cvvis",
+ "defbi",
+ "defc",
+ "dch1",
+ "dl1",
+ "devt",
+ "dial",
+ "dsl",
+ "dclk",
+ "dispc",
+ "hd",
+ "enacs",
+ "endbi",
+ "smacs",
+ "smam",
+ "blink",
+ "bold",
+ "smcup",
+ "smdc",
+ "dim",
+ "swidm",
+ "sdrfq",
+ "ehhlm",
+ "smir",
+ "sitm",
+ "elhlm",
+ "slm",
+ "elohlm",
+ "smicm",
+ "snlq",
+ "snrmq",
+ "smpch",
+ "prot",
+ "rev",
+ "erhlm",
+ "smsc",
+ "invis",
+ "sshm",
+ "smso",
+ "ssubm",
+ "ssupm",
+ "ethlm",
+ "smul",
+ "sum",
+ "evhlm",
+ "smxon",
+ "ech",
+ "rmacs",
+ "rmam",
+ "sgr0",
+ "rmcup",
+ "rmdc",
+ "rwidm",
+ "rmir",
+ "ritm",
+ "rlm",
+ "rmicm",
+ "rmpch",
+ "rmsc",
+ "rshm",
+ "rmso",
+ "rsubm",
+ "rsupm",
+ "rmul",
+ "rum",
+ "rmxon",
+ "pause",
+ "hook",
+ "flash",
+ "ff",
+ "fsl",
+ "getm",
+ "wingo",
+ "hup",
+ "is1",
+ "is2",
+ "is3",
+ "if",
+ "iprog",
+ "initc",
+ "initp",
+ "ich1",
+ "il1",
+ "ip",
+ "ka1",
+ "ka3",
+ "kb2",
+ "kbs",
+ "kbeg",
+ "kcbt",
+ "kc1",
+ "kc3",
+ "kcan",
+ "ktbc",
+ "kclr",
+ "kclo",
+ "kcmd",
+ "kcpy",
+ "kcrt",
+ "kctab",
+ "kdch1",
+ "kdl1",
+ "kcud1",
+ "krmir",
+ "kend",
+ "kent",
+ "kel",
+ "ked",
+ "kext",
+ "kf0",
+ "kf1",
+ "kf2",
+ "kf3",
+ "kf4",
+ "kf5",
+ "kf6",
+ "kf7",
+ "kf8",
+ "kf9",
+ "kf10",
+ "kf11",
+ "kf12",
+ "kf13",
+ "kf14",
+ "kf15",
+ "kf16",
+ "kf17",
+ "kf18",
+ "kf19",
+ "kf20",
+ "kf21",
+ "kf22",
+ "kf23",
+ "kf24",
+ "kf25",
+ "kf26",
+ "kf27",
+ "kf28",
+ "kf29",
+ "kf30",
+ "kf31",
+ "kf32",
+ "kf33",
+ "kf34",
+ "kf35",
+ "kf36",
+ "kf37",
+ "kf38",
+ "kf39",
+ "kf40",
+ "kf41",
+ "kf42",
+ "kf43",
+ "kf44",
+ "kf45",
+ "kf46",
+ "kf47",
+ "kf48",
+ "kf49",
+ "kf50",
+ "kf51",
+ "kf52",
+ "kf53",
+ "kf54",
+ "kf55",
+ "kf56",
+ "kf57",
+ "kf58",
+ "kf59",
+ "kf60",
+ "kf61",
+ "kf62",
+ "kf63",
+ "kfnd",
+ "khlp",
+ "khome",
+ "kich1",
+ "kil1",
+ "kcub1",
+ "kll",
+ "kmrk",
+ "kmsg",
+ "kmous",
+ "kmov",
+ "knxt",
+ "knp",
+ "kopn",
+ "kopt",
+ "kpp",
+ "kprv",
+ "kprt",
+ "krdo",
+ "kref",
+ "krfr",
+ "krpl",
+ "krst",
+ "kres",
+ "kcuf1",
+ "ksav",
+ "kBEG",
+ "kCAN",
+ "kCMD",
+ "kCPY",
+ "kCRT",
+ "kDC",
+ "kDL",
+ "kslt",
+ "kEND",
+ "kEOL",
+ "kEXT",
+ "kind",
+ "kFND",
+ "kHLP",
+ "kHOM",
+ "kIC",
+ "kLFT",
+ "kMSG",
+ "kMOV",
+ "kNXT",
+ "kOPT",
+ "kPRV",
+ "kPRT",
+ "kri",
+ "kRDO",
+ "kRPL",
+ "kRIT",
+ "kRES",
+ "kSAV",
+ "kSPD",
+ "khts",
+ "kUND",
+ "kspd",
+ "kund",
+ "kcuu1",
+ "rmkx",
+ "smkx",
+ "lf0",
+ "lf1",
+ "lf2",
+ "lf3",
+ "lf4",
+ "lf5",
+ "lf6",
+ "lf7",
+ "lf8",
+ "lf9",
+ "lf10",
+ "fln",
+ "rmln",
+ "smln",
+ "rmm",
+ "smm",
+ "mhpa",
+ "mcud1",
+ "mcub1",
+ "mcuf1",
+ "mvpa",
+ "mcuu1",
+ "minfo",
+ "nel",
+ "porder",
+ "oc",
+ "op",
+ "pad",
+ "dch",
+ "dl",
+ "cud",
+ "mcud",
+ "ich",
+ "indn",
+ "il",
+ "cub",
+ "mcub",
+ "cuf",
+ "mcuf",
+ "rin",
+ "cuu",
+ "mcuu",
+ "pctrm",
+ "pfkey",
+ "pfloc",
+ "pfxl",
+ "pfx",
+ "pln",
+ "mc0",
+ "mc5p",
+ "mc4",
+ "mc5",
+ "pulse",
+ "qdial",
+ "rmclk",
+ "rep",
+ "rfi",
+ "reqmp",
+ "rs1",
+ "rs2",
+ "rs3",
+ "rf",
+ "rc",
+ "vpa",
+ "sc",
+ "scesc",
+ "ind",
+ "ri",
+ "scs",
+ "s0ds",
+ "s1ds",
+ "s2ds",
+ "s3ds",
+ "sgr1",
+ "setab",
+ "setaf",
+ "sgr",
+ "setb",
+ "smgb",
+ "smgbp",
+ "sclk",
+ "setcolor",
+ "scp",
+ "setf",
+ "smgl",
+ "smglp",
+ "smglr",
+ "slines",
+ "slength",
+ "smgr",
+ "smgrp",
+ "hts",
+ "smgtb",
+ "smgt",
+ "smgtp",
+ "wind",
+ "sbim",
+ "scsd",
+ "rbim",
+ "rcsd",
+ "subcs",
+ "supcs",
+ "ht",
+ "docr",
+ "tsl",
+ "tone",
+ "u0",
+ "u1",
+ "u2",
+ "u3",
+ "u4",
+ "u5",
+ "u6",
+ "u7",
+ "u8",
+ "u9",
+ "uc",
+ "hu",
+ "wait",
+ "xoffc",
+ "xonc",
+ "zerom",
+};
+
+#include <stdlib.h>
+
+static uint32_t
+_ti_strhash(const void * __restrict key, size_t keylen)
+{
+ static const uint16_t g[789] = {
+ 0x0000, 0x0000, 0x0000, 0x0081, 0x003c, 0x0000, 0x00db, 0x0123,
+ 0x0000, 0x0045, 0x0000, 0x0000, 0x0000, 0x00b1, 0x0131, 0x0096,
+ 0x0000, 0x006a, 0x0160, 0x0000, 0x0172, 0x0138, 0x0000, 0x0000,
+ 0x00a4, 0x00bd, 0x0127, 0x0084, 0x0067, 0x0109, 0x008f, 0x0000,
+ 0x0000, 0x017c, 0x0000, 0x0053, 0x0156, 0x0000, 0x0000, 0x0000,
+ 0x00b3, 0x0000, 0x00b9, 0x0096, 0x0058, 0x00b6, 0x00fc, 0x0000,
+ 0x0058, 0x0000, 0x017d, 0x0000, 0x0006, 0x0000, 0x011f, 0x0000,
+ 0x0000, 0x00bb, 0x0021, 0x0151, 0x00c7, 0x008c, 0x0000, 0x0058,
+ 0x008a, 0x011a, 0x0093, 0x0000, 0x0000, 0x0000, 0x013e, 0x0163,
+ 0x0000, 0x0000, 0x0000, 0x0047, 0x0110, 0x00be, 0x0000, 0x001e,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00b1, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x00c5, 0x0034, 0x0181, 0x00be, 0x0094, 0x0000,
+ 0x0103, 0x002a, 0x0072, 0x0000, 0x0000, 0x0000, 0x0085, 0x0000,
+ 0x00d5, 0x0000, 0x0000, 0x0000, 0x00c3, 0x0158, 0x00f6, 0x0000,
+ 0x00e3, 0x0000, 0x0076, 0x0055, 0x016c, 0x0000, 0x008b, 0x0013,
+ 0x0000, 0x0000, 0x0183, 0x0000, 0x016a, 0x0152, 0x0183, 0x0013,
+ 0x0000, 0x0000, 0x0032, 0x0043, 0x006c, 0x012c, 0x0000, 0x0000,
+ 0x0155, 0x0073, 0x0000, 0x0000, 0x0168, 0x0000, 0x0000, 0x0000,
+ 0x0078, 0x001b, 0x00d2, 0x0020, 0x0000, 0x0000, 0x0015, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0090, 0x00fb, 0x0007, 0x0000,
+ 0x0000, 0x00ee, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00c2,
+ 0x0095, 0x0034, 0x0089, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0022, 0x00b9, 0x0173, 0x0157, 0x0000, 0x0000,
+ 0x0000, 0x0129, 0x0000, 0x000f, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x00ef, 0x0000, 0x0000, 0x00a4,
+ 0x0000, 0x0000, 0x0000, 0x0168, 0x00ec, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x016d, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x00e3, 0x0000, 0x0000, 0x0000, 0x00d1, 0x0000, 0x0000, 0x006b,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0063,
+ 0x0000, 0x004b, 0x0175, 0x00ab, 0x011f, 0x0000, 0x0000, 0x003d,
+ 0x0000, 0x0105, 0x007a, 0x013e, 0x0000, 0x013b, 0x0127, 0x000a,
+ 0x00ee, 0x0044, 0x0068, 0x013d, 0x008d, 0x0000, 0x0000, 0x0000,
+ 0x012a, 0x00f8, 0x0077, 0x0000, 0x0000, 0x0048, 0x0000, 0x0155,
+ 0x0000, 0x0107, 0x0159, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0073, 0x010f, 0x0166, 0x00fa, 0x0117, 0x0000, 0x0034,
+ 0x0000, 0x0000, 0x00d0, 0x0000, 0x001d, 0x012f, 0x0000, 0x0000,
+ 0x017e, 0x0019, 0x0000, 0x0087, 0x0000, 0x0000, 0x013e, 0x013a,
+ 0x0000, 0x0125, 0x0000, 0x0023, 0x014b, 0x0000, 0x0000, 0x0000,
+ 0x009d, 0x0133, 0x0000, 0x0000, 0x017d, 0x0000, 0x0000, 0x0000,
+ 0x0081, 0x0183, 0x0000, 0x0000, 0x0000, 0x00b7, 0x0000, 0x0000,
+ 0x0000, 0x012a, 0x0112, 0x0000, 0x0000, 0x0102, 0x0000, 0x0074,
+ 0x0000, 0x0000, 0x012d, 0x0179, 0x00aa, 0x0000, 0x00be, 0x0000,
+ 0x0000, 0x0164, 0x0161, 0x002c, 0x0030, 0x0000, 0x00ae, 0x013f,
+ 0x00ba, 0x0075, 0x0000, 0x00bd, 0x0000, 0x0086, 0x0000, 0x0000,
+ 0x0188, 0x008e, 0x0000, 0x00e1, 0x007f, 0x0000, 0x0116, 0x0000,
+ 0x0000, 0x0000, 0x011a, 0x015a, 0x0000, 0x00a0, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0076, 0x0000, 0x000d, 0x0044, 0x016e, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x011d, 0x00aa, 0x0064, 0x0010,
+ 0x0000, 0x003e, 0x0000, 0x0000, 0x0117, 0x00a4, 0x0063, 0x0000,
+ 0x012b, 0x0000, 0x0000, 0x012b, 0x0031, 0x0074, 0x0044, 0x0000,
+ 0x017f, 0x0017, 0x0179, 0x0041, 0x0000, 0x004c, 0x0041, 0x0000,
+ 0x0061, 0x000d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0064,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a2, 0x0000, 0x00a3,
+ 0x011b, 0x014e, 0x0000, 0x00be, 0x0000, 0x006e, 0x00d3, 0x0000,
+ 0x0000, 0x0000, 0x0180, 0x0000, 0x00a1, 0x0000, 0x0000, 0x00e6,
+ 0x0000, 0x0000, 0x0089, 0x0000, 0x0000, 0x00d0, 0x009f, 0x0032,
+ 0x0000, 0x0000, 0x00e8, 0x00d9, 0x0000, 0x0002, 0x004b, 0x0000,
+ 0x0000, 0x0129, 0x0000, 0x016b, 0x0104, 0x0163, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0165, 0x0000,
+ 0x0000, 0x0000, 0x0155, 0x0017, 0x00c3, 0x0069, 0x0000, 0x00e2,
+ 0x0000, 0x0000, 0x0000, 0x0092, 0x0000, 0x00b0, 0x0045, 0x0107,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0103, 0x0000, 0x0000, 0x0000, 0x0034, 0x0000, 0x0130, 0x0000,
+ 0x0000, 0x00cb, 0x0000, 0x0000, 0x0138, 0x00e7, 0x009a, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0119, 0x0000, 0x0000, 0x0120, 0x0000,
+ 0x00c0, 0x012a, 0x008e, 0x0000, 0x0012, 0x014a, 0x0000, 0x0000,
+ 0x0000, 0x009c, 0x0000, 0x00fc, 0x0000, 0x0121, 0x001f, 0x016b,
+ 0x0000, 0x0000, 0x0021, 0x00fd, 0x0022, 0x00af, 0x0051, 0x0000,
+ 0x00d6, 0x0000, 0x0000, 0x00d0, 0x010e, 0x0000, 0x0000, 0x00c1,
+ 0x0159, 0x0000, 0x00b8, 0x0000, 0x0000, 0x010f, 0x0024, 0x0115,
+ 0x0109, 0x0000, 0x001b, 0x0000, 0x00b5, 0x00d8, 0x0039, 0x00de,
+ 0x0000, 0x008f, 0x011d, 0x004a, 0x0000, 0x00a5, 0x00ea, 0x0144,
+ 0x0000, 0x0027, 0x016f, 0x014e, 0x000b, 0x002c, 0x014b, 0x000a,
+ 0x0046, 0x00b3, 0x00bb, 0x0180, 0x0000, 0x0000, 0x0157, 0x0000,
+ 0x0057, 0x0000, 0x0000, 0x00d1, 0x0181, 0x00d1, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0078, 0x00e3, 0x0000, 0x0000, 0x0000, 0x0036,
+ 0x00d0, 0x0000, 0x0000, 0x0000, 0x0065, 0x0140, 0x0000, 0x0000,
+ 0x0000, 0x006f, 0x00a4, 0x0107, 0x0000, 0x0000, 0x0000, 0x0046,
+ 0x0000, 0x00b4, 0x015b, 0x0000, 0x004d, 0x0000, 0x0000, 0x0000,
+ 0x0041, 0x0111, 0x00ff, 0x0000, 0x0000, 0x0000, 0x00a5, 0x0086,
+ 0x007d, 0x0162, 0x0000, 0x00f5, 0x0000, 0x0080, 0x0079, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x00a6, 0x00e7, 0x0172, 0x0000, 0x0000,
+ 0x00dd, 0x0000, 0x001e, 0x0136, 0x0000, 0x0000, 0x00fb, 0x00e6,
+ 0x0099, 0x0159, 0x0000, 0x0000, 0x0000, 0x0000, 0x00ff, 0x0000,
+ 0x0000, 0x014b, 0x0000, 0x0000, 0x0000, 0x000b, 0x0000, 0x0000,
+ 0x017d, 0x0000, 0x009a, 0x012e, 0x0135, 0x0000, 0x0000, 0x015c,
+ 0x0171, 0x0000, 0x0000, 0x0036, 0x010f, 0x000b, 0x0171, 0x00f9,
+ 0x0000, 0x0000, 0x00cb, 0x0000, 0x0000, 0x0000, 0x005a, 0x0091,
+ 0x0000, 0x0135, 0x004c, 0x0118, 0x0000, 0x0000, 0x0000, 0x0138,
+ 0x0000, 0x0000, 0x016e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0041, 0x0000, 0x00b1, 0x0119, 0x0000, 0x0111, 0x0111, 0x0146,
+ 0x0059, 0x0005, 0x0000, 0x0000, 0x0186, 0x0000, 0x014a, 0x00df,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000a, 0x015f,
+ 0x0000, 0x0000, 0x0000, 0x0163, 0x0000, 0x0000, 0x000c, 0x0000,
+ 0x0000, 0x00dd, 0x0000, 0x0000, 0x00f9, 0x0000, 0x0000, 0x0164,
+ 0x0176, 0x00bf, 0x0133, 0x0032, 0x013a, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0088, 0x015d, 0x00b8, 0x0078, 0x005b, 0x0000, 0x0027,
+ 0x017e, 0x0000, 0x007c, 0x011f, 0x0000, 0x0000, 0x0169, 0x0000,
+ 0x00f2, 0x0000, 0x00b2, 0x00e1, 0x010d,
+ };
+ uint32_t h[3];
+
+ mi_vector_hash(key, keylen, 0x00000001U, h);
+ return (g[h[0] % 789] + g[h[1] % 789]) % 394;
+}
+
+const char *
+_ti_strid(ssize_t idx)
+{
+
+ if ((size_t)idx >= __arraycount(_ti_strids))
+ return NULL;
+ return _ti_strids[idx];
+}
+
+ssize_t
+_ti_strindex(const char *key)
+{
+ uint32_t idx;
+
+ idx = _ti_strhash((const unsigned char *)key, strlen(key));
+ if (idx >= __arraycount(_ti_strids) ||
+ strcmp(key, _ti_strids[idx]) != 0)
+ return -1;
+ return idx;
+}
Index: contrib/mg/help.c
===================================================================
--- /dev/null
+++ contrib/mg/help.c
@@ -0,0 +1,235 @@
+/* $OpenBSD: help.c,v 1.35 2015/03/19 21:22:15 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Help functions for Mg 2
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "def.h"
+#include "funmap.h"
+#include "kbd.h"
+#include "key.h"
+#include "macro.h"
+
+static int showall(struct buffer *, KEYMAP *, char *);
+static int findbind(KEYMAP *, PF, char *, size_t);
+
+/*
+ * Read a key from the keyboard, and look it up in the keymap.
+ * Display the name of the function currently bound to the key.
+ */
+/* ARGSUSED */
+int
+desckey(int f, int n)
+{
+ KEYMAP *curmap;
+ PF funct;
+ int c, m, i, num;
+ char *pep;
+ char dprompt[80];
+
+ if (inmacro)
+ return (TRUE); /* ignore inside keyboard macro */
+
+ num = strlcpy(dprompt, "Describe key briefly: ", sizeof(dprompt));
+ if (num >= sizeof(dprompt))
+ num = sizeof(dprompt) - 1;
+ pep = dprompt + num;
+ key.k_count = 0;
+ m = curbp->b_nmodes;
+ curmap = curbp->b_modes[m]->p_map;
+ for (;;) {
+ for (;;) {
+ ewprintf("%s", dprompt);
+ pep[-1] = ' ';
+ pep = getkeyname(pep, sizeof(dprompt) - (pep - dprompt),
+ key.k_chars[key.k_count++] = c = getkey(FALSE));
+ if ((funct = doscan(curmap, c, &curmap)) != NULL)
+ break;
+ *pep++ = '-';
+ *pep = '\0';
+ }
+ if (funct != rescan)
+ break;
+ if (ISUPPER(key.k_chars[key.k_count - 1])) {
+ funct = doscan(curmap,
+ TOLOWER(key.k_chars[key.k_count - 1]), &curmap);
+ if (funct == NULL) {
+ *pep++ = '-';
+ *pep = '\0';
+ continue;
+ }
+ if (funct != rescan)
+ break;
+ }
+nextmode:
+ if (--m < 0)
+ break;
+ curmap = curbp->b_modes[m]->p_map;
+ for (i = 0; i < key.k_count; i++) {
+ funct = doscan(curmap, key.k_chars[i], &curmap);
+ if (funct != NULL) {
+ if (i == key.k_count - 1 && funct != rescan)
+ goto found;
+ funct = rescan;
+ goto nextmode;
+ }
+ }
+ *pep++ = '-';
+ *pep = '\0';
+ }
+found:
+ if (funct == rescan || funct == selfinsert)
+ ewprintf("%k is not bound to any function");
+ else if ((pep = (char *)function_name(funct)) != NULL)
+ ewprintf("%k runs the command %s", pep);
+ else
+ ewprintf("%k is bound to an unnamed function");
+ return (TRUE);
+}
+
+/*
+ * This function creates a table, listing all of the command
+ * keys and their current bindings, and stores the table in the
+ * *help* pop-up buffer. This lets Mg produce it's own wall chart.
+ */
+/* ARGSUSED */
+int
+wallchart(int f, int n)
+{
+ int m;
+ struct buffer *bp;
+
+ bp = bfind("*help*", TRUE);
+ if (bclear(bp) != TRUE)
+ /* clear it out */
+ return (FALSE);
+ bp->b_flag |= BFREADONLY;
+ for (m = curbp->b_nmodes; m > 0; m--) {
+ if ((addlinef(bp, "Local keybindings for mode %s:",
+ curbp->b_modes[m]->p_name) == FALSE) ||
+ (showall(bp, curbp->b_modes[m]->p_map, "") == FALSE) ||
+ (addline(bp, "") == FALSE))
+ return (FALSE);
+ }
+ if ((addline(bp, "Global bindings:") == FALSE) ||
+ (showall(bp, fundamental_map, "") == FALSE))
+ return (FALSE);
+ return (popbuftop(bp, WNONE));
+}
+
+static int
+showall(struct buffer *bp, KEYMAP *map, char *prefix)
+{
+ KEYMAP *newmap;
+ char buf[80], keybuf[16];
+ PF fun;
+ int c;
+
+ if (addline(bp, "") == FALSE)
+ return (FALSE);
+
+ /* XXX - 256 ? */
+ for (c = 0; c < 256; c++) {
+ fun = doscan(map, c, &newmap);
+ if (fun == rescan || fun == selfinsert)
+ continue;
+ getkeyname(buf, sizeof(buf), c);
+ (void)snprintf(keybuf, sizeof(keybuf), "%s%s ", prefix, buf);
+ if (fun == NULL) {
+ if (showall(bp, newmap, keybuf) == FALSE)
+ return (FALSE);
+ } else {
+ if (addlinef(bp, "%-16s%s", keybuf,
+ function_name(fun)) == FALSE)
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+int
+help_help(int f, int n)
+{
+ KEYMAP *kp;
+ PF funct;
+
+ if ((kp = name_map("help")) == NULL)
+ return (FALSE);
+ ewprintf("a b c: ");
+ do {
+ funct = doscan(kp, getkey(FALSE), NULL);
+ } while (funct == NULL || funct == help_help);
+
+ if (macrodef && macrocount < MAXMACRO)
+ macro[macrocount - 1].m_funct = funct;
+
+ return ((*funct)(f, n));
+}
+
+/* ARGSUSED */
+int
+apropos_command(int f, int n)
+{
+ struct buffer *bp;
+ struct list *fnames, *el;
+ char string[32];
+
+ if (eread("apropos: ", string, sizeof(string), EFNUL | EFNEW) == NULL)
+ return (ABORT);
+ /* FALSE means we got a 0 character string, which is fine */
+ bp = bfind("*help*", TRUE);
+ if (bclear(bp) == FALSE)
+ return (FALSE);
+
+ fnames = complete_function_list("");
+ for (el = fnames; el != NULL; el = el->l_next) {
+ char buf[32];
+
+ if (strstr(el->l_name, string) == NULL)
+ continue;
+
+ buf[0] = '\0';
+ findbind(fundamental_map, name_function(el->l_name),
+ buf, sizeof(buf));
+
+ if (addlinef(bp, "%-32s%s", el->l_name, buf) == FALSE) {
+ free_file_list(fnames);
+ return (FALSE);
+ }
+ }
+ free_file_list(fnames);
+ return (popbuftop(bp, WNONE));
+}
+
+static int
+findbind(KEYMAP *map, PF fun, char *buf, size_t len)
+{
+ KEYMAP *newmap;
+ PF nfun;
+ char buf2[16], keybuf[16];
+ int c;
+
+ /* XXX - 256 ? */
+ for (c = 0; c < 256; c++) {
+ nfun = doscan(map, c, &newmap);
+ if (nfun == fun) {
+ getkeyname(buf, len, c);
+ return (TRUE);
+ }
+ if (nfun == NULL) {
+ if (findbind(newmap, fun, buf2, sizeof(buf2)) == TRUE) {
+ getkeyname(keybuf, sizeof(keybuf), c);
+ (void)snprintf(buf, len, "%s %s", keybuf, buf2);
+ return (TRUE);
+ }
+ }
+ }
+ return (FALSE);
+}
Index: contrib/mg/interpreter.c
===================================================================
--- /dev/null
+++ contrib/mg/interpreter.c
@@ -0,0 +1,979 @@
+/* $OpenBSD: interpreter.c,v 1.32 2021/05/12 11:13:23 lum Exp $ */
+/*
+ * This file is in the public domain.
+ *
+ * Author: Mark Lumsden <mark@showcomplex.com>
+ */
+
+/*
+ * This file attempts to add some 'scripting' functionality into mg.
+ *
+ * The initial goal is to give mg the ability to use it's existing functions
+ * and structures in a linked-up way. Hopefully resulting in user definable
+ * functions. The syntax is 'scheme' like but currently it is not a scheme
+ * interpreter.
+ *
+ * At the moment there is no manual page reference to this file. The code below
+ * is liable to change, so use at your own risk!
+ *
+ * If you do want to do some testing, you can add some lines to your .mg file
+ * like:
+ *
+ * 1. Give multiple arguments to a function that usually would accept only one:
+ * (find-file "a.txt" "b.txt" "c.txt")
+ *
+ * 2. Define a single value variable:
+ * (define myfile "d.txt")
+ *
+ * 3. Define a list:
+ * (define myfiles(list "e.txt" "f.txt"))
+ *
+ * 4. Use the previously defined variable or list:
+ * (find-file myfiles)
+ *
+ * To do:
+ * 1. multiline parsing - currently only single lines supported.
+ * 2. parsing for '(' and ')' throughout whole string and evaluate correctly.
+ * 3. conditional execution.
+ * 4. have memory allocated dynamically for variable values.
+ * 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-]
+ * at the moment.
+ * 6. display line numbers with parsing errors.
+ * 7. oh so many things....
+ * [...]
+ * n. implement user definable functions.
+ *
+ * Notes:
+ * - Currently calls to excline() from this file have the line length and
+ * line number set to zero.
+ * That's because excline() uses '\0' as the end of line indicator
+ * and only the call to foundparen() within excline() uses excline's 2nd
+ * and 3rd arguments.
+ * Importantly, any lines sent to there from here will not be
+ * coming back here.
+ */
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <limits.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+#include "funmap.h"
+
+#ifdef MGLOG
+#include "kbd.h"
+#include "log.h"
+#endif
+
+static int multiarg(char *, char *, int);
+static int isvar(char **, char **, int);
+/*static int dofunc(char **, char **, int);*/
+static int founddef(char *, int, int, int, int);
+static int foundlst(char *, int, int, int);
+static int expandvals(char *, char *, char *);
+static int foundfun(char *, int);
+static int doregex(char *, char *);
+static void clearexp(void);
+static int parse(char *, const char *, const char *, int, int, int, int);
+static int parsdef(char *, const char *, const char *, int, int, int);
+static int parsval(char *, const char *, const char *, int, int, int);
+static int parsexp(char *, const char *, const char *, int, int, int);
+
+static int exitinterpreter(char *, char *, int);
+
+TAILQ_HEAD(exphead, expentry) ehead;
+struct expentry {
+ TAILQ_ENTRY(expentry) eentry;
+ char *fun; /* The 1st string found between parens. */
+ char funbuf[BUFSIZE];
+ const char *par1; /* Parenthesis at start of string */
+ const char *par2; /* Parenthesis at end of string */
+ int expctr; /* An incremental counter:+1 for each exp */
+ int blkid; /* Which block are we in? */
+};
+
+/*
+ * Structure for scheme keywords.
+ */
+#define NUMSCHKEYS 4
+#define MAXLENSCHKEYS 17 /* 17 = longest keyword (16) + 1 */
+
+char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] =
+ {
+ "define",
+ "list",
+ "if",
+ "lambda"
+ };
+
+static const char lp = '(';
+static const char rp = ')';
+static char *defnam = NULL;
+static int lnm;
+
+/*
+ * Line has a '(' as the first non-white char.
+ * Do some very basic parsing of line.
+ * Multi-line not supported at the moment, To do.
+ */
+int
+foundparen(char *funstr, int llen, int lnum)
+{
+ const char *lrp = NULL;
+ char *p, *begp = NULL, *endp = NULL, *prechr;
+ char *lastchr = NULL;
+ int i, ret, pctr, expctr, blkid, inquote, esc;
+ int elen, spc, ns;
+
+ pctr = expctr = inquote = esc = elen = spc = ns = 0;
+ blkid = 1;
+ lnm = lnum;
+
+ /*
+ * load expressions into a list called 'expentry', to be processd
+ * when all are obtained.
+ * Not really live code at the moment. Just part of the process of
+ * working out what needs to be done.
+ */
+ TAILQ_INIT(&ehead);
+
+ /*
+ * Check for blocks of code with opening and closing ().
+ * One block = (cmd p a r a m)
+ * Two blocks = (cmd p a r a m s)(hola)
+ * Two blocks = (cmd p a r (list a m s))(hola)
+ * Only single line at moment, but more for multiline.
+ */
+ p = funstr;
+
+ for (i = 0; i < llen; ++i, p++) {
+ if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') {
+ if (*p == ')')
+ return(dobeep_num("Extra ')' found on line:",
+ lnm));
+ return(dobeep_num("Error line:", lnm));
+ }
+ if (begp != NULL)
+ elen++;
+
+ if (*p == '\\') {
+ esc = 1;
+ } else if (*p == '(') {
+ if (lastchr != NULL && *lastchr == '(')
+ return(dobeep_num("Multiple consecutive "\
+ "left parantheses line", lnm));
+ if (inquote == 0) {
+ if (begp != NULL) {
+ if (*prechr == ' ')
+ ns--;
+ if (endp == NULL)
+ *p = '\0';
+ else
+ *endp = '\0';
+
+ ret = parse(begp, lrp, &lp, blkid,
+ ++expctr, elen - spc, ns);
+ if (!ret) {
+ cleanup();
+ return(ret);
+ }
+ elen = 0;
+ }
+ lrp = &lp;
+ begp = endp = NULL;
+ pctr++;
+ } else if (inquote != 1) {
+ cleanup();
+ return(dobeep_num("Opening and closing quote "\
+ "char error line:", lnm));
+ }
+ esc = spc = 0;
+ } else if (*p == ')') {
+ if (lastchr != NULL && *lastchr == '(')
+ return(dobeep_num("Empty parenthesis "\
+ "not supported line", lnm));
+ if (inquote == 0) {
+ if (begp != NULL) {
+ if (*prechr == ' ')
+ ns--;
+ if (endp == NULL)
+ *p = '\0';
+ else
+ *endp = '\0';
+
+ ret = parse(begp, lrp, &rp, blkid,
+ ++expctr, elen - spc, ns);
+ if (!ret) {
+ cleanup();
+ return(ret);
+ }
+ elen = 0;
+ }
+ lrp = &rp;
+ begp = endp = NULL;
+ pctr--;
+ } else if (inquote != 1) {
+ cleanup();
+ return(dobeep_num("Opening and closing quote "\
+ "char error line:", lnm));
+ }
+ esc = spc = 0;
+ } else if (*p != ' ' && *p != '\t') {
+ if (begp == NULL) {
+ begp = p;
+ if (*begp == '"' || isdigit(*begp))
+ return(dobeep_num("First char of "\
+ "expression error line:", lnm));
+ }
+ if (*p == '"') {
+ if (inquote == 0 && esc == 0) {
+ if (*prechr != ' ' && *prechr != '\t')
+ return(dobeep_num("Parse error"\
+ " line:", lnm));
+ inquote++;
+ } else if (inquote > 0 && esc == 1)
+ esc = 0;
+ else
+ inquote--;
+ } else if (*prechr == '"' && inquote == 0) {
+ return(dobeep_num("Parse error line:", lnm));
+ }
+ endp = NULL;
+ spc = 0;
+ } else if (endp == NULL && (*p == ' ' || *p == '\t')) {
+ if (inquote == 0) {
+ *p = ' ';
+ endp = p;
+ spc++;
+ if (begp != NULL)
+ ns++;
+ }
+ esc = 0;
+ } else if (*p == '\t' || *p == ' ') {
+ if (inquote == 0) {
+ *p = ' ';
+ spc++;
+ }
+ esc = 0;
+ }
+ if (*p != '\t' && *p != ' ' && inquote == 0)
+ lastchr = p;
+
+ if (pctr == 0) {
+ blkid++;
+ expctr = 0;
+ defnam = NULL;
+ }
+ prechr = p;
+ }
+
+ if (pctr != 0) {
+ cleanup();
+ return(dobeep_num("Opening and closing parentheses error line:",
+ lnm));
+ }
+ if (ret == FALSE)
+ cleanup();
+ else
+ clearexp(); /* leave lists but remove expressions */
+
+ return (ret);
+}
+
+
+static int
+parse(char *begp, const char *par1, const char *par2, int blkid, int expctr,
+ int elen, int ns)
+{
+ char *regs;
+ int ret = FALSE;
+
+ if (strncmp(begp, "define", 6) == 0) {
+ ret = parsdef(begp, par1, par2, blkid, expctr, elen);
+ if (ret == TRUE || ret == FALSE)
+ return (ret);
+ } else if (strncmp(begp, "list", 4) == 0)
+ return(parsval(begp, par1, par2, blkid, expctr, elen));
+
+ regs = "^exit$";
+ if (doregex(regs, begp))
+ return(exitinterpreter(NULL, NULL, FALSE));
+
+ /* mg function name regex */
+ regs = "^[A-Za-z-]+$";
+ if (doregex(regs, begp))
+ return(excline(begp, 0, 0));
+
+ /* Corner case 1 */
+ if (strncmp(begp, "global-set-key ", 15) == 0)
+ /* function name as 2nd param screws up multiarg. */
+ return(excline(begp, 0, 0));
+
+ /* Corner case 2 */
+ if (strncmp(begp, "define-key ", 11) == 0)
+ /* function name as 3rd param screws up multiarg. */
+ return(excline(begp, 0, 0));
+
+ return (parsexp(begp, par1, par2, blkid, expctr, elen));
+}
+
+static int
+parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr,
+ int elen)
+{
+ char *regs;
+
+ if ((defnam == NULL) && (expctr != 1))
+ return(dobeep_num("'define' incorrectly used line:", lnm));
+
+ /* Does the line have a incorrect variable 'define' like: */
+ /* (define i y z) */
+ regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$";
+ if (doregex(regs, begp))
+ return(dobeep_num("Invalid use of define line:", lnm));
+
+ /* Does the line have a single variable 'define' like: */
+ /* (define i 0) */
+ regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$";
+ if (doregex(regs, begp)) {
+ if (par1 == &lp && par2 == &rp && expctr == 1)
+ return(founddef(begp, blkid, expctr, 1, elen));
+ return(dobeep_num("Invalid use of define line:", lnm));
+ }
+ /* Does the line have '(define i(' */
+ regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$";
+ if (doregex(regs, begp)) {
+ if (par1 == &lp && par2 == &lp && expctr == 1)
+ return(founddef(begp, blkid, expctr, 0, elen));
+ return(dobeep_num("Invalid use of 'define' line:", lnm));
+ }
+ /* Does the line have '(define (' */
+ regs = "^define$";
+ if (doregex(regs, begp)) {
+ if (par1 == &lp && par2 == &lp && expctr == 1)
+ return(foundfun(begp, expctr));
+ return(dobeep_num("Invalid use of 'define' line:", lnm));
+ }
+
+ return (ABORT);
+}
+
+static int
+parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr,
+ int elen)
+{
+ char *regs;
+
+ /* Does the line have 'list' */
+ regs = "^list$";
+ if (doregex(regs, begp))
+ return(dobeep_num("Invalid use of list line:", lnm));
+
+ /* Does the line have a 'list' like: */
+ /* (list "a" "b") */
+ regs = "^list[ ]+.*$";
+ if (doregex(regs, begp)) {
+ if (expctr == 1)
+ return(dobeep_num("list with no-where to go.", lnm));
+
+ if (par1 == &lp && expctr > 1)
+ return(foundlst(begp, blkid, expctr, elen));
+
+ return(dobeep_num("Invalid use of list line:", lnm));
+ }
+ return (FALSE);
+}
+
+static int
+parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr,
+ int elen)
+{
+ struct expentry *e1 = NULL;
+ PF funcp;
+ char *cmdp, *fendp, *valp, *fname, *funb = NULL;;
+ int numparams, ret;
+
+ cmdp = begp;
+ fendp = strchr(cmdp, ' ');
+ *fendp = '\0';
+
+ /*
+ * If no extant mg command found, just return.
+ */
+ if ((funcp = name_function(cmdp)) == NULL)
+ return (dobeep_msgs("Unknown command: ", cmdp));
+
+ numparams = numparams_function(funcp);
+ if (numparams == 0)
+ return (dobeep_msgs("Command takes no arguments:", cmdp));
+
+ if (numparams == -1)
+ return (dobeep_msgs("Interactive command found:", cmdp));
+
+ if ((e1 = malloc(sizeof(struct expentry))) == NULL) {
+ cleanup();
+ return (dobeep_msg("malloc Error"));
+ }
+ TAILQ_INSERT_HEAD(&ehead, e1, eentry);
+ if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) {
+ cleanup();
+ return(dobeep_msg("strndup error"));
+ }
+ cmdp = e1->fun;
+ fname = e1->fun;
+ e1->funbuf[0] = '\0';
+ funb = e1->funbuf;
+ e1->expctr = expctr;
+ e1->blkid = blkid;
+ /* need to think about these two */
+ e1->par1 = par1;
+ e1->par2 = par2;
+
+ *fendp = ' ';
+ valp = fendp + 1;
+
+ ret = expandvals(cmdp, valp, funb);
+ if (!ret)
+ return (ret);
+
+ return (multiarg(fname, funb, numparams));
+}
+
+/*
+ * Pass a list of arguments to a function.
+ */
+static int
+multiarg(char *cmdp, char *argbuf, int numparams)
+{
+ char excbuf[BUFSIZE];
+ char *argp, *p, *s = " ";
+ char *regs;
+ int spc, numspc;
+ int fin, inquote;
+
+ argp = argbuf;
+ spc = 1; /* initially fake a space so we find first argument */
+ numspc = fin = inquote = 0;
+
+ for (p = argbuf; *p != '\0'; p++) {
+ if (*(p + 1) == '\0')
+ fin = 1;
+
+ if (*p != ' ') {
+ if (*p == '"') {
+ if (inquote == 1)
+ inquote = 0;
+ else
+ inquote = 1;
+ }
+ if (spc == 1)
+ if ((numspc % numparams) == 0) {
+ argp = p;
+ }
+ spc = 0;
+ }
+ if ((*p == ' ' && inquote == 0) || fin) {
+ if (spc == 1)/* || (numspc % numparams == 0))*/
+ continue;
+ if ((numspc % numparams) != (numparams - 1)) {
+ numspc++;
+ continue;
+ }
+ if (*p == ' ') {
+ *p = '\0'; /* terminate arg string */
+ }
+ excbuf[0] = '\0';
+ regs = "[\"]+.*[\"]+";
+
+ if (!doregex(regs, argp)) {
+ const char *errstr;
+ int iters;
+
+ iters = strtonum(argp, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ return (dobeep_msgs("Var not found:",
+ argp));
+ }
+
+ if (strlcpy(excbuf, cmdp, sizeof(excbuf))
+ >= sizeof(excbuf))
+ return (dobeep_msg("strlcpy error"));
+ if (strlcat(excbuf, s, sizeof(excbuf))
+ >= sizeof(excbuf))
+ return (dobeep_msg("strlcat error"));
+ if (strlcat(excbuf, argp, sizeof(excbuf))
+ >= sizeof(excbuf))
+ return (dobeep_msg("strlcat error"));
+
+ excline(excbuf, 0, 0);
+
+ if (fin)
+ break;
+
+ *p = ' '; /* unterminate arg string */
+ numspc++;
+ spc = 1;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Is an item a value or a variable?
+ */
+static int
+isvar(char **argp, char **varbuf, int sizof)
+{
+ struct varentry *v1 = NULL;
+
+ if (SLIST_EMPTY(&varhead))
+ return (FALSE);
+#ifdef MGLOG
+ mglog_isvar(*varbuf, *argp, sizof);
+#endif
+ SLIST_FOREACH(v1, &varhead, entry) {
+ if (strcmp(*argp, v1->v_name) == 0) {
+ (void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof);
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+
+static int
+foundfun(char *defstr, int expctr)
+{
+ return (TRUE);
+}
+
+static int
+foundlst(char *defstr, int blkid, int expctr, int elen)
+{
+ char *p;
+
+ p = strstr(defstr, " ");
+ p = skipwhite(p);
+ expandvals(NULL, p, defnam);
+
+ return (TRUE);
+}
+
+/*
+ * 'define' strings follow the regex in parsdef().
+ */
+static int
+founddef(char *defstr, int blkid, int expctr, int hasval, int elen)
+{
+ struct varentry *vt, *v1 = NULL;
+ char *p, *vnamep, *vendp = NULL, *valp;
+
+ p = strstr(defstr, " "); /* move to first ' ' char. */
+ vnamep = skipwhite(p); /* find first char of var name. */
+ vendp = vnamep;
+
+ /* now find the end of the define/list name */
+ while (1) {
+ ++vendp;
+ if (*vendp == ' ')
+ break;
+ }
+ *vendp = '\0';
+
+ /*
+ * Check list name is not an existing mg function.
+ */
+ if (name_function(vnamep) != NULL)
+ return(dobeep_msgs("Variable/function name clash:", vnamep));
+
+ if (!SLIST_EMPTY(&varhead)) {
+ SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
+ if (strcmp(vnamep, v1->v_name) == 0)
+ SLIST_REMOVE(&varhead, v1, varentry, entry);
+ }
+ }
+ if ((v1 = malloc(sizeof(struct varentry))) == NULL)
+ return (ABORT);
+ SLIST_INSERT_HEAD(&varhead, v1, entry);
+ if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL)
+ return(dobeep_msg("strndup error"));
+ vnamep = v1->v_name;
+ v1->v_count = 0;
+ v1->v_vals = NULL;
+ v1->v_buf[0] = '\0';
+
+ defnam = v1->v_buf;
+
+ if (hasval) {
+ valp = skipwhite(vendp + 1);
+
+ expandvals(NULL, valp, defnam);
+ defnam = NULL;
+ }
+ *vendp = ' ';
+ return (TRUE);
+}
+
+
+static int
+expandvals(char *cmdp, char *valp, char *bp)
+{
+ char excbuf[BUFSIZE], argbuf[BUFSIZE];
+ char contbuf[BUFSIZE], varbuf[BUFSIZE];
+ char *argp, *endp, *p, *v, *s = " ";
+ char *regs;
+ int spc, cnt;
+ int inlist, sizof, fin, inquote;
+
+ /* now find the first argument */
+ p = skipwhite(valp);
+
+ if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
+ return (dobeep_msg("strlcpy error"));
+ argp = argbuf;
+ spc = 1; /* initially fake a space so we find first argument */
+ inlist = fin = inquote = cnt = spc = 0;
+
+ for (p = argbuf; *p != '\0'; p++) {
+ if (*(p + 1) == '\0')
+ fin = 1;
+
+ if (*p != ' ') {
+ if (*p == '"') {
+ if (inquote == 1)
+ inquote = 0;
+ else
+ inquote = 1;
+ }
+ if (spc == 1)
+ argp = p;
+ spc = 0;
+ }
+ if ((*p == ' ' && inquote == 0) || fin) {
+ if (spc == 1)
+ continue;
+ /* terminate arg string */
+ if (*p == ' ') {
+ *p = '\0';
+ }
+ endp = p + 1;
+ excbuf[0] = '\0';
+ varbuf[0] = '\0';
+ contbuf[0] = '\0';
+ sizof = sizeof(varbuf);
+ v = varbuf;
+ regs = "[\"]+.*[\"]+";
+ if (doregex(regs, argp))
+ ; /* found quotes */
+ else if (isvar(&argp, &v, sizof)) {
+
+ (void)(strlcat(varbuf, " ",
+ sizof) >= sizof);
+
+ *p = ' ';
+ (void)(strlcpy(contbuf, endp,
+ sizeof(contbuf)) >= sizeof(contbuf));
+
+ (void)(strlcat(varbuf, contbuf,
+ sizof) >= sizof);
+
+ argbuf[0] = ' ';
+ argbuf[1] = '\0';
+ (void)(strlcat(argbuf, varbuf,
+ sizof) >= sizof);
+
+ p = argp = argbuf;
+ spc = 1;
+ fin = 0;
+ continue;
+ } else {
+ const char *errstr;
+ int iters;
+
+ iters = strtonum(argp, 0, INT_MAX, &errstr);
+ if (errstr != NULL)
+ return (dobeep_msgs("Var not found:",
+ argp));
+ }
+#ifdef MGLOG
+ mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE);
+#endif
+ if (*bp != '\0') {
+ if (strlcat(bp, s, BUFSIZE) >= BUFSIZE)
+ return (dobeep_msg("strlcat error"));
+ }
+ if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) {
+ return (dobeep_msg("strlcat error"));
+ }
+/* v1->v_count++;*/
+
+ if (fin)
+ break;
+
+ *p = ' '; /* unterminate arg string */
+ spc = 1;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Finished with buffer evaluation, so clean up any vars.
+ * Perhaps keeps them in mg even after use,...
+ */
+/*static int
+clearvars(void)
+{
+ struct varentry *v1 = NULL;
+
+ while (!SLIST_EMPTY(&varhead)) {
+ v1 = SLIST_FIRST(&varhead);
+ SLIST_REMOVE_HEAD(&varhead, entry);
+ free(v1->v_name);
+ free(v1);
+ }
+ return (FALSE);
+}
+*/
+/*
+ * Finished with block evaluation, so clean up any expressions.
+ */
+static void
+clearexp(void)
+{
+ struct expentry *e1 = NULL;
+
+ while (!TAILQ_EMPTY(&ehead)) {
+ e1 = TAILQ_FIRST(&ehead);
+ TAILQ_REMOVE(&ehead, e1, eentry);
+ free(e1->fun);
+ free(e1);
+ }
+ return;
+}
+
+/*
+ * Cleanup before leaving.
+ */
+void
+cleanup(void)
+{
+ defnam = NULL;
+
+ clearexp();
+/* clearvars();*/
+}
+
+/*
+ * Test a string against a regular expression.
+ */
+static int
+doregex(char *r, char *e)
+{
+ regex_t regex_buff;
+
+ if (regcomp(&regex_buff, r, REG_EXTENDED)) {
+ regfree(&regex_buff);
+ return(dobeep_num("Regex compilation error line:", lnm));
+ }
+ if (!regexec(&regex_buff, e, 0, NULL, 0)) {
+ regfree(&regex_buff);
+ return(TRUE);
+ }
+ regfree(&regex_buff);
+ return(FALSE);
+}
+
+/*
+ * Display a message so it is apparent that this is the method which stopped
+ * execution.
+ */
+static int
+exitinterpreter(char *ptr, char *dobuf, int dosiz)
+{
+ cleanup();
+ if (batch == 0)
+ return(dobeep_msg("Interpreter exited via exit command."));
+ return(FALSE);
+}
+
+/*
+ * All code below commented out (until end of file).
+ *
+ * Need to think about how interpreter functions are done.
+ * Probably don't have a choice with string-append().
+
+static int getenvironmentvariable(char *, char *, int);
+static int stringappend(char *, char *, int);
+
+typedef int (*PFI)(char *, char *, int);
+
+
+struct ifunmap {
+ PFI fn_funct;
+ const char *fn_name;
+ struct ifunmap *fn_next;
+};
+static struct ifunmap *ifuns;
+
+static struct ifunmap ifunctnames[] = {
+ {exitinterpreter, "exit"},
+ {getenvironmentvariable, "get-environment-variable"},
+ {stringappend, "string-append"},
+ {NULL, NULL}
+};
+
+void
+ifunmap_init(void)
+{
+ struct ifunmap *fn;
+
+ for (fn = ifunctnames; fn->fn_name != NULL; fn++) {
+ fn->fn_next = ifuns;
+ ifuns = fn;
+ }
+}
+
+PFI
+name_ifun(const char *ifname)
+{
+ struct ifunmap *fn;
+
+ for (fn = ifuns; fn != NULL; fn = fn->fn_next) {
+ if (strcmp(fn->fn_name, ifname) == 0)
+ return (fn->fn_funct);
+ }
+
+ return (NULL);
+}
+
+
+int
+dofunc(char **ifname, char **tmpbuf, int sizof)
+{
+ PFI fnc;
+ char *p, *tmp;
+
+ p = strstr(*ifname, " ");
+ *p = '\0';
+
+ fnc = name_ifun(*ifname);
+ if (fnc == NULL)
+ return (FALSE);
+
+ *p = ' ';
+
+ tmp = *tmpbuf;
+
+ fnc(p, tmp, sizof);
+
+ return (TRUE);
+}
+
+static int
+getenvironmentvariable(char *ptr, char *dobuf, int dosiz)
+{
+ char *t;
+ char *tmp;
+ const char *q = "\"";
+
+ t = skipwhite(ptr);
+
+ if (t[0] == *q || t[strlen(t) - 1] == *q)
+ return (dobeep_msgs("Please remove '\"' around:", t));
+ if ((tmp = getenv(t)) == NULL || *tmp == '\0')
+ return(dobeep_msgs("Envar not found:", t));
+
+ dobuf[0] = '\0';
+ if (strlcat(dobuf, q, dosiz) >= dosiz)
+ return (dobeep_msg("strlcat error"));
+ if (strlcat(dobuf, tmp, dosiz) >= dosiz)
+ return (dobeep_msg("strlcat error"));
+ if (strlcat(dobuf, q, dosiz) >= dosiz)
+ return (dobeep_msg("strlcat error"));
+
+ return (TRUE);
+}
+
+static int
+stringappend(char *ptr, char *dobuf, int dosiz)
+{
+ char varbuf[BUFSIZE], funbuf[BUFSIZE];
+ char *p, *f, *v, *vendp;
+ int sizof, fin = 0;
+
+ varbuf[0] = funbuf[0] = '\0';
+ f = funbuf;
+ v = varbuf;
+ sizof = sizeof(varbuf);
+ *dobuf = '\0';
+
+ p = skipwhite(ptr);
+
+ while (*p != '\0') {
+ vendp = p;
+ while (1) {
+ if (*vendp == ' ') {
+ break;
+ } else if (*vendp == '\0') {
+ fin = 1;
+ break;
+ }
+ ++vendp;
+ }
+ *vendp = '\0';
+
+ if (isvar(&p, &v, sizof)) {
+ if (v[0] == '"' && v[strlen(v) - 1] == '"' ) {
+ v[strlen(v) - 1] = '\0';
+ v = v + 1;
+ }
+ if (strlcat(f, v, sizof) >= sizof)
+ return (dobeep_msg("strlcat error"));
+ } else {
+ if (p[0] == '"' && p[strlen(p) - 1] == '"' ) {
+ p[strlen(p) - 1] = '\0';
+ p = p + 1;
+ }
+ if (strlcat(f, p, sizof) >= sizof)
+ return (dobeep_msg("strlcat error"));
+ }
+ if (fin)
+ break;
+ vendp++;
+ if (*vendp == '\0')
+ break;
+ p = skipwhite(vendp);
+ }
+
+ (void)snprintf(dobuf, dosiz, "\"%s\"", f);
+
+ return (TRUE);
+}
+
+Index: main.c
+===================================================================
+RCS file: /cvs/src/usr.bin/mg/main.c,v
+retrieving revision 1.89
+diff -u -p -u -p -r1.89 main.c
+--- main.c 20 Mar 2021 09:00:49 -0000 1.89
++++ main.c 12 Apr 2021 17:58:52 -0000
+@@ -133,10 +133,12 @@ main(int argc, char **argv)
+ extern void grep_init(void);
+ extern void cmode_init(void);
+ extern void dired_init(void);
++ extern void ifunmap_init(void);
+
+ dired_init();
+ grep_init();
+ cmode_init();
++ ifunmap_init();
+ }
+
+
+*/
Index: contrib/mg/kbd.h
===================================================================
--- /dev/null
+++ contrib/mg/kbd.h
@@ -0,0 +1,57 @@
+/* $OpenBSD: kbd.h,v 1.19 2015/03/19 21:48:05 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * kbd.h: type definitions for symbol.c and kbd.c for mg experimental
+ */
+
+struct map_element {
+ KCHAR k_base; /* first key in element */
+ KCHAR k_num; /* last key in element */
+ PF *k_funcp; /* pointer to array of pointers */
+ /* to functions */
+ struct keymap_s *k_prefmap; /* keymap of ONLY prefix key in */
+ /* element */
+};
+
+/*
+ * Predefined keymaps are NOT type KEYMAP because final array needs
+ * dimension. If any changes are made to this struct, they must be reflected
+ * in all keymap declarations.
+ */
+
+#define KEYMAPE(NUM) { \
+ short map_num; /* elements used */ \
+ short map_max; /* elements allocated */\
+ PF map_default; /* default function */ \
+ struct map_element map_element[NUM]; /* really [e_max] */ \
+}
+typedef struct keymap_s KEYMAPE(1) KEYMAP;
+
+/* Number of map_elements to grow an overflowed keymap by */
+#define MAPGROW 3
+#define MAPINIT (MAPGROW+1)
+
+/* Max number of default bindings added to avoid creating new element */
+#define MAPELEDEF 4
+
+struct maps_s {
+ KEYMAP *p_map;
+ const char *p_name;
+ struct maps_s *p_next;
+};
+
+extern struct maps_s *maps;
+extern struct maps_s fundamental_mode;
+#define fundamental_map (fundamental_mode.p_map)
+
+int dobindkey(KEYMAP *, const char *, const char *);
+KEYMAP *name_map(const char *);
+struct maps_s *name_mode(const char *);
+PF doscan(KEYMAP *, int, KEYMAP **);
+void maps_init(void);
+int maps_add(KEYMAP *, const char *);
+
+extern struct map_element *ele;
+extern struct maps_s *defb_modes[];
Index: contrib/mg/kbd.c
===================================================================
--- /dev/null
+++ contrib/mg/kbd.c
@@ -0,0 +1,462 @@
+/* $OpenBSD: kbd.c,v 1.35 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Terminal independent keyboard handling.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "def.h"
+#include "kbd.h"
+#include "key.h"
+#include "macro.h"
+
+#ifdef MGLOG
+#include "log.h"
+#endif
+
+#define METABIT 0x80
+
+#define PROMPTL 80
+char prompt[PROMPTL] = "", *promptp = prompt;
+
+static int mgwrap(PF, int, int);
+
+static int use_metakey = TRUE;
+static int pushed = FALSE;
+static int pushedc;
+
+struct map_element *ele;
+struct key key;
+int rptcount;
+
+/*
+ * Toggle the value of use_metakey
+ */
+int
+do_meta(int f, int n)
+{
+ if (f & FFARG)
+ use_metakey = n > 0;
+ else
+ use_metakey = !use_metakey;
+ ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis");
+ return (TRUE);
+}
+
+static int bs_map = 0;
+
+/*
+ * Toggle backspace mapping
+ */
+int
+bsmap(int f, int n)
+{
+ if (f & FFARG)
+ bs_map = n > 0;
+ else
+ bs_map = !bs_map;
+ ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis");
+ return (TRUE);
+}
+
+void
+ungetkey(int c)
+{
+ if (use_metakey && pushed && c == CCHR('['))
+ pushedc |= METABIT;
+ else
+ pushedc = c;
+ pushed = TRUE;
+}
+
+int
+getkey(int flag)
+{
+ int c;
+
+ if (flag && !pushed) {
+ if (prompt[0] != '\0' && ttwait(2000)) {
+ /* avoid problems with % */
+ ewprintf("%s", prompt);
+ /* put the cursor back */
+ update(CMODE);
+ epresf = KCLEAR;
+ }
+ if (promptp > prompt)
+ *(promptp - 1) = ' ';
+ }
+ if (pushed) {
+ c = pushedc;
+ pushed = FALSE;
+ } else
+ c = ttgetc();
+
+ if (bs_map) {
+ if (c == CCHR('H'))
+ c = CCHR('?');
+ else if (c == CCHR('?'))
+ c = CCHR('H');
+ }
+ if (use_metakey && (c & METABIT)) {
+ pushedc = c & ~METABIT;
+ pushed = TRUE;
+ c = CCHR('[');
+ }
+ if (flag && promptp < &prompt[PROMPTL - 5]) {
+ promptp = getkeyname(promptp,
+ sizeof(prompt) - (promptp - prompt) - 1, c);
+ *promptp++ = '-';
+ *promptp = '\0';
+ }
+ return (c);
+}
+
+/*
+ * doscan scans a keymap for a keyboard character and returns a pointer
+ * to the function associated with that character. Sets ele to the
+ * keymap element the keyboard was found in as a side effect.
+ */
+PF
+doscan(KEYMAP *map, int c, KEYMAP **newmap)
+{
+ struct map_element *elec = &map->map_element[0];
+ struct map_element *last = &map->map_element[map->map_num];
+ PF ret;
+
+ while (elec < last && c > elec->k_num)
+ elec++;
+
+ /* used by prefix and binding code */
+ ele = elec;
+ if (elec >= last || c < elec->k_base)
+ ret = map->map_default;
+ else
+ ret = elec->k_funcp[c - elec->k_base];
+ if (ret == NULL && newmap != NULL)
+ *newmap = elec->k_prefmap;
+
+ return (ret);
+}
+
+int
+doin(void)
+{
+ KEYMAP *curmap;
+ PF funct;
+
+ *(promptp = prompt) = '\0';
+ curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
+ key.k_count = 0;
+ while ((funct = doscan(curmap, (key.k_chars[key.k_count++] =
+ getkey(TRUE)), &curmap)) == NULL)
+ /* nothing */;
+
+#ifdef MGLOG
+ if (!mglog(funct, curmap))
+ ewprintf("Problem with logging");
+#endif
+
+ if (macrodef && macrocount < MAXMACRO)
+ macro[macrocount++].m_funct = funct;
+
+ return (mgwrap(funct, 0, 1));
+}
+
+int
+rescan(int f, int n)
+{
+ int c;
+ KEYMAP *curmap;
+ int i;
+ PF fp = NULL;
+ int md = curbp->b_nmodes;
+
+ for (;;) {
+ if (ISUPPER(key.k_chars[key.k_count - 1])) {
+ c = TOLOWER(key.k_chars[key.k_count - 1]);
+ curmap = curbp->b_modes[md]->p_map;
+ for (i = 0; i < key.k_count - 1; i++) {
+ if ((fp = doscan(curmap, (key.k_chars[i]),
+ &curmap)) != NULL)
+ break;
+ }
+ if (fp == NULL) {
+ if ((fp = doscan(curmap, c, NULL)) == NULL)
+ while ((fp = doscan(curmap,
+ key.k_chars[key.k_count++] =
+ getkey(TRUE), &curmap)) == NULL)
+ /* nothing */;
+ if (fp != rescan) {
+ if (macrodef && macrocount <= MAXMACRO)
+ macro[macrocount - 1].m_funct
+ = fp;
+ return (mgwrap(fp, f, n));
+ }
+ }
+ }
+ /* try previous mode */
+ if (--md < 0)
+ return (ABORT);
+ curmap = curbp->b_modes[md]->p_map;
+ for (i = 0; i < key.k_count; i++) {
+ if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL)
+ break;
+ }
+ if (fp == NULL) {
+ while ((fp = doscan(curmap, key.k_chars[i++] =
+ getkey(TRUE), &curmap)) == NULL)
+ /* nothing */;
+ key.k_count = i;
+ }
+ if (fp != rescan && i >= key.k_count - 1) {
+ if (macrodef && macrocount <= MAXMACRO)
+ macro[macrocount - 1].m_funct = fp;
+ return (mgwrap(fp, f, n));
+ }
+ }
+}
+
+int
+universal_argument(int f, int n)
+{
+ KEYMAP *curmap;
+ PF funct;
+ int c, nn = 4;
+
+ if (f & FFUNIV)
+ nn *= n;
+ for (;;) {
+ key.k_chars[0] = c = getkey(TRUE);
+ key.k_count = 1;
+ if (c == '-')
+ return (negative_argument(f, nn));
+ if (c >= '0' && c <= '9')
+ return (digit_argument(f, nn));
+ curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
+ while ((funct = doscan(curmap, c, &curmap)) == NULL) {
+ key.k_chars[key.k_count++] = c = getkey(TRUE);
+ }
+ if (funct != universal_argument) {
+ if (macrodef && macrocount < MAXMACRO - 1) {
+ if (f & FFARG)
+ macrocount--;
+ macro[macrocount++].m_count = nn;
+ macro[macrocount++].m_funct = funct;
+ }
+ return (mgwrap(funct, FFUNIV, nn));
+ }
+ nn <<= 2;
+ }
+}
+
+/* ARGSUSED */
+int
+digit_argument(int f, int n)
+{
+ KEYMAP *curmap;
+ PF funct;
+ int nn, c;
+
+ nn = key.k_chars[key.k_count - 1] - '0';
+ for (;;) {
+ c = getkey(TRUE);
+ if (c < '0' || c > '9')
+ break;
+ nn *= 10;
+ nn += c - '0';
+ }
+ key.k_chars[0] = c;
+ key.k_count = 1;
+ curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
+ while ((funct = doscan(curmap, c, &curmap)) == NULL) {
+ key.k_chars[key.k_count++] = c = getkey(TRUE);
+ }
+ if (macrodef && macrocount < MAXMACRO - 1) {
+ if (f & FFARG)
+ macrocount--;
+ else
+ macro[macrocount - 1].m_funct = universal_argument;
+ macro[macrocount++].m_count = nn;
+ macro[macrocount++].m_funct = funct;
+ }
+ return (mgwrap(funct, FFOTHARG, nn));
+}
+
+int
+negative_argument(int f, int n)
+{
+ KEYMAP *curmap;
+ PF funct;
+ int c;
+ int nn = 0;
+
+ for (;;) {
+ c = getkey(TRUE);
+ if (c < '0' || c > '9')
+ break;
+ nn *= 10;
+ nn += c - '0';
+ }
+ if (nn)
+ nn = -nn;
+ else
+ nn = -n;
+ key.k_chars[0] = c;
+ key.k_count = 1;
+ curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
+ while ((funct = doscan(curmap, c, &curmap)) == NULL) {
+ key.k_chars[key.k_count++] = c = getkey(TRUE);
+ }
+ if (macrodef && macrocount < MAXMACRO - 1) {
+ if (f & FFARG)
+ macrocount--;
+ else
+ macro[macrocount - 1].m_funct = universal_argument;
+ macro[macrocount++].m_count = nn;
+ macro[macrocount++].m_funct = funct;
+ }
+ return (mgwrap(funct, FFNEGARG, nn));
+}
+
+/*
+ * Insert a character. While defining a macro, create a "LINE" containing
+ * all inserted characters.
+ */
+int
+selfinsert(int f, int n)
+{
+ struct line *lp;
+ int c;
+ int count;
+
+ if (n < 0)
+ return (FALSE);
+ if (n == 0)
+ return (TRUE);
+ c = key.k_chars[key.k_count - 1];
+
+ if (macrodef && macrocount < MAXMACRO) {
+ if (f & FFARG)
+ macrocount -= 2;
+
+ /* last command was insert -- tack on the end */
+ if (lastflag & CFINS) {
+ macrocount--;
+ /* Ensure the line can handle the new characters */
+ if (maclcur->l_size < maclcur->l_used + n) {
+ if (lrealloc(maclcur, maclcur->l_used + n) ==
+ FALSE)
+ return (FALSE);
+ }
+ maclcur->l_used += n;
+ /* Copy in the new data */
+ for (count = maclcur->l_used - n;
+ count < maclcur->l_used; count++)
+ maclcur->l_text[count] = c;
+ } else {
+ macro[macrocount - 1].m_funct = insert;
+ if ((lp = lalloc(n)) == NULL)
+ return (FALSE);
+ lp->l_bp = maclcur;
+ lp->l_fp = maclcur->l_fp;
+ maclcur->l_fp = lp;
+ maclcur = lp;
+ for (count = 0; count < n; count++)
+ lp->l_text[count] = c;
+ }
+ thisflag |= CFINS;
+ }
+ if (c == *curbp->b_nlchr) {
+ do {
+ count = lnewline();
+ } while (--n && count == TRUE);
+ return (count);
+ }
+
+ /* overwrite mode */
+ if (curbp->b_flag & BFOVERWRITE) {
+ lchange(WFEDIT);
+ while (curwp->w_doto < llength(curwp->w_dotp) && n--)
+ lputc(curwp->w_dotp, curwp->w_doto++, c);
+ if (n <= 0)
+ return (TRUE);
+ }
+ return (linsert(n, c));
+}
+
+/*
+ * selfinsert() can't be called directly from a startup file or by
+ * 'eval-current-buffer' since it is by design, meant to be called interactively
+ * as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to
+ * be used by excline(). Having ask_selfinsert() helps with regression testing.
+ * No manual page entry since use case is a bit obscure. See 'insert' command.
+ */
+int
+ask_selfinsert(int f, int n)
+{
+ char *c, cbuf[2];
+
+ if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf),
+ EFNEW)) == NULL || (c[0] == '\0'))
+ return (ABORT);
+
+ key.k_chars[0] = *c;
+ key.k_chars[1] = '\0';
+ key.k_count = 1;
+
+ return (selfinsert(FFRAND, 1));
+}
+
+/*
+ * This could be implemented as a keymap with everything defined as self-insert.
+ */
+int
+quote(int f, int n)
+{
+ int c;
+
+ key.k_count = 1;
+ if ((key.k_chars[0] = getkey(TRUE)) >= '0' && key.k_chars[0] <= '7') {
+ key.k_chars[0] -= '0';
+ if ((c = getkey(TRUE)) >= '0' && c <= '7') {
+ key.k_chars[0] <<= 3;
+ key.k_chars[0] += c - '0';
+ if ((c = getkey(TRUE)) >= '0' && c <= '7') {
+ key.k_chars[0] <<= 3;
+ key.k_chars[0] += c - '0';
+ } else
+ ungetkey(c);
+ } else
+ ungetkey(c);
+ }
+ return (selfinsert(f, n));
+}
+
+/*
+ * Wraper function to count invocation repeats.
+ * We ignore any function whose sole purpose is to get us
+ * to the intended function.
+ */
+static int
+mgwrap(PF funct, int f, int n)
+{
+ static PF ofp;
+
+ if (funct != rescan &&
+ funct != negative_argument &&
+ funct != digit_argument &&
+ funct != universal_argument) {
+ if (funct == ofp)
+ rptcount++;
+ else
+ rptcount = 0;
+ ofp = funct;
+ }
+
+ return ((*funct)(f, n));
+}
Index: contrib/mg/key.h
===================================================================
--- /dev/null
+++ contrib/mg/key.h
@@ -0,0 +1,14 @@
+/* $OpenBSD: key.h,v 1.6 2019/06/22 15:38:15 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/* key.h: Insert file for mg 2 functions that need to reference key pressed */
+
+#define MAXKEY 8 /* maximum number of prefix chars */
+
+struct key { /* the character sequence in a key */
+ int k_count; /* number of chars */
+ KCHAR k_chars[MAXKEY]; /* chars */
+};
+
+extern struct key key;
Index: contrib/mg/keymap.c
===================================================================
--- /dev/null
+++ contrib/mg/keymap.c
@@ -0,0 +1,577 @@
+/* $OpenBSD: keymap.c,v 1.59 2021/04/20 10:02:50 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Keyboard maps. This is character set dependent. The terminal specific
+ * parts of building the keymap has been moved to a better place.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+#include "kbd.h"
+
+/*
+ * initial keymap declarations, deepest first
+ */
+
+static PF cHcG[] = {
+ ctrlg, /* ^G */
+ help_help /* ^H */
+};
+
+static PF cHa[] = {
+ apropos_command, /* a */
+ wallchart, /* b */
+ desckey /* c */
+};
+
+struct KEYMAPE (2) helpmap = {
+ 2,
+ 2,
+ rescan,
+ {
+ {
+ CCHR('G'), CCHR('H'), cHcG, NULL
+ },
+ {
+ 'a', 'c', cHa, NULL
+ }
+ }
+};
+
+static PF cCsc[] = {
+ cscallerfuncs, /* c */
+ csdefinition, /* d */
+ csegrep, /* e */
+ csfindfile, /* f */
+ rescan, /* g */
+ rescan, /* h */
+ csfindinc, /* i */
+ rescan, /* j */
+ rescan, /* k */
+ rescan, /* l */
+ rescan, /* m */
+ csnextmatch, /* n */
+ rescan, /* o */
+ csprevmatch, /* p */
+ rescan, /* q */
+ rescan, /* r */
+ cssymbol, /* s */
+ csfindtext /* t */
+};
+
+static struct KEYMAPE (1) cCsmap = {
+ 1,
+ 1,
+ rescan,
+ {
+ {
+ 'c', 't', cCsc, NULL
+ }
+ }
+};
+
+static PF cCs[] = {
+ NULL /* s */
+};
+
+struct KEYMAPE (2) ccmap = {
+ 2,
+ 2,
+ rescan,
+ {
+ {
+ CCHR('@'), CCHR('@'), (PF[]){ rescan }, NULL
+ },
+ {
+ 's', 's', cCs, (KEYMAP *) & cCsmap
+ }
+ }
+};
+
+static PF cX4cF[] = {
+ poptofile, /* ^f */
+ ctrlg /* ^g */
+};
+static PF cX4b[] = {
+ poptobuffer, /* b */
+ rescan, /* c */
+ rescan, /* d */
+ rescan, /* e */
+ poptofile /* f */
+};
+static struct KEYMAPE (2) cX4map = {
+ 2,
+ 2,
+ rescan,
+ {
+ {
+ CCHR('F'), CCHR('G'), cX4cF, NULL
+ },
+ {
+ 'b', 'f', cX4b, NULL
+ }
+ }
+};
+
+static PF cXcB[] = {
+ listbuffers, /* ^B */
+ quit, /* ^C */
+ rescan, /* ^D */
+ rescan, /* ^E */
+ filevisit, /* ^F */
+ ctrlg /* ^G */
+};
+
+static PF cXcJ[] = {
+ dired_jump, /* ^J */
+ rescan, /* ^K */
+ lowerregion, /* ^L */
+ rescan, /* ^M */
+ rescan, /* ^N */
+ deblank, /* ^O */
+ rescan, /* ^P */
+ togglereadonly, /* ^Q */
+ filevisitro, /* ^R */
+ filesave, /* ^S */
+ rescan, /* ^T */
+ upperregion, /* ^U */
+ filevisitalt, /* ^V */
+ filewrite, /* ^W */
+ swapmark /* ^X */
+};
+
+static PF cXlp[] = {
+ definemacro, /* ( */
+ finishmacro /* ) */
+};
+
+static PF cX0[] = {
+ delwind, /* 0 */
+ onlywind, /* 1 */
+ splitwind, /* 2 */
+ rescan, /* 3 */
+ NULL /* 4 */
+};
+
+static PF cXeq[] = {
+ showcpos /* = */
+};
+
+static PF cXcar[] = {
+ enlargewind, /* ^ */
+ rescan, /* _ */
+ next_error, /* ` */
+ rescan, /* a */
+ usebuffer, /* b */
+ rescan, /* c */
+ rescan, /* d */
+ executemacro, /* e */
+ setfillcol, /* f */
+ gotoline, /* g */
+ markbuffer, /* h */
+ fileinsert, /* i */
+ rescan, /* j */
+ killbuffer_cmd, /* k */
+ rescan, /* l */
+ rescan, /* m */
+ nextwind, /* n */
+ nextwind, /* o */
+ prevwind, /* p */
+ rescan, /* q */
+ rescan, /* r */
+ savebuffers, /* s */
+ rescan, /* t */
+ undo /* u */
+};
+
+struct KEYMAPE (6) cXmap = {
+ 6,
+ 6,
+ rescan,
+ {
+ {
+ CCHR('B'), CCHR('G'), cXcB, NULL
+ },
+ {
+ CCHR('J'), CCHR('X'), cXcJ, NULL
+ },
+ {
+ '(', ')', cXlp, NULL
+ },
+ {
+ '0', '4', cX0, (KEYMAP *) & cX4map
+ },
+ {
+ '=', '=', cXeq, NULL
+ },
+ {
+ '^', 'u', cXcar, NULL
+ }
+ }
+};
+
+static PF metacG[] = {
+ ctrlg /* ^G */
+};
+
+static PF metacV[] = {
+ pagenext /* ^V */
+};
+
+static PF metaspex[] = {
+ justone, /* space */
+ shellcommand /* ! */
+};
+
+static PF metapct[] = {
+ queryrepl /* % */
+};
+
+static PF metami[] = {
+ poptag, /* * */
+ rescan, /* + */
+ rescan, /* , */
+ negative_argument, /* - */
+ findtag, /* . */
+ rescan, /* / */
+ digit_argument, /* 0 */
+ digit_argument, /* 1 */
+ digit_argument, /* 2 */
+ digit_argument, /* 3 */
+ digit_argument, /* 4 */
+ digit_argument, /* 5 */
+ digit_argument, /* 6 */
+ digit_argument, /* 7 */
+ digit_argument, /* 8 */
+ digit_argument, /* 9 */
+ rescan, /* : */
+ rescan, /* ; */
+ gotobob, /* < */
+ rescan, /* = */
+ gotoeob /* > */
+};
+
+static PF metasqf[] = {
+ NULL, /* [ */
+ delwhite, /* \ */
+ rescan, /* ] */
+ joinline, /* ^ */
+ rescan, /* _ */
+ rescan, /* ` */
+ rescan, /* a */
+ backword, /* b */
+ capword, /* c */
+ delfword, /* d */
+ rescan, /* e */
+ forwword, /* f */
+ rescan, /* g */
+ markpara /* h */
+};
+
+static PF metal[] = {
+ lowerword, /* l */
+ backtoindent, /* m */
+ rescan, /* n */
+ rescan, /* o */
+ rescan, /* p */
+ fillpara, /* q */
+ backsearch, /* r */
+ forwsearch, /* s */
+ transposeword, /* t */
+ upperword, /* u */
+ backpage, /* v */
+ copyregion, /* w */
+ extend, /* x */
+ rescan, /* y */
+ rescan, /* z */
+ gotobop, /* { */
+ piperegion, /* | */
+ gotoeop /* } */
+};
+
+static PF metasqlZ[] = {
+ rescan /* Z */
+};
+
+static PF metatilde[] = {
+ notmodified, /* ~ */
+ delbword /* DEL */
+};
+
+struct KEYMAPE (1) metasqlmap = {
+ 1,
+ 1,
+ rescan,
+ {
+ {
+ 'Z', 'Z', metasqlZ, NULL
+ }
+ }
+};
+
+struct KEYMAPE (8) metamap = {
+ 8,
+ 8,
+ rescan,
+ {
+ {
+ CCHR('G'), CCHR('G'), metacG, NULL
+ },
+ {
+ CCHR('V'), CCHR('V'), metacV, NULL
+ },
+ {
+ ' ', '!', metaspex, NULL
+ },
+ {
+ '%', '%', metapct, NULL
+ },
+ {
+ '*', '>', metami, NULL
+ },
+ {
+ '[', 'h', metasqf, (KEYMAP *) &metasqlmap
+ },
+ {
+ 'l', '}', metal, NULL
+ },
+ {
+ '~', CCHR('?'), metatilde, NULL
+ }
+ }
+};
+
+static PF fund_at[] = {
+ setmark, /* ^@ */
+ gotobol, /* ^A */
+ backchar, /* ^B */
+ NULL, /* ^C */
+ forwdel, /* ^D */
+ gotoeol, /* ^E */
+ forwchar, /* ^F */
+ ctrlg, /* ^G */
+};
+
+static PF fund_h[] = {
+ NULL, /* ^H */
+};
+
+
+/* ^I is selfinsert */
+static PF fund_CJ[] = {
+ lfindent, /* ^J */
+ killline, /* ^K */
+ reposition, /* ^L */
+ enewline, /* ^M */
+ forwline, /* ^N */
+ openline, /* ^O */
+ backline, /* ^P */
+ quote, /* ^Q */
+ backisearch, /* ^R */
+ forwisearch, /* ^S */
+ twiddle, /* ^T */
+ universal_argument, /* ^U */
+ forwpage, /* ^V */
+ killregion, /* ^W */
+ NULL, /* ^X */
+ yank, /* ^Y */
+ spawncli /* ^Z */
+};
+
+static PF fund_esc[] = {
+ NULL, /* esc */
+ rescan, /* ^\ selfinsert is default on fundamental */
+ rescan, /* ^] */
+ rescan, /* ^^ */
+ undo /* ^_ */
+};
+
+static PF fund_del[] = {
+ backdel /* DEL */
+};
+
+static PF fund_cb[] = {
+ showmatch /* ) ] } */
+};
+
+static struct KEYMAPE (8) fundmap = {
+ 8,
+ 8,
+ selfinsert,
+ {
+ {
+ CCHR('@'), CCHR('G'), fund_at, (KEYMAP *) & ccmap
+ },
+ {
+ CCHR('H'), CCHR('H'), fund_h, (KEYMAP *) & helpmap
+ },
+ {
+ CCHR('J'), CCHR('Z'), fund_CJ, (KEYMAP *) & cXmap
+ },
+ {
+ CCHR('['), CCHR('_'), fund_esc, (KEYMAP *) & metamap
+ },
+ {
+ ')', ')', fund_cb, NULL
+ },
+ {
+ ']', ']', fund_cb, NULL
+ },
+ {
+ '}', '}', fund_cb, NULL
+ },
+ {
+ CCHR('?'), CCHR('?'), fund_del, NULL
+ },
+ }
+};
+
+static PF fill_sp[] = {
+ fillword /* ' ' */
+};
+
+static struct KEYMAPE (1) fillmap = {
+ 1,
+ 1,
+ rescan,
+ {
+ { ' ', ' ', fill_sp, NULL }
+ }
+};
+
+static PF indent_lf[] = {
+ enewline, /* ^J */
+ rescan, /* ^K */
+ rescan, /* ^L */
+ lfindent /* ^M */
+};
+
+static struct KEYMAPE (1) indntmap = {
+ 1,
+ 1,
+ rescan,
+ {
+ {
+ CCHR('J'), CCHR('M'), indent_lf, NULL
+ }
+ }
+};
+
+#ifdef NOTAB
+static PF notab_tab[] = {
+ space_to_tabstop /* ^I */
+};
+
+static struct KEYMAPE (1) notabmap = {
+ 1,
+ 1,
+ rescan,
+ {
+ {
+ CCHR('I'), CCHR('I'), notab_tab, NULL
+ }
+ }
+};
+#endif /* NOTAB */
+
+static struct KEYMAPE (1) overwmap = {
+ 0,
+ 1, /* 1 to avoid 0 sized array */
+ rescan,
+ {
+ /* unused dummy entry for VMS C */
+ {
+ (KCHAR)0, (KCHAR)0, NULL, NULL
+ }
+ }
+};
+
+
+/*
+ * The basic (root) keyboard map
+ */
+struct maps_s fundamental_mode = { (KEYMAP *)&fundmap, "fundamental" };
+
+/*
+ * give names to the maps, for use by help etc. If the map is to be bindable,
+ * it must also be listed in the function name table below with the same
+ * name. Maps created dynamically currently don't get added here, thus are
+ * unnamed. Modes are just named keymaps with functions to add/subtract them
+ * from a buffer's list of modes. If you change a mode name, change it in
+ * modes.c also.
+ */
+
+static struct maps_s map_table[] = {
+ {(KEYMAP *) &fillmap, "fill",},
+ {(KEYMAP *) &indntmap, "indent",},
+#ifdef NOTAB
+ {(KEYMAP *) &notabmap, "notab",},
+#endif /* NOTAB */
+ {(KEYMAP *) &overwmap, "overwrite",},
+ {(KEYMAP *) &metamap, "esc prefix",},
+ {(KEYMAP *) &cXmap, "c-x prefix",},
+ {(KEYMAP *) &cX4map, "c-x 4 prefix",},
+ {(KEYMAP *) &helpmap, "help",},
+ {NULL, NULL}
+};
+
+struct maps_s *maps;
+
+void
+maps_init(void)
+{
+ int i;
+ struct maps_s *mp;
+
+ maps = &fundamental_mode;
+ for (i = 0; map_table[i].p_name != NULL; i++) {
+ mp = &map_table[i];
+ mp->p_next = maps;
+ maps = mp;
+ }
+}
+
+/*
+ * Insert a new (named) keymap at the head of the keymap list.
+ */
+int
+maps_add(KEYMAP *map, const char *name)
+{
+ struct maps_s *mp;
+
+ if ((mp = malloc(sizeof(*mp))) == NULL)
+ return (FALSE);
+
+ mp->p_name = name;
+ mp->p_map = map;
+ mp->p_next = maps;
+ maps = mp;
+
+ return (TRUE);
+}
+
+struct maps_s *
+name_mode(const char *name)
+{
+ struct maps_s *mp;
+
+ for (mp = maps; mp != NULL; mp = mp->p_next)
+ if (strcmp(mp->p_name, name) == 0)
+ return (mp);
+ return (NULL);
+}
+
+KEYMAP *
+name_map(const char *name)
+{
+ struct maps_s *mp;
+
+ return ((mp = name_mode(name)) == NULL ? NULL : mp->p_map);
+}
Index: contrib/mg/line.c
===================================================================
--- /dev/null
+++ contrib/mg/line.c
@@ -0,0 +1,624 @@
+/* $OpenBSD: line.c,v 1.63 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Text line handling.
+ *
+ * The functions in this file are a general set of line management
+ * utilities. They are the only routines that touch the text. They
+ * also touch the buffer and window structures to make sure that the
+ * necessary updating gets done.
+ *
+ * Note that this code only updates the dot and mark values in the window
+ * list. Since all the code acts on the current window, the buffer that
+ * we are editing must be displayed, which means that "b_nwnd" is non-zero,
+ * which means that the dot and mark values in the buffer headers are
+ * nonsense.
+ */
+
+#include <sys/queue.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+
+int casereplace = TRUE;
+
+/*
+ * Preserve the case of the replaced string.
+ */
+int
+setcasereplace(int f, int n)
+{
+ if (f & FFARG)
+ casereplace = n > 0;
+ else
+ casereplace = !casereplace;
+ ewprintf("Case-replace is %sabled", casereplace ? "en" : "dis");
+ return (TRUE);
+}
+
+/*
+ * Allocate a new line of size `used'. lrealloc() can be called if the line
+ * ever needs to grow beyond that.
+ */
+struct line *
+lalloc(int used)
+{
+ struct line *lp;
+
+ if ((lp = malloc(sizeof(*lp))) == NULL)
+ return (NULL);
+ lp->l_text = NULL;
+ lp->l_size = 0;
+ lp->l_used = used; /* XXX */
+ if (lrealloc(lp, used) == FALSE) {
+ free(lp);
+ return (NULL);
+ }
+ return (lp);
+}
+
+int
+lrealloc(struct line *lp, int newsize)
+{
+ char *tmp;
+
+ if (lp->l_size < newsize) {
+ if ((tmp = realloc(lp->l_text, newsize)) == NULL)
+ return (FALSE);
+ lp->l_text = tmp;
+ lp->l_size = newsize;
+ }
+ return (TRUE);
+}
+
+/*
+ * Delete line "lp". Fix all of the links that might point to it (they are
+ * moved to offset 0 of the next line. Unlink the line from whatever buffer
+ * it might be in, and release the memory. The buffers are updated too; the
+ * magic conditions described in the above comments don't hold here.
+ */
+void
+lfree(struct line *lp)
+{
+ struct buffer *bp;
+ struct mgwin *wp;
+
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_linep == lp)
+ wp->w_linep = lp->l_fp;
+ if (wp->w_dotp == lp) {
+ wp->w_dotp = lp->l_fp;
+ wp->w_doto = 0;
+ }
+ if (wp->w_markp == lp) {
+ wp->w_markp = lp->l_fp;
+ wp->w_marko = 0;
+ }
+ }
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ if (bp->b_nwnd == 0) {
+ if (bp->b_dotp == lp) {
+ bp->b_dotp = lp->l_fp;
+ bp->b_doto = 0;
+ }
+ if (bp->b_markp == lp) {
+ bp->b_markp = lp->l_fp;
+ bp->b_marko = 0;
+ }
+ }
+ }
+ lp->l_bp->l_fp = lp->l_fp;
+ lp->l_fp->l_bp = lp->l_bp;
+ free(lp->l_text);
+ free(lp);
+}
+
+/*
+ * This routine is called when a character changes in place in the current
+ * buffer. It updates all of the required flags in the buffer and window
+ * system. The flag used is passed as an argument; if the buffer is being
+ * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
+ * mode line needs to be updated (the "*" has to be set).
+ */
+void
+lchange(int flag)
+{
+ struct mgwin *wp;
+
+ /* update mode lines if this is the first change. */
+ if ((curbp->b_flag & BFCHG) == 0) {
+ flag |= WFMODE;
+ curbp->b_flag |= BFCHG;
+ }
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == curbp) {
+ wp->w_rflag |= flag;
+ if (wp != curwp)
+ wp->w_rflag |= WFFULL;
+ }
+ }
+}
+
+/*
+ * Insert "n" copies of the character "c" at the current location of dot.
+ * In the easy case all that happens is the text is stored in the line.
+ * In the hard case, the line has to be reallocated. When the window list
+ * is updated, take special care; I screwed it up once. You always update
+ * dot in the current window. You update mark and a dot in another window
+ * if it is greater than the place where you did the insert. Return TRUE
+ * if all is well, and FALSE on errors.
+ */
+int
+linsert(int n, int c)
+{
+ struct line *lp1;
+ struct mgwin *wp;
+ RSIZE i;
+ int doto;
+ int s;
+
+ if (!n)
+ return (TRUE);
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read only");
+ return (FALSE);
+ }
+
+ lchange(WFEDIT);
+
+ /* current line */
+ lp1 = curwp->w_dotp;
+
+ /* special case for the end */
+ if (lp1 == curbp->b_headp) {
+ struct line *lp2, *lp3;
+
+ /* now should only happen in empty buffer */
+ if (curwp->w_doto != 0) {
+ dobeep();
+ ewprintf("bug: linsert");
+ return (FALSE);
+ }
+ /* allocate a new line */
+ if ((lp2 = lalloc(n)) == NULL)
+ return (FALSE);
+ /* previous line */
+ lp3 = lp1->l_bp;
+ /* link in */
+ lp3->l_fp = lp2;
+ lp2->l_fp = lp1;
+ lp1->l_bp = lp2;
+ lp2->l_bp = lp3;
+ for (i = 0; i < n; ++i)
+ lp2->l_text[i] = c;
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_linep == lp1)
+ wp->w_linep = lp2;
+ if (wp->w_dotp == lp1)
+ wp->w_dotp = lp2;
+ if (wp->w_markp == lp1)
+ wp->w_markp = lp2;
+ }
+ undo_add_insert(lp2, 0, n);
+ curwp->w_doto = n;
+ return (TRUE);
+ }
+ /* save for later */
+ doto = curwp->w_doto;
+
+ if ((lp1->l_used + n) > lp1->l_size) {
+ if (lrealloc(lp1, lp1->l_used + n) == FALSE)
+ return (FALSE);
+ }
+ lp1->l_used += n;
+ if (lp1->l_used != n)
+ memmove(&lp1->l_text[doto + n], &lp1->l_text[doto],
+ lp1->l_used - n - doto);
+
+ /* Add the characters */
+ for (i = 0; i < n; ++i)
+ lp1->l_text[doto + i] = c;
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_dotp == lp1) {
+ if (wp == curwp || wp->w_doto > doto)
+ wp->w_doto += n;
+ }
+ if (wp->w_markp == lp1) {
+ if (wp->w_marko > doto)
+ wp->w_marko += n;
+ }
+ }
+ undo_add_insert(curwp->w_dotp, doto, n);
+ return (TRUE);
+}
+
+/*
+ * Do the work of inserting a newline at the given line/offset.
+ * If mark is on the current line, we may have to move the markline
+ * to keep line numbers in sync.
+ * lnewline_at assumes the current buffer is writable. Checking for
+ * this fact should be done by the caller.
+ */
+int
+lnewline_at(struct line *lp1, int doto)
+{
+ struct line *lp2;
+ struct mgwin *wp;
+ int nlen, tcurwpdotline;
+
+ lchange(WFFULL);
+
+ curwp->w_bufp->b_lines++;
+ /* Check if mark is past dot (even on current line) */
+ if (curwp->w_markline > curwp->w_dotline ||
+ (curwp->w_dotline == curwp->w_markline &&
+ curwp->w_marko >= doto))
+ curwp->w_markline++;
+
+ tcurwpdotline = curwp->w_dotline;
+
+ /* If start of line, allocate a new line instead of copying */
+ if (doto == 0) {
+ /* new first part */
+ if ((lp2 = lalloc(0)) == NULL)
+ return (FALSE);
+ lp2->l_bp = lp1->l_bp;
+ lp1->l_bp->l_fp = lp2;
+ lp2->l_fp = lp1;
+ lp1->l_bp = lp2;
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_linep == lp1)
+ wp->w_linep = lp2;
+ if (wp->w_dotline >= tcurwpdotline &&
+ wp->w_bufp == curwp->w_bufp)
+ wp->w_dotline++;
+ }
+ undo_add_boundary(FFRAND, 1);
+ undo_add_insert(lp2, 0, 1);
+ undo_add_boundary(FFRAND, 1);
+ return (TRUE);
+ }
+
+ /* length of new part */
+ nlen = llength(lp1) - doto;
+
+ /* new second half line */
+ if ((lp2 = lalloc(nlen)) == NULL)
+ return (FALSE);
+ if (nlen != 0)
+ bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
+ lp1->l_used = doto;
+ lp2->l_bp = lp1;
+ lp2->l_fp = lp1->l_fp;
+ lp1->l_fp = lp2;
+ lp2->l_fp->l_bp = lp2;
+ /* Windows */
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_dotp == lp1 && wp->w_doto >= doto) {
+ wp->w_dotp = lp2;
+ wp->w_doto -= doto;
+ wp->w_dotline++;
+ } else if (wp->w_dotline > tcurwpdotline &&
+ wp->w_bufp == curwp->w_bufp)
+ wp->w_dotline++;
+ if (wp->w_markp == lp1 && wp->w_marko >= doto) {
+ wp->w_markp = lp2;
+ wp->w_marko -= doto;
+ }
+ }
+ undo_add_boundary(FFRAND, 1);
+ undo_add_insert(lp1, llength(lp1), 1);
+ undo_add_boundary(FFRAND, 1);
+ return (TRUE);
+}
+
+/*
+ * Insert a newline into the buffer at the current location of dot in the
+ * current window.
+ */
+int
+lnewline(void)
+{
+ int s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read only");
+ return (FALSE);
+ }
+ return (lnewline_at(curwp->w_dotp, curwp->w_doto));
+}
+
+/*
+ * This function deletes "n" bytes, starting at dot. (actually, n+1, as the
+ * newline is included) It understands how to deal with end of lines, etc.
+ * It returns TRUE if all of the characters were deleted, and FALSE if
+ * they were not (because dot ran into the end of the buffer).
+ * The "kflag" indicates either no insertion, or direction of insertion
+ * into the kill buffer.
+ */
+int
+ldelete(RSIZE n, int kflag)
+{
+ struct line *dotp;
+ RSIZE chunk;
+ struct mgwin *wp;
+ int doto;
+ char *cp1, *cp2;
+ size_t len;
+ char *sv = NULL;
+ int end;
+ int s;
+ int rval = FALSE;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read only");
+ goto out;
+ }
+ len = n;
+ if ((sv = calloc(1, len + 1)) == NULL)
+ goto out;
+ end = 0;
+
+ undo_add_delete(curwp->w_dotp, curwp->w_doto, n, (kflag & KREG));
+
+ while (n != 0) {
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ /* Hit the end of the buffer */
+ if (dotp == curbp->b_headp)
+ goto out;
+ /* Size of the chunk */
+ chunk = dotp->l_used - doto;
+
+ if (chunk > n)
+ chunk = n;
+ /* End of line, merge */
+ if (chunk == 0) {
+ if (dotp == blastlp(curbp))
+ goto out;
+ lchange(WFFULL);
+ if (ldelnewline() == FALSE)
+ goto out;
+ end = strlcat(sv, curbp->b_nlchr, len + 1);
+ --n;
+ continue;
+ }
+ lchange(WFEDIT);
+ /* Scrunch text */
+ cp1 = &dotp->l_text[doto];
+ memcpy(&sv[end], cp1, chunk);
+ end += chunk;
+ sv[end] = '\0';
+ for (cp2 = cp1 + chunk; cp2 < &dotp->l_text[dotp->l_used];
+ cp2++)
+ *cp1++ = *cp2;
+ dotp->l_used -= (int)chunk;
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_dotp == dotp && wp->w_doto >= doto) {
+ wp->w_doto -= chunk;
+ if (wp->w_doto < doto)
+ wp->w_doto = doto;
+ }
+ if (wp->w_markp == dotp && wp->w_marko >= doto) {
+ wp->w_marko -= chunk;
+ if (wp->w_marko < doto)
+ wp->w_marko = doto;
+ }
+ }
+ n -= chunk;
+ }
+ if (kchunk(sv, (RSIZE)len, kflag) != TRUE)
+ goto out;
+ rval = TRUE;
+out:
+ free(sv);
+ return (rval);
+}
+
+/*
+ * Delete a newline and join the current line with the next line. If the next
+ * line is the magic header line always return TRUE; merging the last line
+ * with the header line can be thought of as always being a successful
+ * operation. Even if nothing is done, this makes the kill buffer work
+ * "right". If the mark is past the dot (actually, markline > dotline),
+ * decrease the markline accordingly to keep line numbers in sync.
+ * Easy cases can be done by shuffling data around. Hard cases
+ * require that lines be moved about in memory. Return FALSE on error and
+ * TRUE if all looks ok. We do not update w_dotline here, as deletes are done
+ * after moves.
+ */
+int
+ldelnewline(void)
+{
+ struct line *lp1, *lp2, *lp3;
+ struct mgwin *wp;
+ int s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read only");
+ return (FALSE);
+ }
+
+ lp1 = curwp->w_dotp;
+ lp2 = lp1->l_fp;
+ /* at the end of the buffer */
+ if (lp2 == curbp->b_headp)
+ return (TRUE);
+ /* Keep line counts in sync */
+ curwp->w_bufp->b_lines--;
+ if (curwp->w_markline > curwp->w_dotline)
+ curwp->w_markline--;
+ if (lp2->l_used <= lp1->l_size - lp1->l_used) {
+ bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used);
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_linep == lp2)
+ wp->w_linep = lp1;
+ if (wp->w_dotp == lp2) {
+ wp->w_dotp = lp1;
+ wp->w_doto += lp1->l_used;
+ }
+ if (wp->w_markp == lp2) {
+ wp->w_markp = lp1;
+ wp->w_marko += lp1->l_used;
+ }
+ }
+ lp1->l_used += lp2->l_used;
+ lp1->l_fp = lp2->l_fp;
+ lp2->l_fp->l_bp = lp1;
+ free(lp2);
+ return (TRUE);
+ }
+ if ((lp3 = lalloc(lp1->l_used + lp2->l_used)) == NULL)
+ return (FALSE);
+ bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used);
+ bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used);
+ lp1->l_bp->l_fp = lp3;
+ lp3->l_fp = lp2->l_fp;
+ lp2->l_fp->l_bp = lp3;
+ lp3->l_bp = lp1->l_bp;
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_linep == lp1 || wp->w_linep == lp2)
+ wp->w_linep = lp3;
+ if (wp->w_dotp == lp1)
+ wp->w_dotp = lp3;
+ else if (wp->w_dotp == lp2) {
+ wp->w_dotp = lp3;
+ wp->w_doto += lp1->l_used;
+ }
+ if (wp->w_markp == lp1)
+ wp->w_markp = lp3;
+ else if (wp->w_markp == lp2) {
+ wp->w_markp = lp3;
+ wp->w_marko += lp1->l_used;
+ }
+ }
+ free(lp1);
+ free(lp2);
+ return (TRUE);
+}
+
+/*
+ * Replace plen characters before dot with argument string. Control-J
+ * characters in st are interpreted as newlines. There is a casehack
+ * disable flag (normally it likes to match case of replacement to what
+ * was there).
+ */
+int
+lreplace(RSIZE plen, char *st)
+{
+ RSIZE rlen; /* replacement length */
+ struct line *lp;
+ RSIZE n;
+ int s, doto, is_query_capitalised = 0, is_query_allcaps = 0;
+ int is_replace_alllower = 0;
+ char *repl = NULL;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read only");
+ return (FALSE);
+ }
+
+ if ((repl = strdup(st)) == NULL) {
+ dobeep();
+ ewprintf("out of memory");
+ return (FALSE);
+ }
+ rlen = strlen(repl);
+
+ undo_boundary_enable(FFRAND, 0);
+ (void)backchar(FFARG | FFRAND, (int)plen);
+
+ if (casereplace != TRUE)
+ goto done;
+
+ lp = curwp->w_dotp;
+ if (ltext(lp) == NULL)
+ goto done;
+ doto = curwp->w_doto;
+ n = plen;
+
+ is_query_capitalised = isupper((unsigned char)lgetc(lp, doto));
+
+ if (is_query_capitalised) {
+ for (n = 0, is_query_allcaps = 1; n < plen && is_query_allcaps;
+ n++) {
+ is_query_allcaps = !isalpha((unsigned char)lgetc(lp,
+ doto)) || isupper((unsigned char)lgetc(lp, doto));
+ doto++;
+ if (doto == llength(lp)) {
+ doto = 0;
+ lp = lforw(lp);
+ n++; /* \n is implicit in the buffer */
+ }
+ }
+ }
+
+ for (n = 0, is_replace_alllower = 1; n < rlen && is_replace_alllower;
+ n++)
+ is_replace_alllower = !isupper((unsigned char)repl[n]);
+
+ if (is_replace_alllower) {
+ if (is_query_allcaps) {
+ for (n = 0; n < rlen; n++)
+ repl[n] = toupper((unsigned char)repl[n]);
+ } else if (is_query_capitalised) {
+ repl[0] = toupper((unsigned char)repl[0]);
+ }
+ }
+
+ done:
+ (void)ldelete(plen, KNONE);
+ region_put_data(repl, rlen);
+ lchange(WFFULL);
+
+ undo_boundary_enable(FFRAND, 1);
+
+ free(repl);
+ return (TRUE);
+}
+
+/*
+ * Allocate and return the supplied line as a C string
+ */
+char *
+linetostr(const struct line *ln)
+{
+ int len;
+ char *line;
+
+ len = llength(ln);
+ if (len == INT_MAX) /* (len + 1) overflow */
+ return (NULL);
+
+ if ((line = malloc(len + 1)) == NULL)
+ return (NULL);
+
+ (void)memcpy(line, ltext(ln), len);
+ line[len] = '\0';
+
+ return (line);
+}
Index: contrib/mg/log.h
===================================================================
--- /dev/null
+++ contrib/mg/log.h
@@ -0,0 +1,33 @@
+/* $OpenBSD: log.h,v 1.6 2021/03/02 13:06:50 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Specifically for mg logging functionality.
+ *
+ */
+int mglog(PF, void *);
+int mgloginit(void);
+int mglog_execbuf( const char* const,
+ const char* const,
+ const char* const,
+ const char* const,
+ const int,
+ const int,
+ const char* const,
+ const char* const,
+ const char* const
+ );
+
+int mglog_isvar( const char* const,
+ const char* const,
+ const int
+ );
+int mglog_misc( const char *, ...);
+
+extern const char *mglogpath_lines;
+extern const char *mglogpath_undo;
+extern const char *mglogpath_window;
+extern const char *mglogpath_key;
+extern const char *mglogpath_interpreter;
+extern const char *mglogpath_misc;
Index: contrib/mg/log.c
===================================================================
--- /dev/null
+++ contrib/mg/log.c
@@ -0,0 +1,408 @@
+/* $OpenBSD: log.c,v 1.12 2021/03/02 13:06:50 lum Exp $ */
+
+/*
+ * This file is in the public domain.
+ *
+ * Author: Mark Lumsden <mark@showcomplex.com>
+ *
+ */
+
+/*
+ * Record a history of an mg session for temporal debugging.
+ * Sometimes pressing a key will set the scene for a bug only visible
+ * dozens of keystrokes later. gdb has its limitations in this scenario.
+ *
+ * Note this file is not compiled into mg by default, you will need to
+ * amend the 'Makefile' for that to happen. Because of this, the code
+ * is subject to bit-rot. However, I know myself and others have
+ * written similar functionally often enough, that recording the below
+ * in a code repository could aid the developement efforts of mg, even
+ * if it requires a bit of effort to get working. The current code is
+ * written in the spirit of debugging (quickly and perhaps not ideal,
+ * but it does what is required well enough). Should debugging become
+ * more formalised within mg, then I would expect that to change.
+ *
+ * If you open a file with long lines to run through this debugging
+ * code, you may run into problems with the 1st fprintf statement in
+ * in the mglog_lines() function. mg sometimes segvs at a strlen call
+ * in fprintf - possibly something to do with the format string?
+ * "%s%p b^%p f.%p %d %d\t%c|%s\n"
+ * When I get time I will look into it. But since my debugging
+ * generally revolves around a file like:
+ *
+ * abc
+ * def
+ * ghk
+ *
+ * I don't experience this bug. Just note it for future investigation.
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+
+#include "def.h"
+#include "key.h"
+#include "kbd.h"
+#include "funmap.h"
+#include "chrdef.h"
+
+#include "log.h"
+
+static char *mglogfiles_create(FILE **, char *);
+static int mglog_lines(PF);
+static int mglog_undo(void);
+static int mglog_window(void);
+static int mglog_key(KEYMAP *map);
+
+const char *mglogdir;
+const char *mglogpath_lines;
+const char *mglogpath_undo;
+const char *mglogpath_window;
+const char *mglogpath_key;
+const char *mglogpath_interpreter;
+const char *mglogpath_misc;
+int mgloglevel;
+
+FILE *fd_lines;
+FILE *fd_undo;
+FILE *fd_window;
+FILE *fd_key;
+FILE *fd_interpreter;
+FILE *fd_misc;
+
+int
+mglog(PF funct, void *map)
+{
+ if(!mglog_lines(funct))
+ ewprintf("Problem logging lines");
+ if(!mglog_undo())
+ ewprintf("Problem logging undo");
+ if(!mglog_window())
+ ewprintf("Problem logging window");
+ if(!mglog_key(map))
+ ewprintf("Problem logging key");
+
+ return (TRUE);
+}
+
+
+static int
+mglog_key(KEYMAP *map)
+{
+ PF *pfp;
+
+ if (ISWORD(*key.k_chars)) {
+ fprintf(fd_key, "k_count:%d k_chars:%hd\tchr:%c\t", key.k_count,
+ *key.k_chars, CHARMASK(*key.k_chars));
+ } else {
+ fprintf(fd_key, "k_count:%d k_chars:%hd\t\t", key.k_count,
+ *key.k_chars);
+ }
+ fprintf(fd_key, "map:%p %d %d %p %hd %hd\n",
+ map,
+ map->map_num,
+ map->map_max,
+ map->map_default,
+ map->map_element->k_base,
+ map->map_element->k_num
+ );
+ for (pfp = map->map_element->k_funcp; *pfp != NULL; pfp++)
+ fprintf(fd_key, "%s ", function_name(*pfp));
+
+ fprintf(fd_key, "\n\n");
+ fflush(fd_key);
+ return (TRUE);
+}
+
+static int
+mglog_window(void)
+{
+ struct mgwin *wp;
+ int i;
+
+ for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) {
+ fprintf(fd_window,
+ "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \
+ " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \
+ " wmkl%d\n",
+ i,
+ wp,
+ &wp->w_list,
+ wp->w_bufp,
+ wp->w_linep,
+ wp->w_dotp,
+ wp->w_markp,
+ wp->w_doto,
+ wp->w_marko,
+ wp->w_toprow,
+ wp->w_ntrows,
+ wp->w_frame,
+ wp->w_rflag,
+ wp->w_flag,
+ wp->w_wrapline,
+ wp->w_dotline,
+ wp->w_markline
+ );
+ }
+ fflush(fd_window);
+ return (TRUE);
+}
+
+static int
+mglog_undo(void)
+{
+ struct undo_rec *rec;
+ char buf[4096], tmp[1024];
+ int num;
+ char *jptr;
+
+ jptr = "^J"; /* :) */
+ /*
+ * From undo_dump()
+ */
+ num = 0;
+ TAILQ_FOREACH(rec, &curbp->b_undo, next) {
+ num++;
+ fprintf(fd_undo, "%d:\t %s at %d ", num,
+ (rec->type == DELETE) ? "DELETE":
+ (rec->type == DELREG) ? "DELREGION":
+ (rec->type == INSERT) ? "INSERT":
+ (rec->type == BOUNDARY) ? "----" :
+ (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
+ rec->pos
+ );
+ if (rec->content) {
+ (void)strlcat(buf, "\"", sizeof(buf));
+ snprintf(tmp, sizeof(tmp), "%.*s",
+ *rec->content == '\n' ? 2 : rec->region.r_size,
+ *rec->content == '\n' ? jptr : rec->content);
+ (void)strlcat(buf, tmp, sizeof(buf));
+ (void)strlcat(buf, "\"", sizeof(buf));
+ }
+ snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
+ if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
+ dobeep();
+ ewprintf("Undo record too large. Aborted.");
+ return (FALSE);
+ }
+ fprintf(fd_undo, "%s\n", buf);
+ tmp[0] = buf[0] = '\0';
+ }
+ fprintf(fd_undo, "\t [end-of-undo]\n\n");
+ fflush(fd_undo);
+
+ return (TRUE);
+}
+
+static int
+mglog_lines(PF funct)
+{
+ struct line *lp;
+ char *curline, *tmp, o;
+ int i;
+
+ i = 0;
+
+ fprintf(fd_lines, "%s\n", function_name(funct));
+ lp = bfirstlp(curbp);
+
+ for(;;) {
+ i++;
+ curline = " ";
+ o = ' ';
+ if (i == curwp->w_dotline) {
+ curline = ">";
+ if (lp->l_used > 0 && curwp->w_doto < lp->l_used)
+ o = lp->l_text[curwp->w_doto];
+ else
+ o = '-';
+ }
+ if (lp->l_size == 0)
+ tmp = " ";
+ else
+ tmp = lp->l_text;
+
+ /* segv on fprintf below with long lines */
+ fprintf(fd_lines, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline,
+ lp, lp->l_bp, lp->l_fp,
+ lp->l_size, lp->l_used, o, tmp);
+
+ lp = lforw(lp);
+ if (lp == curbp->b_headp) {
+ fprintf(fd_lines, " %p b^%p f.%p [bhead]\n(EOB)\n",
+ lp, lp->l_bp, lp->l_fp);
+
+ fprintf(fd_lines, "lines:raw:%d buf:%d wdot:%d\n\n",
+ i, curbp->b_lines, curwp->w_dotline);
+
+ break;
+ }
+ }
+ fflush(fd_lines);
+
+ return (TRUE);
+}
+
+/*
+ * See what the eval variable code is up to.
+ */
+int
+mglog_isvar(
+ const char* const argbuf,
+ const char* const argp,
+ const int sizof
+)
+{
+
+ fprintf(fd_interpreter, " argbuf:%s,argp:%s,sizof:%d<\n",
+ argbuf,
+ argp,
+ sizof);
+
+ fflush(fd_interpreter);
+ return (TRUE);
+}
+
+/*
+ * See what the eval line code is up to.
+ */
+int
+mglog_execbuf(
+ const char* const pre,
+ const char* const excbuf,
+ const char* const argbuf,
+ const char* const argp,
+ const int last,
+ const int inlist,
+ const char* const cmdp,
+ const char* const p,
+ const char* const contbuf
+)
+{
+ fprintf(fd_interpreter, "%sexcbuf:%s,argbuf:%s,argp:%s,last:%d,inlist:%d,"\
+ "cmdp:%s,p:%s,contbuf:%s<\n",
+ pre,
+ excbuf,
+ argbuf,
+ argp,
+ last,
+ inlist,
+ cmdp,
+ p,
+ contbuf
+ );
+ fflush(fd_interpreter);
+ return (TRUE);
+}
+
+/*
+ * Misc. logging for various subsystems
+ */
+int
+mglog_misc(
+ const char *fmt,
+ ...
+)
+{
+ va_list ap;
+ int rc;
+
+ va_start(ap, fmt);
+ rc = vfprintf(fd_misc, fmt, ap);
+ va_end(ap);
+ fflush(fd_misc);
+
+ if (rc < 0)
+ return (FALSE);
+
+ return (TRUE);
+}
+
+
+
+/*
+ * Make sure logging to log files can happen.
+ */
+int
+mgloginit(void)
+{
+ struct stat sb;
+ mode_t dir_mode, f_mode, oumask;
+ char *mglogfile_lines, *mglogfile_undo, *mglogfile_window;
+ char *mglogfile_key, *mglogfile_interpreter, *mglogfile_misc;
+
+ mglogdir = "./log/";
+ mglogfile_lines = "line.log";
+ mglogfile_undo = "undo.log";
+ mglogfile_window = "window.log";
+ mglogfile_key = "key.log";
+ mglogfile_interpreter = "interpreter.log";
+ mglogfile_misc = "misc.log";
+
+ /*
+ * Change mgloglevel for desired level of logging.
+ * log.h has relevant level info.
+ */
+ mgloglevel = 1;
+
+ oumask = umask(0);
+ f_mode = 0777& ~oumask;
+ dir_mode = f_mode | S_IWUSR | S_IXUSR;
+
+ if(stat(mglogdir, &sb)) {
+ if (mkdir(mglogdir, dir_mode) != 0)
+ return (FALSE);
+ if (chmod(mglogdir, f_mode) == -1)
+ return (FALSE);
+ }
+ mglogpath_lines = mglogfiles_create(&fd_lines, mglogfile_lines);
+ if (mglogpath_lines == NULL)
+ return (FALSE);
+ mglogpath_undo = mglogfiles_create(&fd_undo, mglogfile_undo);
+ if (mglogpath_undo == NULL)
+ return (FALSE);
+ mglogpath_window = mglogfiles_create(&fd_window, mglogfile_window);
+ if (mglogpath_window == NULL)
+ return (FALSE);
+ mglogpath_key = mglogfiles_create(&fd_key, mglogfile_key);
+ if (mglogpath_key == NULL)
+ return (FALSE);
+ mglogpath_interpreter = mglogfiles_create(&fd_interpreter,
+ mglogfile_interpreter);
+ if (mglogpath_interpreter == NULL)
+ return (FALSE);
+ mglogpath_misc = mglogfiles_create(&fd_misc, mglogfile_misc);
+ if (mglogpath_misc == NULL)
+ return (FALSE);
+
+ return (TRUE);
+}
+
+
+static char *
+mglogfiles_create(FILE ** fd, char *mglogfile)
+{
+ char tmp[NFILEN], *tmp2;
+
+ if (strlcpy(tmp, mglogdir, sizeof(tmp)) >
+ sizeof(tmp))
+ return (NULL);
+ if (strlcat(tmp, mglogfile, sizeof(tmp)) >
+ sizeof(tmp))
+ return (NULL);
+ if ((tmp2 = strndup(tmp, NFILEN)) == NULL)
+ return (NULL);
+
+ if ((*fd = fopen(tmp2, "w")) == NULL)
+ return (NULL);
+
+ return (tmp2);
+}
Index: contrib/mg/macro.h
===================================================================
--- /dev/null
+++ contrib/mg/macro.h
@@ -0,0 +1,21 @@
+/* $OpenBSD: macro.h,v 1.7 2005/11/18 20:56:53 deraadt Exp $ */
+
+/* This file is in the public domain. */
+
+/* definitions for keyboard macros */
+
+#define MAXMACRO 256 /* maximum functs in a macro */
+
+extern int inmacro;
+extern int macrodef;
+extern int macrocount;
+
+union macrodef {
+ PF m_funct;
+ int m_count; /* for count-prefix */
+};
+
+extern union macrodef macro[MAXMACRO];
+
+extern struct line *maclhead;
+extern struct line *maclcur;
Index: contrib/mg/macro.c
===================================================================
--- /dev/null
+++ contrib/mg/macro.c
@@ -0,0 +1,112 @@
+/* $OpenBSD: macro.c,v 1.16 2015/03/19 21:22:15 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Keyboard macros.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "def.h"
+#include "key.h"
+#include "macro.h"
+
+int inmacro = FALSE; /* Macro playback in progess */
+int macrodef = FALSE; /* Macro recording in progress */
+int macrocount = 0;
+
+struct line *maclhead = NULL;
+struct line *maclcur;
+
+union macrodef macro[MAXMACRO];
+
+/* ARGSUSED */
+int
+definemacro(int f, int n)
+{
+ struct line *lp1, *lp2;
+
+ macrocount = 0;
+
+ if (macrodef) {
+ ewprintf("already defining macro");
+ return (macrodef = FALSE);
+ }
+
+ /* free lines allocated for string arguments */
+ if (maclhead != NULL) {
+ for (lp1 = maclhead->l_fp; lp1 != maclhead; lp1 = lp2) {
+ lp2 = lp1->l_fp;
+ free(lp1);
+ }
+ free(lp1);
+ }
+
+ if ((maclhead = lp1 = lalloc(0)) == NULL)
+ return (FALSE);
+
+ ewprintf("Defining Keyboard Macro...");
+ maclcur = lp1->l_fp = lp1->l_bp = lp1;
+ return (macrodef = TRUE);
+}
+
+/* ARGSUSED */
+int
+finishmacro(int f, int n)
+{
+ if (macrodef == TRUE) {
+ macrodef = FALSE;
+ ewprintf("End Keyboard Macro Definition");
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/* ARGSUSED */
+int
+executemacro(int f, int n)
+{
+ int i, j, flag, num;
+ PF funct;
+
+ if (macrodef ||
+ (macrocount >= MAXMACRO && macro[MAXMACRO - 1].m_funct
+ != finishmacro)) {
+ dobeep();
+ ewprintf("Macro too long. Aborting.");
+ return (FALSE);
+ }
+
+ if (macrocount == 0)
+ return (TRUE);
+
+ inmacro = TRUE;
+
+ for (i = n; i > 0; i--) {
+ maclcur = maclhead->l_fp;
+ flag = 0;
+ num = 1;
+ for (j = 0; j < macrocount - 1; j++) {
+ funct = macro[j].m_funct;
+ if (funct == universal_argument) {
+ flag = FFARG;
+ num = macro[++j].m_count;
+ continue;
+ }
+ if ((*funct)(flag, num) != TRUE) {
+ inmacro = FALSE;
+ return (FALSE);
+ }
+ lastflag = thisflag;
+ thisflag = 0;
+ flag = 0;
+ num = 1;
+ }
+ }
+ inmacro = FALSE;
+ return (TRUE);
+}
Index: contrib/mg/main.c
===================================================================
--- /dev/null
+++ contrib/mg/main.c
@@ -0,0 +1,343 @@
+/* $OpenBSD: main.c,v 1.90 2021/05/03 12:18:43 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Mainline.
+ */
+
+#include <sys/queue.h>
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#if defined(__linux__) || defined(__CYGWIN__)
+#include <pty.h>
+#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#include "def.h"
+#include "kbd.h"
+#include "funmap.h"
+#include "macro.h"
+
+#ifdef MGLOG
+#include "log.h"
+#endif
+
+int thisflag; /* flags, this command */
+int lastflag; /* flags, last command */
+int curgoal; /* goal column */
+int startrow; /* row to start */
+int doaudiblebell; /* audible bell toggle */
+int dovisiblebell; /* visible bell toggle */
+int dblspace; /* sentence end #spaces */
+int allbro; /* all buffs read-only */
+int batch; /* for regress tests */
+struct buffer *curbp; /* current buffer */
+struct buffer *bheadp; /* BUFFER list head */
+struct mgwin *curwp; /* current window */
+struct mgwin *wheadp; /* MGWIN listhead */
+struct vhead varhead; /* Variable list head */
+char pat[NPAT]; /* pattern */
+
+static void edinit(struct buffer *);
+static void pty_init(void);
+static __dead void usage(void);
+
+extern char *__progname;
+extern void closetags(void);
+
+static __dead void
+usage()
+{
+ fprintf(stderr, "usage: %s [-nR] [-b file] [-f mode] [-u file] "
+ "[+number] [file ...]\n",
+ __progname);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char *cp, *conffile = NULL, *init_fcn_name = NULL;
+ char *batchfile = NULL;
+ PF init_fcn = NULL;
+ int o, i, nfiles;
+ int nobackups = 0;
+ struct buffer *bp = NULL;
+
+#ifdef HAVE_PLEDGE
+ if (pledge("stdio rpath wpath cpath fattr chown getpw tty proc exec",
+ NULL) == -1)
+ err(1, "pledge");
+#endif
+
+ while ((o = getopt(argc, argv, "nRb:f:u:")) != -1)
+ switch (o) {
+ case 'b':
+ batch = 1;
+ batchfile = optarg;
+ break;
+ case 'R':
+ allbro = 1;
+ break;
+ case 'n':
+ nobackups = 1;
+ break;
+ case 'f':
+ if (init_fcn_name != NULL)
+ errx(1, "cannot specify more than one "
+ "initial function");
+ init_fcn_name = optarg;
+ break;
+ case 'u':
+ conffile = optarg;
+ break;
+ default:
+ usage();
+ }
+
+ if (batch && (conffile != NULL)) {
+ fprintf(stderr, "%s: -b and -u are mutually exclusive.\n",
+ __progname);
+ exit(1);
+ }
+ if (batch) {
+ pty_init();
+ conffile = batchfile;
+ }
+ if (conffile != NULL && access(conffile, R_OK) != 0) {
+ fprintf(stderr, "%s: Problem with file: %s\n", __progname,
+ conffile);
+ exit(1);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ setlocale(LC_CTYPE, "");
+
+ maps_init(); /* Keymaps and modes. */
+ funmap_init(); /* Functions. */
+
+#ifdef MGLOG
+ if (!mgloginit())
+ errx(1, "Unable to create logging environment.");
+#endif
+
+ /*
+ * This is where we initialize standalone extensions that should
+ * be loaded dynamically sometime in the future.
+ */
+ {
+ extern void grep_init(void);
+ extern void cmode_init(void);
+ extern void dired_init(void);
+
+ dired_init();
+ grep_init();
+ cmode_init();
+ }
+
+ if (init_fcn_name &&
+ (init_fcn = name_function(init_fcn_name)) == NULL)
+ errx(1, "Unknown function `%s'", init_fcn_name);
+
+ vtinit(); /* Virtual terminal. */
+ dirinit(); /* Get current directory. */
+ edinit(bp); /* Buffers, windows. */
+ ttykeymapinit(); /* Symbols, bindings. */
+ bellinit(); /* Audible and visible bell. */
+ dblspace = 1; /* two spaces for sentence end. */
+
+ /*
+ * doing update() before reading files causes the error messages from
+ * the file I/O show up on the screen. (and also an extra display of
+ * the mode line if there are files specified on the command line.)
+ */
+ update(CMODE);
+
+ /* user startup file. */
+ if ((cp = startupfile(NULL, conffile)) != NULL)
+ (void)load(cp);
+
+ if (batch)
+ return (0);
+
+ /*
+ * Now ensure any default buffer modes from the startup file are
+ * given to any files opened when parsing the startup file.
+ * Note *scratch* will also be updated.
+ */
+ for (bp = bheadp; bp != NULL; bp = bp->b_bufp) {
+ bp->b_flag = defb_flag;
+ for (i = 0; i <= defb_nmodes; i++) {
+ bp->b_modes[i] = defb_modes[i];
+ }
+ }
+
+ /* Force FFOTHARG=1 so that this mode is enabled, not simply toggled */
+ if (init_fcn)
+ init_fcn(FFOTHARG, 1);
+
+ if (nobackups)
+ makebkfile(FFARG, 0);
+
+ for (nfiles = 0, i = 0; i < argc; i++) {
+ if (argv[i][0] == '+' && strlen(argv[i]) >= 2) {
+ long long lval;
+ const char *errstr;
+
+ lval = strtonum(&argv[i][1], INT_MIN, INT_MAX, &errstr);
+ if (argv[i][1] == '\0' || errstr != NULL)
+ goto notnum;
+ startrow = lval;
+ } else {
+notnum:
+ cp = adjustname(argv[i], FALSE);
+ if (cp != NULL) {
+ if (nfiles == 1)
+ splitwind(0, 1);
+
+ if (fisdir(cp) == TRUE) {
+ (void)do_dired(cp);
+ continue;
+ }
+ if ((curbp = findbuffer(cp)) == NULL) {
+ vttidy();
+ errx(1, "Can't find current buffer!");
+ }
+ (void)showbuffer(curbp, curwp, 0);
+ if (readin(cp) != TRUE)
+ killbuffer(curbp);
+ else {
+ /* Ensure enabled, not just toggled */
+ if (init_fcn_name)
+ init_fcn(FFOTHARG, 1);
+ nfiles++;
+ }
+ if (allbro)
+ curbp->b_flag |= BFREADONLY;
+ }
+ }
+ }
+
+ if (nfiles > 2)
+ listbuffers(0, 1);
+
+ /* fake last flags */
+ thisflag = 0;
+ for (;;) {
+ if (epresf == KCLEAR)
+ eerase();
+ if (epresf == TRUE)
+ epresf = KCLEAR;
+ if (winch_flag) {
+ do_redraw(0, 0, TRUE);
+ winch_flag = 0;
+ }
+ update(CMODE);
+ lastflag = thisflag;
+ thisflag = 0;
+
+ switch (doin()) {
+ case TRUE:
+ break;
+ case ABORT:
+ ewprintf("Quit");
+ /* FALLTHRU */
+ case FALSE:
+ default:
+ macrodef = FALSE;
+ }
+ }
+}
+
+/*
+ * Initialize default buffer and window. Default buffer is called *scratch*.
+ */
+static void
+edinit(struct buffer *bp)
+{
+ struct mgwin *wp;
+
+ bheadp = NULL;
+ bp = bfind("*scratch*", TRUE); /* Text buffer. */
+ if (bp == NULL)
+ panic("edinit");
+
+ wp = new_window(bp);
+ if (wp == NULL)
+ panic("edinit: Out of memory");
+
+ curbp = bp; /* Current buffer. */
+ wheadp = wp;
+ curwp = wp;
+ wp->w_wndp = NULL; /* Initialize window. */
+ wp->w_linep = wp->w_dotp = bp->b_headp;
+ wp->w_ntrows = nrow - 2; /* 2 = mode, echo. */
+ wp->w_rflag = WFMODE | WFFULL; /* Full. */
+}
+
+/*
+ * Create pty for batch mode.
+ */
+static void
+pty_init(void)
+{
+ struct winsize ws;
+ int master;
+ int slave;
+
+ memset(&ws, 0, sizeof(ws));
+ ws.ws_col = 80,
+ ws.ws_row = 24;
+
+ openpty(&master, &slave, NULL, NULL, &ws);
+ login_tty(slave);
+
+ return;
+}
+
+/*
+ * Quit command. If an argument, always quit. Otherwise confirm if a buffer
+ * has been changed and not written out. Normally bound to "C-x C-c".
+ */
+/* ARGSUSED */
+int
+quit(int f, int n)
+{
+ int s;
+
+ if ((s = anycb(FALSE)) == ABORT)
+ return (ABORT);
+ if (s == FIOERR || s == UERROR)
+ return (FALSE);
+ if (s == FALSE
+ || eyesno("Modified buffers exist; really exit") == TRUE) {
+ vttidy();
+ closetags();
+ exit(0);
+ }
+ return (TRUE);
+}
+
+/*
+ * User abort. Should be called by any input routine that sees a C-g to abort
+ * whatever C-g is aborting these days. Currently does nothing.
+ */
+/* ARGSUSED */
+int
+ctrlg(int f, int n)
+{
+ return (ABORT);
+}
Index: contrib/mg/match.c
===================================================================
--- /dev/null
+++ contrib/mg/match.c
@@ -0,0 +1,190 @@
+/* $OpenBSD: match.c,v 1.22 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Limited parenthesis matching routines
+ *
+ * The hacks in this file implement automatic matching * of (), [], {}, and
+ * other characters. It would be better to have a full-blown syntax table,
+ * but there's enough overhead in the editor as it is.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "def.h"
+#include "key.h"
+
+static int balance(void);
+static void displaymatch(struct line *, int);
+
+/*
+ * Balance table. When balance() encounters a character that is to be
+ * matched, it first searches this table for a balancing left-side character.
+ * If the character is not in the table, the character is balanced by itself.
+ */
+static struct balance {
+ char left, right;
+} bal[] = {
+ { '(', ')' },
+ { '[', ']' },
+ { '{', '}' },
+ { '<', '>' },
+ { '\0', '\0' }
+};
+
+/*
+ * Hack to show matching paren. Self-insert character, then show matching
+ * character, if any. Bound to "blink-and-insert". Used in the mg startup
+ * file to amend the default cursor behaviour of a key press, e.g:
+ * global-set-key "%" blink-and-insert
+ */
+int
+showmatch(int f, int n)
+{
+ int i, s;
+
+ for (i = 0; i < n; i++) {
+ if ((s = selfinsert(FFRAND, 1)) != TRUE)
+ return (s);
+ /* unbalanced -- warn user */
+ if (balance() != TRUE)
+ dobeep();
+ }
+ return (TRUE);
+}
+
+/*
+ * Search for and display a matching character.
+ *
+ * This routine does the real work of searching backward
+ * for a balancing character. If such a balancing character
+ * is found, it uses displaymatch() to display the match.
+ */
+static int
+balance(void)
+{
+ struct line *clp;
+ int cbo;
+ int c, i, depth;
+ int rbal, lbal;
+
+ rbal = key.k_chars[key.k_count - 1];
+
+ /* See if there is a matching character -- default to the same */
+ lbal = rbal;
+ for (i = 0; bal[i].right != '\0'; i++)
+ if (bal[i].right == rbal) {
+ lbal = bal[i].left;
+ break;
+ }
+
+ /*
+ * Move behind the inserted character. We are always guaranteed
+ * that there is at least one character on the line.
+ */
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto - 1;
+
+ /* init nesting depth */
+ depth = 0;
+
+ for (;;) {
+ if (cbo == 0) {
+ clp = lback(clp); /* beginning of line */
+ if (clp == curbp->b_headp)
+ return (FALSE);
+ cbo = llength(clp) + 1;
+ }
+ if (--cbo == llength(clp))
+ c = *curbp->b_nlchr; /* end of line */
+ else
+ c = lgetc(clp, cbo); /* somewhere in middle */
+
+ /*
+ * Check for a matching character. If still in a nested
+ * level, pop out of it and continue search. This check
+ * is done before the nesting check so single-character
+ * matches will work too.
+ */
+ if (c == lbal) {
+ if (depth == 0) {
+ displaymatch(clp, cbo);
+ return (TRUE);
+ } else
+ depth--;
+ }
+ /* Check for another level of nesting. */
+ if (c == rbal)
+ depth++;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Display matching character. Matching characters that are not in the
+ * current window are displayed in the echo line. If in the current window,
+ * move dot to the matching character, sit there a while, then move back.
+ */
+static void
+displaymatch(struct line *clp, int cbo)
+{
+ struct line *tlp;
+ int tbo;
+ int cp;
+ int bufo;
+ int c;
+ int inwindow;
+ char buf[NLINE];
+
+ /*
+ * Figure out if matching char is in current window by
+ * searching from the top of the window to dot.
+ */
+ inwindow = FALSE;
+ for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp);
+ tlp = lforw(tlp))
+ if (tlp == clp)
+ inwindow = TRUE;
+
+ if (inwindow == TRUE) {
+ tlp = curwp->w_dotp; /* save current position */
+ tbo = curwp->w_doto;
+
+ curwp->w_dotp = clp; /* move to new position */
+ curwp->w_doto = cbo;
+ curwp->w_rflag |= WFMOVE;
+
+ update(CMODE); /* show match */
+ ttwait(1000); /* wait for key or 1 second */
+
+ curwp->w_dotp = tlp; /* return to old position */
+ curwp->w_doto = tbo;
+ curwp->w_rflag |= WFMOVE;
+ update(CMODE);
+ } else {
+ /* match is not in this window, so display line in echo area */
+ bufo = 0;
+ for (cp = 0; cp < llength(clp); cp++) {
+ c = lgetc(clp, cp);
+ if (c != '\t'
+#ifdef NOTAB
+ || (curbp->b_flag & BFNOTAB)
+#endif
+ )
+ if (ISCTRL(c)) {
+ buf[bufo++] = '^';
+ buf[bufo++] = CCHR(c);
+ } else
+ buf[bufo++] = c;
+ else
+ do {
+ buf[bufo++] = ' ';
+ } while (bufo & 7);
+ }
+ buf[bufo++] = '\0';
+ ewprintf("Matches %s", buf);
+ }
+}
Index: contrib/mg/mg.1
===================================================================
--- /dev/null
+++ contrib/mg/mg.1
@@ -0,0 +1,1140 @@
+.\" $OpenBSD: mg.1,v 1.125 2021/05/02 14:13:17 lum Exp $
+.\" This file is in the public domain.
+.\"
+.Dd $Mdocdate: May 2 2021 $
+.Dt MG 1
+.Os
+.Sh NAME
+.Nm mg
+.Nd emacs-like text editor
+.Sh SYNOPSIS
+.Nm mg
+.Op Fl nR
+.Op Fl b Ar file
+.Op Fl f Ar mode
+.Op Fl u Ar file
+.Op + Ns Ar number
+.Op Ar
+.Sh DESCRIPTION
+.Nm
+is intended to be a small, fast, and portable editor for
+people who can't (or don't want to) run emacs for one
+reason or another, or are not familiar with the
+.Xr vi 1
+editor.
+It is compatible with emacs because there shouldn't
+be any reason to learn more editor types than emacs or
+.Xr vi 1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It + Ns Ar number
+Go to the line specified by number (do not insert
+a space between the
+.Sq +
+sign and the number).
+If a negative number is specified, the line number counts
+backwards from the end of the file i.e. +-1 will be the last
+line of the file, +-2 will be second last, and so on.
+.It Fl b Ar file
+Turn on batch mode and execute the
+.Nm
+commands found in the specified
+.Ar file
+and then terminate.
+.It Fl f Ar mode
+Run the mode command for all buffers created from
+arguments on the command line, including the
+scratch buffer and all files.
+.It Fl n
+Turn off backup file generation.
+.It Fl R
+Files specified on the command line will be opened read-only.
+.It Fl u Ar file
+Use
+.Ar file
+as the startup file, instead of the default
+.Pa ~/.mg .
+.El
+.Sh WINDOWS AND BUFFERS
+When a file is loaded into
+.Nm ,
+it is stored in a
+.Em buffer .
+This buffer may be displayed on the screen in more than one window.
+At present, windows may only be split horizontally, so each window is
+delineated by a modeline at the bottom.
+If changes are made to a buffer, it will be reflected in all open windows.
+.Pp
+If a file is changed outside
+.Nm
+and its buffer is about to be changed,
+.Nm
+prompts if the change should go ahead (y), not go ahead (n) or if the buffer
+should be reverted (r) to the latest file on disk.
+.Pp
+If a buffer name begins and ends with an asterisk, the buffer is considered
+throwaway; i.e. the user will not be prompted to save changes when
+the buffer is killed.
+.Sh POINT AND MARK
+The current cursor location in
+.Nm
+is called the
+.Em point
+(or
+.Em dot ) .
+It is possible to define a window-specific region of text by setting a second
+location, called the
+.Em mark .
+The
+.Em region
+is the text between point and mark inclusive.
+Deleting the character at the mark position leaves
+the mark at the point of deletion.
+.Pp
+Note: The point and mark are window-specific in
+.Nm ,
+not buffer-specific, as in other emacs flavours.
+.Sh BACKUP FILES
+Backup files have a
+.Sq ~
+character appended to the file name and
+are created in the current working directory by default.
+Whether to create backup files or not can be toggled with the
+make-backup-files command.
+The backup file location can either be in the current
+working directory, or all backups can be moved to a
+.Pa ~/.mg.d
+directory where files retain their path name to retain uniqueness.
+Use the backup-to-home-directory to alternate between these two locations.
+Further, if any application creates backup files in
+.Pa /tmp ,
+these can be left with the leave-tmpdir-backups command.
+.Sh TAGS
+.Nm
+supports tag files created by
+.Xr ctags 1 ,
+allowing the user to quickly locate various object definitions.
+Note though that emacs uses etags, not ctags.
+.Sh CSCOPE
+.Nm
+supports navigating source code using cscope.
+However,
+.Nm
+requires cscope and cscope-indexer executables to be present in
+.Ev PATH
+for it to work.
+.Sh DEFAULT KEY BINDINGS
+Normal editing commands are very similar to GNU Emacs.
+In the following examples, C-x means Control-x, and M-x means Meta-x,
+where the Meta key may be either a special key on the keyboard
+or the ALT key; otherwise ESC followed by the key X works as well.
+.Pp
+.Bl -tag -width xxxxxxxxxxxx -offset indent -compact
+.It C-SPC
+set-mark-command
+.It C-a
+beginning-of-line
+.It C-b
+backward-char
+.It C-c s c
+cscope-find-functions-calling-this-function
+.It C-c s d
+cscope-find-global-definition
+.It C-c s e
+cscope-find-egrep-pattern
+.It C-c s f
+cscope-find-this-file
+.It C-c s i
+cscope-find-files-including-file
+.It C-c s n
+cscope-next-symbol
+.It C-c s p
+cscope-prev-symbol
+.It C-c s s
+cscope-find-this-symbol
+.It C-c s t
+cscope-find-this-text-string
+.It C-d
+delete-char
+.It C-e
+end-of-line
+.It C-f
+forward-char
+.It C-g
+keyboard-quit
+.It C-h C-h
+help-help
+.It C-h a
+apropos
+.It C-h b
+describe-bindings
+.It C-h c
+describe-key-briefly
+.It C-j
+newline-and-indent
+.It C-k
+kill-line
+.It C-l
+recenter
+.It RET
+newline
+.It C-n
+next-line
+.It C-o
+open-line
+.It C-p
+previous-line
+.It C-q
+quoted-insert
+.It C-r
+isearch-backward
+.It C-s
+isearch-forward
+.It C-t
+transpose-chars
+.It C-u
+universal-argument
+.It C-v
+scroll-up
+.It C-w
+kill-region
+.It C-x C-b
+list-buffers
+.It C-x C-c
+save-buffers-kill-emacs
+.It C-x C-f
+find-file
+.It C-x C-j
+dired-jump
+.It C-x C-g
+keyboard-quit
+.It C-x C-l
+downcase-region
+.It C-x C-o
+delete-blank-lines
+.It C-x C-q
+toggle-read-only
+.It C-x C-r
+find-file-read-only
+.It C-x C-s
+save-buffer
+.It C-x C-u
+upcase-region
+.It C-x C-v
+find-alternate-file
+.It C-x C-w
+write-file
+.It C-x C-x
+exchange-point-and-mark
+.It C-x (
+start-kbd-macro
+.It C-x \&)
+end-kbd-macro
+.It C-x 0
+delete-window
+.It C-x 1
+delete-other-windows
+.It C-x 2
+split-window-vertically
+.It C-x 4 C-f
+find-file-other-window
+.It C-x 4 C-g
+keyboard-quit
+.It C-x 4 b
+switch-to-buffer-other-window
+.It C-x 4 f
+find-file-other-window
+.It C-x =
+what-cursor-position
+.It C-x ^
+enlarge-window
+.It C-x `
+next-error
+.It C-x b
+switch-to-buffer
+.It C-x d
+dired
+.It C-x e
+call-last-kbd-macro
+.It C-x f
+set-fill-column
+.It C-x g
+goto-line
+.It C-x h
+mark-whole-buffer
+.It C-x i
+insert-file
+.It C-x k
+kill-buffer
+.It C-x n
+other-window
+.It C-x o
+other-window
+.It C-x p
+previous-window
+.It C-x s
+save-some-buffers
+.It C-x u
+undo
+.It C-y
+yank
+.It C-z
+suspend-emacs
+.It M-C-v
+scroll-other-window
+.It M-SPC
+just-one-space
+.It M-!
+shell-command
+.It M-.
+find-tag
+.It M-*
+pop-tag-mark
+.It M-%
+query-replace
+.It M-<
+beginning-of-buffer
+.It M->
+end-of-buffer
+.It M-\e
+delete-horizontal-space
+.It M-^
+join-line
+.It M-b
+backward-word
+.It M-c
+capitalize-word
+.It M-d
+kill-word
+.It M-f
+forward-word
+.It M-h
+mark-paragraph
+.It M-l
+downcase-word
+.It M-m
+back-to-indentation
+.It M-q
+fill-paragraph
+.It M-r
+search-backward
+.It M-s
+search-forward
+.It M-t
+transpose-words
+.It M-u
+upcase-word
+.It M-v
+scroll-down
+.It M-w
+copy-region-as-kill
+.It M-x
+execute-extended-command
+.It M-{
+backward-paragraph
+.It M-|
+shell-command-on-region
+.It M-}
+forward-paragraph
+.It M-~
+not-modified
+.It M-DEL
+backward-kill-word
+.It C-_
+undo
+.It )
+blink-and-insert
+.It DEL
+delete-backward-char
+.El
+.Pp
+For a complete description of
+.Nm
+commands, see
+.Sx MG COMMANDS .
+To see the active keybindings at any time, type
+.Dq M-x describe-bindings .
+.Sh MG COMMANDS
+Commands are invoked by
+.Dq M-x ,
+or by binding to a key.
+Many commands take an optional numerical parameter,
+.Va n .
+This parameter is set either by
+M-<n> (where
+.Va n
+is the numerical argument) before the command, or by
+one or more invocations of the universal argument, usually bound to C-u.
+When invoked in this manner, the value of the numeric parameter to
+be passed is displayed in the minibuffer before the M-x.
+One common use of the parameter is in mode toggles (e.g.\&
+make-backup-files).
+If no parameter is supplied, the mode is toggled to its
+alternate state.
+If a positive parameter is supplied, the mode is forced to on.
+Otherwise, it is forced to off.
+.\"
+.Bl -tag -width xxxxx
+.It apropos
+Help Apropos.
+Prompt the user for a string, open the *help* buffer,
+and list all
+.Nm
+commands that contain that string.
+.It audible-bell
+Toggle the audible system bell.
+.It auto-execute
+Register an auto-execute hook; that is, specify a filename pattern
+(conforming to the shell's filename globbing rules) and an associated
+function to execute when a file matching the specified pattern
+is read into a buffer.
+.It auto-fill-mode
+Toggle auto-fill mode (sometimes called mail-mode) in the current buffer,
+where text inserted past the fill column is automatically wrapped
+to a new line.
+Can be set globally with set-default-mode.
+.It auto-indent-mode
+Toggle indent mode in the current buffer,
+where indentation is preserved after a newline.
+Can be set globally with set-default-mode.
+.It back-to-indentation
+Move the dot to the first non-whitespace character on the current line.
+.It backup-to-home-directory
+Save backup copies to a
+.Pa ~/.mg.d
+directory instead of working directory.
+Requires make-backup-files to be on.
+.It backward-char
+Move cursor backwards one character.
+.It backward-kill-word
+Kill text backwards by
+.Va n
+words.
+.It backward-paragraph
+Move cursor backwards
+.Va n
+paragraphs.
+Paragraphs are delimited by <NL><NL> or <NL><TAB> or <NL><SPACE>.
+.It backward-word
+Move cursor backwards by the specified number of words.
+.It beginning-of-buffer
+Move cursor to the top of the buffer.
+If set, keep mark's position, otherwise set at current position.
+A numeric argument
+.Va n
+will move n/10th of the way from the top.
+.It beginning-of-line
+Move cursor to the beginning of the line.
+.It blink-and-insert
+Self-insert a character, then search backwards and blink its
+matching delimiter.
+For delimiters other than
+parenthesis, brackets, and braces, the character itself
+is used as its own match.
+Can be used in the startup file with the global-set-key command.
+.It bsmap-mode
+Toggle bsmap mode, where DEL and C-h are swapped.
+.It c-mode
+Toggle a KNF-compliant mode for editing C program files.
+.It call-last-kbd-macro
+Invoke the keyboard macro.
+.It capitalize-word
+Capitalize
+.Va n
+words; i.e. convert the first character of the word to
+upper case, and subsequent letters to lower case.
+.It cd
+Change the global working directory.
+See also global-wd-mode.
+.It column-number-mode
+Toggle whether the column number is displayed in the modeline.
+.It copy-region-as-kill
+Copy all of the characters in the region to the kill buffer,
+clearing the mark afterwards.
+This is a bit like a kill-region followed by a yank.
+.It count-matches
+Count the number of lines matching the supplied regular expression.
+.It count-non-matches
+Count the number of lines not matching the supplied regular expression.
+.It cscope-find-this-symbol
+List the matches for the given symbol.
+.It cscope-find-global-definition
+List global definitions for the given literal.
+.It cscope-find-called-functions
+List functions called from the given function.
+.It cscope-find-functions-calling-this-function
+List functions calling the given function.
+.It cscope-find-this-text-string
+List locations matching the given text string.
+.It cscope-find-egrep-pattern
+List locations matching the given extended regular expression pattern.
+.It cscope-find-this-file
+List filenames matching the given filename.
+.It cscope-find-files-including-file
+List files that #include the given filename.
+.It cscope-next-symbol
+Navigate to the next match.
+.It cscope-prev-symbol
+Navigate to the previous match.
+.It cscope-next-file
+Navigate to the next file.
+.It cscope-prev-file
+Navigate to the previous file.
+.It cscope-create-list-of-files-to-index
+Create cscope's List and Index in the given directory.
+.It define-key
+Prompts the user for a named keymap (mode),
+a key, and an
+.Nm
+command, then creates a keybinding in the appropriate
+map.
+.It delete-backward-char
+Delete backwards
+.Va n
+characters.
+Like delete-char, this actually does a kill if presented
+with an argument.
+.It delete-blank-lines
+Delete blank lines around dot.
+If dot is sitting on a blank line, this command
+deletes all the blank lines above and below the current line.
+Otherwise, it deletes all of the blank lines after the current line.
+.It delete-char
+Delete
+.Va n
+characters forward.
+If any argument is present, it kills rather than deletes,
+saving the result in the kill buffer.
+.It delete-horizontal-space
+Delete any whitespace around the dot.
+.It delete-leading-space
+Delete leading whitespace on the current line.
+.It delete-trailing-space
+Delete trailing whitespace on the current line.
+.It delete-matching-lines
+Delete all lines after dot that contain a string matching
+the supplied regular expression.
+.It delete-non-matching-lines
+Delete all lines after dot that don't contain a string matching
+the supplied regular expression.
+.It delete-other-windows
+Make the current window the only window visible on the screen.
+.It delete-window
+Delete current window.
+.It describe-bindings
+List all global and local keybindings, putting the result in
+the *help* buffer.
+.It describe-key-briefly
+Read a key from the keyboard, and look it up in the keymap.
+Display the name of the function currently bound to the key.
+.It diff-buffer-with-file
+View the differences between buffer and its associated file.
+.It digit-argument
+Process a numerical argument for keyboard-invoked functions.
+.It dired-jump
+Open a dired buffer containing the current buffer's directory location.
+.It downcase-region
+Set all characters in the region to lower case.
+.It downcase-word
+Set characters to lower case, starting at the dot, and ending
+.Va n
+words away.
+.It emacs-version
+Return an
+.Nm
+version string.
+.It end-kbd-macro
+Stop defining a keyboard macro.
+.It end-of-buffer
+Move cursor to the end of the buffer.
+If set, keep mark's position, otherwise set at current position.
+A numeric argument
+.Va n
+will move n/10th of the way from the end.
+.It end-of-line
+Move cursor to the end of the line.
+.It enlarge-window
+Enlarge the current window by shrinking either the window above
+or below it.
+.It eval-current-buffer
+Evaluate the current buffer as a series of
+.Nm
+commands.
+Useful for testing
+.Nm
+startup files.
+.It eval-expression
+Get one line from the user, and run it.
+Useful for testing expressions in
+.Nm
+startup files.
+.It exchange-point-and-mark
+Swap the values of "dot" and "mark" in the current window.
+Return an error if no mark is set.
+.It execute-extended-command
+Invoke an extended command; i.e. M-x.
+Call the message line routine to read in the command name and apply
+autocompletion to it.
+When it comes back, look the name up in the symbol table and run the
+command if it is found, passing arguments as necessary.
+Print an error if there is anything wrong.
+.It fill-paragraph
+Justify a paragraph, wrapping text at the current fill column.
+.It find-file
+Select a file for editing.
+First check if the file can be found
+in another buffer; if it is there, just switch to that buffer.
+If the file cannot be found, create a new buffer, read in the
+file from disk, and switch to the new buffer.
+.It find-file-read-only
+Same as find-file, except the new buffer is set to read-only.
+.It find-alternate-file
+Replace the current file with an alternate one.
+Semantics for finding the replacement file are the same as
+find-file, except the current buffer is killed before the switch.
+If the kill fails, or is aborted, revert to the original file.
+.It find-file-other-window
+Opens the specified file in a second buffer.
+Splits the current window if necessary.
+.It find-tag
+Jump to definition of tag at dot.
+.It forward-char
+Move cursor forwards (or backwards, if
+.Va n
+is negative)
+.Va n
+characters.
+Returns an error if the end of buffer is reached.
+.It forward-paragraph
+Move forward
+.Va n
+paragraphs.
+Paragraphs are delimited by <NL><NL> or <NL><TAB> or <NL><SPACE>.
+.It forward-word
+Move the cursor forward by the specified number of words.
+.It global-set-key
+Bind a key in the global (fundamental) key map.
+.It global-unset-key
+Unbind a key from the global (fundamental) key map; i.e. set it to 'rescan'.
+.It global-wd-mode
+Toggle global working-directory mode.
+When enabled,
+.Nm
+defaults to opening files (and executing commands like compile and grep)
+relative to the global working directory.
+When disabled, a working directory is set for each buffer.
+.It goto-line
+Go to a specific line.
+If an argument is present, then
+it is the line number, else prompt for a line number to use.
+.It help-help
+Prompts for one of (a)propos, (b)indings, des(c)ribe key briefly.
+.It insert
+Insert a string, mainly for use from macros.
+.It insert-buffer
+Insert the contents of another buffer at dot.
+.It insert-file
+Insert a file into the current buffer at dot.
+.It insert-with-wrap
+Insert the bound character with word wrap.
+Check to see if we're past the fill column, and if so,
+justify this line.
+.It isearch-backward
+Use incremental searching, initially in the reverse direction.
+isearch ignores any explicit arguments.
+If invoked during macro definition or evaluation, the non-incremental
+search-backward is invoked instead.
+.It isearch-forward
+Use incremental searching, initially in the forward direction.
+isearch ignores any explicit arguments.
+If invoked during macro definition or evaluation, the non-incremental
+search-forward is invoked instead.
+.It join-line
+Join the current line to the previous.
+If called with an argument,
+join the next line to the current one.
+.It just-one-space
+Delete any whitespace around dot, then insert a space.
+.It keyboard-quit
+Abort the current action.
+.It kill-buffer
+Dispose of a buffer, by name.
+If the buffer name does not start and end with an asterisk,
+prompt the user if the buffer
+has been changed.
+.It kill-line
+Kill line.
+If called without an argument, it kills from dot to the end
+of the line, unless it is at the end of the line, when it kills the
+newline.
+If called with an argument of 0, it kills from the start of the
+line to dot.
+If called with a positive argument, it kills from dot
+forward over that number of newlines.
+If called with a negative argument
+it kills any text before dot on the current line, then it kills back
+abs(n) lines.
+.It kill-paragraph
+Delete
+.Va n
+paragraphs starting with the current one.
+.It kill-region
+Kill the currently defined region.
+.It kill-word
+Delete forward
+.Va n
+words.
+.It leave-tmpdir-backups
+Modifies the behaviour of backup-to-home-directory.
+Backup files that would normally reside in
+.Pa /tmp
+are left there and not moved to the
+.Pa ~/.mg.d
+directory.
+.It line-number-mode
+Toggle whether the line number is displayed in the modeline.
+.It list-buffers
+Display the list of available buffers.
+The first column in the output indicates which buffer is active with a '>'
+character.
+The second column indicates which buffers are modified.
+The third column indicates which buffers are read-only.
+The remaining columns are self-explanatory.
+.It load
+Prompt the user for a filename, and then execute commands
+from that file.
+.It local-set-key
+Bind a key mapping in the local (topmost) mode.
+.It local-unset-key
+Unbind a key mapping in the local (topmost) mode.
+.It make-backup-files
+Toggle generation of backup files.
+Enabled by default.
+.It make-directory
+Prompt the user for a path or directory name which is then created.
+.It mark-paragraph
+Mark
+.Va n
+paragraphs.
+.It mark-whole-buffer
+Marks whole buffer as a region by putting dot at the beginning and mark
+at the end of buffer.
+.It meta-key-mode
+When disabled, the meta key can be used to insert extended-ascii (8-bit)
+characters.
+When enabled, the meta key acts as usual.
+.It negative-argument
+Process a negative argument for keyboard-invoked functions.
+.It newline
+Insert a newline into the current buffer.
+.It newline-and-indent
+Insert a newline, then enough tabs and spaces to duplicate the indentation
+of the previous line.
+Assumes tabs are every eight characters.
+.It next-line
+Move forward
+.Va n
+lines.
+.\" .It no-tab-mode
+.\" Toggle notab mode.
+.\" In this mode, spaces are inserted rather than tabs.
+.It not-modified
+Turn off the modified flag in the current buffer.
+.It open-line
+Open up some blank space.
+Essentially, insert
+.Va n
+newlines, then back up over them.
+.It other-window
+The command to make the next (down the screen) window the current
+window.
+There are no real errors, although the command does nothing if
+there is only 1 window on the screen.
+.It overwrite-mode
+Toggle overwrite mode in the current buffer,
+where typing overwrites existing characters rather than inserting them.
+Can be set globally with set-default-mode.
+.It prefix-region
+Inserts a prefix string before each line of a region.
+The prefix string is settable by using 'set-prefix-string'.
+.It previous-line
+Move backwards
+.Va n
+lines.
+.It previous-window
+This command makes the previous (up the screen) window the
+current window.
+There are no errors, although the command does not do
+a lot if there is only 1 window.
+.It pop-tag-mark
+Return to position where find-tag was previously invoked.
+.It push-shell
+Suspend
+.Nm
+and switch to alternate screen, if available.
+.It pwd
+Display current (global) working directory in the status area.
+.It query-replace
+Query Replace.
+Search and replace strings selectively, prompting after each match.
+.It replace-regexp
+Replace regular expression globally without individual prompting.
+.It replace-string
+Replace string globally without individual prompting.
+.It query-replace-regexp
+Replace strings selectively.
+Does a search and replace operation using regular
+expressions for both patterns.
+.It quoted-insert
+Insert the next character verbatim into the current buffer; i.e. ignore
+any function bound to that key.
+.It re-search-again
+Perform a regular expression search again, using the same search
+string and direction as the last search command.
+.It re-search-backward
+Search backwards using a regular expression.
+Get a search string from the user, and search, starting at dot
+and proceeding toward the front of the buffer.
+If found, dot is left
+pointing at the first character of the pattern [the last character that
+was matched].
+.It re-search-forward
+Search forward using a regular expression.
+Get a search string from the user and search for it starting at dot.
+If found, move dot to just after the matched characters.
+display does all
+the hard stuff.
+If not found, it just prints a message.
+.It recenter
+Reposition dot in the current window.
+By default, the dot is centered.
+If given a positive argument (n), the display is repositioned to line
+n.
+If
+.Va n
+is negative, it is that line from the bottom.
+.It redraw-display
+Refresh the display.
+Recomputes all window sizes in case something has changed.
+.It revert-buffer
+Revert the current buffer to the latest file on disk.
+.It save-buffer
+Save the contents of the current buffer if it has been changed,
+optionally creating a backup copy.
+.It save-buffers-kill-emacs
+Offer to save modified buffers and quit
+.Nm .
+.It save-some-buffers
+Look through the list of buffers, offering to save any buffer that
+has been changed.
+Buffers that are not associated with files (such
+as *scratch*, *grep*, *compile*) are ignored.
+.It scroll-down
+Scroll backwards
+.Va n
+pages.
+A two-line overlap between pages is
+assumed.
+If given a repeat argument, scrolls back lines, not pages.
+.It scroll-one-line-down
+Scroll the display down
+.Va n
+lines without changing the cursor position.
+.It scroll-one-line-up
+Scroll the display
+.Va n
+lines up without moving the cursor position.
+.It scroll-other-window
+Scroll the next window in the window list window forward
+.Va n
+pages.
+.It scroll-up
+Scroll forward one page.
+A two-line overlap between pages is
+assumed.
+If given a repeat argument, scrolls back lines, not pages.
+.It search-again
+Search again, using the same search string and direction as the last
+search command.
+.It search-backward
+Reverse search.
+Get a search string from the user, and search, starting
+at dot and proceeding toward the front of the buffer.
+If found, dot is
+left pointing at the first character of the pattern (the last character
+that was matched).
+.It search-forward
+Search forward.
+Get a search string from the user, and search for it
+starting at dot.
+If found, dot gets moved to just after the matched
+characters, if not found, print a message.
+.It self-insert-command
+Insert a character.
+.It sentence-end-double-space
+Toggle double or single spaces for end of sentences.
+Double is the default.
+Currently only affects fill-paragraph.
+.It set-case-fold-search
+Set case-fold searching, causing case not to matter
+in regular expression searches.
+This is the default.
+.It set-case-replace
+Preserve the case of the replaced string.
+This is the default.
+.It set-default-mode
+Append the supplied mode to the list of default modes
+used by subsequent buffer creation.
+Built in modes include: fill, indent and overwrite.
+.It set-fill-column
+Prompt the user for a fill column.
+Used by auto-fill-mode.
+.It set-mark-command
+Sets the mark in the current window to the current dot location.
+.It set-prefix-string
+Sets the prefix string to be used by the 'prefix-region' command.
+.It shell-command
+Execute external command from mini-buffer.
+.It shell-command-on-region
+Provide the text in region to the shell command as input.
+.It shrink-window
+Shrink current window by one line.
+The window immediately below is expanded to pick up the slack.
+If only one window is present, this command has no effect.
+.It split-window-vertically
+Split the current window.
+A window smaller than 3 lines cannot be split.
+.It start-kbd-macro
+Start defining a keyboard macro.
+Macro definition is ended by invoking end-kbd-macro.
+.It suspend-emacs
+Suspend
+.Nm
+and switch back to alternate screen, if in use.
+.It switch-to-buffer
+Prompt and switch to a new buffer in the current window.
+.It switch-to-buffer-other-window
+Switch to buffer in another window.
+.It toggle-read-only
+Toggle the read-only flag on the current buffer.
+.It toggle-read-only-all
+Toggle the read-only flag on all non-ephemeral buffers.
+A simple toggle that switches a global read-only flag either on
+or off.
+.It transpose-chars
+Transpose the two characters in front of and under dot,
+then move forward one character.
+Treat newline characters the same as any other.
+.It transpose-paragraphs
+Transpose adjacent paragraphs.
+If multiple iterations are requested, the current paragraph will
+be moved
+.Va n
+paragraphs forward.
+.It transpose-words
+Transpose adjacent words.
+.It undo
+Undo the most recent action.
+If invoked again without an intervening command,
+move the undo pointer to the previous action and undo it.
+.It undo-boundary
+Add an undo boundary.
+This is not usually done interactively.
+.It undo-boundary-toggle
+Toggle whether undo boundaries are generated.
+Undo boundaries are often disabled before operations that should
+be considered atomically undoable.
+.It undo-enable
+Toggle whether undo information is kept.
+.It undo-list
+Show the undo records for the current buffer in a new buffer.
+.It universal-argument
+Repeat the next command 4 times.
+Usually bound to C-u.
+This command may be stacked; e.g.\&
+C-u C-u C-f moves the cursor forward 16 characters.
+.It upcase-region
+Upper case region.
+Change all of the lower case characters in the region to
+upper case.
+.It upcase-word
+Move the cursor forward by the specified number of words.
+As it moves, convert any characters to upper case.
+.It visible-bell
+Toggle the visible bell.
+If this toggle is on, the modeline will flash.
+.It visit-tags-table
+Record name of the tags file to be used for subsequent find-tag.
+.It what-cursor-position
+Display a bunch of useful information about the current location of
+dot.
+The character under the cursor (in octal), the current line, row,
+and column, and approximate position of the cursor in the file (as a
+percentage) is displayed.
+The column position assumes an infinite
+position display; it does not truncate just because the screen does.
+.It write-file
+Ask for a file name and write the contents of the current buffer to
+that file.
+Update the remembered file name and clear the buffer
+changed flag.
+.It yank
+Yank text from kill-buffer.
+Unlike emacs, the
+.Nm
+kill buffer consists only
+of the most recent kill.
+It is not a ring.
+.El
+.Sh MG DIRED KEY BINDINGS
+Specific key bindings are available in dired mode.
+.Pp
+.Bl -tag -width xxxxxxxxxxxxxxxxxx -offset indent -compact
+.It DEL
+dired-unmark-backward
+.It RET, e, f and C-m
+dired-find-file
+.It SPC, n
+dired-next-line
+.It !
+dired-shell-command
+.It +
+dired-create-directory
+.It a
+dired-find-alternate-file
+.It c
+dired-do-copy
+.It d and C-d
+dired-flag-file-deletion
+.It g
+dired-revert
+.It j
+dired-goto-file
+.It o
+dired-find-file-other-window
+.It p
+dired-previous-line
+.It q
+quit-window
+.It r
+dired-do-rename
+.It u
+dired-unmark
+.It x
+dired-do-flagged-delete
+.It C-v
+dired-scroll-down
+.It M-v
+dired-scroll-up
+.El
+.Sh MG DIRED COMMANDS
+The following are a list of the commands specific to dired mode:
+.Bl -tag -width Ds
+.It dired-create-directory
+Create a directory.
+.It dired-do-copy
+Copy the file listed on the current line of the dired buffer.
+.It dired-do-flagged-delete
+Delete the files that have been flagged for deletion.
+.It dired-do-rename
+Rename the file listed on the current line of the dired buffer.
+.It dired-find-alternate-file
+Replace the current dired buffer with an alternate one as specified
+by the position of the cursor in the dired buffer.
+.It dired-find-file
+Open the file on the current line of the dired buffer.
+If the cursor is on a directory it will be opened in dired mode.
+.It dired-flag-file-deletion
+Flag the file listed on the current line for deletion.
+This is indicated in the buffer by putting a D at the left margin.
+No files are actually deleted until the function dired-do-flagged-delete
+is executed.
+.It dired-find-file-other-window
+Open the file on the current line of the dired buffer in a
+different window.
+.It dired-goto-file
+Move the cursor to a file name in the dired buffer.
+.It dired-next-line
+Move the cursor to the next line.
+.It dired-other-window
+This function works just like dired, except that it puts the
+dired buffer in another window.
+.It dired-previous-line
+Move the cursor to the previous line.
+.It dired-revert
+Refresh the dired buffer while retaining any flags.
+.It dired-scroll-down
+Scroll down the dired buffer.
+.It dired-scroll-up
+Scroll up the dired buffer.
+.It dired-shell-command
+Pipe the file under the current cursor position through a shell command.
+.It dired-unmark
+Remove the deletion flag for the file on the current line.
+.It dired-unmark-backward
+Remove the deletion flag from the file listed on the previous line
+of the dired buffer, then move up to that line.
+.It quit-window
+Close the current dired buffer.
+.El
+.Sh CONFIGURATION FILES
+There are two configuration files,
+.Pa .mg
+and
+.Pa .mg-TERM .
+Here,
+.Ev TERM
+represents the name of the terminal type; e.g. if the terminal type
+is set to
+.Dq vt100 ,
+.Nm
+will use
+.Pa .mg-vt100
+as a startup file.
+The terminal type startup file is used first.
+.Pp
+The startup file format is a list of commands, one per line, as used for
+interactive evaluation.
+Strings that are normally entered by the user at any subsequent prompts
+may be specified after the command name; e.g.:
+.Bd -literal -offset indent
+global-set-key ")" self-insert-command
+global-set-key "\e^x\e^f" find-file
+global-set-key "\ee[Z" backward-char
+set-default-mode fill
+set-fill-column 72
+auto-execute *.c c-mode
+.Ed
+.Pp
+Comments can be added to the startup files by placing
+.Sq ;\&
+or
+.Sq #
+as the first character of a line.
+.Sh FILES
+.Bl -tag -width /usr/share/doc/mg/tutorial -compact
+.It Pa ~/.mg
+normal startup file
+.It Pa ~/.mg-TERM
+terminal-specific startup file
+.It Pa ~/.mg.d
+alternative backup file location
+.It Pa /usr/share/doc/mg/tutorial
+concise tutorial
+.El
+.Sh SEE ALSO
+.Xr ctags 1 ,
+.Xr vi 1
+.Sh CAVEATS
+Since it is written completely in C, there is currently no
+language in which extensions can be written;
+however, keys can be rebound and certain parameters can be changed
+in startup files.
+.Pp
+In order to use 8-bit characters (such as German umlauts), the Meta key
+needs to be disabled via the
+.Dq meta-key-mode
+command.
+.Pp
+Multi-byte character sets, such as UTF-8, are not supported.
Index: contrib/mg/mi_vector_hash.c
===================================================================
--- /dev/null
+++ contrib/mg/mi_vector_hash.c
@@ -0,0 +1,168 @@
+/* $NetBSD: mi_vector_hash.c,v 1.1 2013/12/11 01:24:08 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * See http://burtleburtle.net/bob/hash/doobs.html for the full description
+ * and the original version of the code. This version differs by exposing
+ * the full internal state and avoiding byte operations in the inner loop
+ * if the key is aligned correctly.
+ */
+
+#include <sys/cdefs.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define mix(a, b, c) do { \
+ a -= b; a -= c; a ^= (c >> 13); \
+ b -= c; b -= a; b ^= (a << 8); \
+ c -= a; c -= b; c ^= (b >> 13); \
+ a -= b; a -= c; a ^= (c >> 12); \
+ b -= c; b -= a; b ^= (a << 16); \
+ c -= a; c -= b; c ^= (b >> 5); \
+ a -= b; a -= c; a ^= (c >> 3); \
+ b -= c; b -= a; b ^= (a << 10); \
+ c -= a; c -= b; c ^= (b >> 15); \
+} while (/* CONSTCOND */0)
+
+#define FIXED_SEED 0x9e3779b9 /* Golden ratio, arbitrary constant */
+
+/* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */
+static uint32_t
+le32dec(const void *buf)
+{
+ uint8_t const *p = (uint8_t const *) buf;
+
+ return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+void
+mi_vector_hash(const void * __restrict key, size_t len, uint32_t seed,
+ uint32_t hashes[3])
+{
+ static const uint32_t mask[4] = {
+ 0x000000ff, 0x0000ffff, 0x00ffffff, 0xffffffff
+ };
+ uint32_t orig_len, a, b, c;
+ const uint8_t *k;
+
+ orig_len = (uint32_t)len;
+
+ a = b = FIXED_SEED;
+ c = seed;
+
+ if ((uintptr_t)key & 3) {
+ k = key;
+ while (len >= 12) {
+ a += le32dec(k);
+ b += le32dec(k + 4);
+ c += le32dec(k + 8);
+ mix(a, b, c);
+ k += 12;
+ len -= 12;
+ }
+ c += orig_len;
+
+ if (len > 8) {
+ switch (len) {
+ case 11:
+ c += (uint32_t)k[10] << 24;
+ /* FALLTHROUGH */
+ case 10:
+ c += (uint32_t)k[9] << 16;
+ /* FALLTHROUGH */
+ case 9:
+ c += (uint32_t)k[8] << 8;
+ /* FALLTHROUGH */
+ }
+ b += le32dec(k + 4);
+ a += le32dec(k);
+ } else if (len > 4) {
+ switch (len) {
+ case 8:
+ b += (uint32_t)k[7] << 24;
+ /* FALLTHROUGH */
+ case 7:
+ b += (uint32_t)k[6] << 16;
+ /* FALLTHROUGH */
+ case 6:
+ b += (uint32_t)k[5] << 8;
+ /* FALLTHROUGH */
+ case 5:
+ b += k[4];
+ /* FALLTHROUGH */
+ }
+ a += le32dec(k);
+ } else if (len) {
+ switch (len) {
+ case 4:
+ a += (uint32_t)k[3] << 24;
+ /* FALLTHROUGH */
+ case 3:
+ a += (uint32_t)k[2] << 16;
+ /* FALLTHROUGH */
+ case 2:
+ a += (uint32_t)k[1] << 8;
+ /* FALLTHROUGH */
+ case 1:
+ a += k[0];
+ /* FALLTHROUGH */
+ }
+ }
+ } else {
+ const uint32_t *key32 = key;
+ while (len >= 12) {
+ a += le32toh(key32[0]);
+ b += le32toh(key32[1]);
+ c += le32toh(key32[2]);
+ mix(a, b, c);
+ key32 += 3;
+ len -= 12;
+ }
+ c += orig_len;
+
+ if (len > 8) {
+ c += (le32toh(key32[2]) & mask[len - 9]) << 8;
+ b += le32toh(key32[1]);
+ a += le32toh(key32[0]);
+ } else if (len > 4) {
+ b += le32toh(key32[1]) & mask[len - 5];
+ a += le32toh(key32[0]);
+ } else if (len)
+ a += le32toh(key32[0]) & mask[len - 1];
+ }
+ mix(a, b, c);
+ hashes[0] = a;
+ hashes[1] = b;
+ hashes[2] = c;
+}
Index: contrib/mg/modes.c
===================================================================
--- /dev/null
+++ contrib/mg/modes.c
@@ -0,0 +1,174 @@
+/* $OpenBSD: modes.c,v 1.21 2017/05/30 07:05:22 florian Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Commands to toggle modes. Without an argument, these functions will
+ * toggle the given mode. A negative or zero argument will turn the mode
+ * off. A positive argument will turn the mode on.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "def.h"
+#include "kbd.h"
+
+int changemode(int, int, char *);
+
+int defb_nmodes = 0;
+struct maps_s *defb_modes[PBMODES] = { &fundamental_mode };
+int defb_flag = 0;
+
+int
+changemode(int f, int n, char *newmode)
+{
+ int i;
+ struct maps_s *m;
+
+ if ((m = name_mode(newmode)) == NULL) {
+ dobeep();
+ ewprintf("Can't find mode %s", newmode);
+ return (FALSE);
+ }
+ if (!(f & FFARG)) {
+ for (i = 0; i <= curbp->b_nmodes; i++)
+ if (curbp->b_modes[i] == m) {
+ /* mode already set */
+ n = 0;
+ break;
+ }
+ }
+ if (n > 0) {
+ for (i = 0; i <= curbp->b_nmodes; i++)
+ if (curbp->b_modes[i] == m)
+ /* mode already set */
+ return (TRUE);
+ if (curbp->b_nmodes >= PBMODES - 1) {
+ dobeep();
+ ewprintf("Too many modes");
+ return (FALSE);
+ }
+ curbp->b_modes[++(curbp->b_nmodes)] = m;
+ } else {
+ /* fundamental is b_modes[0] and can't be unset */
+ for (i = 1; i <= curbp->b_nmodes && m != curbp->b_modes[i];
+ i++);
+ if (i > curbp->b_nmodes)
+ return (TRUE); /* mode wasn't set */
+ for (; i < curbp->b_nmodes; i++)
+ curbp->b_modes[i] = curbp->b_modes[i + 1];
+ curbp->b_nmodes--;
+ }
+ upmodes(curbp);
+ return (TRUE);
+}
+
+int
+indentmode(int f, int n)
+{
+ return (changemode(f, n, "indent"));
+}
+
+int
+fillmode(int f, int n)
+{
+ return (changemode(f, n, "fill"));
+}
+
+#ifdef NOTAB
+int
+notabmode(int f, int n)
+{
+ if (changemode(f, n, "notab") == FALSE)
+ return (FALSE);
+ if (f & FFARG) {
+ if (n <= 0)
+ curbp->b_flag &= ~BFNOTAB;
+ else
+ curbp->b_flag |= BFNOTAB;
+ } else
+ curbp->b_flag ^= BFNOTAB;
+ return (TRUE);
+}
+#endif /* NOTAB */
+
+int
+overwrite_mode(int f, int n)
+{
+ if (changemode(f, n, "overwrite") == FALSE)
+ return (FALSE);
+ if (f & FFARG) {
+ if (n <= 0)
+ curbp->b_flag &= ~BFOVERWRITE;
+ else
+ curbp->b_flag |= BFOVERWRITE;
+ } else
+ curbp->b_flag ^= BFOVERWRITE;
+ return (TRUE);
+}
+
+int
+set_default_mode(int f, int n)
+{
+ int i;
+ struct maps_s *m;
+ char modebuf[32], *bufp;
+
+ if ((bufp = eread("Set Default Mode: ", modebuf, sizeof(modebuf),
+ EFNEW)) == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ return (FALSE);
+ if ((m = name_mode(modebuf)) == NULL) {
+ dobeep();
+ ewprintf("can't find mode %s", modebuf);
+ return (FALSE);
+ }
+ if (!(f & FFARG)) {
+ for (i = 0; i <= defb_nmodes; i++)
+ if (defb_modes[i] == m) {
+ /* mode already set */
+ n = 0;
+ break;
+ }
+ }
+ if (n > 0) {
+ for (i = 0; i <= defb_nmodes; i++)
+ if (defb_modes[i] == m)
+ /* mode already set */
+ return (TRUE);
+ if (defb_nmodes >= PBMODES - 1) {
+ dobeep();
+ ewprintf("Too many modes");
+ return (FALSE);
+ }
+ defb_modes[++defb_nmodes] = m;
+ } else {
+ /* fundamental is defb_modes[0] and can't be unset */
+ for (i = 1; i <= defb_nmodes && m != defb_modes[i]; i++);
+ if (i > defb_nmodes)
+ /* mode was not set */
+ return (TRUE);
+ for (; i < defb_nmodes; i++)
+ defb_modes[i] = defb_modes[i + 1];
+ defb_nmodes--;
+ }
+ if (strcmp(modebuf, "overwrite") == 0) {
+ if (n <= 0)
+ defb_flag &= ~BFOVERWRITE;
+ else
+ defb_flag |= BFOVERWRITE;
+ }
+#ifdef NOTAB
+ if (strcmp(modebuf, "notab") == 0) {
+ if (n <= 0)
+ defb_flag &= ~BFNOTAB;
+ else
+ defb_flag |= BFNOTAB;
+ }
+#endif /* NOTAB */
+ return (TRUE);
+}
Index: contrib/mg/nbperf-bdz.c
===================================================================
--- /dev/null
+++ contrib/mg/nbperf-bdz.c
@@ -0,0 +1,302 @@
+/* $NetBSD: nbperf-bdz.c,v 1.9 2014/04/30 21:04:58 joerg Exp $ */
+/*-
+ * Copyright (c) 2009, 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nbperf.h"
+
+/*
+ * A full description of the algorithm can be found in:
+ * "Simple and Space-Efficient Minimal Perfect Hash Functions"
+ * by Botelho, Pagh and Ziviani, proceeedings of WADS 2007.
+ */
+
+/*
+ * The algorithm is based on random, acyclic 3-graphs.
+ *
+ * Each edge in the represents a key. The vertices are the reminder of
+ * the hash function mod n. n = cm with c > 1.23. This ensures that
+ * an acyclic graph can be found with a very high probality.
+ *
+ * An acyclic graph has an edge order, where at least one vertex of
+ * each edge hasn't been seen before. It is declares the first unvisited
+ * vertex as authoritive for the edge and assigns a 2bit value to unvisited
+ * vertices, so that the sum of all vertices of the edge modulo 4 is
+ * the index of the authoritive vertex.
+ */
+
+#include "graph3.h"
+
+struct state {
+ struct graph3 graph;
+ uint32_t *visited;
+ uint32_t *holes64k;
+ uint16_t *holes64;
+ uint8_t *g;
+ uint32_t *result_map;
+};
+
+static void
+assign_nodes(struct state *state)
+{
+ struct edge3 *e;
+ size_t i, j;
+ uint32_t t, r, holes;
+
+ for (i = 0; i < state->graph.v; ++i)
+ state->g[i] = 3;
+
+ for (i = 0; i < state->graph.e; ++i) {
+ j = state->graph.output_order[i];
+ e = &state->graph.edges[j];
+ if (!state->visited[e->left]) {
+ r = 0;
+ t = e->left;
+ } else if (!state->visited[e->middle]) {
+ r = 1;
+ t = e->middle;
+ } else {
+ if (state->visited[e->right])
+ abort();
+ r = 2;
+ t = e->right;
+ }
+
+ state->visited[t] = 2 + j;
+ if (state->visited[e->left] == 0)
+ state->visited[e->left] = 1;
+ if (state->visited[e->middle] == 0)
+ state->visited[e->middle] = 1;
+ if (state->visited[e->right] == 0)
+ state->visited[e->right] = 1;
+
+ state->g[t] = (9 + r - state->g[e->left] - state->g[e->middle]
+ - state->g[e->right]) % 3;
+ }
+
+ holes = 0;
+ for (i = 0; i < state->graph.v; ++i) {
+ if (i % 65536 == 0)
+ state->holes64k[i >> 16] = holes;
+
+ if (i % 64 == 0)
+ state->holes64[i >> 6] = holes - state->holes64k[i >> 16];
+
+ if (state->visited[i] > 1) {
+ j = state->visited[i] - 2;
+ state->result_map[j] = i - holes;
+ }
+
+ if (state->g[i] == 3)
+ ++holes;
+ }
+}
+
+static void
+print_hash(struct nbperf *nbperf, struct state *state)
+{
+ uint64_t sum;
+ size_t i;
+
+ fprintf(nbperf->output, "#include <stdlib.h>\n");
+ fprintf(nbperf->output, "#include <strings.h>\n\n");
+
+ fprintf(nbperf->output, "%suint32_t\n",
+ nbperf->static_hash ? "static " : "");
+ fprintf(nbperf->output,
+ "%s(const void * __restrict key, size_t keylen)\n",
+ nbperf->hash_name);
+ fprintf(nbperf->output, "{\n");
+
+ fprintf(nbperf->output,
+ "\tstatic const uint64_t g1[%" PRId32 "] = {\n",
+ (state->graph.v + 63) / 64);
+ sum = 0;
+ for (i = 0; i < state->graph.v; ++i) {
+ sum |= ((uint64_t)state->g[i] & 1) << (i & 63);
+ if (i % 64 == 63) {
+ fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s",
+ (i / 64 % 2 == 0 ? "\t " : " "),
+ sum,
+ (i / 64 % 2 == 1 ? "\n" : ""));
+ sum = 0;
+ }
+ }
+ if (i % 64 != 0) {
+ fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s",
+ (i / 64 % 2 == 0 ? "\t " : " "),
+ sum,
+ (i / 64 % 2 == 1 ? "\n" : ""));
+ }
+ fprintf(nbperf->output, "%s\t};\n", (i % 2 ? "\n" : ""));
+
+ fprintf(nbperf->output,
+ "\tstatic const uint64_t g2[%" PRId32 "] = {\n",
+ (state->graph.v + 63) / 64);
+ sum = 0;
+ for (i = 0; i < state->graph.v; ++i) {
+ sum |= (((uint64_t)state->g[i] & 2) >> 1) << (i & 63);
+ if (i % 64 == 63) {
+ fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s",
+ (i / 64 % 2 == 0 ? "\t " : " "),
+ sum,
+ (i / 64 % 2 == 1 ? "\n" : ""));
+ sum = 0;
+ }
+ }
+ if (i % 64 != 0) {
+ fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s",
+ (i / 64 % 2 == 0 ? "\t " : " "),
+ sum,
+ (i / 64 % 2 == 1 ? "\n" : ""));
+ }
+ fprintf(nbperf->output, "%s\t};\n", (i % 2 ? "\n" : ""));
+
+ fprintf(nbperf->output,
+ "\tstatic const uint32_t holes64k[%" PRId32 "] = {\n",
+ (state->graph.v + 65535) / 65536);
+ for (i = 0; i < state->graph.v; i += 65536)
+ fprintf(nbperf->output, "%s0x%08" PRIx32 ",%s",
+ (i / 65536 % 4 == 0 ? "\t " : " "),
+ state->holes64k[i >> 16],
+ (i / 65536 % 4 == 3 ? "\n" : ""));
+ fprintf(nbperf->output, "%s\t};\n", (i / 65536 % 4 ? "\n" : ""));
+
+ fprintf(nbperf->output,
+ "\tstatic const uint16_t holes64[%" PRId32 "] = {\n",
+ (state->graph.v + 63) / 64);
+ for (i = 0; i < state->graph.v; i += 64)
+ fprintf(nbperf->output, "%s0x%04" PRIx32 ",%s",
+ (i / 64 % 4 == 0 ? "\t " : " "),
+ state->holes64[i >> 6],
+ (i / 64 % 4 == 3 ? "\n" : ""));
+ fprintf(nbperf->output, "%s\t};\n", (i / 64 % 4 ? "\n" : ""));
+
+ fprintf(nbperf->output, "\tuint64_t m;\n");
+ fprintf(nbperf->output, "\tuint32_t idx, i, idx2;\n");
+ fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size);
+
+ (*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h");
+
+ fprintf(nbperf->output, "\n\th[0] = h[0] %% %" PRIu32 ";\n",
+ state->graph.v);
+ fprintf(nbperf->output, "\th[1] = h[1] %% %" PRIu32 ";\n",
+ state->graph.v);
+ fprintf(nbperf->output, "\th[2] = h[2] %% %" PRIu32 ";\n",
+ state->graph.v);
+
+ fprintf(nbperf->output,
+ "\tidx = 9 + ((g1[h[0] >> 6] >> (h[0] & 63)) &1)\n"
+ "\t + ((g1[h[1] >> 6] >> (h[1] & 63)) & 1)\n"
+ "\t + ((g1[h[2] >> 6] >> (h[2] & 63)) & 1)\n"
+ "\t - ((g2[h[0] >> 6] >> (h[0] & 63)) & 1)\n"
+ "\t - ((g2[h[1] >> 6] >> (h[1] & 63)) & 1)\n"
+ "\t - ((g2[h[2] >> 6] >> (h[2] & 63)) & 1);\n"
+ );
+
+ fprintf(nbperf->output,
+ "\tidx = h[idx %% 3];\n");
+ fprintf(nbperf->output,
+ "\tidx2 = idx - holes64[idx >> 6] - holes64k[idx >> 16];\n"
+ "\tidx2 -= popcount64(g1[idx >> 6] & g2[idx >> 6]\n"
+ "\t & (((uint64_t)1 << (idx & 63)) - 1));\n"
+ "\treturn idx2;\n");
+
+ fprintf(nbperf->output, "}\n");
+
+ if (nbperf->map_output != NULL) {
+ for (i = 0; i < state->graph.e; ++i)
+ fprintf(nbperf->map_output, "%" PRIu32 "\n",
+ state->result_map[i]);
+ }
+}
+
+int
+bpz_compute(struct nbperf *nbperf)
+{
+ struct state state;
+ int retval = -1;
+ uint32_t v, e;
+
+ if (nbperf->c == 0)
+ nbperf->c = 1.24;
+ if (nbperf->c < 1.24)
+ errx(1, "The argument for option -c must be at least 1.24");
+ if (nbperf->hash_size < 3)
+ errx(1, "The hash function must generate at least 3 values");
+
+ (*nbperf->seed_hash)(nbperf);
+ e = nbperf->n;
+ v = nbperf->c * nbperf->n;
+ if (1.24 * nbperf->n > v)
+ ++v;
+ if (v < 10)
+ v = 10;
+
+ graph3_setup(&state.graph, v, e);
+
+ state.holes64k = calloc(sizeof(uint32_t), (v + 65535) / 65536);
+ state.holes64 = calloc(sizeof(uint16_t), (v + 63) / 64 );
+ state.g = calloc(sizeof(uint32_t), v | 63);
+ state.visited = calloc(sizeof(uint32_t), v);
+ state.result_map = calloc(sizeof(uint32_t), e);
+
+ if (state.holes64k == NULL || state.holes64 == NULL ||
+ state.g == NULL || state.visited == NULL ||
+ state.result_map == NULL)
+ err(1, "malloc failed");
+
+ if (graph3_hash(nbperf, &state.graph))
+ goto failed;
+ if (graph3_output_order(&state.graph))
+ goto failed;
+ assign_nodes(&state);
+ print_hash(nbperf, &state);
+
+ retval = 0;
+
+failed:
+ graph3_free(&state.graph);
+ free(state.visited);
+ free(state.g);
+ free(state.holes64k);
+ free(state.holes64);
+ free(state.result_map);
+ return retval;
+}
Index: contrib/mg/nbperf-chm.c
===================================================================
--- /dev/null
+++ contrib/mg/nbperf-chm.c
@@ -0,0 +1,268 @@
+/* $NetBSD: nbperf-chm.c,v 1.3 2011/10/21 23:47:11 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <err.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "nbperf.h"
+
+#ifdef BUILD_CHM3
+#include "graph3.h"
+#else
+#include "graph2.h"
+#endif
+
+/*
+ * A full description of the algorithm can be found in:
+ * "An optimal algorithm for generating minimal perfect hash functions"
+ * by Czech, Havas and Majewski in Information Processing Letters,
+ * 43(5):256-264, October 1992.
+ */
+
+/*
+ * The algorithm is based on random, acyclic graphs.
+ *
+ * Each edge in the represents a key. The vertices are the reminder of
+ * the hash function mod n. n = cm with c > 2, otherwise the propability
+ * of finding an acyclic graph is very low (for 2-graphs). The constant
+ * for 3-graphs is 1.24.
+ *
+ * After the hashing phase, the graph is checked for cycles.
+ * A cycle-free graph is either empty or has a vertex of degree 1.
+ * Removing the edge for this vertex doesn't change this property,
+ * so applying this recursively reduces the size of the graph.
+ * If the graph is empty at the end of the process, it was acyclic.
+ *
+ * The assignment step now sets g[i] := 0 and processes the edges
+ * in reverse order of removal. That ensures that at least one vertex
+ * is always unvisited and can be assigned.
+ */
+
+struct state {
+#ifdef BUILD_CHM3
+ struct graph3 graph;
+#else
+ struct graph2 graph;
+#endif
+ uint32_t *g;
+ uint8_t *visited;
+};
+
+static void
+assign_nodes(struct state *state)
+{
+#ifdef BUILD_CHM3
+ struct edge3 *e;
+#else
+ struct edge2 *e;
+#endif
+ size_t i;
+ uint32_t e_idx;
+
+ for (i = 0; i < state->graph.e; ++i) {
+ e_idx = state->graph.output_order[i];
+ e = &state->graph.edges[e_idx];
+
+#ifdef BUILD_CHM3
+ if (!state->visited[e->left]) {
+ state->g[e->left] = (2 * state->graph.e + e_idx
+ - state->g[e->middle] - state->g[e->right])
+ % state->graph.e;
+ } else if (!state->visited[e->middle]) {
+ state->g[e->middle] = (2 * state->graph.e + e_idx
+ - state->g[e->left] - state->g[e->right])
+ % state->graph.e;
+ } else {
+ state->g[e->right] = (2 * state->graph.e + e_idx
+ - state->g[e->left] - state->g[e->middle])
+ % state->graph.e;
+ }
+ state->visited[e->left] = 1;
+ state->visited[e->middle] = 1;
+ state->visited[e->right] = 1;
+#else
+ if (!state->visited[e->left]) {
+ state->g[e->left] = (state->graph.e + e_idx
+ - state->g[e->right]) % state->graph.e;
+ } else {
+ state->g[e->right] = (state->graph.e + e_idx
+ - state->g[e->left]) % state->graph.e;
+ }
+ state->visited[e->left] = 1;
+ state->visited[e->right] = 1;
+#endif
+ }
+}
+
+static void
+print_hash(struct nbperf *nbperf, struct state *state)
+{
+ uint32_t i, per_line;
+ const char *g_type;
+ int g_width;
+
+ fprintf(nbperf->output, "#include <stdlib.h>\n\n");
+
+ fprintf(nbperf->output, "%suint32_t\n",
+ nbperf->static_hash ? "static " : "");
+ fprintf(nbperf->output,
+ "%s(const void * __restrict key, size_t keylen)\n",
+ nbperf->hash_name);
+ fprintf(nbperf->output, "{\n");
+ if (state->graph.v >= 65536) {
+ g_type = "uint32_t";
+ g_width = 8;
+ per_line = 4;
+ } else if (state->graph.v >= 256) {
+ g_type = "uint16_t";
+ g_width = 4;
+ per_line = 8;
+ } else {
+ g_type = "uint8_t";
+ g_width = 2;
+ per_line = 10;
+ }
+ fprintf(nbperf->output, "\tstatic const %s g[%" PRId32 "] = {\n",
+ g_type, state->graph.v);
+ for (i = 0; i < state->graph.v; ++i) {
+ fprintf(nbperf->output, "%s0x%0*" PRIx32 ",%s",
+ (i % per_line == 0 ? "\t " : " "),
+ g_width, state->g[i],
+ (i % per_line == per_line - 1 ? "\n" : ""));
+ }
+ if (i % per_line != 0)
+ fprintf(nbperf->output, "\n\t};\n");
+ else
+ fprintf(nbperf->output, "\t};\n");
+ fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size);
+ (*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h");
+#ifdef BUILD_CHM3
+ fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + "
+ "g[h[1] %% %" PRIu32 "] + "
+ "g[h[2] %% %" PRIu32"]) %% %" PRIu32 ";\n",
+ state->graph.v, state->graph.v, state->graph.v, state->graph.e);
+#else
+ fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + "
+ "g[h[1] %% %" PRIu32"]) %% %" PRIu32 ";\n",
+ state->graph.v, state->graph.v, state->graph.e);
+#endif
+ fprintf(nbperf->output, "}\n");
+
+ if (nbperf->map_output != NULL) {
+ for (i = 0; i < state->graph.e; ++i)
+ fprintf(nbperf->map_output, "%" PRIu32 "\n", i);
+ }
+}
+
+int
+#ifdef BUILD_CHM3
+chm3_compute(struct nbperf *nbperf)
+#else
+chm_compute(struct nbperf *nbperf)
+#endif
+{
+ struct state state;
+ int retval = -1;
+ uint32_t v, e;
+
+#ifdef BUILD_CHM3
+ if (nbperf->c == 0)
+ nbperf-> c = 1.24;
+
+ if (nbperf->c < 1.24)
+ errx(1, "The argument for option -c must be at least 1.24");
+
+ if (nbperf->hash_size < 3)
+ errx(1, "The hash function must generate at least 3 values");
+#else
+ if (nbperf->c == 0)
+ nbperf-> c = 2;
+
+ if (nbperf->c < 2)
+ errx(1, "The argument for option -c must be at least 2");
+
+ if (nbperf->hash_size < 2)
+ errx(1, "The hash function must generate at least 2 values");
+#endif
+
+ (*nbperf->seed_hash)(nbperf);
+ e = nbperf->n;
+ v = nbperf->c * nbperf->n;
+#ifdef BUILD_CHM3
+ if (v == 1.24 * nbperf->n)
+ ++v;
+ if (v < 10)
+ v = 10;
+#else
+ if (v == 2 * nbperf->n)
+ ++v;
+#endif
+
+ state.g = calloc(sizeof(uint32_t), v);
+ state.visited = calloc(sizeof(uint8_t), v);
+ if (state.g == NULL || state.visited == NULL)
+ err(1, "malloc failed");
+
+#ifdef BUILD_CHM3
+ graph3_setup(&state.graph, v, e);
+ if (graph3_hash(nbperf, &state.graph))
+ goto failed;
+ if (graph3_output_order(&state.graph))
+ goto failed;
+#else
+ graph2_setup(&state.graph, v, e);
+ if (graph2_hash(nbperf, &state.graph))
+ goto failed;
+ if (graph2_output_order(&state.graph))
+ goto failed;
+#endif
+ assign_nodes(&state);
+ print_hash(nbperf, &state);
+
+ retval = 0;
+
+failed:
+#ifdef BUILD_CHM3
+ graph3_free(&state.graph);
+#else
+ graph2_free(&state.graph);
+#endif
+ free(state.g);
+ free(state.visited);
+ return retval;
+}
Index: contrib/mg/nbperf-chm3.c
===================================================================
--- /dev/null
+++ contrib/mg/nbperf-chm3.c
@@ -0,0 +1,4 @@
+/* $NetBSD: nbperf-chm3.c,v 1.1 2009/08/15 16:21:05 joerg Exp $ */
+
+#define BUILD_CHM3
+#include "nbperf-chm.c"
Index: contrib/mg/nbperf.h
===================================================================
--- /dev/null
+++ contrib/mg/nbperf.h
@@ -0,0 +1,61 @@
+/* $NetBSD: nbperf.h,v 1.4 2013/01/31 16:32:02 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define NBPERF_MAX_HASH_SIZE 3
+
+struct nbperf {
+ FILE *output;
+ FILE *map_output;
+ const char *hash_name;
+ int static_hash;
+ size_t n;
+ const void * __restrict * keys;
+ const size_t *keylens;
+ int first_round, has_duplicates;
+
+ double c;
+
+ size_t hash_size;
+ void (*seed_hash)(struct nbperf *);
+ void (*print_hash)(struct nbperf *, const char *, const char *, const char *,
+ const char *);
+ void (*compute_hash)(struct nbperf *, const void *, size_t,
+ uint32_t *);
+ uint32_t seed[1];
+};
+
+int chm_compute(struct nbperf *);
+int chm3_compute(struct nbperf *);
+int bpz_compute(struct nbperf *);
+void mi_vector_hash(const void * __restrict, size_t, uint32_t,
+ uint32_t[3]);
Index: contrib/mg/nbperf.c
===================================================================
--- /dev/null
+++ contrib/mg/nbperf.c
@@ -0,0 +1,255 @@
+/* $NetBSD: nbperf.c,v 1.5 2013/01/31 16:32:02 joerg Exp $ */
+/*-
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Joerg Sonnenberger.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/endian.h>
+
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "nbperf.h"
+
+static int predictable;
+
+static __dead
+void usage(void)
+{
+ fprintf(stderr,
+ "%s [-ps] [-c utilisation] [-i iterations] [-n name] "
+ "[-o output] input\n",
+ getprogname());
+ exit(1);
+}
+
+#if HAVE_NBTOOL_CONFIG_H && !defined(__NetBSD__)
+#define arc4random() rand()
+#endif
+
+static void
+mi_vector_hash_seed_hash(struct nbperf *nbperf)
+{
+ static uint32_t predictable_counter;
+ if (predictable)
+ nbperf->seed[0] = predictable_counter++;
+ else
+ nbperf->seed[0] = arc4random();
+}
+
+static void
+mi_vector_hash_compute(struct nbperf *nbperf, const void *key, size_t keylen,
+ uint32_t *hashes)
+{
+ mi_vector_hash(key, keylen, nbperf->seed[0], hashes);
+}
+
+static void
+mi_vector_hash_print_hash(struct nbperf *nbperf, const char *indent,
+ const char *key, const char *keylen, const char *hash)
+{
+ fprintf(nbperf->output,
+ "%smi_vector_hash(%s, %s, 0x%08" PRIx32 "U, %s);\n",
+ indent, key, keylen, nbperf->seed[0], hash);
+}
+
+static void
+set_hash(struct nbperf *nbperf, const char *arg)
+{
+ if (strcmp(arg, "mi_vector_hash") == 0) {
+ nbperf->hash_size = 3;
+ nbperf->seed_hash = mi_vector_hash_seed_hash;
+ nbperf->compute_hash = mi_vector_hash_compute;
+ nbperf->print_hash = mi_vector_hash_print_hash;
+ return;
+ }
+ if (nbperf->hash_size > NBPERF_MAX_HASH_SIZE)
+ errx(1, "Hash function creates too many output values");
+ errx(1, "Unknown hash function: %s", arg);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct nbperf nbperf = {
+ .c = 0,
+ .hash_name = "hash",
+ .map_output = NULL,
+ .output = NULL,
+ .static_hash = 0,
+ .first_round = 1,
+ .has_duplicates = 0,
+ };
+ FILE *input;
+ size_t curlen = 0, curalloc = 0;
+ char *line, *eos;
+ ssize_t line_len;
+ size_t line_allocated;
+ const void **keys = NULL;
+ size_t *keylens = NULL;
+ uint32_t max_iterations = 0xffffffU;
+ long long tmp;
+ int looped, ch;
+ int (*build_hash)(struct nbperf *) = chm_compute;
+
+ set_hash(&nbperf, "mi_vector_hash");
+
+ while ((ch = getopt(argc, argv, "a:c:h:i:m:n:o:ps")) != -1) {
+ switch (ch) {
+ case 'a':
+ /* Accept bdz as alias for netbsd-6 compat. */
+ if (strcmp(optarg, "chm") == 0)
+ build_hash = chm_compute;
+ else if (strcmp(optarg, "chm3") == 0)
+ build_hash = chm3_compute;
+ else if (strcmp(optarg, "bpz") == 0 ||
+ strcmp(optarg, "bdz") == 0)
+ build_hash = bpz_compute;
+ else
+ errx(1, "Unsupport algorithm: %s", optarg);
+ break;
+ case 'c':
+ errno = 0;
+ nbperf.c = strtod(optarg, &eos);
+ if (errno || eos[0] || !nbperf.c)
+ errx(2, "Invalid argument for -c");
+ break;
+ case 'h':
+ set_hash(&nbperf, optarg);
+ break;
+ case 'i':
+ errno = 0;
+ tmp = strtoll(optarg, &eos, 0);
+ if (errno || eos == optarg || eos[0] ||
+ tmp < 0 || tmp > 0xffffffffU)
+ errx(2, "Iteration count must be "
+ "a 32bit integer");
+ max_iterations = (uint32_t)tmp;
+ break;
+ case 'm':
+ if (nbperf.map_output)
+ fclose(nbperf.map_output);
+ nbperf.map_output = fopen(optarg, "w");
+ if (nbperf.map_output == NULL)
+ err(2, "cannot open map file");
+ break;
+ case 'n':
+ nbperf.hash_name = optarg;
+ break;
+ case 'o':
+ if (nbperf.output)
+ fclose(nbperf.output);
+ nbperf.output = fopen(optarg, "w");
+ if (nbperf.output == NULL)
+ err(2, "cannot open output file");
+ break;
+ case 'p':
+ predictable = 1;
+ break;
+ case 's':
+ nbperf.static_hash = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+
+ if (argc == 1) {
+ input = fopen(argv[0], "r");
+ if (input == NULL)
+ err(1, "can't open input file");
+ } else
+ input = stdin;
+
+ if (nbperf.output == NULL)
+ nbperf.output = stdout;
+
+ line = NULL;
+ line_allocated = 0;
+ while ((line_len = getline(&line, &line_allocated, input)) != -1) {
+ if (line_len && line[line_len - 1] == '\n')
+ --line_len;
+ if (curlen == curalloc) {
+ if (curalloc < 256)
+ curalloc = 256;
+ else
+ curalloc += curalloc;
+ keys = realloc(keys, curalloc * sizeof(*keys));
+ if (keys == NULL)
+ err(1, "realloc failed");
+ keylens = realloc(keylens,
+ curalloc * sizeof(*keylens));
+ if (keylens == NULL)
+ err(1, "realloc failed");
+ }
+ if ((keys[curlen] = strndup(line, line_len)) == NULL)
+ err(1, "malloc failed");
+ keylens[curlen] = line_len;
+ ++curlen;
+ }
+ free(line);
+
+ if (input != stdin)
+ fclose(input);
+
+ nbperf.n = curlen;
+ nbperf.keys = keys;
+ nbperf.keylens = keylens;
+
+ looped = 0;
+ while ((*build_hash)(&nbperf)) {
+ if (nbperf.has_duplicates)
+ errx(1, "Duplicate keys detected");
+ fputc('.', stderr);
+ looped = 1;
+ if (max_iterations == 0xffffffffU)
+ continue;
+ if (--max_iterations == 0) {
+ fputc('\n', stderr);
+ errx(1, "Iteration count reached");
+ }
+ }
+ if (looped)
+ fputc('\n', stderr);
+
+ return 0;
+}
Index: contrib/mg/paragraph.c
===================================================================
--- /dev/null
+++ contrib/mg/paragraph.c
@@ -0,0 +1,499 @@
+/* $OpenBSD: paragraph.c,v 1.46 2018/11/17 09:52:34 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6
+ * and GNU-ified by mwm@ucbvax. Several bug fixes by blarson@usc-oberon.
+ */
+
+#include <sys/queue.h>
+#include <ctype.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "def.h"
+
+static int fillcol = 70;
+
+#define MAXWORD 256
+
+static int findpara(void);
+static int do_gotoeop(int, int, int *);
+
+/*
+ * Move to start of paragraph.
+ * Move backwards by line, checking from the 1st character forwards for the
+ * existence a non-space. If a non-space character is found, move to the
+ * preceding line. Keep doing this until a line with only spaces is found or
+ * the start of buffer.
+ */
+/* ARGSUSED */
+int
+gotobop(int f, int n)
+{
+ int col, nospace;
+
+ /* the other way... */
+ if (n < 0)
+ return (gotoeop(f, -n));
+
+ while (n-- > 0) {
+ nospace = 0;
+ while (lback(curwp->w_dotp) != curbp->b_headp) {
+ curwp->w_doto = 0;
+ col = 0;
+
+ while (col < llength(curwp->w_dotp) &&
+ (isspace(lgetc(curwp->w_dotp, col))))
+ col++;
+
+ if (col >= llength(curwp->w_dotp)) {
+ if (nospace)
+ break;
+ } else
+ nospace = 1;
+
+ curwp->w_dotline--;
+ curwp->w_dotp = lback(curwp->w_dotp);
+ }
+ }
+ /* force screen update */
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+}
+
+/*
+ * Move to end of paragraph.
+ * See comments for gotobop(). Same, but moving forwards.
+ */
+/* ARGSUSED */
+int
+gotoeop(int f, int n)
+{
+ int i;
+
+ return(do_gotoeop(f, n, &i));
+}
+
+int
+do_gotoeop(int f, int n, int *i)
+{
+ int col, nospace, j = 0;
+
+ /* the other way... */
+ if (n < 0)
+ return (gotobop(f, -n));
+
+ /* for each one asked for */
+ while (n-- > 0) {
+ *i = ++j;
+ nospace = 0;
+ while (lforw(curwp->w_dotp) != curbp->b_headp) {
+ col = 0;
+ curwp->w_doto = 0;
+
+ while (col < llength(curwp->w_dotp) &&
+ (isspace(lgetc(curwp->w_dotp, col))))
+ col++;
+
+ if (col >= llength(curwp->w_dotp)) {
+ if (nospace)
+ break;
+ } else
+ nospace = 1;
+
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_dotline++;
+
+ }
+ }
+ /* do not continue after end of buffer */
+ if (lforw(curwp->w_dotp) == curbp->b_headp) {
+ gotoeol(FFRAND, 1);
+ curwp->w_rflag |= WFMOVE;
+ return (FALSE);
+ }
+
+ /* force screen update */
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+}
+
+/*
+ * Justify a paragraph. Fill the current paragraph according to the current
+ * fill column.
+ */
+/* ARGSUSED */
+int
+fillpara(int f, int n)
+{
+ int c; /* current char during scan */
+ int wordlen; /* length of current word */
+ int clength; /* position on line during fill */
+ int i; /* index during word copy */
+ int eopflag; /* Are we at the End-Of-Paragraph? */
+ int firstflag; /* first word? (needs no space) */
+ int newlength; /* tentative new line length */
+ int eolflag; /* was at end of line */
+ int retval; /* return value */
+ struct line *eopline; /* pointer to line just past EOP */
+ char wbuf[MAXWORD]; /* buffer for current word */
+
+ if (n == 0)
+ return (TRUE);
+
+ undo_boundary_enable(FFRAND, 0);
+
+ /* record the pointer to the line just past the EOP */
+ (void)gotoeop(FFRAND, 1);
+ if (curwp->w_doto != 0) {
+ /* paragraph ends at end of buffer */
+ (void)lnewline();
+ eopline = lforw(curwp->w_dotp);
+ } else
+ eopline = curwp->w_dotp;
+
+ /* and back top the beginning of the paragraph */
+ (void)gotobop(FFRAND, 1);
+
+ /* initialize various info */
+ while (inword() == 0 && forwchar(FFRAND, 1));
+
+ clength = curwp->w_doto;
+ wordlen = 0;
+
+ /* scan through lines, filling words */
+ firstflag = TRUE;
+ eopflag = FALSE;
+ while (!eopflag) {
+
+ /* get the next character in the paragraph */
+ if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) {
+ c = ' ';
+ if (lforw(curwp->w_dotp) == eopline)
+ eopflag = TRUE;
+ } else
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+
+ /* and then delete it */
+ if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) {
+ retval = FALSE;
+ goto cleanup;
+ }
+
+ /* if not a separator, just add it in */
+ if (c != ' ' && c != '\t') {
+ if (wordlen < MAXWORD - 1)
+ wbuf[wordlen++] = c;
+ else {
+ /*
+ * You lose chars beyond MAXWORD if the word
+ * is too long. I'm too lazy to fix it now; it
+ * just silently truncated the word before,
+ * so I get to feel smug.
+ */
+ ewprintf("Word too long!");
+ }
+ } else if (wordlen) {
+
+ /* calculate tentative new length with word added */
+ newlength = clength + 1 + wordlen;
+
+ /*
+ * if at end of line or at doublespace and previous
+ * character was one of '.','?','!' doublespace here.
+ * behave the same way if a ')' is preceded by a
+ * [.?!] and followed by a doublespace.
+ */
+ if (dblspace && (!eopflag && ((eolflag ||
+ curwp->w_doto == llength(curwp->w_dotp) ||
+ (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' '
+ || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) ||
+ (wbuf[wordlen - 1] == ')' && wordlen >= 2 &&
+ ISEOSP(wbuf[wordlen - 2])))) &&
+ wordlen < MAXWORD - 1))
+ wbuf[wordlen++] = ' ';
+
+ /* at a word break with a word waiting */
+ if (newlength <= fillcol) {
+ /* add word to current line */
+ if (!firstflag) {
+ (void)linsert(1, ' ');
+ ++clength;
+ }
+ firstflag = FALSE;
+ } else {
+ if (curwp->w_doto > 0 &&
+ lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') {
+ curwp->w_doto -= 1;
+ (void)ldelete((RSIZE) 1, KNONE);
+ }
+ /* start a new line */
+ (void)lnewline();
+ clength = 0;
+ }
+
+ /* and add the word in in either case */
+ for (i = 0; i < wordlen; i++) {
+ (void)linsert(1, wbuf[i]);
+ ++clength;
+ }
+ wordlen = 0;
+ }
+ }
+ /* and add a last newline for the end of our new paragraph */
+ (void)lnewline();
+
+ /*
+ * We really should wind up where we started, (which is hard to keep
+ * track of) but I think the end of the last line is better than the
+ * beginning of the blank line.
+ */
+ (void)backchar(FFRAND, 1);
+ retval = TRUE;
+cleanup:
+ undo_boundary_enable(FFRAND, 1);
+ return (retval);
+}
+
+/*
+ * Delete n paragraphs. Move to the beginning of the current paragraph, or if
+ * the cursor is on an empty line, move down the buffer to the first line with
+ * non-space characters. Then mark n paragraphs and delete.
+ */
+/* ARGSUSED */
+int
+killpara(int f, int n)
+{
+ int lineno, status;
+
+ if (n == 0)
+ return (TRUE);
+
+ if (findpara() == FALSE)
+ return (TRUE);
+
+ /* go to the beginning of the paragraph */
+ (void)gotobop(FFRAND, 1);
+
+ /* take a note of the line number for after deletions and set mark */
+ lineno = curwp->w_dotline;
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ (void)gotoeop(FFRAND, n);
+
+ if ((status = killregion(FFRAND, 1)) != TRUE)
+ return (status);
+
+ curwp->w_dotline = lineno;
+ return (TRUE);
+}
+
+/*
+ * Mark n paragraphs starting with the n'th and working our way backwards.
+ * This leaves the cursor at the beginning of the paragraph where markpara()
+ * was invoked.
+ */
+/* ARGSUSED */
+int
+markpara(int f, int n)
+{
+ int i = 0;
+
+ if (n == 0)
+ return (TRUE);
+
+ clearmark(FFARG, 0);
+
+ if (findpara() == FALSE)
+ return (TRUE);
+
+ (void)do_gotoeop(FFRAND, n, &i);
+
+ /* set the mark here */
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+
+ (void)gotobop(FFRAND, i);
+
+ return (TRUE);
+}
+
+/*
+ * Transpose the current paragraph with the following paragraph. If invoked
+ * multiple times, transpose to the n'th paragraph. If invoked between
+ * paragraphs, move to the previous paragraph, then continue.
+ */
+/* ARGSUSED */
+int
+transposepara(int f, int n)
+{
+ int i = 0, status;
+ char flg;
+
+ if (n == 0)
+ return (TRUE);
+
+ undo_boundary_enable(FFRAND, 0);
+
+ /* find a paragraph, set mark, then goto the end */
+ gotobop(FFRAND, 1);
+ curwp->w_markp = curwp->w_dotp;
+ curwp->w_marko = curwp->w_doto;
+ (void)gotoeop(FFRAND, 1);
+
+ /* take a note of buffer flags - we may need them */
+ flg = curbp->b_flag;
+
+ /* clean out kill buffer then kill region */
+ kdelete();
+ if ((status = killregion(FFRAND, 1)) != TRUE)
+ return (status);
+
+ /*
+ * Now step through n paragraphs. If we reach the end of buffer,
+ * stop and paste the killed region back, then display a message.
+ */
+ if (do_gotoeop(FFRAND, n, &i) == FALSE) {
+ ewprintf("Cannot transpose paragraph, end of buffer reached.");
+ (void)gotobop(FFRAND, i);
+ (void)yank(FFRAND, 1);
+ curbp->b_flag = flg;
+ return (FALSE);
+ }
+ (void)yank(FFRAND, 1);
+
+ undo_boundary_enable(FFRAND, 1);
+
+ return (TRUE);
+}
+
+/*
+ * Go down the buffer until we find a line with non-space characters.
+ */
+int
+findpara(void)
+{
+ int col, nospace = 0;
+
+ /* we move forward to find a para to mark */
+ do {
+ curwp->w_doto = 0;
+ col = 0;
+
+ /* check if we are on a blank line */
+ while (col < llength(curwp->w_dotp)) {
+ if (!isspace(lgetc(curwp->w_dotp, col)))
+ nospace = 1;
+ col++;
+ }
+ if (nospace)
+ break;
+
+ if (lforw(curwp->w_dotp) == curbp->b_headp)
+ return (FALSE);
+
+ curwp->w_dotp = lforw(curwp->w_dotp);
+ curwp->w_dotline++;
+ } while (1);
+
+ return (TRUE);
+}
+
+/*
+ * Insert char with work wrap. Check to see if we're past fillcol, and if so,
+ * justify this line. As a last step, justify the line.
+ */
+/* ARGSUSED */
+int
+fillword(int f, int n)
+{
+ char c;
+ int col, i, nce;
+
+ for (i = col = 0; col <= fillcol; ++i, ++col) {
+ if (i == curwp->w_doto)
+ return selfinsert(f, n);
+ c = lgetc(curwp->w_dotp, i);
+ if (c == '\t'
+#ifdef NOTAB
+ && !(curbp->b_flag & BFNOTAB)
+#endif
+ )
+ col |= 0x07;
+ else if (ISCTRL(c) != FALSE)
+ ++col;
+ }
+ if (curwp->w_doto != llength(curwp->w_dotp)) {
+ (void)selfinsert(f, n);
+ nce = llength(curwp->w_dotp) - curwp->w_doto;
+ } else
+ nce = 0;
+ curwp->w_doto = i;
+
+ if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t')
+ do {
+ (void)backchar(FFRAND, 1);
+ } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
+ c != '\t' && curwp->w_doto > 0);
+
+ if (curwp->w_doto == 0)
+ do {
+ (void)forwchar(FFRAND, 1);
+ } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' &&
+ c != '\t' && curwp->w_doto < llength(curwp->w_dotp));
+
+ (void)delwhite(FFRAND, 1);
+ (void)lnewline();
+ i = llength(curwp->w_dotp) - nce;
+ curwp->w_doto = i > 0 ? i : 0;
+ curwp->w_rflag |= WFMOVE;
+ if (nce == 0 && curwp->w_doto != 0)
+ return (fillword(f, n));
+ return (TRUE);
+}
+
+/*
+ * Set fill column to n for justify.
+ */
+int
+setfillcol(int f, int n)
+{
+ char buf[32], *rep;
+ const char *es;
+ int nfill;
+
+ if ((f & FFARG) != 0) {
+ fillcol = n;
+ } else {
+ if ((rep = eread("Set fill-column: ", buf, sizeof(buf),
+ EFNEW | EFCR)) == NULL)
+ return (ABORT);
+ else if (rep[0] == '\0')
+ return (FALSE);
+ nfill = strtonum(rep, 0, INT_MAX, &es);
+ if (es != NULL) {
+ dobeep();
+ ewprintf("Invalid fill column: %s", rep);
+ return (FALSE);
+ }
+ fillcol = nfill;
+ ewprintf("Fill column set to %d", fillcol);
+ }
+ return (TRUE);
+}
+
+int
+sentencespace(int f, int n)
+{
+ if (f & FFARG)
+ dblspace = n > 1;
+ else
+ dblspace = !dblspace;
+
+ return (TRUE);
+}
Index: contrib/mg/pathnames.h
===================================================================
--- /dev/null
+++ contrib/mg/pathnames.h
@@ -0,0 +1,11 @@
+/* $OpenBSD: pathnames.h,v 1.1 2012/06/18 07:14:55 jasper Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * standard path names
+ */
+
+#define _PATH_MG_DIR "~/.mg.d"
+#define _PATH_MG_STARTUP "%s/.mg"
+#define _PATH_MG_TERM "%s/.mg-%s"
Index: contrib/mg/re_search.c
===================================================================
--- /dev/null
+++ contrib/mg/re_search.c
@@ -0,0 +1,665 @@
+/* $OpenBSD: re_search.c,v 1.36 2021/04/22 19:50:55 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * regular expression search commands for Mg
+ *
+ * This file contains functions to implement several of gnuemacs's regular
+ * expression functions for Mg. Several of the routines below are just minor
+ * re-arrangements of Mg's non-regular expression search functions. Some of
+ * them are similar in structure to the original MicroEMACS, others are
+ * modifications of Rich Ellison's code. Peter Newton re-wrote about half of
+ * them from scratch.
+ */
+
+#ifdef REGEX
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "def.h"
+#include "macro.h"
+
+#define SRCH_BEGIN (0) /* search sub-codes */
+#define SRCH_FORW (-1)
+#define SRCH_BACK (-2)
+#define SRCH_NOPR (-3)
+#define SRCH_ACCM (-4)
+#define SRCH_MARK (-5)
+
+#define RE_NMATCH 10 /* max number of matches */
+#define REPLEN 256 /* max length of replacement string */
+
+char re_pat[NPAT]; /* regex pattern */
+int re_srch_lastdir = SRCH_NOPR; /* last search flags */
+int casefoldsearch = TRUE; /* does search ignore case? */
+
+static int re_doreplace(RSIZE, char *);
+static int re_forwsrch(void);
+static int re_backsrch(void);
+static int re_readpattern(char *);
+static int killmatches(int);
+static int countmatches(int);
+
+/*
+ * Search forward.
+ * Get a search string from the user and search for it starting at ".". If
+ * found, move "." to just after the matched characters. display does all
+ * the hard stuff. If not found, it just prints a message.
+ */
+/* ARGSUSED */
+int
+re_forwsearch(int f, int n)
+{
+ int s;
+
+ if ((s = re_readpattern("RE Search")) != TRUE)
+ return (s);
+ if (re_forwsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", re_pat);
+ return (FALSE);
+ }
+ re_srch_lastdir = SRCH_FORW;
+ return (TRUE);
+}
+
+/*
+ * Reverse search.
+ * Get a search string from the user, and search, starting at "."
+ * and proceeding toward the front of the buffer. If found "." is left
+ * pointing at the first character of the pattern [the last character that
+ * was matched].
+ */
+/* ARGSUSED */
+int
+re_backsearch(int f, int n)
+{
+ int s;
+
+ if ((s = re_readpattern("RE Search backward")) != TRUE)
+ return (s);
+ if (re_backsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", re_pat);
+ return (FALSE);
+ }
+ re_srch_lastdir = SRCH_BACK;
+ return (TRUE);
+}
+
+/*
+ * Search again, using the same search string and direction as the last search
+ * command. The direction has been saved in "srch_lastdir", so you know which
+ * way to go.
+ *
+ * XXX: This code has problems -- some incompatibility(?) with extend.c causes
+ * match to fail when it should not.
+ */
+/* ARGSUSED */
+int
+re_searchagain(int f, int n)
+{
+ if (re_srch_lastdir == SRCH_NOPR) {
+ dobeep();
+ ewprintf("No last search");
+ return (FALSE);
+ }
+ if (re_srch_lastdir == SRCH_FORW) {
+ if (re_forwsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", re_pat);
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+ if (re_srch_lastdir == SRCH_BACK)
+ if (re_backsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", re_pat);
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+/* Compiled regex goes here-- changed only when new pattern read */
+static regex_t regex_buff;
+static regmatch_t regex_match[RE_NMATCH];
+
+/*
+ * Re-Query Replace.
+ * Replace strings selectively. Does a search and replace operation.
+ */
+/* ARGSUSED */
+int
+re_queryrepl(int f, int n)
+{
+ int rcnt = 0; /* replacements made so far */
+ int plen, s; /* length of found string */
+ char news[NPAT]; /* replacement string */
+
+ if ((s = re_readpattern("RE Query replace")) != TRUE)
+ return (s);
+ if (eread("Query replace %s with: ", news, NPAT,
+ EFNUL | EFNEW | EFCR, re_pat) == NULL)
+ return (ABORT);
+ ewprintf("Query replacing %s with %s:", re_pat, news);
+
+ /*
+ * Search forward repeatedly, checking each time whether to insert
+ * or not. The "!" case makes the check always true, so it gets put
+ * into a tighter loop for efficiency.
+ */
+ while (re_forwsrch() == TRUE) {
+retry:
+ update(CMODE);
+ switch (getkey(FALSE)) {
+ case ' ':
+ plen = regex_match[0].rm_eo - regex_match[0].rm_so;
+ if (re_doreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ break;
+
+ case '.':
+ plen = regex_match[0].rm_eo - regex_match[0].rm_so;
+ if (re_doreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ goto stopsearch;
+
+ case CCHR('G'): /* ^G */
+ (void)ctrlg(FFRAND, 0);
+ goto stopsearch;
+ case CCHR('['): /* ESC */
+ case '`':
+ goto stopsearch;
+ case '!':
+ do {
+ plen = regex_match[0].rm_eo - regex_match[0].rm_so;
+ if (re_doreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ } while (re_forwsrch() == TRUE);
+ goto stopsearch;
+
+ case CCHR('?'): /* To not replace */
+ break;
+
+ default:
+ ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
+ goto retry;
+ }
+ }
+
+stopsearch:
+ curwp->w_rflag |= WFFULL;
+ update(CMODE);
+ if (!inmacro) {
+ if (rcnt == 0)
+ ewprintf("(No replacements done)");
+ else if (rcnt == 1)
+ ewprintf("(1 replacement done)");
+ else
+ ewprintf("(%d replacements done)", rcnt);
+ }
+ return (TRUE);
+}
+
+int
+re_repl(int f, int n)
+{
+ int rcnt = 0; /* replacements made so far */
+ int plen, s; /* length of found string */
+ char news[NPAT]; /* replacement string */
+
+ if ((s = re_readpattern("RE Replace")) != TRUE)
+ return (s);
+ if (eread("Replace %s with: ", news, NPAT,
+ EFNUL | EFNEW | EFCR, re_pat) == NULL)
+ return (ABORT);
+
+ while (re_forwsrch() == TRUE) {
+ plen = regex_match[0].rm_eo - regex_match[0].rm_so;
+ if (re_doreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ }
+
+ curwp->w_rflag |= WFFULL;
+ update(CMODE);
+ if (!inmacro)
+ ewprintf("(%d replacement(s) done)", rcnt);
+
+ return(TRUE);
+}
+
+/*
+ * Routine re_doreplace calls lreplace to make replacements needed by
+ * re_query replace. Its reason for existence is to deal with \1, \2. etc.
+ * plen: length to remove
+ * st: replacement string
+ */
+static int
+re_doreplace(RSIZE plen, char *st)
+{
+ int j, k, s, more, num, state;
+ struct line *clp;
+ char repstr[REPLEN];
+
+ clp = curwp->w_dotp;
+ more = TRUE;
+ j = 0;
+ state = 0;
+ num = 0;
+
+ /* The following FSA parses the replacement string */
+ while (more) {
+ switch (state) {
+ case 0:
+ if (*st == '\\') {
+ st++;
+ state = 1;
+ } else if (*st == '\0')
+ more = FALSE;
+ else {
+ repstr[j] = *st;
+ j++;
+ if (j >= REPLEN)
+ return (FALSE);
+ st++;
+ }
+ break;
+ case 1:
+ if (*st >= '0' && *st <= '9') {
+ num = *st - '0';
+ st++;
+ state = 2;
+ } else if (*st == '\0')
+ more = FALSE;
+ else {
+ repstr[j] = *st;
+ j++;
+ if (j >= REPLEN)
+ return (FALSE);
+ st++;
+ state = 0;
+ }
+ break;
+ case 2:
+ if (*st >= '0' && *st <= '9') {
+ num = 10 * num + *st - '0';
+ st++;
+ } else {
+ if (num >= RE_NMATCH)
+ return (FALSE);
+ k = regex_match[num].rm_eo - regex_match[num].rm_so;
+ if (j + k >= REPLEN)
+ return (FALSE);
+ bcopy(&(clp->l_text[regex_match[num].rm_so]),
+ &repstr[j], k);
+ j += k;
+ if (*st == '\0')
+ more = FALSE;
+ if (*st == '\\') {
+ st++;
+ state = 1;
+ } else {
+ repstr[j] = *st;
+ j++;
+ if (j >= REPLEN)
+ return (FALSE);
+ st++;
+ state = 0;
+ }
+ }
+ break;
+ } /* switch (state) */
+ } /* while (more) */
+
+ repstr[j] = '\0';
+ s = lreplace(plen, repstr);
+ return (s);
+}
+
+/*
+ * This routine does the real work of a forward search. The pattern is
+ * sitting in the external variable "pat". If found, dot is updated, the
+ * window system is notified of the change, and TRUE is returned. If the
+ * string isn't found, FALSE is returned.
+ */
+static int
+re_forwsrch(void)
+{
+ int re_flags, tbo, tdotline, error;
+ struct line *clp;
+
+ clp = curwp->w_dotp;
+ tbo = curwp->w_doto;
+ tdotline = curwp->w_dotline;
+
+ if (tbo == clp->l_used)
+ /*
+ * Don't start matching past end of line -- must move to
+ * beginning of next line, unless line is empty or at
+ * end of file.
+ */
+ if (clp != curbp->b_headp && llength(clp) != 0) {
+ clp = lforw(clp);
+ tdotline++;
+ tbo = 0;
+ }
+ /*
+ * Note this loop does not process the last line, but this editor
+ * always makes the last line empty so this is good.
+ */
+ while (clp != (curbp->b_headp)) {
+ re_flags = REG_STARTEND;
+ if (tbo != 0)
+ re_flags |= REG_NOTBOL;
+ regex_match[0].rm_so = tbo;
+ regex_match[0].rm_eo = llength(clp);
+ error = regexec(&regex_buff, ltext(clp) ? ltext(clp) : "",
+ RE_NMATCH, regex_match, re_flags);
+ if (error != 0) {
+ clp = lforw(clp);
+ tdotline++;
+ tbo = 0;
+ } else {
+ curwp->w_doto = regex_match[0].rm_eo;
+ curwp->w_dotp = clp;
+ curwp->w_dotline = tdotline;
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/*
+ * This routine does the real work of a backward search. The pattern is sitting
+ * in the external variable "re_pat". If found, dot is updated, the window
+ * system is notified of the change, and TRUE is returned. If the string isn't
+ * found, FALSE is returned.
+ */
+static int
+re_backsrch(void)
+{
+ struct line *clp;
+ int tbo, tdotline;
+ regmatch_t lastmatch;
+
+ clp = curwp->w_dotp;
+ tbo = curwp->w_doto;
+ tdotline = curwp->w_dotline;
+
+ /* Start search one position to the left of dot */
+ tbo = tbo - 1;
+ if (tbo < 0) {
+ /* must move up one line */
+ clp = lback(clp);
+ tdotline--;
+ tbo = llength(clp);
+ }
+
+ /*
+ * Note this loop does not process the last line, but this editor
+ * always makes the last line empty so this is good.
+ */
+ while (clp != (curbp->b_headp)) {
+ regex_match[0].rm_so = 0;
+ regex_match[0].rm_eo = llength(clp);
+ lastmatch.rm_so = -1;
+ /*
+ * Keep searching until we don't match any longer. Assumes a
+ * non-match does not modify the regex_match array. We have to
+ * do this character-by-character after the first match since
+ * POSIX regexps don't give you a way to do reverse matches.
+ */
+ while (!regexec(&regex_buff, ltext(clp) ? ltext(clp) : "",
+ RE_NMATCH, regex_match, REG_STARTEND) &&
+ regex_match[0].rm_so <= tbo) {
+ memcpy(&lastmatch, &regex_match[0], sizeof(regmatch_t));
+ regex_match[0].rm_so++;
+ regex_match[0].rm_eo = llength(clp);
+ }
+ if (lastmatch.rm_so == -1) {
+ clp = lback(clp);
+ tdotline--;
+ tbo = llength(clp);
+ } else {
+ memcpy(&regex_match[0], &lastmatch, sizeof(regmatch_t));
+ curwp->w_doto = regex_match[0].rm_so;
+ curwp->w_dotp = clp;
+ curwp->w_dotline = tdotline;
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/*
+ * Read a pattern.
+ * Stash it in the external variable "re_pat". The "pat" is
+ * not updated if the user types in an empty line. If the user typed
+ * an empty line, and there is no old pattern, it is an error.
+ * Display the old pattern, in the style of Jeff Lomicka. There is
+ * some do-it-yourself control expansion.
+ */
+static int
+re_readpattern(char *re_prompt)
+{
+ static int dofree = 0;
+ int flags, error, s;
+ char tpat[NPAT], *rep;
+
+ if (re_pat[0] == '\0')
+ rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, re_prompt);
+ else
+ rep = eread("%s (default %s): ", tpat, NPAT,
+ EFNUL | EFNEW | EFCR, re_prompt, re_pat);
+ if (rep == NULL)
+ return (ABORT);
+ if (rep[0] != '\0') {
+ /* New pattern given */
+ (void)strlcpy(re_pat, tpat, sizeof(re_pat));
+ if (casefoldsearch)
+ flags = REG_EXTENDED | REG_ICASE;
+ else
+ flags = REG_EXTENDED;
+ if (dofree)
+ regfree(&regex_buff);
+ error = regcomp(&regex_buff, re_pat, flags);
+ if (error != 0) {
+ char message[256];
+ regerror(error, &regex_buff, message, sizeof(message));
+ dobeep();
+ ewprintf("Regex Error: %s", message);
+ re_pat[0] = '\0';
+ return (FALSE);
+ }
+ dofree = 1;
+ s = TRUE;
+ } else if (rep[0] == '\0' && re_pat[0] != '\0')
+ /* Just using old pattern */
+ s = TRUE;
+ else
+ s = FALSE;
+ return (s);
+}
+
+/*
+ * Cause case to not matter in searches. This is the default. If called
+ * with argument cause case to matter.
+ */
+/* ARGSUSED*/
+int
+setcasefold(int f, int n)
+{
+ if (f & FFARG) {
+ casefoldsearch = FALSE;
+ ewprintf("Case-fold-search unset");
+ } else {
+ casefoldsearch = TRUE;
+ ewprintf("Case-fold-search set");
+ }
+
+ /*
+ * Invalidate the regular expression pattern since I'm too lazy to
+ * recompile it.
+ */
+ re_pat[0] = '\0';
+ return (TRUE);
+}
+
+/*
+ * Delete all lines after dot that contain a string matching regex.
+ */
+/* ARGSUSED */
+int
+delmatchlines(int f, int n)
+{
+ int s;
+
+ if ((s = re_readpattern("Flush lines (containing match for regexp)"))
+ != TRUE)
+ return (s);
+
+ s = killmatches(TRUE);
+ return (s);
+}
+
+/*
+ * Delete all lines after dot that don't contain a string matching regex.
+ */
+/* ARGSUSED */
+int
+delnonmatchlines(int f, int n)
+{
+ int s;
+
+ if ((s = re_readpattern("Keep lines (containing match for regexp)"))
+ != TRUE)
+ return (s);
+
+ s = killmatches(FALSE);
+ return (s);
+}
+
+/*
+ * This function does the work of deleting matching lines.
+ */
+static int
+killmatches(int cond)
+{
+ int s, error;
+ int count = 0;
+ struct line *clp;
+
+ clp = curwp->w_dotp;
+ if (curwp->w_doto == llength(clp))
+ /* Consider dot on next line */
+ clp = lforw(clp);
+
+ while (clp != (curbp->b_headp)) {
+ /* see if line matches */
+ regex_match[0].rm_so = 0;
+ regex_match[0].rm_eo = llength(clp);
+ error = regexec(&regex_buff, ltext(clp) ? ltext(clp) : "",
+ RE_NMATCH, regex_match, REG_STARTEND);
+
+ /* Delete line when appropriate */
+ if ((cond == FALSE && error) || (cond == TRUE && !error)) {
+ curwp->w_doto = 0;
+ curwp->w_dotp = clp;
+ count++;
+ s = ldelete(llength(clp) + 1, KNONE);
+ clp = curwp->w_dotp;
+ curwp->w_rflag |= WFMOVE;
+ if (s == FALSE)
+ return (FALSE);
+ } else
+ clp = lforw(clp);
+ }
+
+ ewprintf("%d line(s) deleted", count);
+ if (count > 0)
+ curwp->w_rflag |= WFMOVE;
+
+ return (TRUE);
+}
+
+/*
+ * Count lines matching regex.
+ */
+/* ARGSUSED */
+int
+cntmatchlines(int f, int n)
+{
+ int s;
+
+ if ((s = re_readpattern("Count lines (matching regexp)")) != TRUE)
+ return (s);
+ s = countmatches(TRUE);
+
+ return (s);
+}
+
+/*
+ * Count lines that fail to match regex.
+ */
+/* ARGSUSED */
+int
+cntnonmatchlines(int f, int n)
+{
+ int s;
+
+ if ((s = re_readpattern("Count lines (not matching regexp)")) != TRUE)
+ return (s);
+ s = countmatches(FALSE);
+
+ return (s);
+}
+
+/*
+ * This function does the work of counting matching lines.
+ */
+int
+countmatches(int cond)
+{
+ int error;
+ int count = 0;
+ struct line *clp;
+
+ clp = curwp->w_dotp;
+ if (curwp->w_doto == llength(clp))
+ /* Consider dot on next line */
+ clp = lforw(clp);
+
+ while (clp != (curbp->b_headp)) {
+ /* see if line matches */
+ regex_match[0].rm_so = 0;
+ regex_match[0].rm_eo = llength(clp);
+ error = regexec(&regex_buff, ltext(clp) ? ltext(clp) : "",
+ RE_NMATCH, regex_match, REG_STARTEND);
+
+ /* Count line when appropriate */
+ if ((cond == FALSE && error) || (cond == TRUE && !error))
+ count++;
+ clp = lforw(clp);
+ }
+
+ if (cond)
+ ewprintf("Number of lines matching: %d", count);
+ else
+ ewprintf("Number of lines not matching: %d", count);
+
+ return (TRUE);
+}
+#endif /* REGEX */
Index: contrib/mg/reallocarray.c
===================================================================
--- /dev/null
+++ contrib/mg/reallocarray.c
@@ -0,0 +1,44 @@
+/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */
+/*
+ * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+#ifndef HAVE_REALLOCARRAY
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+reallocarray(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return realloc(optr, size * nmemb);
+}
+
+#endif /* !HAVE_REALLOCARRAY */
Index: contrib/mg/region.c
===================================================================
--- /dev/null
+++ contrib/mg/region.c
@@ -0,0 +1,682 @@
+/* $OpenBSD: region.c,v 1.39 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Region based commands.
+ * The routines in this file deal with the region, that magic space between
+ * "." and mark. Some functions are commands. Some functions are just for
+ * internal use.
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "def.h"
+
+#define TIMEOUT 10000
+
+static char leftover[BUFSIZ];
+
+static int getregion(struct region *);
+static int iomux(int, char * const, int, struct buffer *);
+static int preadin(int, struct buffer *);
+static void pwriteout(int, char **, int *);
+static int setsize(struct region *, RSIZE);
+static int shellcmdoutput(char * const[], char * const, int);
+
+/*
+ * Kill the region. Ask "getregion" to figure out the bounds of the region.
+ * Move "." to the start, and kill the characters. Mark is cleared afterwards.
+ */
+/* ARGSUSED */
+int
+killregion(int f, int n)
+{
+ int s;
+ struct region region;
+
+ if ((s = getregion(&region)) != TRUE)
+ return (s);
+ /* This is a kill-type command, so do magic kill buffer stuff. */
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ curwp->w_dotp = region.r_linep;
+ curwp->w_doto = region.r_offset;
+ curwp->w_dotline = region.r_lineno;
+ s = ldelete(region.r_size, KFORW | KREG);
+ clearmark(FFARG, 0);
+
+ return (s);
+}
+
+/*
+ * Copy all of the characters in the region to the kill buffer,
+ * clearing the mark afterwards.
+ * This is a bit like a kill region followed by a yank.
+ */
+/* ARGSUSED */
+int
+copyregion(int f, int n)
+{
+ struct line *linep;
+ struct region region;
+ int loffs;
+ int s;
+
+ if ((s = getregion(&region)) != TRUE)
+ return (s);
+
+ /* kill type command */
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+
+ /* current line */
+ linep = region.r_linep;
+
+ /* current offset */
+ loffs = region.r_offset;
+
+ while (region.r_size--) {
+ if (loffs == llength(linep)) { /* End of line. */
+ if ((s = kinsert(*curbp->b_nlchr, KFORW)) != TRUE)
+ return (s);
+ linep = lforw(linep);
+ loffs = 0;
+ } else { /* Middle of line. */
+ if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
+ return (s);
+ ++loffs;
+ }
+ }
+ clearmark(FFARG, 0);
+
+ return (TRUE);
+}
+
+/*
+ * Lower case region. Zap all of the upper case characters in the region to
+ * lower case. Use the region code to set the limits. Scan the buffer, doing
+ * the changes. Call "lchange" to ensure that redisplay is done in all
+ * buffers.
+ */
+/* ARGSUSED */
+int
+lowerregion(int f, int n)
+{
+ struct line *linep;
+ struct region region;
+ int loffs, c, s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+
+ if ((s = getregion(&region)) != TRUE)
+ return (s);
+
+ undo_add_change(region.r_linep, region.r_offset, region.r_size);
+
+ lchange(WFFULL);
+ linep = region.r_linep;
+ loffs = region.r_offset;
+ while (region.r_size--) {
+ if (loffs == llength(linep)) {
+ linep = lforw(linep);
+ loffs = 0;
+ } else {
+ c = lgetc(linep, loffs);
+ if (ISUPPER(c) != FALSE)
+ lputc(linep, loffs, TOLOWER(c));
+ ++loffs;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Upper case region. Zap all of the lower case characters in the region to
+ * upper case. Use the region code to set the limits. Scan the buffer,
+ * doing the changes. Call "lchange" to ensure that redisplay is done in all
+ * buffers.
+ */
+/* ARGSUSED */
+int
+upperregion(int f, int n)
+{
+ struct line *linep;
+ struct region region;
+ int loffs, c, s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+ if ((s = getregion(&region)) != TRUE)
+ return (s);
+
+ undo_add_change(region.r_linep, region.r_offset, region.r_size);
+
+ lchange(WFFULL);
+ linep = region.r_linep;
+ loffs = region.r_offset;
+ while (region.r_size--) {
+ if (loffs == llength(linep)) {
+ linep = lforw(linep);
+ loffs = 0;
+ } else {
+ c = lgetc(linep, loffs);
+ if (ISLOWER(c) != FALSE)
+ lputc(linep, loffs, TOUPPER(c));
+ ++loffs;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * This routine figures out the bound of the region in the current window,
+ * and stores the results into the fields of the REGION structure. Dot and
+ * mark are usually close together, but I don't know the order, so I scan
+ * outward from dot, in both directions, looking for mark. The size is kept
+ * in a long. At the end, after the size is figured out, it is assigned to
+ * the size field of the region structure. If this assignment loses any bits,
+ * then we print an error. This is "type independent" overflow checking. All
+ * of the callers of this routine should be ready to get an ABORT status,
+ * because I might add a "if regions is big, ask before clobbering" flag.
+ */
+static int
+getregion(struct region *rp)
+{
+ struct line *flp, *blp;
+ long fsize, bsize;
+
+ if (curwp->w_markp == NULL) {
+ dobeep();
+ ewprintf("No mark set in this window");
+ return (FALSE);
+ }
+
+ /* "r_size" always ok */
+ if (curwp->w_dotp == curwp->w_markp) {
+ rp->r_linep = curwp->w_dotp;
+ rp->r_lineno = curwp->w_dotline;
+ if (curwp->w_doto < curwp->w_marko) {
+ rp->r_offset = curwp->w_doto;
+ rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto);
+ } else {
+ rp->r_offset = curwp->w_marko;
+ rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko);
+ }
+ return (TRUE);
+ }
+ /* get region size */
+ flp = blp = curwp->w_dotp;
+ bsize = curwp->w_doto;
+ fsize = llength(flp) - curwp->w_doto + 1;
+ while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) {
+ if (lforw(flp) != curbp->b_headp) {
+ flp = lforw(flp);
+ if (flp == curwp->w_markp) {
+ rp->r_linep = curwp->w_dotp;
+ rp->r_offset = curwp->w_doto;
+ rp->r_lineno = curwp->w_dotline;
+ return (setsize(rp,
+ (RSIZE)(fsize + curwp->w_marko)));
+ }
+ fsize += llength(flp) + 1;
+ }
+ if (lback(blp) != curbp->b_headp) {
+ blp = lback(blp);
+ bsize += llength(blp) + 1;
+ if (blp == curwp->w_markp) {
+ rp->r_linep = blp;
+ rp->r_offset = curwp->w_marko;
+ rp->r_lineno = curwp->w_markline;
+ return (setsize(rp,
+ (RSIZE)(bsize - curwp->w_marko)));
+ }
+ }
+ }
+ dobeep();
+ ewprintf("Bug: lost mark");
+ return (FALSE);
+}
+
+/*
+ * Set size, and check for overflow.
+ */
+static int
+setsize(struct region *rp, RSIZE size)
+{
+ rp->r_size = size;
+ if (rp->r_size != size) {
+ dobeep();
+ ewprintf("Region is too large");
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+#define PREFIXLENGTH 40
+static char prefix_string[PREFIXLENGTH] = {'>', '\0'};
+
+/*
+ * Prefix the region with whatever is in prefix_string. Leaves dot at the
+ * beginning of the line after the end of the region. If an argument is
+ * given, prompts for the line prefix string.
+ */
+/* ARGSUSED */
+int
+prefixregion(int f, int n)
+{
+ struct line *first, *last;
+ struct region region;
+ char *prefix = prefix_string;
+ int nline;
+ int s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+ if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE))
+ return (s);
+
+ /* get # of lines to affect */
+ if ((s = getregion(&region)) != TRUE)
+ return (s);
+ first = region.r_linep;
+ last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp;
+ for (nline = 1; first != last; nline++)
+ first = lforw(first);
+
+ /* move to beginning of region */
+ curwp->w_dotp = region.r_linep;
+ curwp->w_doto = region.r_offset;
+ curwp->w_dotline = region.r_lineno;
+
+ /* for each line, go to beginning and insert the prefix string */
+ while (nline--) {
+ (void)gotobol(FFRAND, 1);
+ for (prefix = prefix_string; *prefix; prefix++)
+ (void)linsert(1, *prefix);
+ (void)forwline(FFRAND, 1);
+ }
+ (void)gotobol(FFRAND, 1);
+ return (TRUE);
+}
+
+/*
+ * Set line prefix string. Used by prefixregion.
+ */
+/* ARGSUSED */
+int
+setprefix(int f, int n)
+{
+ char buf[PREFIXLENGTH], *rep;
+ int retval;
+
+ if (prefix_string[0] == '\0')
+ rep = eread("Prefix string: ", buf, sizeof(buf),
+ EFNEW | EFCR);
+ else
+ rep = eread("Prefix string (default %s): ", buf, sizeof(buf),
+ EFNUL | EFNEW | EFCR, prefix_string);
+ if (rep == NULL)
+ return (ABORT);
+ if (rep[0] != '\0') {
+ (void)strlcpy(prefix_string, rep, sizeof(prefix_string));
+ retval = TRUE;
+ } else if (rep[0] == '\0' && prefix_string[0] != '\0') {
+ /* CR -- use old one */
+ retval = TRUE;
+ } else
+ retval = FALSE;
+ return (retval);
+}
+
+int
+region_get_data(struct region *reg, char *buf, int len)
+{
+ int i, off;
+ struct line *lp;
+
+ off = reg->r_offset;
+ lp = reg->r_linep;
+ for (i = 0; i < len; i++) {
+ if (off == llength(lp)) {
+ lp = lforw(lp);
+ if (lp == curbp->b_headp)
+ break;
+ off = 0;
+ buf[i] = *curbp->b_nlchr;
+ } else {
+ buf[i] = lgetc(lp, off);
+ off++;
+ }
+ }
+ buf[i] = '\0';
+ return (i);
+}
+
+void
+region_put_data(const char *buf, int len)
+{
+ int i;
+
+ for (i = 0; buf[i] != '\0' && i < len; i++) {
+ if (buf[i] == *curbp->b_nlchr)
+ lnewline();
+ else
+ linsert(1, buf[i]);
+ }
+}
+
+/*
+ * Mark whole buffer by first traversing to end-of-buffer
+ * and then to beginning-of-buffer. Mark, dot are implicitly
+ * set to eob, bob respectively during traversal.
+ */
+int
+markbuffer(int f, int n)
+{
+ if (gotoeob(f,n) == FALSE)
+ return (FALSE);
+ (void) clearmark(f, n);
+ if (gotobob(f,n) == FALSE)
+ return (FALSE);
+ return (TRUE);
+}
+
+/*
+ * Pipe text from current region to external command.
+ */
+/*ARGSUSED */
+int
+piperegion(int f, int n)
+{
+ struct region region;
+ int len;
+ char *cmd, cmdbuf[NFILEN], *text;
+ char *argv[] = {"sh", "-c", (char *) NULL, (char *) NULL};
+
+ /* C-u M-| is not supported yet */
+ if (n > 1)
+ return (ABORT);
+
+ if (curwp->w_markp == NULL) {
+ dobeep();
+ ewprintf("The mark is not set now, so there is no region");
+ return (FALSE);
+ }
+
+ if ((cmd = eread("Shell command on region: ", cmdbuf, sizeof(cmdbuf),
+ EFNEW | EFCR)) == NULL || (cmd[0] == '\0'))
+ return (ABORT);
+
+ argv[2] = cmd;
+
+ if (getregion(&region) != TRUE)
+ return (FALSE);
+
+ len = region.r_size;
+
+ if ((text = malloc(len + 1)) == NULL) {
+ dobeep();
+ ewprintf("Cannot allocate memory.");
+ return (FALSE);
+ }
+
+ region_get_data(&region, text, len);
+
+ return shellcmdoutput(argv, text, len);
+}
+
+/*
+ * Get command from mini-buffer and execute externally.
+ */
+/*ARGSUSED */
+int
+shellcommand(int f, int n)
+{
+
+ char *cmd, cmdbuf[NFILEN];
+ char *argv[] = {"sh", "-c", (char *) NULL, (char *) NULL};
+
+ if (n > 1)
+ return (ABORT);
+
+ if ((cmd = eread("Shell command: ", cmdbuf, sizeof(cmdbuf),
+ EFNEW | EFCR)) == NULL || (cmd[0] == '\0'))
+ return (ABORT);
+
+ argv[2] = cmd;
+
+ return shellcmdoutput(argv, NULL, 0);
+}
+
+
+int
+shellcmdoutput(char* const argv[], char* const text, int len)
+{
+
+ struct buffer *bp;
+ char *shellp;
+ int ret;
+
+ bp = bfind("*Shell Command Output*", TRUE);
+ bp->b_flag |= BFREADONLY;
+ if (bclear(bp) != TRUE) {
+ free(text);
+ return (FALSE);
+ }
+
+ shellp = getenv("SHELL");
+
+ ret = pipeio(shellp, argv, text, len, bp);
+
+ if (ret == TRUE) {
+ eerase();
+ if (lforw(bp->b_headp) == bp->b_headp)
+ addline(bp, "(Shell command succeeded with no output)");
+ }
+
+ free(text);
+ return (ret);
+}
+
+/*
+ * Create a socketpair, fork and execv path with argv.
+ * STDIN, STDOUT and STDERR of child process are redirected to socket.
+ * Parent writes len chars from text to socket.
+ */
+int
+pipeio(const char* const path, char* const argv[], char* const text, int len,
+ struct buffer *outbp)
+{
+ int s[2], ret;
+ char *err;
+ pid_t pid;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) {
+ dobeep();
+ ewprintf("socketpair error");
+ return (FALSE);
+ }
+
+ switch((pid = fork())) {
+ case -1:
+ dobeep();
+ ewprintf("Can't fork");
+ return (FALSE);
+ case 0:
+ /* Child process */
+ close(s[0]);
+ if (dup2(s[1], STDIN_FILENO) == -1)
+ _exit(1);
+ if (dup2(s[1], STDOUT_FILENO) == -1)
+ _exit(1);
+ if (dup2(s[1], STDERR_FILENO) == -1)
+ _exit(1);
+ if (path == NULL)
+ _exit(1);
+
+ execv(path, argv);
+ err = strerror(errno);
+ write(s[1], err, strlen(err));
+ _exit(1);
+ default:
+ /* Parent process */
+ close(s[1]);
+ ret = iomux(s[0], text, len, outbp);
+ waitpid(pid, NULL, 0); /* Collect child to prevent zombies */
+
+ return (ret);
+ }
+ return (FALSE);
+}
+
+/*
+ * Multiplex read, write on socket fd passed. Put output in outbp
+ * Poll on the fd for both read and write readiness.
+ */
+int
+iomux(int fd, char* const text, int len, struct buffer *outbp)
+{
+ struct pollfd pfd[1];
+ int nfds;
+ char *textcopy;
+
+ textcopy = text;
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ pfd[0].fd = fd;
+
+ /* There is nothing to write if len is zero
+ * but the cmd's output should be read so shutdown
+ * the socket for writing only and don't wait for POLLOUT
+ */
+ if (len == 0) {
+ shutdown(fd, SHUT_WR);
+ pfd[0].events = POLLIN;
+ } else
+ pfd[0].events = POLLIN | POLLOUT;
+
+ while ((nfds = poll(pfd, 1, TIMEOUT)) != -1 ||
+ (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) {
+ if (pfd[0].revents & POLLOUT && len > 0)
+ pwriteout(fd, &textcopy, &len);
+ else if (pfd[0].revents & POLLIN)
+ if (preadin(fd, outbp) == FALSE)
+ break;
+ if (len == 0 && pfd[0].events & POLLOUT)
+ pfd[0].events = POLLIN;
+ }
+ close(fd);
+
+ /* In case if last line doesn't have a '\n' add the leftover
+ * characters to buffer.
+ */
+ if (leftover[0] != '\0') {
+ addline(outbp, leftover);
+ leftover[0] = '\0';
+ }
+ if (nfds == 0) {
+ dobeep();
+ ewprintf("poll timed out");
+ return (FALSE);
+ } else if (nfds == -1) {
+ dobeep();
+ ewprintf("poll error");
+ return (FALSE);
+ }
+ return (popbuftop(outbp, WNONE));
+}
+
+/*
+ * Write some text from region to fd. Once done shutdown the
+ * write end.
+ */
+void
+pwriteout(int fd, char **text, int *len)
+{
+ int w;
+
+ if (((w = send(fd, *text, *len, MSG_NOSIGNAL)) == -1)) {
+ switch(errno) {
+ case EPIPE:
+ *len = -1;
+ break;
+ case EAGAIN:
+ return;
+ }
+ } else
+ *len -= w;
+
+ *text += w;
+ if (*len <= 0)
+ shutdown(fd, SHUT_WR);
+}
+
+/*
+ * Read some data from socket fd, break on '\n' and add
+ * to buffer. If couldn't break on newline hold leftover
+ * characters and append in next iteration.
+ */
+int
+preadin(int fd, struct buffer *bp)
+{
+ int len;
+ char buf[BUFSIZ], *p, *q;
+
+ if ((len = read(fd, buf, BUFSIZ - 1)) <= 0)
+ return (FALSE);
+
+ buf[len] = '\0';
+ p = q = buf;
+ if (leftover[0] != '\0' && ((q = strchr(p, *bp->b_nlchr)) != NULL)) {
+ *q++ = '\0';
+ if (strlcat(leftover, p, sizeof(leftover)) >=
+ sizeof(leftover)) {
+ dobeep();
+ ewprintf("line too long");
+ return (FALSE);
+ }
+ addline(bp, leftover);
+ leftover[0] = '\0';
+ p = q;
+ }
+ while ((q = strchr(p, *bp->b_nlchr)) != NULL) {
+ *q++ = '\0';
+ addline(bp, p);
+ p = q;
+ }
+ if (strlcpy(leftover, p, sizeof(leftover)) >= sizeof(leftover)) {
+ dobeep();
+ ewprintf("line too long");
+ return (FALSE);
+ }
+ return (TRUE);
+}
Index: contrib/mg/search.c
===================================================================
--- /dev/null
+++ contrib/mg/search.c
@@ -0,0 +1,855 @@
+/* $OpenBSD: search.c,v 1.47 2018/07/11 12:21:37 krw Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Search commands.
+ * The functions in this file implement the search commands (both plain and
+ * incremental searches are supported) and the query-replace command.
+ *
+ * The plain old search code is part of the original MicroEMACS "distribution".
+ * The incremental search code and the query-replace code is by Rich Ellison.
+ */
+
+#include <sys/queue.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "def.h"
+#include "macro.h"
+
+#define SRCH_BEGIN (0) /* Search sub-codes. */
+#define SRCH_FORW (-1)
+#define SRCH_BACK (-2)
+#define SRCH_NOPR (-3)
+#define SRCH_ACCM (-4)
+#define SRCH_MARK (-5)
+
+struct srchcom {
+ int s_code;
+ struct line *s_dotp;
+ int s_doto;
+ int s_dotline;
+};
+
+static int isearch(int);
+static void is_cpush(int);
+static void is_lpush(void);
+static void is_pop(void);
+static int is_peek(void);
+static void is_undo(int *, int *);
+static int is_find(int);
+static void is_prompt(int, int, int);
+static void is_dspl(char *, int);
+static int eq(int, int, int);
+
+static struct srchcom cmds[NSRCH];
+static int cip;
+
+int srch_lastdir = SRCH_NOPR; /* Last search flags. */
+
+/*
+ * Search forward. Get a search string from the user, and search for it
+ * starting at ".". If found, "." gets moved to just after the matched
+ * characters, and display does all the hard stuff. If not found, it just
+ * prints a message.
+ */
+/* ARGSUSED */
+int
+forwsearch(int f, int n)
+{
+ int s;
+
+ if ((s = readpattern("Search")) != TRUE)
+ return (s);
+ if (forwsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", pat);
+ return (FALSE);
+ }
+ srch_lastdir = SRCH_FORW;
+ return (TRUE);
+}
+
+/*
+ * Reverse search. Get a search string from the user, and search, starting
+ * at "." and proceeding toward the front of the buffer. If found "." is
+ * left pointing at the first character of the pattern [the last character
+ * that was matched].
+ */
+/* ARGSUSED */
+int
+backsearch(int f, int n)
+{
+ int s;
+
+ if ((s = readpattern("Search backward")) != TRUE)
+ return (s);
+ if (backsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", pat);
+ return (FALSE);
+ }
+ srch_lastdir = SRCH_BACK;
+ return (TRUE);
+}
+
+/*
+ * Search again, using the same search string and direction as the last
+ * search command. The direction has been saved in "srch_lastdir", so you
+ * know which way to go.
+ */
+/* ARGSUSED */
+int
+searchagain(int f, int n)
+{
+ if (srch_lastdir == SRCH_FORW) {
+ if (forwsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", pat);
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+ if (srch_lastdir == SRCH_BACK) {
+ if (backsrch() == FALSE) {
+ dobeep();
+ ewprintf("Search failed: \"%s\"", pat);
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+ dobeep();
+ ewprintf("No last search");
+ return (FALSE);
+}
+
+/*
+ * Use incremental searching, initially in the forward direction.
+ * isearch ignores any explicit arguments.
+ */
+/* ARGSUSED */
+int
+forwisearch(int f, int n)
+{
+ if (macrodef || inmacro)
+ /* We can't isearch in macro. Use search instead */
+ return (forwsearch(f,n));
+ else
+ return (isearch(SRCH_FORW));
+}
+
+/*
+ * Use incremental searching, initially in the reverse direction.
+ * isearch ignores any explicit arguments.
+ */
+/* ARGSUSED */
+int
+backisearch(int f, int n)
+{
+ if (macrodef || inmacro)
+ /* We can't isearch in macro. Use search instead */
+ return (backsearch(f,n));
+ else
+ return (isearch(SRCH_BACK));
+}
+
+/*
+ * Incremental Search.
+ * dir is used as the initial direction to search.
+ * ^M exit from Isearch, set mark
+ * ^S switch direction to forward
+ * ^R switch direction to reverse
+ * ^Q quote next character (allows searching for ^N etc.)
+ * <ESC> exit from Isearch, set mark
+ * <DEL> undoes last character typed. (tricky job to do this correctly).
+ * other ^ exit search, don't set mark
+ * else accumulate into search string
+ */
+static int
+isearch(int dir)
+{
+ struct line *clp; /* Saved line pointer */
+ int c;
+ int cbo; /* Saved offset */
+ int success;
+ int pptr;
+ int firstc;
+ int xcase;
+ int i;
+ char opat[NPAT];
+ int cdotline; /* Saved line number */
+
+ if (macrodef) {
+ dobeep();
+ ewprintf("Can't isearch in macro");
+ return (FALSE);
+ }
+ for (cip = 0; cip < NSRCH; cip++)
+ cmds[cip].s_code = SRCH_NOPR;
+
+ (void)strlcpy(opat, pat, sizeof(opat));
+ cip = 0;
+ pptr = -1;
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ cdotline = curwp->w_dotline;
+ is_lpush();
+ is_cpush(SRCH_BEGIN);
+ success = TRUE;
+ is_prompt(dir, TRUE, success);
+
+ for (;;) {
+ update(CMODE);
+
+ switch (c = getkey(FALSE)) {
+ case CCHR('['):
+ /*
+ * If new characters come in the next 300 msec,
+ * we can assume that they belong to a longer
+ * escaped sequence so we should ungetkey the
+ * ESC to avoid writing out garbage.
+ */
+ if (ttwait(300) == FALSE)
+ ungetkey(c);
+ /* FALLTHRU */
+ case CCHR('M'):
+ srch_lastdir = dir;
+ curwp->w_markp = clp;
+ curwp->w_marko = cbo;
+ curwp->w_markline = cdotline;
+ ewprintf("Mark set");
+ return (TRUE);
+ case CCHR('G'):
+ if (success != TRUE) {
+ while (is_peek() == SRCH_ACCM)
+ is_undo(&pptr, &dir);
+ success = TRUE;
+ is_prompt(dir, pptr < 0, success);
+ break;
+ }
+ curwp->w_dotp = clp;
+ curwp->w_doto = cbo;
+ curwp->w_dotline = cdotline;
+ curwp->w_rflag |= WFMOVE;
+ srch_lastdir = dir;
+ (void)ctrlg(FFRAND, 0);
+ (void)strlcpy(pat, opat, sizeof(pat));
+ return (ABORT);
+ case CCHR('S'):
+ if (dir == SRCH_BACK) {
+ dir = SRCH_FORW;
+ is_lpush();
+ is_cpush(SRCH_FORW);
+ success = TRUE;
+ }
+ if (success == FALSE && dir == SRCH_FORW) {
+ /* wrap the search to beginning */
+ curwp->w_dotp = bfirstlp(curbp);
+ curwp->w_doto = 0;
+ curwp->w_dotline = 1;
+ if (is_find(dir) != FALSE) {
+ is_cpush(SRCH_MARK);
+ success = TRUE;
+ }
+ ewprintf("Overwrapped I-search: %s", pat);
+ break;
+ }
+ is_lpush();
+ pptr = strlen(pat);
+ if (forwchar(FFRAND, 1) == FALSE) {
+ dobeep();
+ success = FALSE;
+ ewprintf("Failed I-search: %s", pat);
+ } else {
+ if (is_find(SRCH_FORW) != FALSE)
+ is_cpush(SRCH_MARK);
+ else {
+ (void)backchar(FFRAND, 1);
+ dobeep();
+ success = FALSE;
+ ewprintf("Failed I-search: %s", pat);
+ }
+ }
+ is_prompt(dir, pptr < 0, success);
+ break;
+ case CCHR('R'):
+ if (dir == SRCH_FORW) {
+ dir = SRCH_BACK;
+ is_lpush();
+ is_cpush(SRCH_BACK);
+ success = TRUE;
+ }
+ if (success == FALSE && dir == SRCH_BACK) {
+ /* wrap the search to end */
+ curwp->w_dotp = blastlp(curbp);
+ curwp->w_doto = llength(curwp->w_dotp);
+ curwp->w_dotline = curwp->w_bufp->b_lines;
+ if (is_find(dir) != FALSE) {
+ is_cpush(SRCH_MARK);
+ success = TRUE;
+ }
+ ewprintf("Overwrapped I-search: %s", pat);
+ break;
+ }
+ is_lpush();
+ pptr = strlen(pat);
+ if (backchar(FFRAND, 1) == FALSE) {
+ dobeep();
+ success = FALSE;
+ } else {
+ if (is_find(SRCH_BACK) != FALSE)
+ is_cpush(SRCH_MARK);
+ else {
+ (void)forwchar(FFRAND, 1);
+ dobeep();
+ success = FALSE;
+ }
+ }
+ is_prompt(dir, pptr < 0, success);
+ break;
+ case CCHR('W'):
+ /* add the rest of the current word to the pattern */
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ firstc = 1;
+ if (pptr == -1)
+ pptr = 0;
+ if (dir == SRCH_BACK) {
+ /* when isearching backwards, cbo is the start of the pattern */
+ cbo += pptr;
+ }
+
+ /* if the search is case insensitive, add to pattern using lowercase */
+ xcase = 0;
+ for (i = 0; pat[i]; i++)
+ if (ISUPPER(CHARMASK(pat[i])))
+ xcase = 1;
+
+ while (cbo < llength(clp)) {
+ c = lgetc(clp, cbo++);
+ if ((!firstc && !isalnum(c)))
+ break;
+
+ if (pptr == NPAT - 1) {
+ dobeep();
+ break;
+ }
+ firstc = 0;
+ if (!xcase && ISUPPER(c))
+ c = TOLOWER(c);
+
+ pat[pptr++] = c;
+ pat[pptr] = '\0';
+ /* cursor only moves when isearching forwards */
+ if (dir == SRCH_FORW) {
+ curwp->w_doto = cbo;
+ curwp->w_rflag |= WFMOVE;
+ update(CMODE);
+ }
+ }
+ is_prompt(dir, pptr < 0, success);
+ break;
+ case CCHR('H'):
+ case CCHR('?'):
+ is_undo(&pptr, &dir);
+ if (is_peek() != SRCH_ACCM)
+ success = TRUE;
+ is_prompt(dir, pptr < 0, success);
+ break;
+ case CCHR('\\'):
+ case CCHR('Q'):
+ c = (char)getkey(FALSE);
+ goto addchar;
+ default:
+ if (ISCTRL(c)) {
+ ungetkey(c);
+ curwp->w_markp = clp;
+ curwp->w_marko = cbo;
+ curwp->w_markline = cdotline;
+ ewprintf("Mark set");
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+ }
+ /* FALLTHRU */
+ case CCHR('I'):
+ case CCHR('J'):
+ addchar:
+ if (pptr == -1)
+ pptr = 0;
+ if (pptr == 0)
+ success = TRUE;
+ if (pptr == NPAT - 1)
+ dobeep();
+ else {
+ pat[pptr++] = c;
+ pat[pptr] = '\0';
+ }
+ is_lpush();
+ if (success != FALSE) {
+ if (is_find(dir) != FALSE)
+ is_cpush(c);
+ else {
+ success = FALSE;
+ dobeep();
+ is_cpush(SRCH_ACCM);
+ }
+ } else
+ is_cpush(SRCH_ACCM);
+ is_prompt(dir, FALSE, success);
+ }
+ }
+ /* NOTREACHED */
+}
+
+static void
+is_cpush(int cmd)
+{
+ if (++cip >= NSRCH)
+ cip = 0;
+ cmds[cip].s_code = cmd;
+}
+
+static void
+is_lpush(void)
+{
+ int ctp;
+
+ ctp = cip + 1;
+ if (ctp >= NSRCH)
+ ctp = 0;
+ cmds[ctp].s_code = SRCH_NOPR;
+ cmds[ctp].s_doto = curwp->w_doto;
+ cmds[ctp].s_dotp = curwp->w_dotp;
+ cmds[ctp].s_dotline = curwp->w_dotline;
+}
+
+static void
+is_pop(void)
+{
+ if (cmds[cip].s_code != SRCH_NOPR) {
+ curwp->w_doto = cmds[cip].s_doto;
+ curwp->w_dotp = cmds[cip].s_dotp;
+ curwp->w_dotline = cmds[cip].s_dotline;
+ curwp->w_rflag |= WFMOVE;
+ cmds[cip].s_code = SRCH_NOPR;
+ }
+ if (--cip <= 0)
+ cip = NSRCH - 1;
+}
+
+static int
+is_peek(void)
+{
+ return (cmds[cip].s_code);
+}
+
+/* this used to always return TRUE (the return value was checked) */
+static void
+is_undo(int *pptr, int *dir)
+{
+ int redo = FALSE;
+
+ switch (cmds[cip].s_code) {
+ case SRCH_BEGIN:
+ case SRCH_NOPR:
+ *pptr = -1;
+ break;
+ case SRCH_MARK:
+ break;
+ case SRCH_FORW:
+ *dir = SRCH_BACK;
+ redo = TRUE;
+ break;
+ case SRCH_BACK:
+ *dir = SRCH_FORW;
+ redo = TRUE;
+ break;
+ case SRCH_ACCM:
+ default:
+ *pptr -= 1;
+ if (*pptr < 0)
+ *pptr = 0;
+ pat[*pptr] = '\0';
+ break;
+ }
+ is_pop();
+ if (redo)
+ is_undo(pptr, dir);
+}
+
+static int
+is_find(int dir)
+{
+ int plen, odoto, odotline;
+ struct line *odotp;
+
+ odoto = curwp->w_doto;
+ odotp = curwp->w_dotp;
+ odotline = curwp->w_dotline;
+ plen = strlen(pat);
+ if (plen != 0) {
+ if (dir == SRCH_FORW) {
+ (void)backchar(FFARG | FFRAND, plen);
+ if (forwsrch() == FALSE) {
+ curwp->w_doto = odoto;
+ curwp->w_dotp = odotp;
+ curwp->w_dotline = odotline;
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+ if (dir == SRCH_BACK) {
+ (void)forwchar(FFARG | FFRAND, plen);
+ if (backsrch() == FALSE) {
+ curwp->w_doto = odoto;
+ curwp->w_dotp = odotp;
+ curwp->w_dotline = odotline;
+ return (FALSE);
+ }
+ return (TRUE);
+ }
+ dobeep();
+ ewprintf("bad call to is_find");
+ return (FALSE);
+ }
+ return (FALSE);
+}
+
+/*
+ * If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used
+ * to print an error message. It also used to return TRUE or FALSE, depending
+ * on if it liked the "dir". However, none of the callers looked at the
+ * status, so I just made the checking vanish.
+ */
+static void
+is_prompt(int dir, int flag, int success)
+{
+ if (dir == SRCH_FORW) {
+ if (success != FALSE)
+ is_dspl("I-search", flag);
+ else
+ is_dspl("Failing I-search", flag);
+ } else if (dir == SRCH_BACK) {
+ if (success != FALSE)
+ is_dspl("I-search backward", flag);
+ else
+ is_dspl("Failing I-search backward", flag);
+ } else
+ ewprintf("Broken call to is_prompt");
+}
+
+/*
+ * Prompt writing routine for the incremental search. The "i_prompt" is just
+ * a string. The "flag" determines whether pat should be printed.
+ */
+static void
+is_dspl(char *i_prompt, int flag)
+{
+ if (flag != FALSE)
+ ewprintf("%s: ", i_prompt);
+ else
+ ewprintf("%s: %s", i_prompt, pat);
+}
+
+/*
+ * Query Replace.
+ * Replace strings selectively. Does a search and replace operation.
+ */
+/* ARGSUSED */
+int
+queryrepl(int f, int n)
+{
+ int s;
+ int rcnt = 0; /* replacements made so far */
+ int plen; /* length of found string */
+ char news[NPAT], *rep; /* replacement string */
+
+ if (macrodef) {
+ dobeep();
+ ewprintf("Can't query replace in macro");
+ return (FALSE);
+ }
+
+ if ((s = readpattern("Query replace")) != TRUE)
+ return (s);
+ if ((rep = eread("Query replace %s with: ", news, NPAT,
+ EFNUL | EFNEW | EFCR, pat)) == NULL)
+ return (ABORT);
+ else if (rep[0] == '\0')
+ news[0] = '\0';
+ ewprintf("Query replacing %s with %s:", pat, news);
+ plen = strlen(pat);
+
+ /*
+ * Search forward repeatedly, checking each time whether to insert
+ * or not. The "!" case makes the check always true, so it gets put
+ * into a tighter loop for efficiency.
+ */
+ while (forwsrch() == TRUE) {
+retry:
+ update(CMODE);
+ switch (getkey(FALSE)) {
+ case 'y':
+ case ' ':
+ if (lreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ break;
+ case '.':
+ if (lreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ goto stopsearch;
+ /* ^G, CR or ESC */
+ case CCHR('G'):
+ (void)ctrlg(FFRAND, 0);
+ goto stopsearch;
+ case CCHR('['):
+ case CCHR('M'):
+ goto stopsearch;
+ case '!':
+ do {
+ if (lreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+ rcnt++;
+ } while (forwsrch() == TRUE);
+ goto stopsearch;
+ case 'n':
+ case CCHR('H'):
+ /* To not replace */
+ case CCHR('?'):
+ break;
+ default:
+ ewprintf("y/n or <SP>/<DEL>: replace/don't, [.] repl-end, [!] repl-rest, <CR>/<ESC> quit");
+ goto retry;
+ }
+ }
+stopsearch:
+ curwp->w_rflag |= WFFULL;
+ update(CMODE);
+ if (rcnt == 1)
+ ewprintf("Replaced 1 occurrence");
+ else
+ ewprintf("Replaced %d occurrences", rcnt);
+ return (TRUE);
+}
+
+/*
+ * Replace string globally without individual prompting.
+ */
+/* ARGSUSED */
+int
+replstr(int f, int n)
+{
+ char news[NPAT];
+ int s, plen, rcnt = 0;
+ char *r;
+
+ if ((s = readpattern("Replace string")) != TRUE)
+ return s;
+
+ r = eread("Replace string %s with: ", news, NPAT,
+ EFNUL | EFNEW | EFCR, pat);
+ if (r == NULL)
+ return (ABORT);
+
+ plen = strlen(pat);
+ while (forwsrch() == TRUE) {
+ update(CMODE);
+ if (lreplace((RSIZE)plen, news) == FALSE)
+ return (FALSE);
+
+ rcnt++;
+ }
+
+ curwp->w_rflag |= WFFULL;
+ update(CMODE);
+
+ if (rcnt == 1)
+ ewprintf("Replaced 1 occurrence");
+ else
+ ewprintf("Replaced %d occurrences", rcnt);
+
+ return (TRUE);
+}
+
+/*
+ * This routine does the real work of a forward search. The pattern is sitting
+ * in the external variable "pat". If found, dot is updated, the window system
+ * is notified of the change, and TRUE is returned. If the string isn't found,
+ * FALSE is returned.
+ */
+int
+forwsrch(void)
+{
+ struct line *clp, *tlp;
+ int cbo, tbo, c, i, xcase = 0;
+ char *pp;
+ int nline;
+
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ nline = curwp->w_dotline;
+ for (i = 0; pat[i]; i++)
+ if (ISUPPER(CHARMASK(pat[i])))
+ xcase = 1;
+ for (;;) {
+ if (cbo == llength(clp)) {
+ if ((clp = lforw(clp)) == curbp->b_headp)
+ break;
+ nline++;
+ cbo = 0;
+ c = CCHR('J');
+ } else
+ c = lgetc(clp, cbo++);
+ if (eq(c, pat[0], xcase) != FALSE) {
+ tlp = clp;
+ tbo = cbo;
+ pp = &pat[1];
+ while (*pp != 0) {
+ if (tbo == llength(tlp)) {
+ tlp = lforw(tlp);
+ if (tlp == curbp->b_headp)
+ goto fail;
+ tbo = 0;
+ c = CCHR('J');
+ if (eq(c, *pp++, xcase) == FALSE)
+ goto fail;
+ nline++;
+ } else {
+ c = lgetc(tlp, tbo++);
+ if (eq(c, *pp++, xcase) == FALSE)
+ goto fail;
+ }
+ }
+ curwp->w_dotp = tlp;
+ curwp->w_doto = tbo;
+ curwp->w_dotline = nline;
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+ }
+fail: ;
+ }
+ return (FALSE);
+}
+
+/*
+ * This routine does the real work of a backward search. The pattern is
+ * sitting in the external variable "pat". If found, dot is updated, the
+ * window system is notified of the change, and TRUE is returned. If the
+ * string isn't found, FALSE is returned.
+ */
+int
+backsrch(void)
+{
+ struct line *clp, *tlp;
+ int cbo, tbo, c, i, xcase = 0;
+ char *epp, *pp;
+ int nline, pline;
+
+ for (epp = &pat[0]; epp[1] != 0; ++epp);
+ clp = curwp->w_dotp;
+ cbo = curwp->w_doto;
+ nline = curwp->w_dotline;
+ for (i = 0; pat[i]; i++)
+ if (ISUPPER(CHARMASK(pat[i])))
+ xcase = 1;
+ for (;;) {
+ if (cbo == 0) {
+ clp = lback(clp);
+ if (clp == curbp->b_headp)
+ return (FALSE);
+ nline--;
+ cbo = llength(clp) + 1;
+ }
+ if (--cbo == llength(clp))
+ c = CCHR('J');
+ else
+ c = lgetc(clp, cbo);
+ if (eq(c, *epp, xcase) != FALSE) {
+ tlp = clp;
+ tbo = cbo;
+ pp = epp;
+ pline = nline;
+ while (pp != &pat[0]) {
+ if (tbo == 0) {
+ tlp = lback(tlp);
+ if (tlp == curbp->b_headp)
+ goto fail;
+ nline--;
+ tbo = llength(tlp) + 1;
+ }
+ if (--tbo == llength(tlp))
+ c = CCHR('J');
+ else
+ c = lgetc(tlp, tbo);
+ if (eq(c, *--pp, xcase) == FALSE) {
+ nline = pline;
+ goto fail;
+ }
+ }
+ curwp->w_dotp = tlp;
+ curwp->w_doto = tbo;
+ curwp->w_dotline = nline;
+ curwp->w_rflag |= WFMOVE;
+ return (TRUE);
+ }
+fail: ;
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Compare two characters. The "bc" comes from the buffer. It has its case
+ * folded out. The "pc" is from the pattern.
+ */
+static int
+eq(int bc, int pc, int xcase)
+{
+ bc = CHARMASK(bc);
+ pc = CHARMASK(pc);
+ if (bc == pc)
+ return (TRUE);
+ if (xcase)
+ return (FALSE);
+ if (ISUPPER(bc))
+ return (TOLOWER(bc) == pc);
+ if (ISUPPER(pc))
+ return (bc == TOLOWER(pc));
+ return (FALSE);
+}
+
+/*
+ * Read a pattern. Stash it in the external variable "pat". The "pat" is not
+ * updated if the user types in an empty line. If the user typed an empty
+ * line, and there is no old pattern, it is an error. Display the old pattern,
+ * in the style of Jeff Lomicka. There is some do-it-yourself control
+ * expansion.
+ */
+int
+readpattern(char *r_prompt)
+{
+ char tpat[NPAT], *rep;
+ int retval;
+
+ if (pat[0] == '\0')
+ rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, r_prompt);
+ else
+ rep = eread("%s (default %s): ", tpat, NPAT,
+ EFNUL | EFNEW | EFCR, r_prompt, pat);
+
+ /* specified */
+ if (rep == NULL) {
+ retval = ABORT;
+ } else if (rep[0] != '\0') {
+ (void)strlcpy(pat, tpat, sizeof(pat));
+ retval = TRUE;
+ } else if (pat[0] != '\0') {
+ retval = TRUE;
+ } else
+ retval = FALSE;
+ return (retval);
+}
Index: contrib/mg/setupterm.c
===================================================================
--- /dev/null
+++ contrib/mg/setupterm.c
@@ -0,0 +1,158 @@
+/* $NetBSD: setupterm.c,v 1.9 2019/04/11 23:52:08 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/ioctl.h>
+#include <err.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+/*
+ * use_env is really a curses function - POSIX mandates it's in curses.h
+ * But it has to live in terminfo because it must precede a call to setupterm().
+ */
+
+static bool __use_env = true;
+
+void
+use_env(bool value)
+{
+
+ __use_env = value;
+}
+#define reterr(code, msg) \
+ do { \
+ if (errret == NULL) \
+ errx(EXIT_FAILURE, msg); \
+ else { \
+ *errret = code; \
+ return ERR; \
+ } \
+ } while (0 /* CONSTCOND */)
+
+#define reterrarg(code, msg, arg) \
+ do { \
+ if (errret == NULL) \
+ errx(EXIT_FAILURE, msg, arg); \
+ else { \
+ *errret = code; \
+ return ERR; \
+ } \
+ } while (0 /* CONSTCOND */)
+
+
+int
+ti_setupterm(TERMINAL **nterm, const char *term, int fildes, int *errret)
+{
+ int error;
+ struct winsize win;
+
+ if (term == NULL)
+ term = getenv("TERM");
+ if (term == NULL || *term == '\0') {
+ *nterm = NULL;
+ reterr(0, "TERM environment variable not set");
+ }
+ if (fildes == STDOUT_FILENO && !isatty(fildes))
+ fildes = STDERR_FILENO;
+
+ *nterm = calloc(1, sizeof(**nterm));
+ if (*nterm == NULL)
+ reterr(-1, "not enough memory to create terminal structure");
+
+ error = _ti_getterm(*nterm, term, 0);
+ if (error != 1) {
+ del_curterm(*nterm);
+ *nterm = NULL;
+ switch (error) {
+ case -1:
+ reterr(error, "cannot access the terminfo database");
+ /* NOTREACHED */
+ case 0:
+ reterrarg(error,
+ "%s: terminal not listed in terminfo database",
+ term);
+ /* NOTREACHED */
+ default:
+ reterr(-1, "unknown error");
+ /* NOTREACHED */
+ }
+ }
+
+ (*nterm)->fildes = fildes;
+ _ti_setospeed(*nterm);
+ if (t_generic_type(*nterm))
+ reterrarg(0, "%s: generic terminal", term);
+ if (t_hard_copy(*nterm))
+ reterrarg(1, "%s: hardcopy terminal", term);
+
+ /* If TIOCGWINSZ works, then set initial lines and columns. */
+ if (ioctl(fildes, TIOCGWINSZ, &win) != -1 &&
+ win.ws_row != 0 && win.ws_col != 0)
+ {
+ t_lines(*nterm) = (short)win.ws_row;
+ t_columns(*nterm) = (short)win.ws_col;
+ }
+
+ /* POSIX 1003.2 requires that the environment override. */
+ if (__use_env) {
+ char *p;
+
+ if ((p = getenv("LINES")) != NULL)
+ t_lines(*nterm) = (short)strtol(p, NULL, 0);
+ if ((p = getenv("COLUMNS")) != NULL)
+ t_columns(*nterm) = (short)strtol(p, NULL, 0);
+ }
+
+ /* POSIX requires 1 for success */
+ if (errret)
+ *errret = 1;
+ return OK;
+}
+
+int
+setupterm(const char *term, int fildes, int *errret)
+{
+ TERMINAL *nterm;
+ int ret;
+
+ if (errret != NULL)
+ *errret = ERR;
+ ret = ti_setupterm(&nterm, term, fildes, errret);
+ if (nterm != NULL)
+ set_curterm(nterm);
+ return ret;
+}
Index: contrib/mg/spawn.c
===================================================================
--- /dev/null
+++ contrib/mg/spawn.c
@@ -0,0 +1,51 @@
+/* $OpenBSD: spawn.c,v 1.12 2015/03/19 21:22:15 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Spawn. Actually just suspends Mg.
+ * Assumes POSIX job control.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <term.h>
+#include <termios.h>
+
+#include "def.h"
+
+/*
+ * This causes mg to send itself a stop signal. It assumes the parent
+ * shell supports POSIX job control. If the terminal supports an alternate
+ * screen, we will switch to it.
+ */
+/* ARGSUSED */
+int
+spawncli(int f, int n)
+{
+ sigset_t oset;
+
+ /* Very similar to what vttidy() does. */
+ ttcolor(CTEXT);
+ ttnowindow();
+ ttmove(nrow - 1, 0);
+ if (epresf != FALSE) {
+ tteeol();
+ epresf = FALSE;
+ }
+ if (ttcooked() == FALSE)
+ return (FALSE);
+
+ /* Exit application mode and tidy. */
+ tttidy();
+ ttflush();
+ (void)sigprocmask(SIG_SETMASK, NULL, &oset);
+ (void)kill(0, SIGTSTP);
+ (void)sigprocmask(SIG_SETMASK, &oset, NULL);
+ ttreinit();
+
+ /* Force repaint. */
+ sgarbf = TRUE;
+ return (ttraw());
+}
Index: contrib/mg/strlcat.c
===================================================================
--- /dev/null
+++ contrib/mg/strlcat.c
@@ -0,0 +1,61 @@
+/* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */
+
+/*
+ * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "config.h"
+
+#ifndef HAVE_STRLCAT
+
+/*
+ * Appends src to string dst of size dsize (unlike strncat, dsize is the
+ * full size of dst, not space left). At most dsize-1 characters
+ * will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
+ * Returns strlen(src) + MIN(dsize, strlen(initial dst)).
+ * If retval >= dsize, truncation occurred.
+ */
+size_t
+strlcat(char *dst, const char *src, size_t dsize)
+{
+ const char *odst = dst;
+ const char *osrc = src;
+ size_t n = dsize;
+ size_t dlen;
+
+ /* Find the end of dst and adjust bytes left but don't go past end. */
+ while (n-- != 0 && *dst != '\0')
+ dst++;
+ dlen = dst - odst;
+ n = dsize - dlen;
+
+ if (n-- == 0)
+ return(dlen + strlen(src));
+ while (*src != '\0') {
+ if (n != 0) {
+ *dst++ = *src;
+ n--;
+ }
+ src++;
+ }
+ *dst = '\0';
+
+ return(dlen + (src - osrc)); /* count does not include NUL */
+}
+
+#endif /* !HAVE_STRLCAT */
Index: contrib/mg/strlcpy.c
===================================================================
--- /dev/null
+++ contrib/mg/strlcpy.c
@@ -0,0 +1,56 @@
+/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */
+
+/*
+ * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "config.h"
+
+#ifndef HAVE_STRLCPY
+
+/*
+ * Copy string src to buffer dst of size dsize. At most dsize-1
+ * chars will be copied. Always NUL terminates (unless dsize == 0).
+ * Returns strlen(src); if retval >= dsize, truncation occurred.
+ */
+size_t
+strlcpy(char *dst, const char *src, size_t dsize)
+{
+ const char *osrc = src;
+ size_t nleft = dsize;
+
+ /* Copy as many bytes as will fit. */
+ if (nleft != 0) {
+ while (--nleft != 0) {
+ if ((*dst++ = *src++) == '\0')
+ break;
+ }
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src. */
+ if (nleft == 0) {
+ if (dsize != 0)
+ *dst = '\0'; /* NUL-terminate dst */
+ while (*src++)
+ ;
+ }
+
+ return(src - osrc - 1); /* count does not include NUL */
+}
+
+#endif /* !HAVE_STRLCPY */
Index: contrib/mg/strndup.c
===================================================================
--- /dev/null
+++ contrib/mg/strndup.c
@@ -0,0 +1,55 @@
+/* $OpenBSD: strndup.c,v 1.3 2019/01/25 00:19:25 millert Exp $ */
+/* $OpenBSD: strnlen.c,v 1.9 2019/01/25 00:19:25 millert Exp $ */
+
+/*
+ * Copyright (c) 2010 Todd C. Miller <millert@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+
+#ifndef HAVE_STRNDUP
+
+static size_t
+strnlen(const char *str, size_t maxlen)
+{
+ const char *cp;
+
+ for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--)
+ ;
+
+ return (size_t)(cp - str);
+}
+
+char *
+strndup(const char *str, size_t maxlen)
+{
+ char *copy;
+ size_t len;
+
+ len = strnlen(str, maxlen);
+ copy = malloc(len + 1);
+ if (copy != NULL) {
+ (void)memcpy(copy, str, len);
+ copy[len] = '\0';
+ }
+
+ return copy;
+}
+
+#endif /* !HAVE_STRNDUP */
Index: contrib/mg/strtonum.c
===================================================================
--- /dev/null
+++ contrib/mg/strtonum.c
@@ -0,0 +1,71 @@
+/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */
+
+/*
+ * Copyright (c) 2004 Ted Unangst and Todd Miller
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+#ifndef HAVE_STRTONUM
+
+#define INVALID 1
+#define TOOSMALL 2
+#define TOOLARGE 3
+
+long long
+strtonum(const char *numstr, long long minval, long long maxval,
+ const char **errstrp)
+{
+ long long ll = 0;
+ int error = 0;
+ char *ep;
+ struct errval {
+ const char *errstr;
+ int err;
+ } ev[4] = {
+ { NULL, 0 },
+ { "invalid", EINVAL },
+ { "too small", ERANGE },
+ { "too large", ERANGE },
+ };
+
+ ev[0].err = errno;
+ errno = 0;
+ if (minval > maxval) {
+ error = INVALID;
+ } else {
+ ll = strtoll(numstr, &ep, 10);
+ if (numstr == ep || *ep != '\0')
+ error = INVALID;
+ else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+ error = TOOSMALL;
+ else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+ error = TOOLARGE;
+ }
+ if (errstrp != NULL)
+ *errstrp = ev[error].errstr;
+ errno = ev[error].err;
+ if (error)
+ ll = 0;
+
+ return (ll);
+}
+
+#endif /* !HAVE_STRTONUM */
Index: contrib/mg/tags.c
===================================================================
--- /dev/null
+++ contrib/mg/tags.c
@@ -0,0 +1,565 @@
+/* $OpenBSD: tags.c,v 1.16 2017/08/06 04:39:45 bcallah Exp $ */
+
+/*
+ * This file is in the public domain.
+ *
+ * Author: Sunil Nimmagadda <sunil@openbsd.org>
+ */
+
+#include <sys/queue.h>
+#include <sys/stat.h>
+#if defined(__linux__) || defined(__DragonFly__) || defined(__APPLE__)
+#include "tree.h"
+#else
+#include <sys/tree.h>
+#endif
+#include <sys/types.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#if defined(__linux__) || defined(__CYGWIN__)
+#include "util.h"
+#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#include "def.h"
+
+struct ctag;
+
+static int addctag(char *);
+static int atbow(void);
+void closetags(void);
+static int ctagcmp(struct ctag *, struct ctag *);
+static int loadbuffer(char *);
+static int loadtags(const char *);
+static int pushtag(char *);
+static int searchpat(char *);
+static struct ctag *searchtag(char *);
+static char *strip(char *, size_t);
+static void unloadtags(void);
+
+#define DEFAULTFN "tags"
+
+char *tagsfn = NULL;
+int loaded = FALSE;
+
+/* ctags(1) entries are parsed and maintained in a tree. */
+struct ctag {
+ RB_ENTRY(ctag) entry;
+ char *tag;
+ char *fname;
+ char *pat;
+};
+RB_HEAD(tagtree, ctag) tags = RB_INITIALIZER(&tags);
+RB_GENERATE(tagtree, ctag, entry, ctagcmp);
+
+struct tagpos {
+ SLIST_ENTRY(tagpos) entry;
+ int doto;
+ int dotline;
+ char *bname;
+};
+SLIST_HEAD(tagstack, tagpos) shead = SLIST_HEAD_INITIALIZER(shead);
+
+int
+ctagcmp(struct ctag *s, struct ctag *t)
+{
+ return strcmp(s->tag, t->tag);
+}
+
+/*
+ * Record the filename that contain tags to be used while loading them
+ * on first use. If a filename is already recorded, ask user to retain
+ * already loaded tags (if any) and unload them if user chooses not to.
+ */
+/* ARGSUSED */
+int
+tagsvisit(int f, int n)
+{
+ char fname[NFILEN], *bufp, *temp;
+ struct stat sb;
+
+ if (getbufcwd(fname, sizeof(fname)) == FALSE)
+ fname[0] = '\0';
+
+ if (strlcat(fname, DEFAULTFN, sizeof(fname)) >= sizeof(fname)) {
+ dobeep();
+ ewprintf("Filename too long");
+ return (FALSE);
+ }
+
+ bufp = eread("Visit tags table (default %s): ", fname,
+ NFILEN, EFFILE | EFCR | EFNEW | EFDEF, DEFAULTFN);
+ if (bufp == NULL)
+ return (ABORT);
+
+ if (stat(bufp, &sb) == -1) {
+ dobeep();
+ ewprintf("stat: %s", strerror(errno));
+ return (FALSE);
+ } else if (S_ISREG(sb.st_mode) == 0) {
+ dobeep();
+ ewprintf("Not a regular file");
+ return (FALSE);
+ } else if (access(bufp, R_OK) == -1) {
+ dobeep();
+ ewprintf("Cannot access file %s", bufp);
+ return (FALSE);
+ }
+
+ if (tagsfn == NULL) {
+ if (bufp[0] == '\0') {
+ if ((tagsfn = strdup(fname)) == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ return (FALSE);
+ }
+ } else {
+ /* bufp points to local variable, so duplicate. */
+ if ((tagsfn = strdup(bufp)) == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ return (FALSE);
+ }
+ }
+ } else {
+ if ((temp = strdup(bufp)) == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ return (FALSE);
+ }
+ free(tagsfn);
+ tagsfn = temp;
+ if (eyorn("Keep current list of tags table also") == FALSE) {
+ ewprintf("Starting a new list of tags table");
+ unloadtags();
+ }
+ loaded = FALSE;
+ }
+ return (TRUE);
+}
+
+/*
+ * Ask user for a tag while treating word at dot as default. Visit tags
+ * file if not yet done, load tags and jump to definition of the tag.
+ */
+int
+findtag(int f, int n)
+{
+ char utok[MAX_TOKEN], dtok[MAX_TOKEN];
+ char *tok, *bufp;
+ int ret;
+
+ if (curtoken(f, n, dtok) == FALSE) {
+ dtok[0] = '\0';
+ bufp = eread("Find tag: ", utok, MAX_TOKEN, EFNUL | EFNEW);
+ } else
+ bufp = eread("Find tag (default %s): ", utok, MAX_TOKEN,
+ EFNUL | EFNEW, dtok);
+
+ if (bufp == NULL)
+ return (ABORT);
+ else if (bufp[0] == '\0')
+ tok = dtok;
+ else
+ tok = utok;
+
+ if (tok[0] == '\0') {
+ dobeep();
+ ewprintf("There is no default tag");
+ return (FALSE);
+ }
+
+ if (tagsfn == NULL)
+ if ((ret = tagsvisit(f, n)) != TRUE)
+ return (ret);
+ if (!loaded) {
+ if (loadtags(tagsfn) == FALSE) {
+ free(tagsfn);
+ tagsfn = NULL;
+ return (FALSE);
+ }
+ loaded = TRUE;
+ }
+ return pushtag(tok);
+}
+
+/*
+ * Free tags tree.
+ */
+void
+unloadtags(void)
+{
+ struct ctag *var, *nxt;
+
+ for (var = RB_MIN(tagtree, &tags); var != NULL; var = nxt) {
+ nxt = RB_NEXT(tagtree, &tags, var);
+ RB_REMOVE(tagtree, &tags, var);
+ /* line parsed with fparseln needs to be freed */
+ free(var->tag);
+ free(var);
+ }
+}
+
+/*
+ * Lookup tag passed in tree and if found, push current location and
+ * buffername onto stack, load the file with tag definition into a new
+ * buffer and position dot at the pattern.
+ */
+/*ARGSUSED */
+int
+pushtag(char *tok)
+{
+ struct ctag *res;
+ struct tagpos *s;
+ char bname[NFILEN];
+ int doto, dotline;
+
+ if ((res = searchtag(tok)) == NULL)
+ return (FALSE);
+
+ doto = curwp->w_doto;
+ dotline = curwp->w_dotline;
+ /* record absolute filenames. Fixes issues when mg's cwd is not the
+ * same as buffer's directory.
+ */
+ if (strlcpy(bname, curbp->b_cwd, sizeof(bname)) >= sizeof(bname)) {
+ dobeep();
+ ewprintf("filename too long");
+ return (FALSE);
+ }
+ if (strlcat(bname, curbp->b_bname, sizeof(bname)) >= sizeof(bname)) {
+ dobeep();
+ ewprintf("filename too long");
+ return (FALSE);
+ }
+
+ if (loadbuffer(res->fname) == FALSE)
+ return (FALSE);
+
+ if (searchpat(res->pat) == TRUE) {
+ if ((s = malloc(sizeof(struct tagpos))) == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ return (FALSE);
+ }
+ if ((s->bname = strdup(bname)) == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ free(s);
+ return (FALSE);
+ }
+ s->doto = doto;
+ s->dotline = dotline;
+ SLIST_INSERT_HEAD(&shead, s, entry);
+ return (TRUE);
+ } else {
+ dobeep();
+ ewprintf("%s: pattern not found", res->tag);
+ return (FALSE);
+ }
+ /* NOTREACHED */
+ return (FALSE);
+}
+
+/*
+ * If tag stack is not empty pop stack and jump to recorded buffer, dot.
+ */
+/* ARGSUSED */
+int
+poptag(int f, int n)
+{
+ struct line *dotp;
+ struct tagpos *s;
+
+ if (SLIST_EMPTY(&shead)) {
+ dobeep();
+ ewprintf("No previous location for find-tag invocation");
+ return (FALSE);
+ }
+ s = SLIST_FIRST(&shead);
+ SLIST_REMOVE_HEAD(&shead, entry);
+ if (loadbuffer(s->bname) == FALSE)
+ return (FALSE);
+ curwp->w_dotline = s->dotline;
+ curwp->w_doto = s->doto;
+
+ /* storing of dotp in tagpos wouldn't work out in cases when
+ * that buffer is killed by user(dangling pointer). Explicitly
+ * traverse till dotline for correct handling.
+ */
+ dotp = curwp->w_bufp->b_headp;
+ while (s->dotline--)
+ dotp = dotp->l_fp;
+
+ curwp->w_dotp = dotp;
+ free(s->bname);
+ free(s);
+ return (TRUE);
+}
+
+/*
+ * Parse the tags file and construct the tags tree. Remove escape
+ * characters while parsing the file.
+ */
+int
+loadtags(const char *fn)
+{
+ char *l;
+ FILE *fd;
+
+ if ((fd = fopen(fn, "r")) == NULL) {
+ dobeep();
+ ewprintf("Unable to open tags file: %s", fn);
+ return (FALSE);
+ }
+ while ((l = fparseln(fd, NULL, NULL, "\\\\\0",
+ FPARSELN_UNESCCONT | FPARSELN_UNESCREST)) != NULL) {
+ if (addctag(l) == FALSE) {
+ fclose(fd);
+ return (FALSE);
+ }
+ }
+ fclose(fd);
+ return (TRUE);
+}
+
+/*
+ * Cleanup and destroy tree and stack.
+ */
+void
+closetags(void)
+{
+ struct tagpos *s;
+
+ while (!SLIST_EMPTY(&shead)) {
+ s = SLIST_FIRST(&shead);
+ SLIST_REMOVE_HEAD(&shead, entry);
+ free(s->bname);
+ free(s);
+ }
+ unloadtags();
+ free(tagsfn);
+}
+
+/*
+ * Strip away any special characters in pattern.
+ * The pattern in ctags isn't a true regular expression. Its of the form
+ * /^xxx$/ or ?^xxx$? and in some cases the "$" would be missing. Strip
+ * the leading and trailing special characters so the pattern matching
+ * would be a simple string compare. Escape character is taken care by
+ * fparseln.
+ */
+char *
+strip(char *s, size_t len)
+{
+ /* first strip trailing special chars */
+ s[len - 1] = '\0';
+ if (s[len - 2] == '$')
+ s[len - 2] = '\0';
+
+ /* then strip leading special chars */
+ s++;
+ if (*s == '^')
+ s++;
+
+ return s;
+}
+
+/*
+ * tags line is of the format "<tag>\t<filename>\t<pattern>". Split them
+ * by replacing '\t' with '\0'. This wouldn't alter the size of malloc'ed
+ * l, and can be freed during cleanup.
+ */
+int
+addctag(char *l)
+{
+ struct ctag *t;
+
+ if ((t = malloc(sizeof(struct ctag))) == NULL) {
+ dobeep();
+ ewprintf("Out of memory");
+ return (FALSE);
+ }
+ t->tag = l;
+ if ((l = strchr(l, '\t')) == NULL)
+ goto cleanup;
+ *l++ = '\0';
+ t->fname = l;
+ if ((l = strchr(l, '\t')) == NULL)
+ goto cleanup;
+ *l++ = '\0';
+ if (*l == '\0')
+ goto cleanup;
+ t->pat = strip(l, strlen(l));
+ RB_INSERT(tagtree, &tags, t);
+ return (TRUE);
+cleanup:
+ free(t);
+ free(l);
+ return (FALSE);
+}
+
+/*
+ * Search through each line of buffer for pattern.
+ */
+int
+searchpat(char *s_pat)
+{
+ struct line *lp;
+ int dotline;
+ size_t plen;
+
+ plen = strlen(s_pat);
+ dotline = 1;
+ lp = lforw(curbp->b_headp);
+ while (lp != curbp->b_headp) {
+ if (ltext(lp) != NULL && plen <= llength(lp) &&
+ (strncmp(s_pat, ltext(lp), plen) == 0)) {
+ curwp->w_doto = 0;
+ curwp->w_dotp = lp;
+ curwp->w_dotline = dotline;
+ return (TRUE);
+ } else {
+ lp = lforw(lp);
+ dotline++;
+ }
+ }
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if dot is at beginning of a word or at beginning
+ * of line, else FALSE.
+ */
+int
+atbow(void)
+{
+ if (curwp->w_doto == 0)
+ return (TRUE);
+ if (ISWORD(curwp->w_dotp->l_text[curwp->w_doto]) &&
+ !ISWORD(curwp->w_dotp->l_text[curwp->w_doto - 1]))
+ return (TRUE);
+ return (FALSE);
+}
+
+/*
+ * Extract the word at dot without changing dot position.
+ */
+int
+curtoken(int f, int n, char *token)
+{
+ struct line *odotp;
+ int odoto, tdoto, odotline, size, r;
+ char c;
+
+ /* Underscore character is to be treated as "inword" while
+ * processing tokens unlike mg's default word traversal. Save
+ * and restore it's cinfo value so that tag matching works for
+ * identifier with underscore.
+ */
+ c = cinfo['_'];
+ cinfo['_'] = _MG_W;
+
+ odotp = curwp->w_dotp;
+ odoto = curwp->w_doto;
+ odotline = curwp->w_dotline;
+
+ /* Move backword unless we are at the beginning of a word or at
+ * beginning of line.
+ */
+ if (!atbow())
+ if ((r = backword(f, n)) == FALSE)
+ goto cleanup;
+
+ tdoto = curwp->w_doto;
+
+ if ((r = forwword(f, n)) == FALSE)
+ goto cleanup;
+
+ /* strip away leading whitespace if any like emacs. */
+ while (ltext(curwp->w_dotp) &&
+ isspace(lgetc(curwp->w_dotp, tdoto)))
+ tdoto++;
+
+ size = curwp->w_doto - tdoto;
+ if (size <= 0 || size >= MAX_TOKEN ||
+ ltext(curwp->w_dotp) == NULL) {
+ r = FALSE;
+ goto cleanup;
+ }
+ strncpy(token, ltext(curwp->w_dotp) + tdoto, size);
+ token[size] = '\0';
+ r = TRUE;
+
+cleanup:
+ cinfo['_'] = c;
+ curwp->w_dotp = odotp;
+ curwp->w_doto = odoto;
+ curwp->w_dotline = odotline;
+ return (r);
+}
+
+/*
+ * Search tagstree for a given token.
+ */
+struct ctag *
+searchtag(char *tok)
+{
+ struct ctag t, *res;
+
+ t.tag = tok;
+ if ((res = RB_FIND(tagtree, &tags, &t)) == NULL) {
+ dobeep();
+ ewprintf("No tag containing %s", tok);
+ return (NULL);
+ }
+ return res;
+}
+
+/*
+ * This is equivalent to filevisit from file.c.
+ * Look around to see if we can find the file in another buffer; if we
+ * can't find it, create a new buffer, read in the text, and switch to
+ * the new buffer. *scratch*, *grep*, *compile* needs to be handled
+ * differently from other buffers which have "filenames".
+ */
+int
+loadbuffer(char *bname)
+{
+ struct buffer *bufp;
+ char *adjf;
+
+ /* check for special buffers which begin with '*' */
+ if (bname[0] == '*') {
+ if ((bufp = bfind(bname, FALSE)) != NULL) {
+ curbp = bufp;
+ return (showbuffer(bufp, curwp, WFFULL));
+ } else {
+ return (FALSE);
+ }
+ } else {
+ if ((adjf = adjustname(bname, TRUE)) == NULL)
+ return (FALSE);
+ if ((bufp = findbuffer(adjf)) == NULL)
+ return (FALSE);
+ }
+ curbp = bufp;
+ if (showbuffer(bufp, curwp, WFFULL) != TRUE)
+ return (FALSE);
+ if (bufp->b_fname[0] == '\0') {
+ if (readin(adjf) != TRUE) {
+ killbuffer(bufp);
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
Index: contrib/mg/term.c
===================================================================
--- /dev/null
+++ contrib/mg/term.c
@@ -0,0 +1,473 @@
+/* $NetBSD: term.c,v 1.29 2018/10/08 20:44:34 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2010, 2011 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cdbr.h"
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#define _PATH_TERMINFO "/usr/share/misc/terminfo"
+
+#ifndef __arraycount
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+static char __ti_database[PATH_MAX];
+const char *_ti_database;
+
+/* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */
+uint16_t
+le16dec(const void *buf)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+
+ return ((p[1] << 8) | p[0]);
+}
+
+/* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */
+static uint32_t
+le32dec(const void *buf)
+{
+ uint8_t const *p = (uint8_t const *) buf;
+
+ return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+}
+
+/* Include a generated list of pre-compiled terminfo descriptions. */
+#include "compiled_terms.c"
+
+static int
+allocset(void *pp, int init, size_t nelem, size_t elemsize)
+{
+ void **p = pp;
+ if (*p) {
+ memset(*p, init, nelem * elemsize);
+ return 0;
+ }
+
+ if ((*p = calloc(nelem, elemsize)) == NULL)
+ return -1;
+
+ if (init != 0)
+ memset(*p, init, nelem * elemsize);
+ return 0;
+}
+
+static int
+_ti_readterm(TERMINAL *term, const char *cap, size_t caplen, int flags)
+{
+ char ver;
+ uint16_t ind, num;
+ size_t len;
+ TERMUSERDEF *ud;
+
+ if (caplen == 0)
+ goto out;
+ ver = *cap++;
+ caplen--;
+ /* Only read version 1 structures */
+ if (ver != 1)
+ goto out;
+
+ if (allocset(&term->flags, 0, TIFLAGMAX+1, sizeof(*term->flags)) == -1)
+ return -1;
+
+ if (allocset(&term->nums, -1, TINUMMAX+1, sizeof(*term->nums)) == -1)
+ return -1;
+
+ if (allocset(&term->strs, 0, TISTRMAX+1, sizeof(*term->strs)) == -1)
+ return -1;
+
+ if (term->_arealen != caplen) {
+ term->_arealen = caplen;
+ term->_area = realloc(term->_area, term->_arealen);
+ if (term->_area == NULL)
+ return -1;
+ }
+ memcpy(term->_area, cap, term->_arealen);
+
+ cap = term->_area;
+ len = le16dec(cap);
+ cap += sizeof(uint16_t);
+ term->name = cap;
+ cap += len;
+ len = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (len == 0)
+ term->_alias = NULL;
+ else {
+ term->_alias = cap;
+ cap += len;
+ }
+ len = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (len == 0)
+ term->desc = NULL;
+ else {
+ term->desc = cap;
+ cap += len;
+ }
+
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (num != 0) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ for (; num != 0; num--) {
+ ind = le16dec(cap);
+ cap += sizeof(uint16_t);
+ term->flags[ind] = *cap++;
+ if (flags == 0 && !VALID_BOOLEAN(term->flags[ind]))
+ term->flags[ind] = 0;
+ }
+ }
+
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (num != 0) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ for (; num != 0; num--) {
+ ind = le16dec(cap);
+ cap += sizeof(uint16_t);
+ term->nums[ind] = (short)le16dec(cap);
+ if (flags == 0 && !VALID_NUMERIC(term->nums[ind]))
+ term->nums[ind] = ABSENT_NUMERIC;
+ cap += sizeof(uint16_t);
+ }
+ }
+
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (num != 0) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ for (; num != 0; num--) {
+ ind = le16dec(cap);
+ cap += sizeof(uint16_t);
+ len = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (len > 0)
+ term->strs[ind] = cap;
+ else if (flags == 0)
+ term->strs[ind] = ABSENT_STRING;
+ else
+ term->strs[ind] = CANCELLED_STRING;
+ cap += len;
+ }
+ }
+
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (num != 0) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (num != term->_nuserdefs) {
+ free(term->_userdefs);
+ term->_userdefs = NULL;
+ term->_nuserdefs = num;
+ }
+ if (allocset(&term->_userdefs, 0, term->_nuserdefs,
+ sizeof(*term->_userdefs)) == -1)
+ return -1;
+ for (num = 0; num < term->_nuserdefs; num++) {
+ ud = &term->_userdefs[num];
+ len = le16dec(cap);
+ cap += sizeof(uint16_t);
+ ud->id = cap;
+ cap += len;
+ ud->type = *cap++;
+ switch (ud->type) {
+ case 'f':
+ ud->flag = *cap++;
+ if (flags == 0 &&
+ !VALID_BOOLEAN(ud->flag))
+ ud->flag = 0;
+ ud->num = ABSENT_NUMERIC;
+ ud->str = ABSENT_STRING;
+ break;
+ case 'n':
+ ud->flag = ABSENT_BOOLEAN;
+ ud->num = (short)le16dec(cap);
+ if (flags == 0 &&
+ !VALID_NUMERIC(ud->num))
+ ud->num = ABSENT_NUMERIC;
+ ud->str = ABSENT_STRING;
+ cap += sizeof(uint16_t);
+ break;
+ case 's':
+ ud->flag = ABSENT_BOOLEAN;
+ ud->num = ABSENT_NUMERIC;
+ len = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (len > 0)
+ ud->str = cap;
+ else if (flags == 0)
+ ud->str = ABSENT_STRING;
+ else
+ ud->str = CANCELLED_STRING;
+ cap += len;
+ break;
+ default:
+ goto out;
+ }
+ }
+ } else {
+ term->_nuserdefs = 0;
+ if (term->_userdefs) {
+ free(term->_userdefs);
+ term->_userdefs = NULL;
+ }
+ }
+
+ return 1;
+out:
+ errno = EINVAL;
+ return -1;
+}
+
+static int
+_ti_checkname(const char *name, const char *termname, const char *termalias)
+{
+ const char *alias, *s;
+ size_t len, l;
+
+ /* Check terminal name matches. */
+ if (strcmp(termname, name) == 0)
+ return 1;
+
+ /* Check terminal aliases match. */
+ if (termalias == NULL)
+ return 0;
+
+ len = strlen(name);
+ alias = termalias;
+ while (*alias != '\0') {
+ s = strchr(alias, '|');
+ if (s == NULL)
+ l = strlen(alias);
+ else
+ l = (size_t)(s - alias);
+ if (len == l && memcmp(alias, name, l) == 0)
+ return 1;
+ if (s == NULL)
+ break;
+ alias = s + 1;
+ }
+
+ /* No match. */
+ return 0;
+}
+
+static int
+_ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags)
+{
+ struct cdbr *db;
+ const void *data;
+ const uint8_t *data8;
+ size_t len, klen;
+ int r;
+
+ r = snprintf(__ti_database, sizeof(__ti_database), "%s.cdb", path);
+ if (r < 0 || (size_t)r > sizeof(__ti_database)) {
+ db = NULL;
+ errno = ENOENT; /* To fall back to a non extension. */
+ } else
+ db = cdbr_open(__ti_database, CDBR_DEFAULT);
+
+ /* Target file *may* be a cdb file without the extension. */
+ if (db == NULL && errno == ENOENT) {
+ len = strlcpy(__ti_database, path, sizeof(__ti_database));
+ if (len < sizeof(__ti_database))
+ db = cdbr_open(__ti_database, CDBR_DEFAULT);
+ }
+ if (db == NULL)
+ return -1;
+
+ r = 0;
+ klen = strlen(name) + 1;
+ if (cdbr_find(db, name, klen, &data, &len) == -1)
+ goto out;
+ data8 = data;
+ if (len == 0)
+ goto out;
+
+ /* If the entry is an alias, load the indexed terminfo description. */
+ if (data8[0] == 2) {
+ if (cdbr_get(db, le32dec(data8 + 1), &data, &len))
+ goto out;
+ data8 = data;
+ }
+
+ r = _ti_readterm(term, data, len, flags);
+ /* Ensure that this is the right terminfo description. */
+ if (r == 1)
+ r = _ti_checkname(name, term->name, term->_alias);
+ /* Remember the database we read. */
+ if (r == 1)
+ _ti_database = __ti_database;
+
+out:
+ cdbr_close(db);
+ return r;
+}
+
+static int
+_ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags)
+{
+ const char *p;
+ char pathbuf[PATH_MAX];
+ size_t l;
+ int r, e;
+
+ e = -1;
+ r = 0;
+ do {
+ for (p = path; *path != '\0' && *path != ':'; path++)
+ continue;
+ l = (size_t)(path - p);
+ if (l != 0 && l + 1 < sizeof(pathbuf)) {
+ memcpy(pathbuf, p, l);
+ pathbuf[l] = '\0';
+ r = _ti_dbgetterm(term, pathbuf, name, flags);
+ if (r == 1)
+ return 1;
+ if (r == 0)
+ e = 0;
+ }
+ } while (*path++ == ':');
+ return e;
+}
+
+static int
+_ti_findterm(TERMINAL *term, const char *name, int flags)
+{
+ int r;
+ char *c, *e;
+
+ _ti_database = NULL;
+ r = 0;
+
+ if ((e = getenv("TERMINFO")) != NULL && *e != '\0') {
+ if (e[0] == '/')
+ return _ti_dbgetterm(term, e, name, flags);
+ }
+
+ c = NULL;
+ if (e == NULL && (c = getenv("TERMCAP")) != NULL) {
+ if (*c != '\0' && *c != '/') {
+ c = strdup(c);
+ if (c != NULL) {
+ e = captoinfo(c);
+ free(c);
+ }
+ }
+ }
+
+ if (e != NULL) {
+ TIC *tic;
+
+ if (c == NULL)
+ e = strdup(e); /* So we don't destroy env */
+ if (e == NULL)
+ tic = NULL;
+ else {
+ tic = _ti_compile(e, TIC_WARNING |
+ TIC_ALIAS | TIC_DESCRIPTION | TIC_EXTRA);
+ free(e);
+ }
+ if (tic != NULL &&
+ _ti_checkname(name, tic->name, tic->alias) == 1)
+ {
+ uint8_t *f;
+ ssize_t len;
+
+ len = _ti_flatten(&f, tic);
+ if (len != -1) {
+ r = _ti_readterm(term, (char *)f, (size_t)len,
+ flags);
+ free(f);
+ }
+ }
+ _ti_freetic(tic);
+ if (r == 1) {
+ if (c == NULL)
+ _ti_database = "$TERMINFO";
+ else
+ _ti_database = "$TERMCAP";
+ return r;
+ }
+ }
+
+ if ((e = getenv("TERMINFO_DIRS")) != NULL)
+ return _ti_dbgettermp(term, e, name, flags);
+
+ if ((e = getenv("HOME")) != NULL) {
+ char homepath[PATH_MAX];
+
+ if (snprintf(homepath, sizeof(homepath), "%s/.terminfo", e) > 0)
+ r = _ti_dbgetterm(term, homepath, name, flags);
+ }
+ if (r != 1)
+ r = _ti_dbgettermp(term, _PATH_TERMINFO, name, flags);
+
+ return r;
+}
+
+int
+_ti_getterm(TERMINAL *term, const char *name, int flags)
+{
+ int r;
+ size_t i;
+ const struct compiled_term *t;
+
+ r = _ti_findterm(term, name, flags);
+ if (r == 1)
+ return r;
+
+ for (i = 0; i < __arraycount(compiled_terms); i++) {
+ t = &compiled_terms[i];
+ if (strcmp(name, t->name) == 0) {
+ r = _ti_readterm(term, t->cap, t->caplen, flags);
+ break;
+ }
+ }
+
+ return r;
+}
Index: contrib/mg/term_private.h
===================================================================
--- /dev/null
+++ contrib/mg/term_private.h
@@ -0,0 +1,163 @@
+/* $NetBSD: term_private.h,v 1.11 2013/01/24 10:41:28 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2010, 2013 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TERM_PRIVATE_H_
+#define _TERM_PRIVATE_H_
+
+/* This header should only be used by libterminfo, tic and infocmp. */
+
+/* The terminfo database structure is private to us,
+ * so it's documented here.
+ * terminfo defines the largest number as 32767 and the largest
+ * compiled entry as 4093 bytes long.
+ * Thus, we store all numbers as uint16_t, including string length.
+ * All strings are prefixed by length, including the null terminator.
+ * The largest string length we can handle is 65535 bytes,
+ * including the null terminator.
+ * The largest capability block we can handle is 65535 bytes.
+ * This means that we exceed the current terminfo defined limits.
+ *
+ * Version 1 capabilities are defined as:
+ * header byte (always 1)
+ * name
+ * description,
+ * cap length, num flags, index, char,
+ * cap length, num numbers, index, number,
+ * cap length, num strings, index, string,
+ * cap length, num undefined caps, name, type (char), flag, number, string
+ *
+ * Version 2 entries are aliases and defined as:
+ * header byte (always 2)
+ * 32bit id of the corresponding terminal in the file
+ * name
+ *
+ * The database itself is created using cdbw(3) and the numbers are
+ * always stored as little endian.
+ */
+
+#include <sys/types.h>
+
+#define _TERMINFO
+
+/* We use the same ncurses tic macros so that our data is identical
+ * when a caller uses the long name macros to access te terminfo data
+ * directly. */
+#define ABSENT_BOOLEAN ((signed char)-1) /* 255 */
+#define ABSENT_NUMERIC (-1)
+#define ABSENT_STRING (char *)0
+#define CANCELLED_BOOLEAN ((signed char)-2) /* 254 */
+#define CANCELLED_NUMERIC (-2)
+#define CANCELLED_STRING (char *)(-1)
+#define VALID_BOOLEAN(s) ((unsigned char)(s) <= 1) /* reject "-1" */
+#define VALID_NUMERIC(s) ((s) >= 0)
+#define VALID_STRING(s) ((s) != CANCELLED_STRING && (s) != ABSENT_STRING)
+
+typedef struct {
+ const char *id;
+ char type;
+ char flag;
+ short num;
+ const char *str;
+} TERMUSERDEF;
+
+typedef struct {
+ int fildes;
+ /* We need to expose these so that the macros work */
+ const char *name;
+ const char *desc;
+ signed char *flags;
+ short *nums;
+ const char **strs;
+ /* Storage area for terminfo data */
+ char *_area;
+ size_t _arealen;
+ size_t _nuserdefs;
+ TERMUSERDEF *_userdefs;
+ /* So we don't rely on the global ospeed */
+ short _ospeed;
+ /* Output buffer for tparm */
+ char *_buf;
+ size_t _buflen;
+ size_t _bufpos;
+ /* A-Z static variables for tparm */
+ long _snums[26];
+ /* aliases of the terminal, | separated */
+ const char *_alias;
+} TERMINAL;
+
+extern const char * _ti_database;
+
+ssize_t _ti_flagindex(const char *);
+ssize_t _ti_numindex(const char *);
+ssize_t _ti_strindex(const char *);
+const char * _ti_flagid(ssize_t);
+const char * _ti_numid(ssize_t);
+const char * _ti_strid(ssize_t);
+int _ti_getterm(TERMINAL *, const char *, int);
+void _ti_setospeed(TERMINAL *);
+
+/* libterminfo can compile terminfo strings too */
+#define TIC_WARNING (1 << 0)
+#define TIC_DESCRIPTION (1 << 1)
+#define TIC_ALIAS (1 << 2)
+#define TIC_COMMENT (1 << 3)
+#define TIC_EXTRA (1 << 4)
+
+#define UINT16_T_MAX 0xffff
+
+typedef struct {
+ char *buf;
+ size_t buflen;
+ size_t bufpos;
+ size_t entries;
+} TBUF;
+
+typedef struct {
+ char *name;
+ char *alias;
+ char *desc;
+ TBUF flags;
+ TBUF nums;
+ TBUF strs;
+ TBUF extras;
+} TIC;
+
+char *_ti_grow_tbuf(TBUF *, size_t);
+char *_ti_get_token(char **, char);
+char *_ti_find_cap(TBUF *, char, short);
+char *_ti_find_extra(TBUF *, const char *);
+size_t _ti_store_extra(TIC *, int, char *, char, char, short,
+ char *, size_t, int);
+TIC *_ti_compile(char *, int);
+ssize_t _ti_flatten(uint8_t **, const TIC *);
+void _ti_freetic(TIC *);
+
+#define TPARM_MAX 9 /* not likely to change */
+int _ti_parm_analyse(const char *, int *, int);
+#endif
Index: contrib/mg/termcap.c
===================================================================
--- /dev/null
+++ contrib/mg/termcap.c
@@ -0,0 +1,607 @@
+/* $NetBSD: termcap.c,v 1.22 2017/05/04 09:42:23 roy Exp $ */
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+#ifndef __arraycount
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+extern void mi_vector_hash(const void * __restrict, size_t, uint32_t,
+ uint32_t[3]);
+
+#include "termcap_map.c"
+#include "termcap_hash.c"
+
+char *UP;
+char *BC;
+
+/* ARGSUSED */
+int
+tgetent(__unused char *bp, const char *name)
+{
+ int errret;
+ static TERMINAL *last = NULL;
+
+ /* Free the old term */
+ if (cur_term != NULL) {
+ if (last != NULL && cur_term != last)
+ del_curterm(last);
+ last = cur_term;
+ }
+ errret = -1;
+ if (setupterm(name, STDOUT_FILENO, &errret) != 0)
+ return errret;
+
+ if (last == NULL)
+ last = cur_term;
+
+ if (pad_char != NULL)
+ PC = pad_char[0];
+ UP = __UNCONST(cursor_up);
+ BC = __UNCONST(cursor_left);
+ return 1;
+}
+
+int
+tgetflag(const char *id2)
+{
+ uint32_t ind;
+ size_t i;
+ TERMUSERDEF *ud;
+ const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
+
+ if (cur_term == NULL)
+ return 0;
+
+ ind = _t_flaghash((const unsigned char *)id, strlen(id));
+ if (ind < __arraycount(_ti_cap_flagids)) {
+ if (strcmp(id, _ti_cap_flagids[ind].id) == 0)
+ return cur_term->flags[_ti_cap_flagids[ind].ti];
+ }
+ for (i = 0; i < cur_term->_nuserdefs; i++) {
+ ud = &cur_term->_userdefs[i];
+ if (ud->type == 'f' && strcmp(ud->id, id) == 0)
+ return ud->flag;
+ }
+ return 0;
+}
+
+int
+tgetnum(const char *id2)
+{
+ uint32_t ind;
+ size_t i;
+ TERMUSERDEF *ud;
+ const TENTRY *te;
+ const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
+
+ if (cur_term == NULL)
+ return -1;
+
+ ind = _t_numhash((const unsigned char *)id, strlen(id));
+ if (ind < __arraycount(_ti_cap_numids)) {
+ te = &_ti_cap_numids[ind];
+ if (strcmp(id, te->id) == 0) {
+ if (!VALID_NUMERIC(cur_term->nums[te->ti]))
+ return ABSENT_NUMERIC;
+ return cur_term->nums[te->ti];
+ }
+ }
+ for (i = 0; i < cur_term->_nuserdefs; i++) {
+ ud = &cur_term->_userdefs[i];
+ if (ud->type == 'n' && strcmp(ud->id, id) == 0) {
+ if (!VALID_NUMERIC(ud->num))
+ return ABSENT_NUMERIC;
+ return ud->num;
+ }
+ }
+ return -1;
+}
+
+char *
+tgetstr(const char *id2, char **area)
+{
+ uint32_t ind;
+ size_t i;
+ TERMUSERDEF *ud;
+ const char *str;
+ const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' };
+
+ if (cur_term == NULL)
+ return NULL;
+
+ str = NULL;
+ ind = _t_strhash((const unsigned char *)id, strlen(id));
+ if (ind < __arraycount(_ti_cap_strids)) {
+ if (strcmp(id, _ti_cap_strids[ind].id) == 0) {
+ str = cur_term->strs[_ti_cap_strids[ind].ti];
+ if (str == NULL)
+ return NULL;
+ }
+ }
+ if (str != NULL)
+ for (i = 0; i < cur_term->_nuserdefs; i++) {
+ ud = &cur_term->_userdefs[i];
+ if (ud->type == 's' && strcmp(ud->id, id) == 0)
+ str = ud->str;
+ }
+
+ /* XXX: FXIXME
+ * We should fix sgr0(me) as it has a slightly different meaning
+ * for termcap. */
+
+ if (str != NULL && area != NULL && *area != NULL) {
+ char *s;
+ s = *area;
+ strcpy(*area, str);
+ *area += strlen(*area) + 1;
+ return s;
+ }
+
+ return __UNCONST(str);
+}
+
+char *
+tgoto(const char *cm, int destcol, int destline)
+{
+
+ return tiparm(cm, destline, destcol);
+}
+
+static const char *
+flagname(const char *key)
+{
+ uint32_t idx;
+
+ idx = _t_flaghash((const unsigned char *)key, strlen(key));
+ if (idx < __arraycount(_ti_cap_flagids) &&
+ strcmp(key, _ti_cap_flagids[idx].id) == 0)
+ return _ti_flagid(_ti_cap_flagids[idx].ti);
+ return key;
+}
+
+static const char *
+numname(const char *key)
+{
+ uint32_t idx;
+
+ idx = _t_numhash((const unsigned char *)key, strlen(key));
+ if (idx < __arraycount(_ti_cap_numids) &&
+ strcmp(key, _ti_cap_numids[idx].id) == 0)
+ return _ti_numid(_ti_cap_numids[idx].ti);
+ return key;
+}
+
+static const char *
+strname(const char *key)
+{
+ uint32_t idx;
+
+ idx = _t_strhash((const unsigned char *)key, strlen(key));
+ if (idx < __arraycount(_ti_cap_strids) &&
+ strcmp(key, _ti_cap_strids[idx].id) == 0)
+ return _ti_strid(_ti_cap_strids[idx].ti);
+
+ if (strcmp(key, "tc") == 0)
+ return "use";
+
+ return key;
+}
+
+/* Print a parameter if needed */
+static size_t
+printparam(char **dst, char p, bool *nop)
+{
+ if (*nop) {
+ *nop = false;
+ return 0;
+ }
+
+ *(*dst)++ = '%';
+ *(*dst)++ = 'p';
+ *(*dst)++ = '0' + p;
+ return 3;
+}
+
+/* Convert a termcap character into terminfo equivalents */
+static size_t
+printchar(char **dst, const char **src)
+{
+ char v;
+ size_t l;
+
+ l = 4;
+ v = *++(*src);
+ if (v == '\\') {
+ v = *++(*src);
+ switch (v) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ v = 0;
+ while (isdigit((unsigned char) **src))
+ v = 8 * v + (*(*src)++ - '0');
+ (*src)--;
+ break;
+ case '\0':
+ v = '\\';
+ break;
+ }
+ } else if (v == '^')
+ v = *++(*src) & 0x1f;
+ *(*dst)++ = '%';
+ if (isgraph((unsigned char )v) &&
+ v != ',' && v != '\'' && v != '\\' && v != ':')
+ {
+ *(*dst)++ = '\'';
+ *(*dst)++ = v;
+ *(*dst)++ = '\'';
+ } else {
+ *(*dst)++ = '{';
+ if (v > 99) {
+ *(*dst)++ = '0'+ v / 100;
+ l++;
+ }
+ if (v > 9) {
+ *(*dst)++ = '0' + ((int) (v / 10)) % 10;
+ l++;
+ }
+ *(*dst)++ = '0' + v % 10;
+ *(*dst)++ = '}';
+ }
+ return l;
+}
+
+/* Convert termcap commands into terminfo commands */
+static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+";
+static const char fmtD[] = "%p0%p0%{2}%*%-";
+static const char fmtIf[] = "%p0%p0%?";
+static const char fmtThen[] = "%>%t";
+static const char fmtElse[] = "%+%;";
+
+static char *
+strval(const char *val)
+{
+ char *info, *ip, c, p;
+ const char *ps, *pe;
+ bool nop;
+ size_t len, l;
+
+ len = 1024; /* no single string should be bigger */
+ info = ip = malloc(len);
+ if (info == NULL)
+ return 0;
+
+ /* Move the = */
+ *ip++ = *val++;
+
+ /* Set ps and pe to point to the start and end of the padding */
+ if (isdigit((unsigned char)*val)) {
+ for (ps = pe = val;
+ isdigit((unsigned char)*val) || *val == '.';
+ val++)
+ pe++;
+ if (*val == '*') {
+ val++;
+ pe++;
+ }
+ } else
+ ps = pe = NULL;
+
+ nop = false;
+ l = 0;
+ p = 1;
+ for (; *val != '\0'; val++) {
+ if (l + 2 > len)
+ goto elen;
+ if (*val != '%') {
+ if (*val == ',') {
+ if (l + 3 > len)
+ goto elen;
+ *ip++ = '\\';
+ l++;
+ }
+ *ip++ = *val;
+ l++;
+ continue;
+ }
+ switch (c = *++(val)) {
+ case 'B':
+ if (l + sizeof(fmtB) > len)
+ goto elen;
+ memcpy(ip, fmtB, sizeof(fmtB) - 1);
+ /* Replace the embedded parameters with real ones */
+ ip[2] += p;
+ ip[19] += p;
+ ip += sizeof(fmtB) - 1;
+ l += sizeof(fmtB) - 1;
+ nop = true;
+ continue;
+ case 'D':
+ if (l + sizeof(fmtD) > len)
+ goto elen;
+ memcpy(ip, fmtD, sizeof(fmtD) - 1);
+ /* Replace the embedded parameters with real ones */
+ ip[2] += p;
+ ip[5] += p;
+ ip += sizeof(fmtD) - 1;
+ l += sizeof(fmtD) - 1;
+ nop = true;
+ continue;
+ case 'r':
+ /* non op as switched below */
+ break;
+ case '2': /* FALLTHROUGH */
+ case '3': /* FALLTHROUGH */
+ case 'd':
+ if (l + 7 > len)
+ goto elen;
+ l += printparam(&ip, p, &nop);
+ *ip++ = '%';
+ if (c != 'd') {
+ *ip++ = c;
+ l++;
+ }
+ *ip++ = 'd';
+ l += 2;
+ break;
+ case '+':
+ if (l + 13 > len)
+ goto elen;
+ l += printparam(&ip, p, &nop);
+ l += printchar(&ip, &val);
+ *ip++ = '%';
+ *ip++ = c;
+ *ip++ = '%';
+ *ip++ = 'c';
+ l += 7;
+ break;
+ case '>':
+ if (l + sizeof(fmtIf) + sizeof(fmtThen) +
+ sizeof(fmtElse) + (6 * 2) > len)
+ goto elen;
+
+ memcpy(ip, fmtIf, sizeof(fmtIf) - 1);
+ /* Replace the embedded parameters with real ones */
+ ip[2] += p;
+ ip[5] += p;
+ ip += sizeof(fmtIf) - 1;
+ l += sizeof(fmtIf) - 1;
+ l += printchar(&ip, &val);
+ memcpy(ip, fmtThen, sizeof(fmtThen) - 1);
+ ip += sizeof(fmtThen) - 1;
+ l += sizeof(fmtThen) - 1;
+ l += printchar(&ip, &val);
+ memcpy(ip, fmtElse, sizeof(fmtElse) - 1);
+ ip += sizeof(fmtElse) - 1;
+ l += sizeof(fmtElse) - 1;
+ l += 16;
+ nop = true;
+ continue;
+ case '.':
+ if (l + 6 > len)
+ goto elen;
+ l += printparam(&ip, p, &nop);
+ *ip++ = '%';
+ *ip++ = 'c';
+ l += 2;
+ break;
+ default:
+ /* Hope it matches a terminfo command. */
+ *ip++ = '%';
+ *ip++ = c;
+ l += 2;
+ if (c == 'i')
+ continue;
+ break;
+ }
+ /* Swap p1 and p2 */
+ p = 3 - p;
+ }
+
+ /* \E\ is valid termcap.
+ * We need to escape the final \ for terminfo. */
+ if (l > 2 && info[l - 1] == '\\' &&
+ (info[l - 2] != '\\' && info[l - 2] != '^'))
+ {
+ if (l + 1 > len)
+ goto elen;
+ *ip++ = '\\';
+ }
+
+ /* Add our padding at the end. */
+ if (ps != NULL) {
+ size_t n = (size_t)(pe - ps);
+ if (l + n + 4 > len)
+ goto elen;
+ *ip++ = '$';
+ *ip++ = '<';
+ strncpy(ip, ps, n);
+ ip += n;
+ *ip++ = '/';
+ *ip++ = '>';
+ }
+
+ *ip = '\0';
+ return info;
+
+elen:
+ free(info);
+ errno = ENOMEM;
+ return NULL;
+}
+
+typedef struct {
+ const char *name;
+ const char *cap;
+} DEF_INFO;
+
+static DEF_INFO def_infos[] = {
+ { "bel", "^G" },
+ { "cr", "^M" },
+ { "cud1", "^J" },
+ { "ht", "^I" },
+ { "ind", "^J" },
+ { "kbs", "^H" },
+ { "kcub1", "^H" },
+ { "kcud1", "^J" },
+ { "nel", "^M^J" }
+};
+
+char *
+captoinfo(char *cap)
+{
+ char *info, *ip, *token, *val, *p, tok[3];
+ const char *name;
+ size_t len, lp, nl, vl, rl;
+ int defs[__arraycount(def_infos)], fv;
+
+ len = strlen(cap) * 2;
+ len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */
+ info = ip = malloc(len);
+ if (info == NULL)
+ return NULL;
+
+ memset(defs, 0, sizeof(defs));
+ lp = 0;
+ tok[2] = '\0';
+ for (token = _ti_get_token(&cap, ':');
+ token != NULL;
+ token = _ti_get_token(&cap, ':'))
+ {
+ if (token[0] == '\0')
+ continue;
+ name = token;
+ val = p = NULL;
+ fv = nl = 0;
+ if (token[1] != '\0') {
+ tok[0] = token[0];
+ tok[1] = token[1];
+ nl = 1;
+ if (token[2] == '\0') {
+ name = flagname(tok);
+ val = NULL;
+ } else if (token[2] == '#') {
+ name = numname(tok);
+ val = token + 2;
+ } else if (token[2] == '=') {
+ name = strname(tok);
+ val = strval(token + 2);
+ fv = 1;
+ } else
+ nl = 0;
+ }
+ /* If not matched we may need to convert padding still. */
+ if (nl == 0) {
+ p = strchr(name, '=');
+ if (p != NULL) {
+ val = strval(p);
+ *p = '\0';
+ fv = 1;
+ }
+ }
+
+ /* See if this sets a default. */
+ for (nl = 0; nl < __arraycount(def_infos); nl++) {
+ if (strcmp(name, def_infos[nl].name) == 0) {
+ defs[nl] = 1;
+ break;
+ }
+ }
+
+ nl = strlen(name);
+ if (val == NULL)
+ vl = 0;
+ else
+ vl = strlen(val);
+ rl = nl + vl + 3; /* , \0 */
+
+ if (lp + rl > len) {
+ if (rl < 256)
+ len += 256;
+ else
+ len += rl;
+ p = realloc(info, len);
+ if (p == NULL) {
+ if (fv == 1)
+ free(val);
+ return NULL;
+ }
+ info = p;
+ }
+
+ if (ip != info) {
+ *ip++ = ',';
+ *ip++ = ' ';
+ }
+
+ strcpy(ip, name);
+ ip += nl;
+ if (val != NULL) {
+ strcpy(ip, val);
+ ip += vl;
+ if (fv == 1)
+ free(val);
+ }
+ }
+
+ /* Add any defaults not set above. */
+ for (nl = 0; nl < __arraycount(def_infos); nl++) {
+ if (defs[nl] == 0) {
+ *ip++ = ',';
+ *ip++ = ' ';
+ strcpy(ip, def_infos[nl].name);
+ ip += strlen(def_infos[nl].name);
+ *ip++ = '=';
+ strcpy(ip, def_infos[nl].cap);
+ ip += strlen(def_infos[nl].cap);
+ }
+ }
+
+ *ip = '\0';
+ return info;
+}
+
Index: contrib/mg/termcap_hash.c
===================================================================
--- /dev/null
+++ contrib/mg/termcap_hash.c
@@ -0,0 +1,158 @@
+/* DO NOT EDIT
+ * Automatically generated from termcap.c */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "term_private.h"
+
+static uint32_t
+_t_flaghash(const void * __restrict key, size_t keylen)
+{
+ static const uint8_t g[75] = {
+ 0x0b, 0x17, 0x00, 0x06, 0x0b, 0x00, 0x23, 0x03, 0x0f, 0x1a,
+ 0x13, 0x00, 0x04, 0x24, 0x1f, 0x1a, 0x00, 0x00, 0x1e, 0x00,
+ 0x18, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x1e, 0x0c, 0x00,
+ 0x00, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x07,
+ 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1b,
+ 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x12, 0x1b,
+ 0x00, 0x16, 0x12, 0x00, 0x03,
+ };
+ uint32_t h[3];
+
+ mi_vector_hash(key, keylen, 0x00000000U, h);
+ return (g[h[0] % 75] + g[h[1] % 75]) % 37;
+}
+
+#include <stdlib.h>
+
+static uint32_t
+_t_numhash(const void * __restrict key, size_t keylen)
+{
+ static const uint8_t g[67] = {
+ 0x00, 0x1e, 0x01, 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x07, 0x00, 0x0f,
+ 0x03, 0x1c, 0x0c, 0x00, 0x08, 0x11, 0x10, 0x00, 0x0c, 0x0d,
+ 0x06, 0x00, 0x06, 0x1b, 0x1d, 0x00, 0x00, 0x00, 0x18, 0x1a,
+ 0x00, 0x04, 0x00, 0x0e, 0x20, 0x1b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0a, 0x1e, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x19, 0x1e, 0x00, 0x02, 0x00, 0x05, 0x00,
+ };
+ uint32_t h[3];
+
+ mi_vector_hash(key, keylen, 0x00000005U, h);
+ return (g[h[0] % 67] + g[h[1] % 67]) % 33;
+}
+
+#include <stdlib.h>
+
+static uint32_t
+_t_strhash(const void * __restrict key, size_t keylen)
+{
+ static const uint16_t g[789] = {
+ 0x0172, 0x0088, 0x0000, 0x013f, 0x00a0, 0x0037, 0x0151, 0x0000,
+ 0x0151, 0x0000, 0x0000, 0x0000, 0x0000, 0x006a, 0x0000, 0x0082,
+ 0x0000, 0x0000, 0x00d4, 0x0025, 0x015e, 0x0000, 0x002e, 0x00b1,
+ 0x0000, 0x0122, 0x00b1, 0x0000, 0x017f, 0x0141, 0x0172, 0x0000,
+ 0x0000, 0x000a, 0x010c, 0x0185, 0x014f, 0x00c1, 0x0011, 0x0000,
+ 0x0000, 0x012e, 0x0000, 0x0045, 0x0067, 0x00c4, 0x000d, 0x012e,
+ 0x0000, 0x0004, 0x0000, 0x005f, 0x016c, 0x00df, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x017d, 0x0099, 0x014a, 0x0000, 0x0000, 0x0032,
+ 0x011e, 0x000a, 0x0000, 0x0037, 0x0057, 0x0065, 0x00d4, 0x00d5,
+ 0x0000, 0x00c2, 0x00f0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x00d6, 0x016d, 0x0000, 0x0086, 0x0000, 0x0114,
+ 0x00ac, 0x0000, 0x0000, 0x00d6, 0x0000, 0x0000, 0x0000, 0x008a,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x013c, 0x0000, 0x0042, 0x0000,
+ 0x002e, 0x0000, 0x0000, 0x013e, 0x0032, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0130, 0x0000, 0x0000, 0x013c, 0x0073,
+ 0x0000, 0x0095, 0x0000, 0x0000, 0x0000, 0x011e, 0x0012, 0x0000,
+ 0x0067, 0x002b, 0x0000, 0x0000, 0x0000, 0x00c7, 0x0109, 0x0006,
+ 0x0000, 0x00ff, 0x00d6, 0x0000, 0x0051, 0x0187, 0x0000, 0x0000,
+ 0x0114, 0x0000, 0x00b5, 0x00c9, 0x000d, 0x0000, 0x0000, 0x0000,
+ 0x017c, 0x00b5, 0x0171, 0x0000, 0x003b, 0x0000, 0x0000, 0x0089,
+ 0x016f, 0x0000, 0x0000, 0x009a, 0x002f, 0x007c, 0x013e, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x011b, 0x0000, 0x0000, 0x00d4, 0x0087,
+ 0x0000, 0x0000, 0x0000, 0x0015, 0x00f3, 0x0000, 0x00c2, 0x0092,
+ 0x0157, 0x0000, 0x0000, 0x0000, 0x0161, 0x0000, 0x0000, 0x0033,
+ 0x0000, 0x00ff, 0x0156, 0x0000, 0x001c, 0x0000, 0x0000, 0x0139,
+ 0x0156, 0x0000, 0x0098, 0x0000, 0x0069, 0x0093, 0x0000, 0x0000,
+ 0x012b, 0x0000, 0x0000, 0x0168, 0x015b, 0x0075, 0x0000, 0x00c1,
+ 0x00dd, 0x0000, 0x0038, 0x0000, 0x00e6, 0x0000, 0x0000, 0x0165,
+ 0x00a7, 0x0000, 0x0022, 0x0000, 0x00a9, 0x0000, 0x006d, 0x0000,
+ 0x0000, 0x0000, 0x013a, 0x0000, 0x004d, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0103, 0x0006, 0x006b, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0106, 0x0000, 0x0000, 0x0000, 0x00aa, 0x013a, 0x0090, 0x00d8,
+ 0x0000, 0x0000, 0x016b, 0x0000, 0x002b, 0x0000, 0x0000, 0x0183,
+ 0x0174, 0x0000, 0x0120, 0x0000, 0x0167, 0x00b0, 0x00b0, 0x0000,
+ 0x0111, 0x0000, 0x0000, 0x015a, 0x0177, 0x0000, 0x0000, 0x0000,
+ 0x0102, 0x0158, 0x0000, 0x0098, 0x0028, 0x00fd, 0x0168, 0x0032,
+ 0x0000, 0x0000, 0x0000, 0x0029, 0x00cd, 0x0000, 0x0000, 0x005c,
+ 0x0041, 0x0000, 0x0000, 0x015f, 0x0000, 0x0034, 0x017c, 0x00fc,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a4, 0x0176,
+ 0x0000, 0x0000, 0x0140, 0x0000, 0x0164, 0x009c, 0x010e, 0x0000,
+ 0x0000, 0x0000, 0x00a2, 0x00ad, 0x0000, 0x0170, 0x0000, 0x00f6,
+ 0x00a3, 0x0000, 0x017f, 0x0053, 0x0183, 0x0000, 0x0000, 0x00e8,
+ 0x0188, 0x0103, 0x0000, 0x0080, 0x005c, 0x016e, 0x0027, 0x0000,
+ 0x00b7, 0x0000, 0x0000, 0x0000, 0x0150, 0x0078, 0x0000, 0x0000,
+ 0x00b7, 0x0000, 0x0000, 0x0038, 0x004b, 0x007b, 0x0000, 0x0000,
+ 0x00b6, 0x0103, 0x0000, 0x006b, 0x0083, 0x0000, 0x0013, 0x00f3,
+ 0x0000, 0x0031, 0x00bd, 0x0119, 0x005a, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x000a, 0x008a, 0x0000, 0x0000, 0x0000, 0x0010, 0x0000,
+ 0x0000, 0x0000, 0x0116, 0x0000, 0x00b8, 0x0156, 0x0000, 0x0054,
+ 0x0000, 0x0000, 0x0092, 0x0000, 0x0000, 0x0000, 0x0000, 0x008b,
+ 0x0172, 0x0000, 0x0000, 0x0188, 0x0000, 0x005d, 0x0000, 0x0000,
+ 0x003b, 0x0000, 0x0066, 0x00ff, 0x0000, 0x0000, 0x0000, 0x007c,
+ 0x0000, 0x00e0, 0x0000, 0x0000, 0x0151, 0x00cc, 0x007d, 0x0000,
+ 0x015b, 0x000c, 0x00e1, 0x0000, 0x0000, 0x006b, 0x0076, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010f, 0x00f2, 0x0000,
+ 0x012f, 0x0107, 0x00b7, 0x0000, 0x00d8, 0x0174, 0x00ea, 0x009d,
+ 0x006d, 0x0000, 0x0000, 0x00d0, 0x0046, 0x012f, 0x00bd, 0x0000,
+ 0x0120, 0x00ed, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0042,
+ 0x00e8, 0x0000, 0x0000, 0x00c7, 0x0136, 0x0000, 0x00e8, 0x0002,
+ 0x0000, 0x00ce, 0x0000, 0x0000, 0x0000, 0x017a, 0x0004, 0x00f7,
+ 0x00a2, 0x0000, 0x00da, 0x0046, 0x0037, 0x007f, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0184, 0x0000, 0x0000, 0x012a, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x00e3, 0x00b7, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x013a, 0x0000, 0x00a6, 0x0000, 0x0065, 0x0000, 0x0000,
+ 0x0000, 0x00cb, 0x010f, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x003a, 0x0140, 0x0000, 0x0107, 0x0095, 0x00f2, 0x0000, 0x0000,
+ 0x005b, 0x0000, 0x015c, 0x012f, 0x0000, 0x0171, 0x0000, 0x001e,
+ 0x0000, 0x0000, 0x00be, 0x0000, 0x0000, 0x0113, 0x0000, 0x011e,
+ 0x0000, 0x000c, 0x0000, 0x00a0, 0x0000, 0x0147, 0x006b, 0x00cb,
+ 0x0025, 0x00f5, 0x0000, 0x0000, 0x0000, 0x00df, 0x00d9, 0x0000,
+ 0x00e9, 0x00ab, 0x00f3, 0x0000, 0x0000, 0x0000, 0x00eb, 0x0087,
+ 0x0000, 0x00cb, 0x0000, 0x0000, 0x0123, 0x0000, 0x0016, 0x0000,
+ 0x011a, 0x0000, 0x00c8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x011c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x0000,
+ 0x0091, 0x00fb, 0x0000, 0x0000, 0x0000, 0x0165, 0x00bc, 0x0024,
+ 0x015f, 0x0000, 0x0070, 0x00e1, 0x0000, 0x0000, 0x0175, 0x0048,
+ 0x00c6, 0x0000, 0x008f, 0x00e2, 0x0176, 0x003f, 0x0000, 0x0000,
+ 0x0000, 0x0035, 0x0000, 0x0011, 0x009e, 0x0000, 0x00d3, 0x003c,
+ 0x0000, 0x00b0, 0x003e, 0x0180, 0x0000, 0x0064, 0x00ef, 0x0000,
+ 0x0000, 0x0145, 0x0000, 0x0000, 0x00cf, 0x0000, 0x00e7, 0x0041,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x017c, 0x007e, 0x014d, 0x013b,
+ 0x0000, 0x0000, 0x0000, 0x0122, 0x0000, 0x0000, 0x0000, 0x0069,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x00d5, 0x0187, 0x0137, 0x0000,
+ 0x00f8, 0x0000, 0x0115, 0x008e, 0x0088, 0x0000, 0x0000, 0x0000,
+ 0x008f, 0x0000, 0x0000, 0x009d, 0x0068, 0x0139, 0x015b, 0x009c,
+ 0x0039, 0x0000, 0x00b0, 0x0000, 0x0110, 0x0000, 0x0059, 0x000b,
+ 0x0000, 0x00d2, 0x0082, 0x0000, 0x0000, 0x0000, 0x0000, 0x005a,
+ 0x013d, 0x0000, 0x0000, 0x004b, 0x0167, 0x0109, 0x0016, 0x0001,
+ 0x0000, 0x00e4, 0x0000, 0x013d, 0x0000, 0x0000, 0x004d, 0x0000,
+ 0x0000, 0x0000, 0x0029, 0x0000, 0x0000, 0x005d, 0x0000, 0x0001,
+ 0x0000, 0x008d, 0x0000, 0x015c, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x00c1, 0x0000, 0x0000, 0x0062, 0x0173, 0x0000, 0x0182,
+ 0x0000, 0x006c, 0x0000, 0x00f1, 0x0000, 0x00e9, 0x012a, 0x0145,
+ 0x0000, 0x0158, 0x000c, 0x015b, 0x0000, 0x0000, 0x0143, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fd,
+ 0x0142, 0x0070, 0x00e3, 0x0116, 0x0168, 0x0000, 0x0000, 0x00e5,
+ 0x00f2, 0x0139, 0x0000, 0x0000, 0x0010, 0x0160, 0x0125, 0x0000,
+ 0x00b3, 0x0158, 0x0062, 0x010a, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x016e, 0x0000, 0x0000, 0x0000,
+ };
+ uint32_t h[3];
+
+ mi_vector_hash(key, keylen, 0x00000002U, h);
+ return (g[h[0] % 789] + g[h[1] % 789]) % 394;
+}
Index: contrib/mg/termcap_map.c
===================================================================
--- /dev/null
+++ contrib/mg/termcap_map.c
@@ -0,0 +1,510 @@
+/* $NetBSD: termcap_map.c,v 1.3 2010/09/22 06:10:51 roy Exp $ */
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+typedef struct {
+ const char id[3];
+ int ti;
+} TENTRY;
+
+static const TENTRY _ti_cap_flagids[] = {
+ { "bw", TICODE_bw },
+ { "am", TICODE_am },
+ { "ut", TICODE_bce },
+ { "cc", TICODE_ccc },
+ { "xs", TICODE_xhp },
+ { "YA", TICODE_xhpa },
+ { "YF", TICODE_cpix },
+ { "YB", TICODE_crxm },
+ { "xt", TICODE_xt },
+ { "xn", TICODE_xenl },
+ { "eo", TICODE_eo },
+ { "gn", TICODE_gn },
+ { "hc", TICODE_hc },
+ { "HC", TICODE_chts },
+ { "km", TICODE_km },
+ { "YC", TICODE_daisy },
+ { "hs", TICODE_hs },
+ { "hl", TICODE_hls },
+ { "in", TICODE_in },
+ { "YG", TICODE_lpix },
+ { "da", TICODE_da },
+ { "db", TICODE_db },
+ { "mi", TICODE_mir },
+ { "ms", TICODE_msgr },
+ { "nx", TICODE_nxon },
+ { "xb", TICODE_xsb },
+ { "NP", TICODE_npc },
+ { "ND", TICODE_ndscr },
+ { "NR", TICODE_nrrmc },
+ { "os", TICODE_os } ,
+ { "5i", TICODE_mc5i },
+ { "YD", TICODE_xvpa },
+ { "YE", TICODE_sam },
+ { "es", TICODE_eslok },
+ { "hz", TICODE_hz },
+ { "ul", TICODE_ul },
+ { "xo", TICODE_xon }
+};
+
+static const TENTRY _ti_cap_numids[] = {
+ { "Yo", TICODE_bitwin },
+ { "Yp", TICODE_bitype },
+ { "Ya", TICODE_bufsz },
+ { "BT", TICODE_btns },
+ { "co", TICODE_cols },
+ { "Yc", TICODE_spinh },
+ { "Yb", TICODE_spinv },
+ { "it", TICODE_it },
+ { "lh", TICODE_lh },
+ { "lw", TICODE_lw },
+ { "li", TICODE_lines },
+ { "lm", TICODE_lm },
+ { "ma", TICODE_ma },
+ { "sg", TICODE_xmc },
+ { "Co", TICODE_colors },
+ { "Yd", TICODE_maddr },
+ { "Ye", TICODE_mjump },
+ { "pa", TICODE_pairs },
+ { "MW", TICODE_wnum },
+ { "Yf", TICODE_mcs },
+ { "Yg", TICODE_mls },
+ { "NC", TICODE_ncv },
+ { "Nl", TICODE_nlab },
+ { "Yh", TICODE_npins },
+ { "Yi", TICODE_orc },
+ { "Yj", TICODE_orl },
+ { "Yk", TICODE_orhi },
+ { "Yl", TICODE_orvi },
+ { "pb", TICODE_pb },
+ { "Ym", TICODE_cps },
+ { "vt", TICODE_vt },
+ { "Yn", TICODE_widcs },
+ { "ws", TICODE_wsl }
+};
+
+static const TENTRY _ti_cap_strids[] = {
+ { "ac", TICODE_acsc },
+ { "S8", TICODE_scesa },
+ { "bt", TICODE_cbt },
+ { "bl", TICODE_bel },
+ { "Yv", TICODE_bicr },
+ { "Zz", TICODE_binel },
+ { "Xy", TICODE_birep },
+ { "cr", TICODE_cr },
+ { "ZA", TICODE_cpi },
+ { "ZB", TICODE_lpi },
+ { "ZC", TICODE_chr },
+ { "ZD", TICODE_cvr },
+ { "cs", TICODE_csr },
+ { "rP", TICODE_rmp },
+ { "Zy", TICODE_csnm },
+ { "ct", TICODE_tbc },
+ { "MC", TICODE_mgc },
+ { "cl", TICODE_clear },
+ { "cb", TICODE_el1 },
+ { "ce", TICODE_el },
+ { "cd", TICODE_ed },
+ { "ci", TICODE_csin },
+ { "Yw", TICODE_colornm },
+ { "ch", TICODE_hpa },
+ { "CC", TICODE_cmdch },
+ { "cm", TICODE_cup },
+ { "do", TICODE_cud1 },
+ { "ho", TICODE_home },
+ { "vi", TICODE_civis },
+ { "le", TICODE_cub1 },
+ { "CM", TICODE_mrcup },
+ { "ve", TICODE_cnorm },
+ { "nd", TICODE_cuf1 },
+ { "ll", TICODE_ll },
+ { "up", TICODE_cuu1 },
+ { "vs", TICODE_cvvis },
+ { "Yx", TICODE_defbi },
+ { "ZE", TICODE_defc },
+ { "dc", TICODE_dch1 },
+ { "dl", TICODE_dl1 },
+ { "dv", TICODE_devt },
+ { "DI", TICODE_dial },
+ { "ds", TICODE_dsl },
+ { "DK", TICODE_dclk },
+ { "S1", TICODE_dispc },
+ { "hd", TICODE_hd },
+ { "eA", TICODE_enacs },
+ { "Yy", TICODE_endbi },
+ { "as", TICODE_smacs },
+ { "SA", TICODE_smam },
+ { "mb", TICODE_blink },
+ { "md", TICODE_bold },
+ { "ti", TICODE_smcup },
+ { "dm", TICODE_smdc },
+ { "mh", TICODE_dim },
+ { "ZF", TICODE_swidm },
+ { "ZG", TICODE_sdrfq },
+ { "Xh", TICODE_ehhlm },
+ { "im", TICODE_smir },
+ { "ZH", TICODE_sitm },
+ { "Xl", TICODE_elhlm },
+ { "ZI", TICODE_slm },
+ { "Xo", TICODE_elohlm },
+ { "ZJ", TICODE_smicm },
+ { "ZK", TICODE_snlq },
+ { "ZL", TICODE_snrmq },
+ { "S2", TICODE_smpch },
+ { "mp", TICODE_prot },
+ { "mr", TICODE_rev },
+ { "Xr", TICODE_erhlm },
+ { "S4", TICODE_smsc },
+ { "mk", TICODE_invis },
+ { "ZM", TICODE_sshm },
+ { "so", TICODE_smso },
+ { "ZN", TICODE_ssubm },
+ { "ZO", TICODE_ssupm },
+ { "Xt", TICODE_ethlm },
+ { "us", TICODE_smul },
+ { "ZP", TICODE_sum },
+ { "Xv", TICODE_evhlm },
+ { "SX", TICODE_smxon },
+ { "ec", TICODE_ech },
+ { "ae", TICODE_rmacs },
+ { "RA", TICODE_rmam },
+ { "me", TICODE_sgr0 },
+ { "te", TICODE_rmcup },
+ { "ed", TICODE_rmdc },
+ { "ZQ", TICODE_rwidm },
+ { "ei", TICODE_rmir },
+ { "ZR", TICODE_ritm },
+ { "ZS", TICODE_rlm },
+ { "ZT", TICODE_rmicm },
+ { "S3", TICODE_rmpch },
+ { "S5", TICODE_rmsc },
+ { "ZU", TICODE_rshm },
+ { "se", TICODE_rmso },
+ { "ZV", TICODE_rsubm },
+ { "ZW", TICODE_rsupm },
+ { "ue", TICODE_rmul },
+ { "ZX", TICODE_rum },
+ { "RX", TICODE_rmxon },
+ { "PA", TICODE_pause },
+ { "fh", TICODE_hook },
+ { "vb", TICODE_flash },
+ { "ff", TICODE_ff },
+ { "fs", TICODE_fsl },
+ { "Gm", TICODE_getm },
+ { "WG", TICODE_wingo },
+ { "HU", TICODE_hup },
+ { "i1", TICODE_is1 },
+ { "i2", TICODE_is2 },
+ { "i3", TICODE_is3 },
+ { "is", TICODE_is2 },
+ { "if", TICODE_if },
+ { "iP", TICODE_iprog },
+ { "Ic", TICODE_initc },
+ { "Ip", TICODE_initp },
+ { "ic", TICODE_ich1 },
+ { "al", TICODE_il1 },
+ { "ip", TICODE_ip },
+ { "K1", TICODE_ka1 },
+ { "K3", TICODE_ka3 },
+ { "K2", TICODE_kb2 },
+ { "kb", TICODE_kbs },
+ { "kB", TICODE_kcbt },
+ { "K4", TICODE_kc1 },
+ { "K5", TICODE_kc3 },
+ { "ka", TICODE_ktbc },
+ { "kC", TICODE_kclr },
+ { "kt", TICODE_kctab },
+ { "kD", TICODE_kdch1 },
+ { "kL", TICODE_kdl1 },
+ { "kd", TICODE_kcud1 },
+ { "kM", TICODE_krmir },
+ { "kE", TICODE_kel },
+ { "kS", TICODE_ked },
+ { "k0", TICODE_kf0 },
+ { "k1", TICODE_kf1 },
+ { "k2", TICODE_kf2 },
+ { "k3", TICODE_kf3 },
+ { "k4", TICODE_kf4 },
+ { "k5", TICODE_kf5 },
+ { "k6", TICODE_kf6 },
+ { "k7", TICODE_kf7 },
+ { "k8", TICODE_kf8 },
+ { "k9", TICODE_kf9 },
+ { "k;", TICODE_kf10 },
+ { "F1", TICODE_kf11 },
+ { "F2", TICODE_kf12 },
+ { "F3", TICODE_kf13 },
+ { "F4", TICODE_kf14 },
+ { "F5", TICODE_kf15 },
+ { "F6", TICODE_kf16 },
+ { "F7", TICODE_kf17 },
+ { "F8", TICODE_kf18 },
+ { "F9", TICODE_kf19 },
+ { "FA", TICODE_kf20 },
+ { "FB", TICODE_kf21 },
+ { "FC", TICODE_kf22 },
+ { "FD", TICODE_kf23 },
+ { "FE", TICODE_kf24 },
+ { "FF", TICODE_kf25 },
+ { "FG", TICODE_kf26 },
+ { "FH", TICODE_kf27 },
+ { "FI", TICODE_kf28 },
+ { "FJ", TICODE_kf29 },
+ { "FK", TICODE_kf30 },
+ { "FL", TICODE_kf31 },
+ { "FM", TICODE_kf32 },
+ { "FN", TICODE_kf33 },
+ { "FO", TICODE_kf34 },
+ { "FP", TICODE_kf35 },
+ { "FQ", TICODE_kf36 },
+ { "FR", TICODE_kf37 },
+ { "FS", TICODE_kf38 },
+ { "FT", TICODE_kf39 },
+ { "FU", TICODE_kf40 },
+ { "FV", TICODE_kf41 },
+ { "FW", TICODE_kf42 },
+ { "FX", TICODE_kf43 },
+ { "FY", TICODE_kf44 },
+ { "FZ", TICODE_kf45 },
+ { "Fa", TICODE_kf46 },
+ { "Fb", TICODE_kf47 },
+ { "Fc", TICODE_kf48 },
+ { "Fd", TICODE_kf49 },
+ { "Fe", TICODE_kf50 },
+ { "Ff", TICODE_kf51 },
+ { "Fg", TICODE_kf52 },
+ { "Fh", TICODE_kf53 },
+ { "Fi", TICODE_kf54 },
+ { "Fj", TICODE_kf55 },
+ { "Fk", TICODE_kf56 },
+ { "Fl", TICODE_kf57 },
+ { "Fm", TICODE_kf58 },
+ { "Fn", TICODE_kf59 },
+ { "Fo", TICODE_kf60 },
+ { "Fp", TICODE_kf61 },
+ { "Fq", TICODE_kf62 },
+ { "Fr", TICODE_kf63 },
+ { "%1", TICODE_khlp },
+ { "kh", TICODE_khome },
+ { "kI", TICODE_kich1 },
+ { "kA", TICODE_kil1 },
+ { "kl", TICODE_kcub1 },
+ { "kH", TICODE_kll },
+ { "%2", TICODE_kmrk },
+ { "%3", TICODE_kmsg },
+ { "Km", TICODE_kmous },
+ { "%4", TICODE_kmov },
+ { "%5", TICODE_knxt },
+ { "kN", TICODE_knp },
+ { "%6", TICODE_kopn },
+ { "%7", TICODE_kopt },
+ { "kP", TICODE_kpp },
+ { "%8", TICODE_kprv },
+ { "%9", TICODE_kprt },
+ { "%0", TICODE_krdo },
+ { "&1", TICODE_kref },
+ { "&2", TICODE_krfr },
+ { "&3", TICODE_krpl },
+ { "&4", TICODE_krst },
+ { "&5", TICODE_kres },
+ { "kr", TICODE_kcuf1 },
+ { "&6", TICODE_ksav },
+ { "&9", TICODE_kBEG },
+ { "&0", TICODE_kCAN },
+ { "*1", TICODE_kCMD },
+ { "*2", TICODE_kCPY },
+ { "*3", TICODE_kCRT },
+ { "*4", TICODE_kDC },
+ { "*5", TICODE_kDL },
+ { "*6", TICODE_kslt },
+ { "*7", TICODE_kEND },
+ { "*8", TICODE_kEOL },
+ { "*9", TICODE_kEXT },
+ { "kF", TICODE_kind },
+ { "*0", TICODE_kFND },
+ { "#1", TICODE_kHLP },
+ { "#2", TICODE_kHOM },
+ { "#3", TICODE_kIC },
+ { "#4", TICODE_kLFT },
+ { "%a", TICODE_kMSG },
+ { "%b", TICODE_kMOV },
+ { "%c", TICODE_kNXT },
+ { "%d", TICODE_kOPT },
+ { "%e", TICODE_kPRV },
+ { "%f", TICODE_kPRT },
+ { "kR", TICODE_kri },
+ { "%g", TICODE_kRDO },
+ { "%h", TICODE_kRPL },
+ { "%i", TICODE_kRIT },
+ { "%j", TICODE_kRES },
+ { "!1", TICODE_kSAV },
+ { "!2", TICODE_kSPD },
+ { "kT", TICODE_khts },
+ { "!3", TICODE_kUND },
+ { "&7", TICODE_kspd },
+ { "&8", TICODE_kund },
+ { "ku", TICODE_kcuu1 },
+ { "ke", TICODE_rmkx },
+ { "ks", TICODE_smkx },
+ { "l0", TICODE_lf0 },
+ { "l1", TICODE_lf1 },
+ { "l2", TICODE_lf2 },
+ { "l3", TICODE_lf3 },
+ { "l4", TICODE_lf4 },
+ { "l5", TICODE_lf5 },
+ { "l6", TICODE_lf6 },
+ { "l7", TICODE_lf7 },
+ { "l8", TICODE_lf8 },
+ { "l9", TICODE_lf9 },
+ { "la", TICODE_lf10 },
+ { "Lf", TICODE_fln },
+ { "LF", TICODE_rmln },
+ { "LO", TICODE_smln },
+ { "mo", TICODE_rmm },
+ { "mm", TICODE_smm },
+ { "ZY", TICODE_mhpa },
+ { "ZZ", TICODE_mcud1 },
+ { "Za", TICODE_mcub1 },
+ { "Zb", TICODE_mcuf1 },
+ { "Zc", TICODE_mvpa },
+ { "Zd", TICODE_mcuu1 },
+ { "Mi", TICODE_minfo },
+ { "nw", TICODE_nel },
+ { "Ze", TICODE_porder },
+ { "oc", TICODE_oc },
+ { "op", TICODE_op },
+ { "pc", TICODE_pad },
+ { "DC", TICODE_dch },
+ { "DL", TICODE_dl },
+ { "DO", TICODE_cud },
+ { "Zf", TICODE_mcud },
+ { "IC", TICODE_ich },
+ { "SF", TICODE_indn },
+ { "AL", TICODE_il },
+ { "LE", TICODE_cub },
+ { "Zg", TICODE_mcub },
+ { "RI", TICODE_cuf },
+ { "Zh", TICODE_mcuf },
+ { "SR", TICODE_rin },
+ { "UP", TICODE_cuu },
+ { "Zi", TICODE_mcuu },
+ { "S6", TICODE_pctrm },
+ { "pk", TICODE_pfkey },
+ { "pl", TICODE_pfloc },
+ { "xl", TICODE_pfxl },
+ { "px", TICODE_pfx },
+ { "pn", TICODE_pln },
+ { "ps", TICODE_mc0 },
+ { "pO", TICODE_mc5p },
+ { "pf", TICODE_mc4 },
+ { "po", TICODE_mc5 },
+ { "PU", TICODE_pulse },
+ { "QD", TICODE_qdial },
+ { "RC", TICODE_rmclk },
+ { "rp", TICODE_rep },
+ { "RF", TICODE_rfi },
+ { "RQ", TICODE_reqmp },
+ { "r1", TICODE_rs1 },
+ { "r2", TICODE_rs2 },
+ { "r3", TICODE_rs3 },
+ { "rf", TICODE_rf },
+ { "rc", TICODE_rc },
+ { "cv", TICODE_vpa },
+ { "sc", TICODE_sc },
+ { "S7", TICODE_scesc },
+ { "sf", TICODE_ind },
+ { "sr", TICODE_ri },
+ { "Zj", TICODE_scs },
+ { "s0", TICODE_s0ds },
+ { "s1", TICODE_s1ds },
+ { "s2", TICODE_s2ds },
+ { "s3", TICODE_s3ds },
+ { "sA", TICODE_sgr1 },
+ { "AB", TICODE_setab },
+ { "AF", TICODE_setaf },
+ { "sa", TICODE_sgr },
+ { "Sb", TICODE_setb },
+ { "Zk", TICODE_smgb },
+ { "Zl", TICODE_smgbp },
+ { "SC", TICODE_sclk },
+ { "Yz", TICODE_slines },
+ { "sL", TICODE_slength },
+ { "sp", TICODE_scp },
+ { "Sf", TICODE_setf },
+ { "ML", TICODE_smgl }, /* We should fallback to TICODE_smglr */
+ { "Zm", TICODE_smglp },
+ { "YZ", TICODE_slines },
+ { "YI", TICODE_slength },
+ { "MR", TICODE_smgr },
+ { "Zn", TICODE_smgrp },
+ { "st", TICODE_hts },
+ { "MT", TICODE_smgtb },
+ { "Zo", TICODE_smgt },
+ { "Zp", TICODE_smgtp },
+ { "wi", TICODE_wind },
+ { "Zq", TICODE_sbim },
+ { "Zr", TICODE_scsd },
+ { "Zs", TICODE_rbim },
+ { "Zt", TICODE_rcsd },
+ { "Zu", TICODE_subcs },
+ { "Zv", TICODE_supcs },
+ { "ta", TICODE_ht },
+ { "Zw", TICODE_docr },
+ { "ts", TICODE_tsl },
+ { "TO", TICODE_tone },
+ { "u0", TICODE_u0 },
+ { "u1", TICODE_u1 },
+ { "u2", TICODE_u2 },
+ { "u3", TICODE_u3 },
+ { "u4", TICODE_u4 },
+ { "u5", TICODE_u5 },
+ { "u6", TICODE_u6 },
+ { "u7", TICODE_u7 },
+ { "u8", TICODE_u8 },
+ { "u9", TICODE_u9 },
+ { "uc", TICODE_uc },
+ { "hu", TICODE_hu },
+ { "WA", TICODE_wait },
+ { "XF", TICODE_xoffc },
+ { "XN", TICODE_xonc },
+ { "Zx", TICODE_zerom },
+
+ /* NetBSD extensions */
+ { "@0", TICODE_kfnd },
+ { "@1", TICODE_kbeg },
+ { "@2", TICODE_kcan },
+ { "@3", TICODE_kclo },
+ { "@4", TICODE_kcmd },
+ { "@5", TICODE_kcpy },
+ { "@6", TICODE_kcrt },
+ { "@7", TICODE_kend },
+ { "@8", TICODE_kent },
+ { "@9", TICODE_kext },
+};
Index: contrib/mg/terminfo_term.h
===================================================================
--- /dev/null
+++ contrib/mg/terminfo_term.h
@@ -0,0 +1,2005 @@
+/* $NetBSD: term.h,v 1.22 2017/03/23 00:39:06 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2010, 2011, 2013 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TERM_H_
+#define _TERM_H_
+
+#ifndef ERR
+#define ERR (-1) /* Error return */
+#define OK (0) /* Success return */
+#endif
+
+/* Define available terminfo flags */
+enum TIFLAGS {
+ TICODE_bw,
+ TICODE_am,
+ TICODE_bce,
+ TICODE_ccc,
+ TICODE_xhp,
+ TICODE_xhpa,
+ TICODE_cpix,
+ TICODE_crxm,
+ TICODE_xt,
+ TICODE_xenl,
+ TICODE_eo,
+ TICODE_gn,
+ TICODE_hc,
+ TICODE_chts,
+ TICODE_km,
+ TICODE_daisy,
+ TICODE_hs,
+ TICODE_hls,
+ TICODE_in,
+ TICODE_lpix,
+ TICODE_da,
+ TICODE_db,
+ TICODE_mir,
+ TICODE_msgr,
+ TICODE_nxon,
+ TICODE_xsb,
+ TICODE_npc,
+ TICODE_ndscr,
+ TICODE_nrrmc,
+ TICODE_os,
+ TICODE_mc5i,
+ TICODE_xvpa,
+ TICODE_sam,
+ TICODE_eslok,
+ TICODE_hz,
+ TICODE_ul,
+ TICODE_xon
+};
+#define TIFLAGMAX TICODE_xon
+
+#define t_auto_left_margin(t) (t)->flags[TICODE_bw]
+#define t_auto_right_margin(t) (t)->flags[TICODE_am]
+#define t_back_color_erase(t) (t)->flags[TICODE_bce]
+#define t_can_change(t) (t)->flags[TICODE_ccc]
+#define t_ceol_standout_glitch(t) (t)->flags[TICODE_xhp]
+#define t_col_addr_glitch(t) (t)->flags[TICODE_xhpa]
+#define t_cpi_changes_res(t) (t)->flags[TICODE_cpix]
+#define t_cr_cancels_micro_mode(t) (t)->flags[TICODE_crxm]
+#define t_dest_tabs_magic_smso(t) (t)->flags[TICODE_xt]
+#define t_eat_newline_glitch(t) (t)->flags[TICODE_xenl]
+#define t_erase_overstrike(t) (t)->flags[TICODE_eo]
+#define t_generic_type(t) (t)->flags[TICODE_gn]
+#define t_hard_copy(t) (t)->flags[TICODE_hc]
+#define t_hard_cursor(t) (t)->flags[TICODE_chts]
+#define t_has_meta_key(t) (t)->flags[TICODE_km]
+#define t_has_print_wheel(t) (t)->flags[TICODE_daisy]
+#define t_has_status_line(t) (t)->flags[TICODE_hs]
+#define t_hue_light_saturation(t) (t)->flags[TICODE_hls]
+#define t_insert_null_glitch(t) (t)->flags[TICODE_in]
+#define t_lpi_changes_yes(t) (t)->flags[TICODE_lpix]
+#define t_memory_above(t) (t)->flags[TICODE_da]
+#define t_memory_below(t) (t)->flags[TICODE_db]
+#define t_move_insert_mode(t) (t)->flags[TICODE_mir]
+#define t_move_standout_mode(t) (t)->flags[TICODE_msgr]
+#define t_needs_xon_xoff(t) (t)->flags[TICODE_nxon]
+#define t_no_esc_ctlc(t) (t)->flags[TICODE_xsb]
+#define t_no_pad_char(t) (t)->flags[TICODE_npc]
+#define t_non_dest_scroll_region(t) (t)->flags[TICODE_ndscr]
+#define t_non_rev_rmcup(t) (t)->flags[TICODE_nrrmc]
+#define t_over_strike(t) (t)->flags[TICODE_os]
+#define t_prtr_silent(t) (t)->flags[TICODE_mc5i]
+#define t_row_addr_glitch(t) (t)->flags[TICODE_xvpa]
+#define t_semi_auto_right_margin(t) (t)->flags[TICODE_sam]
+#define t_status_line_esc_ok(t) (t)->flags[TICODE_eslok]
+#define t_tilde_glitch(t) (t)->flags[TICODE_hz]
+#define t_transparent_underline(t) (t)->flags[TICODE_ul]
+#define t_xon_xoff(t) (t)->flags[TICODE_xon]
+
+#define auto_left_margin t_auto_left_margin(cur_term)
+#define auto_right_margin t_auto_right_margin(cur_term)
+#define back_color_erase t_back_color_erase(cur_term)
+#define can_change t_can_change(cur_term)
+#define ceol_standout_glitch t_ceol_standout_glitch(cur_term)
+#define col_addr_glitch t_col_addr_glitch(cur_term)
+#define cpi_changes_res t_cpi_changes_res(cur_term)
+#define cr_cancels_micro_mode t_cr_cancels_micro_mode(cur_term)
+#define dest_tabs_magic_smso t_dest_tabs_magic_smso(cur_term)
+#define eat_newline_glitch t_eat_newline_glitch(cur_term)
+#define erase_overstrike t_erase_overstrike(cur_term)
+#define generic_type t_generic_type(cur_term)
+#define hard_copy t_hard_copy(cur_term)
+#define hard_cursor t_hard_cursor(cur_term)
+#define has_meta_key t_has_meta_key(cur_term)
+#define has_print_wheel t_has_print_wheel(cur_term)
+#define has_status_line t_has_status_line(cur_term)
+#define hue_light_saturation t_hue_light_saturation(cur_term)
+#define insert_null_glitch t_insert_null_glitch(cur_term)
+#define lpi_changes_yes t_lpi_changes_yes(cur_term)
+#define memory_above t_memory_above(cur_term)
+#define memory_below t_memory_below(cur_term)
+#define move_insert_mode t_move_insert_mode(cur_term)
+#define move_standout_mode t_move_standout_mode(cur_term)
+#define needs_xon_xoff t_needs_xon_xoff(cur_term)
+#define no_esc_ctlc t_no_esc_ctlc(cur_term)
+#define no_pad_char t_no_pad_char(cur_term)
+#define non_dest_scroll_region t_non_dest_scroll_region(cur_term)
+#define non_rev_rmcup t_non_rev_rmcup(cur_term)
+#define over_strike t_over_strike(cur_term)
+#define prtr_silent t_prtr_silent(cur_term)
+#define row_addr_glitch t_row_addr_glitch(cur_term)
+#define semi_auto_right_margin t_semi_auto_right_margin(cur_term)
+#define status_line_esc_ok t_status_line_esc_ok(cur_term)
+#define tilde_glitch t_tilde_glitch(cur_term)
+#define transparent_underline t_transparent_underline(cur_term)
+#define xon_xoff t_xon_xoff(cur_term)
+
+/*
+ * BOOLEAN DESCRIPTIONS
+ *
+ * auto_left_margin: cub1 wraps from column 0 to last column
+ * auto_right_margin: Terminal has automatic margins
+ * back_color_erase: Screen erased with background colour
+ * can_change: Terminal can re-define existing colour
+ * ceol_standout_glitch: Standout not erased by overwriting (hp)
+ * col_addr_glitch: Only positive motion for hpa/mhba caps
+ * cpi_changes_res: Changing character pitch changes resolution
+ * cr_cancels_micro_mode: Using cr turns off micro mode
+ * dest_tabs_magic_smso: Destructive tabs, magic smso char (t1061)
+ * eat_newline_glitch: Newline ignored after 80 columns (Concept)
+ * erase_overstrike: Can erase overstrikes with a blank line
+ * generic_type: Generic line type (e.g. dialup, switch)
+ * hard_copy: Hardcopy terminal
+ * hard_cursor: Cursor is hard to see
+ * has_meta_key: Has a meta key (shift, sets parity bit)
+ * has_print_wheel: Printer needs operator to change character set
+ * has_status_line: Has extra "status line"
+ * hue_light_saturation: Terminal only uses HLS colour notion (Tektronix)
+ * insert_null_glitch: Insert mode distinguishes nulls
+ * lpi_changes_yes: Changing line pitch changes resolution
+ * memory_above: Display may be retained above the screen
+ * memory_below: Display may be retained below the screen
+ * move_insert_mode: Safe to move while in insert mode
+ * move_standout_mode: Safe to move in standout modes
+ * needs_xon_xoff: Padding won't work, xon/xoff required
+ * no_esc_ctlc: Beehive (f1=escape, f2=ctrl C)
+ * no_pad_char: Pad character doesn't exist
+ * non_dest_scroll_region: Scrolling region is nondestructive
+ * non_rev_rmcup: smcup does not reverse rmcup
+ * over_strike: Terminal overstrikes on hard-copy terminal
+ * prtr_silent: Printer won't echo on screen
+ * row_addr_glitch: Only positive motion for vpa/mvpa caps
+ * semi_auto_right_margin: Printing in last column causes cr
+ * status_line_esc_ok: Escape can be used on the status line
+ * tilde_glitch: Hazeltine; can't print tilde (~)
+ * transparent_underline: Underline character overstrikes
+ * xon_xoff: Terminal uses xon/xoff handshaking
+*/
+
+/* Define available terminfo numbers */
+enum TINUMS {
+ TICODE_bitwin,
+ TICODE_bitype,
+ TICODE_bufsz,
+ TICODE_btns,
+ TICODE_cols,
+ TICODE_spinh,
+ TICODE_spinv,
+ TICODE_it,
+ TICODE_lh,
+ TICODE_lw,
+ TICODE_lines,
+ TICODE_lm,
+ TICODE_ma,
+ TICODE_xmc,
+ TICODE_colors,
+ TICODE_maddr,
+ TICODE_mjump,
+ TICODE_pairs,
+ TICODE_wnum,
+ TICODE_mcs,
+ TICODE_mls,
+ TICODE_ncv,
+ TICODE_nlab,
+ TICODE_npins,
+ TICODE_orc,
+ TICODE_orl,
+ TICODE_orhi,
+ TICODE_orvi,
+ TICODE_pb,
+ TICODE_cps,
+ TICODE_vt,
+ TICODE_widcs,
+ TICODE_wsl
+};
+#define TINUMMAX TICODE_wsl
+
+#define t_bit_image_entwining(t) (t)->nums[TICODE_bitwin]
+#define t_bit_image_type(t) (t)->nums[TICODE_bitype]
+#define t_buffer_capacity(t) (t)->nums[TICODE_bufsz]
+#define t_buttons(t) (t)->nums[TICODE_btns]
+#define t_columns(t) (t)->nums[TICODE_cols]
+#define t_dot_horz_spacing(t) (t)->nums[TICODE_spinh]
+#define t_dot_vert_spacing(t) (t)->nums[TICODE_spinv]
+#define t_init_tabs(t) (t)->nums[TICODE_it]
+#define t_label_height(t) (t)->nums[TICODE_lh]
+#define t_label_width(t) (t)->nums[TICODE_lw]
+#define t_lines(t) (t)->nums[TICODE_lines]
+#define t_lines_of_memory(t) (t)->nums[TICODE_lm]
+#define t_max_attributes(t) (t)->nums[TICODE_ma]
+#define t_magic_cookie_glitch(t) (t)->nums[TICODE_xmc]
+#define t_max_colors(t) (t)->nums[TICODE_colors]
+#define t_max_micro_address(t) (t)->nums[TICODE_maddr]
+#define t_max_micro_jump(t) (t)->nums[TICODE_mjump]
+#define t_max_pairs(t) (t)->nums[TICODE_pairs]
+#define t_maximum_windows(t) (t)->nums[TICODE_wnum]
+#define t_micro_col_size(t) (t)->nums[TICODE_mcs]
+#define t_micro_line_size(t) (t)->nums[TICODE_mls]
+#define t_no_color_video(t) (t)->nums[TICODE_ncv]
+#define t_num_labels(t) (t)->nums[TICODE_nlab]
+#define t_number_of_pins(t) (t)->nums[TICODE_npins]
+#define t_output_res_char(t) (t)->nums[TICODE_orc]
+#define t_output_res_line(t) (t)->nums[TICODE_orl]
+#define t_output_res_horz_inch(t) (t)->nums[TICODE_orhi]
+#define t_output_res_vert_inch(t) (t)->nums[TICODE_orvi]
+#define t_padding_baud_rate(t) (t)->nums[TICODE_pb]
+#define t_print_rate(t) (t)->nums[TICODE_cps]
+#define t_virtual_terminal(t) (t)->nums[TICODE_vt]
+#define t_wide_char_size(t) (t)->nums[TICODE_widcs]
+#define t_width_status_line(t) (t)->nums[TICODE_wsl]
+
+#define bit_image_entwining t_bit_image_entwining(cur_term)
+#define bit_image_type t_bit_image_type(cur_term)
+#define buffer_capacity t_buffer_capacity(cur_term)
+#define buttons t_buttons(cur_term)
+#define columns t_columns(cur_term)
+#define dot_horz_spacing t_dot_horz_spacing(cur_term)
+#define dot_vert_spacing t_dot_vert_spacing(cur_term)
+#define init_tabs t_init_tabs(cur_term)
+#define label_height t_label_height(cur_term)
+#define label_width t_label_width(cur_term)
+#define lines t_lines(cur_term)
+#define lines_of_memory t_lines_of_memory(cur_term)
+#define max_attributes t_max_attributes(cur_term)
+#define magic_cookie_glitch t_magic_cookie_glitch(cur_term)
+#define max_colors t_max_colors(cur_term)
+#define max_micro_address t_max_micro_address(cur_term)
+#define max_micro_jump t_max_micro_jump(cur_term)
+#define max_pairs t_max_pairs(cur_term)
+#define maximum_windows t_maximum_windows(cur_term)
+#define micro_col_size t_micro_col_size(cur_term)
+#define micro_line_size t_micro_line_size(cur_term)
+#define no_color_video t_no_color_video(cur_term)
+#define num_labels t_num_labels(cur_term)
+#define number_of_pins t_number_of_pins(cur_term)
+#define output_res_char t_output_res_char(cur_term)
+#define output_res_line t_output_res_line(cur_term)
+#define output_res_horz_inch t_output_res_horz_inch(cur_term)
+#define output_res_vert_inch t_output_res_vert_inch(cur_term)
+#define padding_baud_rate t_padding_baud_rate(cur_term)
+#define print_rate t_print_rate(cur_term)
+#define virtual_terminal t_virtual_terminal(cur_term)
+#define wide_char_size t_wide_char_size(cur_term)
+#define width_status_line t_width_status_line(cur_term)
+
+/*
+ * NUMBER DESCRIPTIONS
+ *
+ * bit_image_entwining: Number of passes for each bit-map row
+ * bit_image_type: Type of bit image device
+ * buffer_capacity: Number of bytes buffered before printing
+ * buttons: Number of buttons on the mouse
+ * columns: Number of columns in a line
+ * dot_horz_spacing: Spacing of dots horizontally in dots per inch
+ * dot_vert_spacing: Spacing of pins vertically in pins per inch
+ * init_tabs: Tabs initially every #1 spaces
+ * label_height: Number of rows in each label
+ * label_width: Numbre of columns in each label
+ * lines: Number of lines on a screen or a page
+ * lines_of_memory: Lines of memory of > lines; 0 means varies
+ * max_attributes: Maximum combined video attributes terminal can display
+ * magic_cookie_glitch: Number of blank characters left by smso or rmso
+ * max_colors: Maximum number of colours on the screen
+ * max_micro_address: Maximum value in micro_..._addresss
+ * max_micro_jump: Maximum value in parm_..._micro
+ * max_pairs: Maximum number of colour-pairs on the screen
+ * maximum_windows: Maximum number of definable windows
+ * micro_col_size: Character step size when in micro mode
+ * micro_line_size: Line step size when in micro mode
+ * no_color_video: Video attributes that can't be used with colours
+ * num_labels: Number of labels on screen (start at 1)
+ * number_of_pins: Number of pins in print-head
+ * output_res_char: Horizontal resolution in units per character
+ * output_res_line: Vertical resolution in units per line
+ * output_res_horz_inch: Horizontal resolution in units per inch
+ * output_res_vert_inch: Vertical resolution in units per inch
+ * padding_baud_rate: Lowest baud rate where padding needed
+ * print_rate: Print rate in characters per second
+ * virtual_terminal: Virtual terminal number
+ * wide_char_size: Character step size when in double-wide mode
+ * width_status_line: Number of columns in status line
+ */
+
+/* Define available terminfo strings */
+enum TISTRS{
+ TICODE_acsc,
+ TICODE_scesa,
+ TICODE_cbt,
+ TICODE_bel,
+ TICODE_bicr,
+ TICODE_binel,
+ TICODE_birep,
+ TICODE_cr,
+ TICODE_cpi,
+ TICODE_lpi,
+ TICODE_chr,
+ TICODE_cvr,
+ TICODE_csr,
+ TICODE_rmp,
+ TICODE_csnm,
+ TICODE_tbc,
+ TICODE_mgc,
+ TICODE_clear,
+ TICODE_el1,
+ TICODE_el,
+ TICODE_ed,
+ TICODE_csin,
+ TICODE_colornm,
+ TICODE_hpa,
+ TICODE_cmdch,
+ TICODE_cwin,
+ TICODE_cup,
+ TICODE_cud1,
+ TICODE_home,
+ TICODE_civis,
+ TICODE_cub1,
+ TICODE_mrcup,
+ TICODE_cnorm,
+ TICODE_cuf1,
+ TICODE_ll,
+ TICODE_cuu1,
+ TICODE_cvvis,
+ TICODE_defbi,
+ TICODE_defc,
+ TICODE_dch1,
+ TICODE_dl1,
+ TICODE_devt,
+ TICODE_dial,
+ TICODE_dsl,
+ TICODE_dclk,
+ TICODE_dispc,
+ TICODE_hd,
+ TICODE_enacs,
+ TICODE_endbi,
+ TICODE_smacs,
+ TICODE_smam,
+ TICODE_blink,
+ TICODE_bold,
+ TICODE_smcup,
+ TICODE_smdc,
+ TICODE_dim,
+ TICODE_swidm,
+ TICODE_sdrfq,
+ TICODE_ehhlm,
+ TICODE_smir,
+ TICODE_sitm,
+ TICODE_elhlm,
+ TICODE_slm,
+ TICODE_elohlm,
+ TICODE_smicm,
+ TICODE_snlq,
+ TICODE_snrmq,
+ TICODE_smpch,
+ TICODE_prot,
+ TICODE_rev,
+ TICODE_erhlm,
+ TICODE_smsc,
+ TICODE_invis,
+ TICODE_sshm,
+ TICODE_smso,
+ TICODE_ssubm,
+ TICODE_ssupm,
+ TICODE_ethlm,
+ TICODE_smul,
+ TICODE_sum,
+ TICODE_evhlm,
+ TICODE_smxon,
+ TICODE_ech,
+ TICODE_rmacs,
+ TICODE_rmam,
+ TICODE_sgr0,
+ TICODE_rmcup,
+ TICODE_rmdc,
+ TICODE_rwidm,
+ TICODE_rmir,
+ TICODE_ritm,
+ TICODE_rlm,
+ TICODE_rmicm,
+ TICODE_rmpch,
+ TICODE_rmsc,
+ TICODE_rshm,
+ TICODE_rmso,
+ TICODE_rsubm,
+ TICODE_rsupm,
+ TICODE_rmul,
+ TICODE_rum,
+ TICODE_rmxon,
+ TICODE_pause,
+ TICODE_hook,
+ TICODE_flash,
+ TICODE_ff,
+ TICODE_fsl,
+ TICODE_getm,
+ TICODE_wingo,
+ TICODE_hup,
+ TICODE_is1,
+ TICODE_is2,
+ TICODE_is3,
+ TICODE_if,
+ TICODE_iprog,
+ TICODE_initc,
+ TICODE_initp,
+ TICODE_ich1,
+ TICODE_il1,
+ TICODE_ip,
+ TICODE_ka1,
+ TICODE_ka3,
+ TICODE_kb2,
+ TICODE_kbs,
+ TICODE_kbeg,
+ TICODE_kcbt,
+ TICODE_kc1,
+ TICODE_kc3,
+ TICODE_kcan,
+ TICODE_ktbc,
+ TICODE_kclr,
+ TICODE_kclo,
+ TICODE_kcmd,
+ TICODE_kcpy,
+ TICODE_kcrt,
+ TICODE_kctab,
+ TICODE_kdch1,
+ TICODE_kdl1,
+ TICODE_kcud1,
+ TICODE_krmir,
+ TICODE_kend,
+ TICODE_kent,
+ TICODE_kel,
+ TICODE_ked,
+ TICODE_kext,
+ TICODE_kf0,
+ TICODE_kf1,
+ TICODE_kf2,
+ TICODE_kf3,
+ TICODE_kf4,
+ TICODE_kf5,
+ TICODE_kf6,
+ TICODE_kf7,
+ TICODE_kf8,
+ TICODE_kf9,
+ TICODE_kf10,
+ TICODE_kf11,
+ TICODE_kf12,
+ TICODE_kf13,
+ TICODE_kf14,
+ TICODE_kf15,
+ TICODE_kf16,
+ TICODE_kf17,
+ TICODE_kf18,
+ TICODE_kf19,
+ TICODE_kf20,
+ TICODE_kf21,
+ TICODE_kf22,
+ TICODE_kf23,
+ TICODE_kf24,
+ TICODE_kf25,
+ TICODE_kf26,
+ TICODE_kf27,
+ TICODE_kf28,
+ TICODE_kf29,
+ TICODE_kf30,
+ TICODE_kf31,
+ TICODE_kf32,
+ TICODE_kf33,
+ TICODE_kf34,
+ TICODE_kf35,
+ TICODE_kf36,
+ TICODE_kf37,
+ TICODE_kf38,
+ TICODE_kf39,
+ TICODE_kf40,
+ TICODE_kf41,
+ TICODE_kf42,
+ TICODE_kf43,
+ TICODE_kf44,
+ TICODE_kf45,
+ TICODE_kf46,
+ TICODE_kf47,
+ TICODE_kf48,
+ TICODE_kf49,
+ TICODE_kf50,
+ TICODE_kf51,
+ TICODE_kf52,
+ TICODE_kf53,
+ TICODE_kf54,
+ TICODE_kf55,
+ TICODE_kf56,
+ TICODE_kf57,
+ TICODE_kf58,
+ TICODE_kf59,
+ TICODE_kf60,
+ TICODE_kf61,
+ TICODE_kf62,
+ TICODE_kf63,
+ TICODE_kfnd,
+ TICODE_khlp,
+ TICODE_khome,
+ TICODE_kich1,
+ TICODE_kil1,
+ TICODE_kcub1,
+ TICODE_kll,
+ TICODE_kmrk,
+ TICODE_kmsg,
+ TICODE_kmous,
+ TICODE_kmov,
+ TICODE_knxt,
+ TICODE_knp,
+ TICODE_kopn,
+ TICODE_kopt,
+ TICODE_kpp,
+ TICODE_kprv,
+ TICODE_kprt,
+ TICODE_krdo,
+ TICODE_kref,
+ TICODE_krfr,
+ TICODE_krpl,
+ TICODE_krst,
+ TICODE_kres,
+ TICODE_kcuf1,
+ TICODE_ksav,
+ TICODE_kBEG,
+ TICODE_kCAN,
+ TICODE_kCMD,
+ TICODE_kCPY,
+ TICODE_kCRT,
+ TICODE_kDC,
+ TICODE_kDL,
+ TICODE_kslt,
+ TICODE_kEND,
+ TICODE_kEOL,
+ TICODE_kEXT,
+ TICODE_kind,
+ TICODE_kFND,
+ TICODE_kHLP,
+ TICODE_kHOM,
+ TICODE_kIC,
+ TICODE_kLFT,
+ TICODE_kMSG,
+ TICODE_kMOV,
+ TICODE_kNXT,
+ TICODE_kOPT,
+ TICODE_kPRV,
+ TICODE_kPRT,
+ TICODE_kri,
+ TICODE_kRDO,
+ TICODE_kRPL,
+ TICODE_kRIT,
+ TICODE_kRES,
+ TICODE_kSAV,
+ TICODE_kSPD,
+ TICODE_khts,
+ TICODE_kUND,
+ TICODE_kspd,
+ TICODE_kund,
+ TICODE_kcuu1,
+ TICODE_rmkx,
+ TICODE_smkx,
+ TICODE_lf0,
+ TICODE_lf1,
+ TICODE_lf2,
+ TICODE_lf3,
+ TICODE_lf4,
+ TICODE_lf5,
+ TICODE_lf6,
+ TICODE_lf7,
+ TICODE_lf8,
+ TICODE_lf9,
+ TICODE_lf10,
+ TICODE_fln,
+ TICODE_rmln,
+ TICODE_smln,
+ TICODE_rmm,
+ TICODE_smm,
+ TICODE_mhpa,
+ TICODE_mcud1,
+ TICODE_mcub1,
+ TICODE_mcuf1,
+ TICODE_mvpa,
+ TICODE_mcuu1,
+ TICODE_minfo,
+ TICODE_nel,
+ TICODE_porder,
+ TICODE_oc,
+ TICODE_op,
+ TICODE_pad,
+ TICODE_dch,
+ TICODE_dl,
+ TICODE_cud,
+ TICODE_mcud,
+ TICODE_ich,
+ TICODE_indn,
+ TICODE_il,
+ TICODE_cub,
+ TICODE_mcub,
+ TICODE_cuf,
+ TICODE_mcuf,
+ TICODE_rin,
+ TICODE_cuu,
+ TICODE_mcuu,
+ TICODE_pctrm,
+ TICODE_pfkey,
+ TICODE_pfloc,
+ TICODE_pfxl,
+ TICODE_pfx,
+ TICODE_pln,
+ TICODE_mc0,
+ TICODE_mc5p,
+ TICODE_mc4,
+ TICODE_mc5,
+ TICODE_pulse,
+ TICODE_qdial,
+ TICODE_rmclk,
+ TICODE_rep,
+ TICODE_rfi,
+ TICODE_reqmp,
+ TICODE_rs1,
+ TICODE_rs2,
+ TICODE_rs3,
+ TICODE_rf,
+ TICODE_rc,
+ TICODE_vpa,
+ TICODE_sc,
+ TICODE_scesc,
+ TICODE_ind,
+ TICODE_ri,
+ TICODE_scs,
+ TICODE_s0ds,
+ TICODE_s1ds,
+ TICODE_s2ds,
+ TICODE_s3ds,
+ TICODE_sgr1,
+ TICODE_setab,
+ TICODE_setaf,
+ TICODE_sgr,
+ TICODE_setb,
+ TICODE_smgb,
+ TICODE_smgbp,
+ TICODE_sclk,
+ TICODE_setcolor,
+ TICODE_scp,
+ TICODE_setf,
+ TICODE_smgl,
+ TICODE_smglp,
+ TICODE_smglr,
+ TICODE_slines,
+ TICODE_slength,
+ TICODE_smgr,
+ TICODE_smgrp,
+ TICODE_hts,
+ TICODE_smgtb,
+ TICODE_smgt,
+ TICODE_smgtp,
+ TICODE_wind,
+ TICODE_sbim,
+ TICODE_scsd,
+ TICODE_rbim,
+ TICODE_rcsd,
+ TICODE_subcs,
+ TICODE_supcs,
+ TICODE_ht,
+ TICODE_docr,
+ TICODE_tsl,
+ TICODE_tone,
+ TICODE_u0,
+ TICODE_u1,
+ TICODE_u2,
+ TICODE_u3,
+ TICODE_u4,
+ TICODE_u5,
+ TICODE_u6,
+ TICODE_u7,
+ TICODE_u8,
+ TICODE_u9,
+ TICODE_uc,
+ TICODE_hu,
+ TICODE_wait,
+ TICODE_xoffc,
+ TICODE_xonc,
+ TICODE_zerom
+};
+#define TISTRMAX TICODE_zerom
+
+#define t_acs_chars(t) (t)->strs[TICODE_acsc]
+#define t_alt_scancode_esc(t) (t)->strs[TICODE_scesa]
+#define t_back_tab(t) (t)->strs[TICODE_cbt]
+#define t_bell(t) (t)->strs[TICODE_bel]
+#define t_bit_image_carriage_return(t) (t)->strs[TICODE_bicr]
+#define t_bit_image_newline(t) (t)->strs[TICODE_binel]
+#define t_bit_image_repeat(t) (t)->strs[TICODE_birep]
+#define t_carriage_return(t) (t)->strs[TICODE_cr]
+#define t_change_char_pitch(t) (t)->strs[TICODE_cpi]
+#define t_change_line_pitch(t) (t)->strs[TICODE_lpi]
+#define t_change_res_horz(t) (t)->strs[TICODE_chr]
+#define t_change_res_vert(t) (t)->strs[TICODE_cvr]
+#define t_change_scroll_region(t) (t)->strs[TICODE_csr]
+#define t_char_padding(t) (t)->strs[TICODE_rmp]
+#define t_char_set_names(t) (t)->strs[TICODE_csnm]
+#define t_clear_all_tabs(t) (t)->strs[TICODE_tbc]
+#define t_clear_margins(t) (t)->strs[TICODE_mgc]
+#define t_clear_screen(t) (t)->strs[TICODE_clear]
+#define t_clr_bol(t) (t)->strs[TICODE_el1]
+#define t_clr_eol(t) (t)->strs[TICODE_el]
+#define t_clr_eos(t) (t)->strs[TICODE_ed]
+#define t_code_set_init(t) (t)->strs[TICODE_csin]
+#define t_color_names(t) (t)->strs[TICODE_colornm]
+#define t_column_address(t) (t)->strs[TICODE_hpa]
+#define t_command_character(t) (t)->strs[TICODE_cmdch]
+#define t_create_window(t) (t)->strs[TICODE_cwin]
+#define t_cursor_address(t) (t)->strs[TICODE_cup]
+#define t_cursor_down(t) (t)->strs[TICODE_cud1]
+#define t_cursor_home(t) (t)->strs[TICODE_home]
+#define t_cursor_invisible(t) (t)->strs[TICODE_civis]
+#define t_cursor_left(t) (t)->strs[TICODE_cub1]
+#define t_cursor_mem_address(t) (t)->strs[TICODE_mrcup]
+#define t_cursor_normal(t) (t)->strs[TICODE_cnorm]
+#define t_cursor_right(t) (t)->strs[TICODE_cuf1]
+#define t_cursor_to_ll(t) (t)->strs[TICODE_ll]
+#define t_cursor_up(t) (t)->strs[TICODE_cuu1]
+#define t_cursor_visible(t) (t)->strs[TICODE_cvvis]
+#define t_define_bit_image_region(t) (t)->strs[TICODE_defbi]
+#define t_define_char(t) (t)->strs[TICODE_defc]
+#define t_delete_character(t) (t)->strs[TICODE_dch1]
+#define t_delete_line(t) (t)->strs[TICODE_dl1]
+#define t_device_type(t) (t)->strs[TICODE_devt]
+#define t_dial_phone(t) (t)->strs[TICODE_dial]
+#define t_dis_status_line(t) (t)->strs[TICODE_dsl]
+#define t_display_clock(t) (t)->strs[TICODE_dclk]
+#define t_display_pc_char(t) (t)->strs[TICODE_dispc]
+#define t_down_half_time(t) (t)->strs[TICODE_hd]
+#define t_ena_acs(t) (t)->strs[TICODE_enacs]
+#define t_end_bit_image_region(t) (t)->strs[TICODE_endbi]
+#define t_enter_alt_charset_mode(t) (t)->strs[TICODE_smacs]
+#define t_enter_am_mode(t) (t)->strs[TICODE_smam]
+#define t_enter_blink_mode(t) (t)->strs[TICODE_blink]
+#define t_enter_bold_mode(t) (t)->strs[TICODE_bold]
+#define t_enter_ca_mode(t) (t)->strs[TICODE_smcup]
+#define t_enter_delete_mode(t) (t)->strs[TICODE_smdc]
+#define t_enter_dim_mode(t) (t)->strs[TICODE_dim]
+#define t_enter_doublewide_mode(t) (t)->strs[TICODE_swidm]
+#define t_enter_draft_quality(t) (t)->strs[TICODE_sdrfq]
+#define t_enter_horizontal_hl_mode(t) (t)->strs[TICODE_ehhlm]
+#define t_enter_insert_mode(t) (t)->strs[TICODE_smir]
+#define t_enter_italics_mode(t) (t)->strs[TICODE_sitm]
+#define t_enter_left_hl_mode(t) (t)->strs[TICODE_elhlm]
+#define t_enter_leftward_mode(t) (t)->strs[TICODE_slm]
+#define t_enter_low_hl_mode(t) (t)->strs[TICODE_elohlm]
+#define t_enter_micro_mode(t) (t)->strs[TICODE_smicm]
+#define t_enter_near_quality_letter(t) (t)->strs[TICODE_snlq]
+#define t_enter_normal_quality(t) (t)->strs[TICODE_snrmq]
+#define t_enter_pc_charset_mode(t) (t)->strs[TICODE_smpch]
+#define t_enter_protected_mode(t) (t)->strs[TICODE_prot]
+#define t_enter_reverse_mode(t) (t)->strs[TICODE_rev]
+#define t_enter_right_hl_mode(t) (t)->strs[TICODE_erhlm]
+#define t_enter_scancode_mode(t) (t)->strs[TICODE_smsc]
+#define t_enter_secure_mode(t) (t)->strs[TICODE_invis]
+#define t_enter_shadow_mode(t) (t)->strs[TICODE_sshm]
+#define t_enter_standout_mode(t) (t)->strs[TICODE_smso]
+#define t_enter_subscript_mode(t) (t)->strs[TICODE_ssubm]
+#define t_enter_superscript_mode(t) (t)->strs[TICODE_ssupm]
+#define t_enter_top_hl_mode(t) (t)->strs[TICODE_ethlm]
+#define t_enter_underline_mode(t) (t)->strs[TICODE_smul]
+#define t_enter_upward_mode(t) (t)->strs[TICODE_sum]
+#define t_enter_vertical_hl_mode(t) (t)->strs[TICODE_evhlm]
+#define t_enter_xon_mode(t) (t)->strs[TICODE_smxon]
+#define t_erase_chars(t) (t)->strs[TICODE_ech]
+#define t_exit_alt_charset_mode(t) (t)->strs[TICODE_rmacs]
+#define t_exit_am_mode(t) (t)->strs[TICODE_rmam]
+#define t_exit_attribute_mode(t) (t)->strs[TICODE_sgr0]
+#define t_exit_ca_mode(t) (t)->strs[TICODE_rmcup]
+#define t_exit_delete_mode(t) (t)->strs[TICODE_rmdc]
+#define t_exit_doublewide_mode(t) (t)->strs[TICODE_rwidm]
+#define t_exit_insert_mode(t) (t)->strs[TICODE_rmir]
+#define t_exit_italics_mode(t) (t)->strs[TICODE_ritm]
+#define t_exit_leftward_mode(t) (t)->strs[TICODE_rlm]
+#define t_exit_micro_mode(t) (t)->strs[TICODE_rmicm]
+#define t_exit_pc_charset_mode(t) (t)->strs[TICODE_rmpch]
+#define t_exit_scancode_mode(t) (t)->strs[TICODE_rmsc]
+#define t_exit_shadow_mode(t) (t)->strs[TICODE_rshm]
+#define t_exit_standout_mode(t) (t)->strs[TICODE_rmso]
+#define t_exit_subscript_mode(t) (t)->strs[TICODE_rsubm]
+#define t_exit_superscript_mode(t) (t)->strs[TICODE_rsupm]
+#define t_exit_underline_mode(t) (t)->strs[TICODE_rmul]
+#define t_exit_upward_mode(t) (t)->strs[TICODE_rum]
+#define t_exit_xon_mode(t) (t)->strs[TICODE_rmxon]
+#define t_fixed_pause(t) (t)->strs[TICODE_pause]
+#define t_flash_hook(t) (t)->strs[TICODE_hook]
+#define t_flash_screen(t) (t)->strs[TICODE_flash]
+#define t_form_feed(t) (t)->strs[TICODE_ff]
+#define t_from_status_line(t) (t)->strs[TICODE_fsl]
+#define t_get_mouse(t) (t)->strs[TICODE_getm]
+#define t_goto_window(t) (t)->strs[TICODE_wingo]
+#define t_hangup(t) (t)->strs[TICODE_hup]
+#define t_init_1string(t) (t)->strs[TICODE_is1]
+#define t_init_2string(t) (t)->strs[TICODE_is2]
+#define t_init_3string(t) (t)->strs[TICODE_is3]
+#define t_init_file(t) (t)->strs[TICODE_if]
+#define t_init_prog(t) (t)->strs[TICODE_iprog]
+#define t_initialize_color(t) (t)->strs[TICODE_initc]
+#define t_initialize_pair(t) (t)->strs[TICODE_initp]
+#define t_insert_character(t) (t)->strs[TICODE_ich1]
+#define t_insert_line(t) (t)->strs[TICODE_il1]
+#define t_insert_padding(t) (t)->strs[TICODE_ip]
+#define t_key_a1(t) (t)->strs[TICODE_ka1]
+#define t_key_a3(t) (t)->strs[TICODE_ka3]
+#define t_key_b2(t) (t)->strs[TICODE_kb2]
+#define t_key_backspace(t) (t)->strs[TICODE_kbs]
+#define t_key_beg(t) (t)->strs[TICODE_kbeg]
+#define t_key_btab(t) (t)->strs[TICODE_kcbt]
+#define t_key_c1(t) (t)->strs[TICODE_kc1]
+#define t_key_c3(t) (t)->strs[TICODE_kc3]
+#define t_key_cancel(t) (t)->strs[TICODE_kcan]
+#define t_key_catab(t) (t)->strs[TICODE_ktbc]
+#define t_key_clear(t) (t)->strs[TICODE_kclr]
+#define t_key_close(t) (t)->strs[TICODE_kclo]
+#define t_key_command(t) (t)->strs[TICODE_kcmd]
+#define t_key_copy(t) (t)->strs[TICODE_kcpy]
+#define t_key_create(t) (t)->strs[TICODE_kcrt]
+#define t_key_ctab(t) (t)->strs[TICODE_kctab]
+#define t_key_dc(t) (t)->strs[TICODE_kdch1]
+#define t_key_dl(t) (t)->strs[TICODE_kdl1]
+#define t_key_down(t) (t)->strs[TICODE_kcud1]
+#define t_key_eic(t) (t)->strs[TICODE_krmir]
+#define t_key_end(t) (t)->strs[TICODE_kend]
+#define t_key_enter(t) (t)->strs[TICODE_kent]
+#define t_key_eol(t) (t)->strs[TICODE_kel]
+#define t_key_eos(t) (t)->strs[TICODE_ked]
+#define t_key_exit(t) (t)->strs[TICODE_kext]
+#define t_key_f0(t) (t)->strs[TICODE_kf0]
+#define t_key_f1(t) (t)->strs[TICODE_kf1]
+#define t_key_f2(t) (t)->strs[TICODE_kf2]
+#define t_key_f3(t) (t)->strs[TICODE_kf3]
+#define t_key_f4(t) (t)->strs[TICODE_kf4]
+#define t_key_f5(t) (t)->strs[TICODE_kf5]
+#define t_key_f6(t) (t)->strs[TICODE_kf6]
+#define t_key_f7(t) (t)->strs[TICODE_kf7]
+#define t_key_f8(t) (t)->strs[TICODE_kf8]
+#define t_key_f9(t) (t)->strs[TICODE_kf9]
+#define t_key_f10(t) (t)->strs[TICODE_kf10]
+#define t_key_f11(t) (t)->strs[TICODE_kf11]
+#define t_key_f12(t) (t)->strs[TICODE_kf12]
+#define t_key_f13(t) (t)->strs[TICODE_kf13]
+#define t_key_f14(t) (t)->strs[TICODE_kf14]
+#define t_key_f15(t) (t)->strs[TICODE_kf15]
+#define t_key_f16(t) (t)->strs[TICODE_kf16]
+#define t_key_f17(t) (t)->strs[TICODE_kf17]
+#define t_key_f18(t) (t)->strs[TICODE_kf18]
+#define t_key_f19(t) (t)->strs[TICODE_kf19]
+#define t_key_f20(t) (t)->strs[TICODE_kf20]
+#define t_key_f21(t) (t)->strs[TICODE_kf21]
+#define t_key_f22(t) (t)->strs[TICODE_kf22]
+#define t_key_f23(t) (t)->strs[TICODE_kf23]
+#define t_key_f24(t) (t)->strs[TICODE_kf24]
+#define t_key_f25(t) (t)->strs[TICODE_kf25]
+#define t_key_f26(t) (t)->strs[TICODE_kf26]
+#define t_key_f27(t) (t)->strs[TICODE_kf27]
+#define t_key_f28(t) (t)->strs[TICODE_kf28]
+#define t_key_f29(t) (t)->strs[TICODE_kf29]
+#define t_key_f30(t) (t)->strs[TICODE_kf30]
+#define t_key_f31(t) (t)->strs[TICODE_kf31]
+#define t_key_f32(t) (t)->strs[TICODE_kf32]
+#define t_key_f33(t) (t)->strs[TICODE_kf33]
+#define t_key_f34(t) (t)->strs[TICODE_kf34]
+#define t_key_f35(t) (t)->strs[TICODE_kf35]
+#define t_key_f36(t) (t)->strs[TICODE_kf36]
+#define t_key_f37(t) (t)->strs[TICODE_kf37]
+#define t_key_f38(t) (t)->strs[TICODE_kf38]
+#define t_key_f39(t) (t)->strs[TICODE_kf39]
+#define t_key_f40(t) (t)->strs[TICODE_kf40]
+#define t_key_f41(t) (t)->strs[TICODE_kf41]
+#define t_key_f42(t) (t)->strs[TICODE_kf42]
+#define t_key_f43(t) (t)->strs[TICODE_kf43]
+#define t_key_f44(t) (t)->strs[TICODE_kf44]
+#define t_key_f45(t) (t)->strs[TICODE_kf45]
+#define t_key_f46(t) (t)->strs[TICODE_kf46]
+#define t_key_f47(t) (t)->strs[TICODE_kf47]
+#define t_key_f48(t) (t)->strs[TICODE_kf48]
+#define t_key_f49(t) (t)->strs[TICODE_kf49]
+#define t_key_f50(t) (t)->strs[TICODE_kf50]
+#define t_key_f51(t) (t)->strs[TICODE_kf51]
+#define t_key_f52(t) (t)->strs[TICODE_kf52]
+#define t_key_f53(t) (t)->strs[TICODE_kf53]
+#define t_key_f54(t) (t)->strs[TICODE_kf54]
+#define t_key_f55(t) (t)->strs[TICODE_kf55]
+#define t_key_f56(t) (t)->strs[TICODE_kf56]
+#define t_key_f57(t) (t)->strs[TICODE_kf57]
+#define t_key_f58(t) (t)->strs[TICODE_kf58]
+#define t_key_f59(t) (t)->strs[TICODE_kf59]
+#define t_key_f60(t) (t)->strs[TICODE_kf60]
+#define t_key_f61(t) (t)->strs[TICODE_kf61]
+#define t_key_f62(t) (t)->strs[TICODE_kf62]
+#define t_key_f63(t) (t)->strs[TICODE_kf63]
+#define t_key_find(t) (t)->strs[TICODE_kfnd]
+#define t_key_help(t) (t)->strs[TICODE_khlp]
+#define t_key_home(t) (t)->strs[TICODE_khome]
+#define t_key_ic(t) (t)->strs[TICODE_kich1]
+#define t_key_il(t) (t)->strs[TICODE_kil1]
+#define t_key_left(t) (t)->strs[TICODE_kcub1]
+#define t_key_ll(t) (t)->strs[TICODE_kll]
+#define t_key_mark(t) (t)->strs[TICODE_kmrk]
+#define t_key_message(t) (t)->strs[TICODE_kmsg]
+#define t_key_mouse(t) (t)->strs[TICODE_kmous]
+#define t_key_move(t) (t)->strs[TICODE_kmov]
+#define t_key_next(t) (t)->strs[TICODE_knxt]
+#define t_key_npage(t) (t)->strs[TICODE_knp]
+#define t_key_open(t) (t)->strs[TICODE_kopn]
+#define t_key_options(t) (t)->strs[TICODE_kopt]
+#define t_key_ppage(t) (t)->strs[TICODE_kpp]
+#define t_key_previous(t) (t)->strs[TICODE_kprv]
+#define t_key_print(t) (t)->strs[TICODE_kprt]
+#define t_key_redo(t) (t)->strs[TICODE_krdo]
+#define t_key_reference(t) (t)->strs[TICODE_kref]
+#define t_key_refresh(t) (t)->strs[TICODE_krfr]
+#define t_key_replace(t) (t)->strs[TICODE_krpl]
+#define t_key_restart(t) (t)->strs[TICODE_krst]
+#define t_key_resume(t) (t)->strs[TICODE_kres]
+#define t_key_right(t) (t)->strs[TICODE_kcuf1]
+#define t_key_save(t) (t)->strs[TICODE_ksav]
+#define t_key_sbeg(t) (t)->strs[TICODE_kBEG]
+#define t_key_scancel(t) (t)->strs[TICODE_kCAN]
+#define t_key_scommand(t) (t)->strs[TICODE_kCMD]
+#define t_key_scopy(t) (t)->strs[TICODE_kCPY]
+#define t_key_screate(t) (t)->strs[TICODE_kCRT]
+#define t_key_sdc(t) (t)->strs[TICODE_kDC]
+#define t_key_sdl(t) (t)->strs[TICODE_kDL]
+#define t_key_select(t) (t)->strs[TICODE_kslt]
+#define t_key_send(t) (t)->strs[TICODE_kEND]
+#define t_key_seol(t) (t)->strs[TICODE_kEOL]
+#define t_key_sexit(t) (t)->strs[TICODE_kEXT]
+#define t_key_sf(t) (t)->strs[TICODE_kind]
+#define t_key_sfind(t) (t)->strs[TICODE_kFND]
+#define t_key_shelp(t) (t)->strs[TICODE_kHLP]
+#define t_key_shome(t) (t)->strs[TICODE_kHOM]
+#define t_key_sic(t) (t)->strs[TICODE_kIC]
+#define t_key_sleft(t) (t)->strs[TICODE_kLFT]
+#define t_key_smessage(t) (t)->strs[TICODE_kMSG]
+#define t_key_smove(t) (t)->strs[TICODE_kMOV]
+#define t_key_snext(t) (t)->strs[TICODE_kNXT]
+#define t_key_soptions(t) (t)->strs[TICODE_kOPT]
+#define t_key_sprevious(t) (t)->strs[TICODE_kPRV]
+#define t_key_sprint(t) (t)->strs[TICODE_kPRT]
+#define t_key_sr(t) (t)->strs[TICODE_kri]
+#define t_key_sredo(t) (t)->strs[TICODE_kRDO]
+#define t_key_sreplace(t) (t)->strs[TICODE_kRPL]
+#define t_key_sright(t) (t)->strs[TICODE_kRIT]
+#define t_key_srsume(t) (t)->strs[TICODE_kRES]
+#define t_key_ssave(t) (t)->strs[TICODE_kSAV]
+#define t_key_ssuspend(t) (t)->strs[TICODE_kSPD]
+#define t_key_stab(t) (t)->strs[TICODE_khts]
+#define t_key_sundo(t) (t)->strs[TICODE_kUND]
+#define t_key_suspend(t) (t)->strs[TICODE_kspd]
+#define t_key_undo(t) (t)->strs[TICODE_kund]
+#define t_key_up(t) (t)->strs[TICODE_kcuu1]
+#define t_keypad_local(t) (t)->strs[TICODE_rmkx]
+#define t_keypad_xmit(t) (t)->strs[TICODE_smkx]
+#define t_lab_f0(t) (t)->strs[TICODE_lf0]
+#define t_lab_f1(t) (t)->strs[TICODE_lf1]
+#define t_lab_f2(t) (t)->strs[TICODE_lf2]
+#define t_lab_f3(t) (t)->strs[TICODE_lf3]
+#define t_lab_f4(t) (t)->strs[TICODE_lf4]
+#define t_lab_f5(t) (t)->strs[TICODE_lf5]
+#define t_lab_f6(t) (t)->strs[TICODE_lf6]
+#define t_lab_f7(t) (t)->strs[TICODE_lf7]
+#define t_lab_f8(t) (t)->strs[TICODE_lf8]
+#define t_lab_f9(t) (t)->strs[TICODE_lf9]
+#define t_lab_f10(t) (t)->strs[TICODE_lf10]
+#define t_label_format(t) (t)->strs[TICODE_fln]
+#define t_label_off(t) (t)->strs[TICODE_rmln]
+#define t_label_on(t) (t)->strs[TICODE_smln]
+#define t_meta_off(t) (t)->strs[TICODE_rmm]
+#define t_meta_on(t) (t)->strs[TICODE_smm]
+#define t_micro_column_address(t) (t)->strs[TICODE_mhpa]
+#define t_micro_down(t) (t)->strs[TICODE_mcud1]
+#define t_micro_left(t) (t)->strs[TICODE_mcub1]
+#define t_micro_right(t) (t)->strs[TICODE_mcuf1]
+#define t_micro_row_address(t) (t)->strs[TICODE_mvpa]
+#define t_micro_up(t) (t)->strs[TICODE_mcuu1]
+#define t_mouse_info(t) (t)->strs[TICODE_minfo]
+#define t_newline(t) (t)->strs[TICODE_nel]
+#define t_order_of_pins(t) (t)->strs[TICODE_porder]
+#define t_orig_colors(t) (t)->strs[TICODE_oc]
+#define t_orig_pair(t) (t)->strs[TICODE_op]
+#define t_pad_char(t) (t)->strs[TICODE_pad]
+#define t_parm_dch(t) (t)->strs[TICODE_dch]
+#define t_parm_delete_line(t) (t)->strs[TICODE_dl]
+#define t_parm_down_cursor(t) (t)->strs[TICODE_cud]
+#define t_parm_down_micro(t) (t)->strs[TICODE_mcud]
+#define t_parm_ich(t) (t)->strs[TICODE_ich]
+#define t_parm_index(t) (t)->strs[TICODE_indn]
+#define t_parm_insert_line(t) (t)->strs[TICODE_il]
+#define t_parm_left_cursor(t) (t)->strs[TICODE_cub]
+#define t_parm_left_micro(t) (t)->strs[TICODE_mcub]
+#define t_parm_right_cursor(t) (t)->strs[TICODE_cuf]
+#define t_parm_right_micro(t) (t)->strs[TICODE_mcuf]
+#define t_parm_rindex(t) (t)->strs[TICODE_rin]
+#define t_parm_up_cursor(t) (t)->strs[TICODE_cuu]
+#define t_parm_up_micro(t) (t)->strs[TICODE_mcuu]
+#define t_pc_term_options(t) (t)->strs[TICODE_pctrm]
+#define t_pkey_key(t) (t)->strs[TICODE_pfkey]
+#define t_pkey_local(t) (t)->strs[TICODE_pfloc]
+#define t_pkey_plab(t) (t)->strs[TICODE_pfxl]
+#define t_pkey_xmit(t) (t)->strs[TICODE_pfx]
+#define t_plab_norm(t) (t)->strs[TICODE_pln]
+#define t_print_screen(t) (t)->strs[TICODE_mc0]
+#define t_ptr_non(t) (t)->strs[TICODE_mc5p]
+#define t_ptr_off(t) (t)->strs[TICODE_mc4]
+#define t_ptr_on(t) (t)->strs[TICODE_mc5]
+#define t_pulse(t) (t)->strs[TICODE_pulse]
+#define t_quick_dial(t) (t)->strs[TICODE_qdial]
+#define t_remove_clock(t) (t)->strs[TICODE_rmclk]
+#define t_repeat_char(t) (t)->strs[TICODE_rep]
+#define t_req_for_input(t) (t)->strs[TICODE_rfi]
+#define t_req_mouse_pos(t) (t)->strs[TICODE_reqmp]
+#define t_reset_1string(t) (t)->strs[TICODE_rs1]
+#define t_reset_2string(t) (t)->strs[TICODE_rs2]
+#define t_reset_3string(t) (t)->strs[TICODE_rs3]
+#define t_reset_file(t) (t)->strs[TICODE_rf]
+#define t_restore_cursor(t) (t)->strs[TICODE_rc]
+#define t_row_address(t) (t)->strs[TICODE_vpa]
+#define t_save_cursor(t) (t)->strs[TICODE_sc]
+#define t_scancode_escape(t) (t)->strs[TICODE_scesc]
+#define t_scroll_forward(t) (t)->strs[TICODE_ind]
+#define t_scroll_reverse(t) (t)->strs[TICODE_ri]
+#define t_select_char_set(t) (t)->strs[TICODE_scs]
+#define t_set0_des_seq(t) (t)->strs[TICODE_s0ds]
+#define t_set1_des_seq(t) (t)->strs[TICODE_s1ds]
+#define t_set2_des_seq(t) (t)->strs[TICODE_s2ds]
+#define t_set3_des_seq(t) (t)->strs[TICODE_s3ds]
+#define t_set_a_attributes(t) (t)->strs[TICODE_sgr1]
+#define t_set_a_background(t) (t)->strs[TICODE_setab]
+#define t_set_a_foreground(t) (t)->strs[TICODE_setaf]
+#define t_set_attributes(t) (t)->strs[TICODE_sgr]
+#define t_set_background(t) (t)->strs[TICODE_setb]
+#define t_set_bottom_margin(t) (t)->strs[TICODE_smgb]
+#define t_set_bottom_margin_parm(t) (t)->strs[TICODE_smgbp]
+#define t_set_clock(t) (t)->strs[TICODE_sclk]
+#define t_set_color_band(t) (t)->strs[TICODE_setcolor]
+#define t_set_color_pair(t) (t)->strs[TICODE_scp]
+#define t_set_foreground(t) (t)->strs[TICODE_setf]
+#define t_set_left_margin(t) (t)->strs[TICODE_smgl]
+#define t_set_left_margin_parm(t) (t)->strs[TICODE_smglp]
+#define t_set_lr_margin(t) (t)->strs[TICODE_smglr]
+#define t_set_page_length(t) (t)->strs[TICODE_slines]
+#define t_set_pglen_inch(t) (t)->strs[TICODE_slength]
+#define t_set_right_margin(t) (t)->strs[TICODE_smgr]
+#define t_set_right_margin_parm(t) (t)->strs[TICODE_smgrp]
+#define t_set_tab(t) (t)->strs[TICODE_hts]
+#define t_set_tb_margin(t) (t)->strs[TICODE_smgtb]
+#define t_set_top_margin(t) (t)->strs[TICODE_smgt]
+#define t_set_top_margin_parm(t) (t)->strs[TICODE_smgtp]
+#define t_set_window(t) (t)->strs[TICODE_wind]
+#define t_start_bit_image(t) (t)->strs[TICODE_sbim]
+#define t_start_char_set_def(t) (t)->strs[TICODE_scsd]
+#define t_stop_bit_image(t) (t)->strs[TICODE_rbim]
+#define t_stop_char_set_def(t) (t)->strs[TICODE_rcsd]
+#define t_subscript_characters(t) (t)->strs[TICODE_subcs]
+#define t_superscript_characters(t) (t)->strs[TICODE_supcs]
+#define t_tab(t) (t)->strs[TICODE_ht]
+#define t_these_cause_cr(t) (t)->strs[TICODE_docr]
+#define t_to_status_line(t) (t)->strs[TICODE_tsl]
+#define t_tone(t) (t)->strs[TICODE_tone]
+#define t_user0(t) (t)->strs[TICODE_u0]
+#define t_user1(t) (t)->strs[TICODE_u1]
+#define t_user2(t) (t)->strs[TICODE_u2]
+#define t_user3(t) (t)->strs[TICODE_u3]
+#define t_user4(t) (t)->strs[TICODE_u4]
+#define t_user5(t) (t)->strs[TICODE_u5]
+#define t_user6(t) (t)->strs[TICODE_u6]
+#define t_user7(t) (t)->strs[TICODE_u7]
+#define t_user8(t) (t)->strs[TICODE_u8]
+#define t_user9(t) (t)->strs[TICODE_u9]
+#define t_underline_char(t) (t)->strs[TICODE_uc]
+#define t_up_half_line(t) (t)->strs[TICODE_hu]
+#define t_wait_tone(t) (t)->strs[TICODE_wait]
+#define t_xoff_character(t) (t)->strs[TICODE_xoffc]
+#define t_xon_character(t) (t)->strs[TICODE_xonc]
+#define t_zero_motion(t) (t)->strs[TICODE_zerom]
+
+#define acs_chars t_acs_chars(cur_term)
+#define alt_scancode_esc t_alt_scancode_esc(cur_term)
+#define back_tab t_back_tab(cur_term)
+#define bell t_bell(cur_term)
+#define bit_image_carriage_return t_bit_image_carriage_return(cur_term)
+#define bit_image_newline t_bit_image_newline(cur_term)
+#define bit_image_repeat t_bit_image_repeat(cur_term)
+#define carriage_return t_carriage_return(cur_term)
+#define change_char_pitch t_change_char_pitch(cur_term)
+#define change_line_pitch t_change_line_pitch(cur_term)
+#define change_res_horz t_change_res_horz(cur_term)
+#define change_res_vert t_change_res_vert(cur_term)
+#define change_scroll_region t_change_scroll_region(cur_term)
+#define char_padding t_char_padding(cur_term)
+#define char_set_names t_char_set_names(cur_term)
+#define clear_all_tabs t_clear_all_tabs(cur_term)
+#define clear_margins t_clear_margins(cur_term)
+#define clear_screen t_clear_screen(cur_term)
+#define clr_bol t_clr_bol(cur_term)
+#define clr_eol t_clr_eol(cur_term)
+#define clr_eos t_clr_eos(cur_term)
+#define code_set_init t_code_set_init(cur_term)
+#define color_names t_color_names(cur_term)
+#define column_address t_column_address(cur_term)
+#define command_character t_command_character(cur_term)
+#define create_window t_create_window(cur_term)
+#define cursor_address t_cursor_address(cur_term)
+#define cursor_down t_cursor_down(cur_term)
+#define cursor_home t_cursor_home(cur_term)
+#define cursor_invisible t_cursor_invisible(cur_term)
+#define cursor_left t_cursor_left(cur_term)
+#define cursor_mem_address t_cursor_mem_address(cur_term)
+#define cursor_normal t_cursor_normal(cur_term)
+#define cursor_right t_cursor_right(cur_term)
+#define cursor_to_ll t_cursor_to_ll(cur_term)
+#define cursor_up t_cursor_up(cur_term)
+#define cursor_visible t_cursor_visible(cur_term)
+#define define_bit_image_region t_define_bit_image_region(cur_term)
+#define define_char t_define_char(cur_term)
+#define delete_character t_delete_character(cur_term)
+#define delete_line t_delete_line(cur_term)
+#define device_type t_device_type(cur_term)
+#define dial_phone t_dial_phone(cur_term)
+#define dis_status_line t_dis_status_line(cur_term)
+#define display_clock t_display_clock(cur_term)
+#define display_pc_char t_display_pc_char(cur_term)
+#define down_half_time t_down_half_time(cur_term)
+#define ena_acs t_ena_acs(cur_term)
+#define end_bit_image_region t_end_bit_image_region(cur_term)
+#define enter_alt_charset_mode t_enter_alt_charset_mode(cur_term)
+#define enter_am_mode t_enter_am_mode(cur_term)
+#define enter_blink_mode t_enter_blink_mode(cur_term)
+#define enter_bold_mode t_enter_bold_mode(cur_term)
+#define enter_ca_mode t_enter_ca_mode(cur_term)
+#define enter_delete_mode t_enter_delete_mode(cur_term)
+#define enter_dim_mode t_enter_dim_mode(cur_term)
+#define enter_doublewide_mode t_enter_doublewide_mode(cur_term)
+#define enter_draft_quality t_enter_draft_quality(cur_term)
+#define enter_horizontal_hl_mode t_enter_horizontal_hl_mode(cur_term)
+#define enter_insert_mode t_enter_insert_mode(cur_term)
+#define enter_italics_mode t_enter_italics_mode(cur_term)
+#define enter_left_hl_mode t_enter_left_hl_mode(cur_term)
+#define enter_leftward_mode t_enter_leftward_mode(cur_term)
+#define enter_low_hl_mode t_enter_low_hl_mode(cur_term)
+#define enter_micro_mode t_enter_micro_mode(cur_term)
+#define enter_near_quality_letter t_enter_near_quality_letter(cur_term)
+#define enter_normal_quality t_enter_normal_quality(cur_term)
+#define enter_pc_charset_mode t_enter_pc_charset_mode(cur_term)
+#define enter_protected_mode t_enter_protected_mode(cur_term)
+#define enter_reverse_mode t_enter_reverse_mode(cur_term)
+#define enter_right_hl_mode t_enter_right_hl_mode(cur_term)
+#define enter_scancode_mode t_enter_scancode_mode(cur_term)
+#define enter_secure_mode t_enter_secure_mode(cur_term)
+#define enter_shadow_mode t_enter_shadow_mode(cur_term)
+#define enter_standout_mode t_enter_standout_mode(cur_term)
+#define enter_subscript_mode t_enter_subscript_mode(cur_term)
+#define enter_superscript_mode t_enter_superscript_mode(cur_term)
+#define enter_top_hl_mode t_enter_top_hl_mode(cur_term)
+#define enter_underline_mode t_enter_underline_mode(cur_term)
+#define enter_upward_mode t_enter_upward_mode(cur_term)
+#define enter_vertical_hl_mode t_enter_vertical_hl_mode(cur_term)
+#define enter_xon_mode t_enter_xon_mode(cur_term)
+#define erase_chars t_erase_chars(cur_term)
+#define exit_alt_charset_mode t_exit_alt_charset_mode(cur_term)
+#define exit_am_mode t_exit_am_mode(cur_term)
+#define exit_attribute_mode t_exit_attribute_mode(cur_term)
+#define exit_ca_mode t_exit_ca_mode(cur_term)
+#define exit_delete_mode t_exit_delete_mode(cur_term)
+#define exit_doublewide_mode t_exit_doublewide_mode(cur_term)
+#define exit_insert_mode t_exit_insert_mode(cur_term)
+#define exit_italics_mode t_exit_italics_mode(cur_term)
+#define exit_leftward_mode t_exit_leftward_mode(cur_term)
+#define exit_micro_mode t_exit_micro_mode(cur_term)
+#define exit_pc_charset_mode t_exit_pc_charset_mode(cur_term)
+#define exit_scancode_mode t_exit_scancode_mode(cur_term)
+#define exit_shadow_mode t_exit_shadow_mode(cur_term)
+#define exit_standout_mode t_exit_standout_mode(cur_term)
+#define exit_subscript_mode t_exit_subscript_mode(cur_term)
+#define exit_superscript_mode t_exit_superscript_mode(cur_term)
+#define exit_underline_mode t_exit_underline_mode(cur_term)
+#define exit_upward_mode t_exit_upward_mode(cur_term)
+#define exit_xon_mode t_exit_xon_mode(cur_term)
+#define fixed_pause t_fixed_pause(cur_term)
+#define flash_hook t_flash_hook(cur_term)
+#define flash_screen t_flash_screen(cur_term)
+#define form_feed t_form_feed(cur_term)
+#define from_status_line t_from_status_line(cur_term)
+#define get_mouse t_get_mouse(cur_term)
+#define goto_window t_goto_window(cur_term)
+#define hangup t_hangup(cur_term)
+#define init_1string t_init_1string(cur_term)
+#define init_2string t_init_2string(cur_term)
+#define init_3string t_init_3string(cur_term)
+#define init_file t_init_file(cur_term)
+#define init_prog t_init_prog(cur_term)
+#define initialize_color t_initialize_color(cur_term)
+#define initialize_pair t_initialize_pair(cur_term)
+#define insert_character t_insert_character(cur_term)
+#define insert_line t_insert_line(cur_term)
+#define insert_padding t_insert_padding(cur_term)
+#define key_a1 t_key_a1(cur_term)
+#define key_a3 t_key_a3(cur_term)
+#define key_b2 t_key_b2(cur_term)
+#define key_backspace t_key_backspace(cur_term)
+#define key_beg t_key_beg(cur_term)
+#define key_btab t_key_btab(cur_term)
+#define key_c1 t_key_c1(cur_term)
+#define key_c3 t_key_c3(cur_term)
+#define key_cancel t_key_cancel(cur_term)
+#define key_catab t_key_catab(cur_term)
+#define key_clear t_key_clear(cur_term)
+#define key_close t_key_close(cur_term)
+#define key_command t_key_command(cur_term)
+#define key_copy t_key_copy(cur_term)
+#define key_create t_key_create(cur_term)
+#define key_ctab t_key_ctab(cur_term)
+#define key_dc t_key_dc(cur_term)
+#define key_dl t_key_dl(cur_term)
+#define key_down t_key_down(cur_term)
+#define key_eic t_key_eic(cur_term)
+#define key_end t_key_end(cur_term)
+#define key_enter t_key_enter(cur_term)
+#define key_eol t_key_eol(cur_term)
+#define key_eos t_key_eos(cur_term)
+#define key_exit t_key_exit(cur_term)
+#define key_f0 t_key_f0(cur_term)
+#define key_f1 t_key_f1(cur_term)
+#define key_f2 t_key_f2(cur_term)
+#define key_f3 t_key_f3(cur_term)
+#define key_f4 t_key_f4(cur_term)
+#define key_f5 t_key_f5(cur_term)
+#define key_f6 t_key_f6(cur_term)
+#define key_f7 t_key_f7(cur_term)
+#define key_f8 t_key_f8(cur_term)
+#define key_f9 t_key_f9(cur_term)
+#define key_f10 t_key_f10(cur_term)
+#define key_f11 t_key_f11(cur_term)
+#define key_f12 t_key_f12(cur_term)
+#define key_f13 t_key_f13(cur_term)
+#define key_f14 t_key_f14(cur_term)
+#define key_f15 t_key_f15(cur_term)
+#define key_f16 t_key_f16(cur_term)
+#define key_f17 t_key_f17(cur_term)
+#define key_f18 t_key_f18(cur_term)
+#define key_f19 t_key_f19(cur_term)
+#define key_f20 t_key_f20(cur_term)
+#define key_f21 t_key_f21(cur_term)
+#define key_f22 t_key_f22(cur_term)
+#define key_f23 t_key_f23(cur_term)
+#define key_f24 t_key_f24(cur_term)
+#define key_f25 t_key_f25(cur_term)
+#define key_f26 t_key_f26(cur_term)
+#define key_f27 t_key_f27(cur_term)
+#define key_f28 t_key_f28(cur_term)
+#define key_f29 t_key_f29(cur_term)
+#define key_f30 t_key_f30(cur_term)
+#define key_f31 t_key_f31(cur_term)
+#define key_f32 t_key_f32(cur_term)
+#define key_f33 t_key_f33(cur_term)
+#define key_f34 t_key_f34(cur_term)
+#define key_f35 t_key_f35(cur_term)
+#define key_f36 t_key_f36(cur_term)
+#define key_f37 t_key_f37(cur_term)
+#define key_f38 t_key_f38(cur_term)
+#define key_f39 t_key_f39(cur_term)
+#define key_f40 t_key_f40(cur_term)
+#define key_f41 t_key_f41(cur_term)
+#define key_f42 t_key_f42(cur_term)
+#define key_f43 t_key_f43(cur_term)
+#define key_f44 t_key_f44(cur_term)
+#define key_f45 t_key_f45(cur_term)
+#define key_f46 t_key_f46(cur_term)
+#define key_f47 t_key_f47(cur_term)
+#define key_f48 t_key_f48(cur_term)
+#define key_f49 t_key_f49(cur_term)
+#define key_f50 t_key_f50(cur_term)
+#define key_f51 t_key_f51(cur_term)
+#define key_f52 t_key_f52(cur_term)
+#define key_f53 t_key_f53(cur_term)
+#define key_f54 t_key_f54(cur_term)
+#define key_f55 t_key_f55(cur_term)
+#define key_f56 t_key_f56(cur_term)
+#define key_f57 t_key_f57(cur_term)
+#define key_f58 t_key_f58(cur_term)
+#define key_f59 t_key_f59(cur_term)
+#define key_f60 t_key_f60(cur_term)
+#define key_f61 t_key_f61(cur_term)
+#define key_f62 t_key_f62(cur_term)
+#define key_f63 t_key_f63(cur_term)
+#define key_find t_key_find(cur_term)
+#define key_help t_key_help(cur_term)
+#define key_home t_key_home(cur_term)
+#define key_ic t_key_ic(cur_term)
+#define key_il t_key_il(cur_term)
+#define key_left t_key_left(cur_term)
+#define key_ll t_key_ll(cur_term)
+#define key_mark t_key_mark(cur_term)
+#define key_message t_key_message(cur_term)
+#define key_mouse t_key_mouse(cur_term)
+#define key_move t_key_move(cur_term)
+#define key_next t_key_next(cur_term)
+#define key_npage t_key_npage(cur_term)
+#define key_open t_key_open(cur_term)
+#define key_options t_key_options(cur_term)
+#define key_ppage t_key_ppage(cur_term)
+#define key_previous t_key_previous(cur_term)
+#define key_print t_key_print(cur_term)
+#define key_redo t_key_redo(cur_term)
+#define key_reference t_key_reference(cur_term)
+#define key_refresh t_key_refresh(cur_term)
+#define key_replace t_key_replace(cur_term)
+#define key_restart t_key_restart(cur_term)
+#define key_resume t_key_resume(cur_term)
+#define key_right t_key_right(cur_term)
+#define key_save t_key_save(cur_term)
+#define key_sbeg t_key_sbeg(cur_term)
+#define key_scancel t_key_scancel(cur_term)
+#define key_scommand t_key_scommand(cur_term)
+#define key_scopy t_key_scopy(cur_term)
+#define key_screate t_key_screate(cur_term)
+#define key_sdc t_key_sdc(cur_term)
+#define key_sdl t_key_sdl(cur_term)
+#define key_select t_key_select(cur_term)
+#define key_send t_key_send(cur_term)
+#define key_seol t_key_seol(cur_term)
+#define key_sexit t_key_sexit(cur_term)
+#define key_sf t_key_sf(cur_term)
+#define key_sfind t_key_sfind(cur_term)
+#define key_shelp t_key_shelp(cur_term)
+#define key_shome t_key_shome(cur_term)
+#define key_sic t_key_sic(cur_term)
+#define key_sleft t_key_sleft(cur_term)
+#define key_smessage t_key_smessage(cur_term)
+#define key_smove t_key_smove(cur_term)
+#define key_snext t_key_snext(cur_term)
+#define key_soptions t_key_soptions(cur_term)
+#define key_sprevious t_key_sprevious(cur_term)
+#define key_sprint t_key_sprint(cur_term)
+#define key_sr t_key_sr(cur_term)
+#define key_sredo t_key_sredo(cur_term)
+#define key_sreplace t_key_sreplace(cur_term)
+#define key_sright t_key_sright(cur_term)
+#define key_srsume t_key_srsume(cur_term)
+#define key_ssave t_key_ssave(cur_term)
+#define key_ssuspend t_key_ssuspend(cur_term)
+#define key_stab t_key_stab(cur_term)
+#define key_sundo t_key_sundo(cur_term)
+#define key_suspend t_key_suspend(cur_term)
+#define key_undo t_key_undo(cur_term)
+#define key_up t_key_up(cur_term)
+#define keypad_local t_keypad_local(cur_term)
+#define keypad_xmit t_keypad_xmit(cur_term)
+#define lab_f0 t_lab_f0(cur_term)
+#define lab_f1 t_lab_f1(cur_term)
+#define lab_f2 t_lab_f2(cur_term)
+#define lab_f3 t_lab_f3(cur_term)
+#define lab_f4 t_lab_f4(cur_term)
+#define lab_f5 t_lab_f5(cur_term)
+#define lab_f6 t_lab_f6(cur_term)
+#define lab_f7 t_lab_f7(cur_term)
+#define lab_f8 t_lab_f8(cur_term)
+#define lab_f9 t_lab_f9(cur_term)
+#define lab_f10 t_lab_f10(cur_term)
+#define label_format t_label_format(cur_term)
+#define label_off t_label_off(cur_term)
+#define label_on t_label_on(cur_term)
+#define meta_off t_meta_off(cur_term)
+#define meta_on t_meta_on(cur_term)
+#define micro_column_address t_micro_column_address(cur_term)
+#define micro_down t_micro_down(cur_term)
+#define micro_left t_micro_left(cur_term)
+#define micro_right t_micro_right(cur_term)
+#define micro_row_address t_micro_row_address(cur_term)
+#define micro_up t_micro_up(cur_term)
+#define mouse_info t_mouse_info(cur_term)
+#define newline t_newline(cur_term)
+#define order_of_pins t_order_of_pins(cur_term)
+#define orig_colors t_orig_colors(cur_term)
+#define orig_pair t_orig_pair(cur_term)
+#define pad_char t_pad_char(cur_term)
+#define parm_dch t_parm_dch(cur_term)
+#define parm_delete_line t_parm_delete_line(cur_term)
+#define parm_down_cursor t_parm_down_cursor(cur_term)
+#define parm_down_micro t_parm_down_micro(cur_term)
+#define parm_ich t_parm_ich(cur_term)
+#define parm_index t_parm_index(cur_term)
+#define parm_insert_line t_parm_insert_line(cur_term)
+#define parm_left_cursor t_parm_left_cursor(cur_term)
+#define parm_left_micro t_parm_left_micro(cur_term)
+#define parm_right_cursor t_parm_right_cursor(cur_term)
+#define parm_right_micro t_parm_right_micro(cur_term)
+#define parm_rindex t_parm_rindex(cur_term)
+#define parm_up_cursor t_parm_up_cursor(cur_term)
+#define parm_up_micro t_parm_up_micro(cur_term)
+#define pc_term_options t_pc_term_options(cur_term)
+#define pkey_key t_pkey_key(cur_term)
+#define pkey_local t_pkey_local(cur_term)
+#define pkey_plab t_pkey_plab(cur_term)
+#define pkey_xmit t_pkey_xmit(cur_term)
+#define plab_norm t_plab_norm(cur_term)
+#define print_screen t_print_screen(cur_term)
+#define ptr_non t_ptr_non(cur_term)
+#define ptr_off t_ptr_off(cur_term)
+#define ptr_on t_ptr_on(cur_term)
+#define pulse t_pulse(cur_term)
+#define quick_dial t_quick_dial(cur_term)
+#define remove_clock t_remove_clock(cur_term)
+#define repeat_char t_repeat_char(cur_term)
+#define req_for_input t_req_for_input(cur_term)
+#define req_mouse_pos t_req_mouse_pos(cur_term)
+#define reset_1string t_reset_1string(cur_term)
+#define reset_2string t_reset_2string(cur_term)
+#define reset_3string t_reset_3string(cur_term)
+#define reset_file t_reset_file(cur_term)
+#define restore_cursor t_restore_cursor(cur_term)
+#define row_address t_row_address(cur_term)
+#define save_cursor t_save_cursor(cur_term)
+#define scancode_escape t_scancode_escape(cur_term)
+#define scroll_forward t_scroll_forward(cur_term)
+#define scroll_reverse t_scroll_reverse(cur_term)
+#define select_char_set t_select_char_set(cur_term)
+#define set0_des_seq t_set0_des_seq(cur_term)
+#define set1_des_seq t_set1_des_seq(cur_term)
+#define set2_des_seq t_set2_des_seq(cur_term)
+#define set3_des_seq t_set3_des_seq(cur_term)
+#define set_a_attributes t_set_a_attributes(cur_term)
+#define set_a_background t_set_a_background(cur_term)
+#define set_a_foreground t_set_a_foreground(cur_term)
+#define set_attributes t_set_attributes(cur_term)
+#define set_background t_set_background(cur_term)
+#define set_bottom_margin t_set_bottom_margin(cur_term)
+#define set_bottom_margin_parm t_set_bottom_margin_parm(cur_term)
+#define set_clock t_set_clock(cur_term)
+#define set_color_band t_set_color_band(cur_term)
+#define set_color_pair t_set_color_pair(cur_term)
+#define set_foreground t_set_foreground(cur_term)
+#define set_left_margin t_set_left_margin(cur_term)
+#define set_left_margin_parm t_set_left_margin_parm(cur_term)
+#define set_lr_margin t_set_lr_margin(cur_term)
+#define set_page_length t_set_page_length(cur_term)
+#define set_pglen_inch t_set_pglen_inch(cur_term)
+#define set_right_margin t_set_right_margin(cur_term)
+#define set_right_margin_parm t_set_right_margin_parm(cur_term)
+#define set_tab t_set_tab(cur_term)
+#define set_tb_margin t_set_tb_margin(cur_term)
+#define set_top_margin t_set_top_margin(cur_term)
+#define set_top_margin_parm t_set_top_margin_parm(cur_term)
+#define set_window t_set_window(cur_term)
+#define start_bit_image t_start_bit_image(cur_term)
+#define start_char_set_def t_start_char_set_def(cur_term)
+#define stop_bit_image t_stop_bit_image(cur_term)
+#define stop_char_set_def t_stop_char_set_def(cur_term)
+#define subscript_characters t_subscript_characters(cur_term)
+#define superscript_characters t_superscript_characters(cur_term)
+#define tab t_tab(cur_term)
+#define these_cause_cr t_these_cause_cr(cur_term)
+#define to_status_line t_to_status_line(cur_term)
+#define tone t_tone(cur_term)
+#define user0 t_user0(cur_term)
+#define user1 t_user1(cur_term)
+#define user2 t_user2(cur_term)
+#define user3 t_user3(cur_term)
+#define user4 t_user4(cur_term)
+#define user5 t_user5(cur_term)
+#define user6 t_user6(cur_term)
+#define user7 t_user7(cur_term)
+#define user8 t_user8(cur_term)
+#define user9 t_user9(cur_term)
+#define underline_char t_underline_char(cur_term)
+#define up_half_line t_up_half_line(cur_term)
+#define wait_tone t_wait_tone(cur_term)
+#define xoff_character t_xoff_character(cur_term)
+#define xon_character t_xon_character(cur_term)
+#define zero_motion t_zero_motion(cur_term)
+
+/*
+ * STRING DESCRIPTIONS
+ *
+ * acs_chars: Graphic charset pairs aAbBcC
+ * alt_scancode_esc: Alternate escape for scancode emulation
+ * back_tab: Back tab
+ * bell: Audible signal (bell)
+ * bit_image_carriage_return: Move to beginning of same row
+ * bit_image_newline: Move to next row of the bit image
+ * bit_image_repeat: Repeat bit-image cell #1 #2 times
+ * carriage_return: Carriage return
+ * change_char_pitch: Change number of characters per inch
+ * change_line_pitch: Change number of lines per inch
+ * change_res_horz: Change horizontal resolution
+ * change_res_vert: Change vertical resolution
+ * change_scroll_region: Change to lines #1 through #2 (VT100)
+ * char_padding: Like ip but when in replace mode
+ * char_set_names: Returns a list of character set names
+ * clear_all_tabs: Clear all tab stops
+ * clear_margins: Clear all margins (top, bottom and sides)
+ * clear_screen: Clear screen and home cursor
+ * clr_bol: Clear to beginning of line, inclusive
+ * clr_eol: Clear to end of line
+ * clr_eos: Clear to end of display
+ * code_set_init: Init sequence for multiple codesets
+ * color_names: Give name for colour #1
+ * column_address: Set horizontal position to absolute #1
+ * command_character: Terminal settable cmd character in prototype
+ * create_window: Define win #1 to go from #2,#3 to #4,#5
+ * cursor_address: Move to row #1, col #2
+ * cursor_down: Down one line
+ * cursor_home: Home cursor (if no cup)
+ * cursor_invisible: Make cursor invisible
+ * cursor_left: Move left one space
+ * cursor_mem_address: Memory relative cursor addressing
+ * cursor_normal: Make cursor appear normal (under vs/vi)
+ * cursor_right: Non-destructive space (cursor or carriage right)
+ * cursor_to_ll: Last line, first column (if no cup)
+ * cursor_up: Upline (cursor up)
+ * cursor_visible: Make cursor very visible
+ * define_bit_image_region: Define rectangular bit-image region
+ * define_char: Define a character in a character set
+ * delete_character: Delete character
+ * delete_line: Delete line
+ * device_type: Indicate language/codeset support
+ * dial_phone: Dial phone number #1
+ * dis_status_line: Disable status line
+ * display_clock: Display time-of-day clock
+ * display_pc_char: Display PC character
+ * down_half_time: Half-line down (forward 1/2 linefeed)
+ * ena_acs: Enable alternate character set
+ * end_bit_image_region: End a bit-image region
+ * enter_alt_charset_mode: Start alternate character set
+ * enter_am_mode: Turn on automatic margins
+ * enter_blink_mode: Turn on blinking
+ * enter_bold_mode: Turn on bold (extra bright) mode
+ * enter_ca_mode: String to begin programs that use cup
+ * enter_delete_mode: Delete mode (enter)
+ * enter_dim_mode: Turn on half-bright mode
+ * enter_doublewide_mode: Enable double wide printing
+ * enter_draft_quality: Set draft qualify print
+ * enter_horizontal_hl_mode: Turn on horizontal highlight mode
+ * enter_insert_mode: Insert mode (enter)
+ * enter_italics_mode: Enable italics
+ * enter_left_hl_mode: Turn on left highlight mode
+ * enter_leftward_mode: Enable leftward carriage motion
+ * enter_low_hl_mode: Turn on low highlight mode
+ * enter_micro_mode: Enable micro motion capabilities
+ * enter_near_quality_letter: Set near-letter quality print
+ * enter_normal_quality: Set normal quality print
+ * enter_pc_charset_mode: Enter PC character display mode
+ * enter_protected_mode: Turn on protected mode
+ * enter_reverse_mode: Turn on reverse video mode
+ * enter_right_hl_mode: Turn on right highlight mode
+ * enter_scancode_mode: Enter PC scancode mode
+ * enter_secure_mode: Turn on blank mode (characters invisible)
+ * enter_shadow_mode: Enable shadow printing
+ * enter_standout_mode: Begin standout mode
+ * enter_subscript_mode: Enable subscript printing
+ * enter_superscript_mode: Enable superscript printing
+ * enter_top_hl_mode: Turn on top highlight mode
+ * enter_underline_mode: Start underscore mode
+ * enter_upward_mode: Enable upward carriage motion
+ * enter_vertical_hl_mode: Turn on verticle highlight mode
+ * enter_xon_mode: Turn on xon/xoff handshaking
+ * erase_chars: Erase #1 characters
+ * exit_alt_charset_mode: End alternate character set
+ * exit_am_mode: Turn off automatic margins
+ * exit_attribute_mode: Turn off all attributes
+ * exit_ca_mode: String to end programs that use cup
+ * exit_delete_mode: End delete mode
+ * exit_doublewide_mode: Disable double wide printing
+ * exit_insert_mode: End insert mode
+ * exit_italics_mode: Disable italics
+ * exit_leftward_mode: Enable rightward (normal) carriage motion
+ * exit_micro_mode: Disable micro motion capabilities
+ * exit_pc_charset_mode: Disable PC character display mode
+ * exit_scancode_mode: Disable PC scancode mode
+ * exit_shadow_mode: Disable shadow printing
+ * exit_standout_mode: End standout mode
+ * exit_subscript_mode: Disable subscript printing
+ * exit_superscript_mode: Disable superscript printing
+ * exit_underline_mode: End underscore mode
+ * exit_upward_mode: Enable downward (normal) carriage motion
+ * exit_xon_mode: Turn off xon/xoff handshaking
+ * fixed_pause: Pause for 2-3 seconds
+ * flash_hook: Flash the switch hook
+ * flash_screen: Visible bell (may move cursor)
+ * form_feed: Hardcopy terminal eject page
+ * from_status_line: Return from status line
+ * get_mouse: Curses should get button events
+ * goto_window: Go to window #1
+ * hangup: Hang-up phone
+ * init_1string: Terminal or printer initialisation string
+ * init_2string: Terminal or printer initialisation string
+ * init_3string: Terminal or printer initialisation string
+ * init_file: Name of initialisation file
+ * init_prog: Path name of program for initialisation
+ * initialize_color: Set colour #1 to RGB #2, #3, #4
+ * initialize_pair: Set colour-pair #1 to fg #2, bg #3
+ * insert_character: Insert character
+ * insert_line: Add new blank line
+ * insert_padding: Insert pad after character inserted
+ * key_a1: upper left of keypad
+ * key_a3: upper right of keypad
+ * key_b2: center of keypad
+ * key_backspace: set by backspace key
+ * key_beg: 1
+ * key_btab: sent by back-tab key
+ * key_c1: lower left of keypad
+ * key_c3: lower right of keypad
+ * key_cancel: 2
+ * key_catab: sent by clear-all-tabs key
+ * key_clear: sent by clear-screen or erase key
+ * key_close: 3
+ * key_command: 4
+ * key_copy: 5
+ * key_create: 6
+ * key_ctab: sent by clear-tab key
+ * key_dc: sent by delete-character key
+ * key_dl: sent by delete-line key
+ * key_down: sent by terminal down-arrow key
+ * key_eic: sent by rmir or smir in insert mode
+ * key_end: 7
+ * key_enter: 8
+ * key_eol: sent by clear-to-end-of-line key
+ * key_eos: sent by clear-to-end-of-screen key
+ * key_exit: 9
+ * key_f0: sent by function key f0
+ * key_f1: sent by function key f1
+ * key_f2: sent by function key f2
+ * key_f3: sent by function key f3
+ * key_f4: sent by function key f4
+ * key_f5: sent by function key f5
+ * key_f6: sent by function key f6
+ * key_f7: sent by function key f7
+ * key_f8: sent by function key f8
+ * key_f9: sent by function key f9
+ * key_f10: sent by function key f10
+ * key_f11: sent by function key f11
+ * key_f12: sent by function key f12
+ * key_f13: sent by function key f13
+ * key_f14: sent by function key f14
+ * key_f15: sent by function key f15
+ * key_f16: sent by function key f16
+ * key_f17: sent by function key f17
+ * key_f18: sent by function key f18
+ * key_f19: sent by function key f19
+ * key_f20: sent by function key f20
+ * key_f21: sent by function key f21
+ * key_f22: sent by function key f22
+ * key_f23: sent by function key f23
+ * key_f24: sent by function key f24
+ * key_f25: sent by function key f25
+ * key_f26: sent by function key f26
+ * key_f27: sent by function key f27
+ * key_f28: sent by function key f28
+ * key_f29: sent by function key f29
+ * key_f30: sent by function key f30
+ * key_f31: sent by function key f31
+ * key_f32: sent by function key f32
+ * key_f33: sent by function key f33
+ * key_f34: sent by function key f34
+ * key_f35: sent by function key f35
+ * key_f36: sent by function key f36
+ * key_f37: sent by function key f37
+ * key_f38: sent by function key f38
+ * key_f39: sent by function key f39
+ * key_f40: sent by function key f40
+ * key_f41: sent by function key f41
+ * key_f42: sent by function key f42
+ * key_f43: sent by function key f43
+ * key_f44: sent by function key f44
+ * key_f45: sent by function key f45
+ * key_f46: sent by function key f46
+ * key_f47: sent by function key f47
+ * key_f48: sent by function key f48
+ * key_f49: sent by function key f49
+ * key_f50: sent by function key f50
+ * key_f51: sent by function key f51
+ * key_f52: sent by function key f52
+ * key_f53: sent by function key f53
+ * key_f54: sent by function key f54
+ * key_f55: sent by function key f55
+ * key_f56: sent by function key f56
+ * key_f57: sent by function key f57
+ * key_f58: sent by function key f58
+ * key_f59: sent by function key f59
+ * key_f60: sent by function key f60
+ * key_f61: sent by function key f61
+ * key_f62: sent by function key f62
+ * key_f63: sent by function key f63
+ * key_find: 0
+ * key_help: sent by help key
+ * key_home: sent by home key
+ * key_ic: sent by ins-char/enter ins-mode key
+ * key_il: sent by insert-line key
+ * key_left: sent by terminal left-arrow key
+ * key_ll: sent by home-down key
+ * key_mark: sent by mark key
+ * key_message: sent by message key
+ * key_mouse: 0631, Mouse event has occured
+ * key_move: sent by move key
+ * key_next: sent by next-object key
+ * key_npage: sent by next-page key
+ * key_open: sent by open key
+ * key_options: sent by options key
+ * key_ppage: sent by previous-page key
+ * key_previous: sent by previous-object key
+ * key_print: sent by print or copy key
+ * key_redo: sent by redo key
+ * key_reference: sent by ref(erence) key
+ * key_refresh: sent by refresh key
+ * key_replace: sent by replace key
+ * key_restart: sent by restart key
+ * key_resume: sent by resume key
+ * key_right: sent by terminal right-arrow key
+ * key_save: sent by save key
+ * key_sbeg: sent by shifted beginning key
+ * key_scancel: sent by shifted cancel key
+ * key_scommand: sent by shifted command key
+ * key_scopy: sent by shifted copy key
+ * key_screate: sent by shifted create key
+ * key_sdc: sent by shifted delete-char key
+ * key_sdl: sent by shifted delete-line key
+ * key_select: sent by select key
+ * key_send: sent by shifted end key
+ * key_seol: sent by shifted clear-line key
+ * key_sexit: sent by shited exit key
+ * key_sf: sent by scroll-forward/down key
+ * key_sfind: sent by shifted find key
+ * key_shelp: sent by shifted help key
+ * key_shome: sent by shifted home key
+ * key_sic: sent by shifted input key
+ * key_sleft: sent by shifted left-arrow key
+ * key_smessage: sent by shifted message key
+ * key_smove: sent by shifted move key
+ * key_snext: sent by shifted next key
+ * key_soptions: sent by shifted options key
+ * key_sprevious: sent by shifted prev key
+ * key_sprint: sent by shifted print key
+ * key_sr: sent by scroll-backwards/up key
+ * key_sredo: sent by shifted redo key
+ * key_sreplace: sent by shifted replace key
+ * key_sright: sent by shifted right-arrow key
+ * key_srsume: sent by shifted resume key
+ * key_ssave: sent by shifted save key
+ * key_ssuspend: sent by shifted suspend key
+ * key_stab: sent by set-tab key
+ * key_sundo: sent by shifted undo key
+ * key_suspend: sent by suspend key
+ * key_undo: sent by undo key
+ * key_up: sent by terminal up-arrow key
+ * keypad_local: Out of "keypad-transmit" mode
+ * keypad_xmit: Put terminal in "keypad-transmit" mode
+ * lab_f0: Labels on function key f0 if not f0
+ * lab_f1: Labels on function key f1 if not f1
+ * lab_f2: Labels on function key f2 if not f2
+ * lab_f3: Labels on function key f3 if not f3
+ * lab_f4: Labels on function key f4 if not f4
+ * lab_f5: Labels on function key f5 if not f5
+ * lab_f6: Labels on function key f6 if not f6
+ * lab_f7: Labels on function key f7 if not f7
+ * lab_f8: Labels on function key f8 if not f8
+ * lab_f9: Labels on function key f9 if not f9
+ * lab_f10: Labels on function key f10 if not f10
+ * label_format: Label format
+ * label_off: Turn off soft labels
+ * label_on: Turn on soft labels
+ * meta_off: Turn off "meta mode"
+ * meta_on: Turn on "meta mode" (8th bit)
+ * micro_column_address: Like column_address for micro adjustment
+ * micro_down: Like cursor_down for micro adjustment
+ * micro_left: Like cursor_left for micro adjustment
+ * micro_right: Like cursor_right for micro adjustment
+ * micro_row_address: Like row_address for micro adjustment
+ * micro_up: Like cursor_up for micro adjustment
+ * mouse_info: Mouse status information
+ * newline: Newline (behaves like cr followed by lf)
+ * order_of_pins: Matches software bits to print-head pins
+ * orig_colors: Set all colour(-pair)s to original ones
+ * orig_pair: Set default colour-pair to the original one
+ * pad_char: Pad character (rather than NULL)
+ * parm_dch: Delete #1 chars
+ * parm_delete_line: Delete #1 lines
+ * parm_down_cursor: Move down #1 lines
+ * parm_down_micro: Like parm_down_cursor for micro adjustment
+ * parm_ich: Insert #1 blank chars
+ * parm_index: Scroll forward #1 lines
+ * parm_insert_line: Add #1 new blank lines
+ * parm_left_cursor: Move cursor left #1 lines
+ * parm_left_micro: Like parm_left_cursor for micro adjustment
+ * parm_right_cursor: Move right #1 spaces
+ * parm_right_micro: Like parm_right_cursor for micro adjustment
+ * parm_rindex: Scroll backward #1 lines
+ * parm_up_cursor: Move cursor up #1 lines
+ * parm_up_micro: Like parm_up_cursor for micro adjustment
+ * pc_term_options: PC terminal options
+ * pkey_key: Prog funct key #1 to type string #2
+ * pkey_local: Prog funct key #1 to execute string #2
+ * pkey_plab: Prog key #1 to xmit string #2 and show string #3
+ * pkey_xmit: Prog funct key #1 to xmit string #2
+ * plab_norm: Prog label #1 to show string #2
+ * print_screen: Print contents of screen
+ * ptr_non: Turn off printer for #1 bytes
+ * ptr_off: Turn off the printer
+ * ptr_on: Turn on the printer
+ * pulse: Select pulse dialing
+ * quick_dial: Dial phone number #1, without progress detection
+ * remove_clock: Remove time-of-day clock
+ * repeat_char: Repeat char #1 #2 times
+ * req_for_input: Send next input char (for ptys)
+ * req_mouse_pos: Request mouse position report
+ * reset_1string: Reset terminal completely to sane modes
+ * reset_2string: Reset terminal completely to sane modes
+ * reset_3string: Reset terminal completely to sane modes
+ * reset_file: Name of file containing reset string
+ * restore_cursor: Restore cursor to position of last sc
+ * row_address: Set vertical position to absolute #1
+ * save_cursor: Save cursor position
+ * scancode_escape: Escape for scancode emulation
+ * scroll_forward: Scroll text up
+ * scroll_reverse: Scroll text down
+ * select_char_set: Select character set
+ * set0_des_seq: Shift into codeset 0 (EUC set 0, ASCII)
+ * set1_des_seq: Shift into codeset 1
+ * set2_des_seq: Shift into codeset 2
+ * set3_des_seq: Shift into codeset 3
+ * set_a_attributes: Define second set of video attributes #1-#6
+ * set_a_background: Set background colour to #1 using ANSI escape
+ * set_a_foreground: Set foreground colour to #1 using ANSI escape
+ * set_attributes: Define first set of video attributes #1-#9
+ * set_background: Set background colour to #1
+ * set_bottom_margin: Set bottom margin at current line
+ * set_bottom_margin_parm: Set bottom margin at line #1 or #2 lines from bottom
+ * set_clock: Set clock to hours (#1), minutes (#2), seconds (#3)
+ * set_color_band: Change ribbon to colour #1
+ * set_color_pair: Set current colour pair to #1
+ * set_foreground: Set foreground colour to #1
+ * set_left_margin: Set left margin at current column
+ * set_left_margin_parm: Set left (right) margin at column #1 (#2)
+ * set_lr_margin: Sets both left and right margins
+ * set_page_length: Set page length to #1 lines
+ * set_pglen_inch: Set page length to #1 hundredth of an inch
+ * set_right_margin: Set right margin at current column
+ * set_right_margin_parm: Set right margin at #1
+ * set_tab: Set a tab in all rows, current column
+ * set_tb_margin: Sets both top and bottom margins
+ * set_top_margin: Set top margin at current line
+ * set_top_margin_parm: Set top (bottom) margin at line #1 (#2)
+ * set_window: Current window is lines #1-#2 cols #3-#4
+ * start_bit_image: Start printing bit image graphics
+ * start_char_set_def: Start definition of a character set
+ * stop_bit_image: End printing bit image graphics
+ * stop_char_set_def: End definition of a character set
+ * subscript_characters: List of "subscript-able" characters
+ * superscript_characters: List of "superscript-able" characters
+ * tab: Tab to next 8-space hardware tab stop
+ * these_cause_cr: Printing any of these characters causes cr
+ * to_status_line: Go to status line, col #1
+ * tone: Select tone touch dialing
+ * user0: User string 0
+ * user1: User string 1
+ * user2: User string 2
+ * user3: User string 3
+ * user4: User string 4
+ * user5: User string 5
+ * user6: User string 6
+ * user7: User string 7
+ * user8: User string 8
+ * user9: User string 9
+ * underline_char: Underscore one char and move past it
+ * up_half_line: Half-line up (reverse 1/2 linefeed)
+ * wait_tone: Wait for dial tone
+ * xoff_character: X-off character
+ * xon_character: X-on character
+ * zero_motion: No motion for the subsequent character
+ */
+
+#ifndef _TERMINFO
+typedef struct {
+ int fildes;
+ /* We need to expose these so that the macros work */
+ const char *name;
+ const char *desc;
+ signed char *flags;
+ short *nums;
+ const char **strs;
+} TERMINAL;
+#endif
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+extern TERMINAL *cur_term;
+
+/* setup functions */
+int setupterm(const char *, int, int *);
+TERMINAL * set_curterm(TERMINAL *);
+int del_curterm(TERMINAL *);
+char * termname(void);
+char * longname(void);
+
+/* information functions */
+int tigetflag(const char *);
+int tigetnum(const char *);
+char * tigetstr(const char *);
+/* You should note that the spec allows stuffing a char * into a long
+ * if the platform allows and the %pN is followed immediately by %l or %s */
+char * tparm(const char *, long, long, long, long, long,
+ long, long, long, long);
+
+/* Non standard functions, but provide a level of thread safety */
+int ti_setupterm(TERMINAL **, const char *, int, int *);
+int ti_getflag(const TERMINAL *, const char *);
+int ti_getnum(const TERMINAL *, const char *);
+const char * ti_getstr(const TERMINAL *, const char *);
+char * ti_parm(TERMINAL *, const char *,
+ long, long, long, long, long, long, long, long, long);
+
+/* These functions do not use PC or speed, but the terminal */
+int ti_puts(const TERMINAL *, const char *, int,
+ int (*)(int, void *), void *);
+int ti_putp(const TERMINAL *, const char *);
+
+/* Using tparm can be kunkly, so provide a variadic function
+ * Numbers have to be passed as int */
+/* This is not standard, but ncurses also provides this */
+char * tiparm(const char *, ...);
+/* And a thread safe version */
+char * ti_tiparm(TERMINAL *, const char *, ...);
+
+#ifdef TPARM_TLPARM
+/* Same as the above, but numbers have to be passed as long */
+char * tlparm(const char *, ...);
+/* And a thread safe version */
+char * ti_tlparm(TERMINAL *, const char *, ...);
+#endif
+
+/* Default to X/Open tparm, but allow it to be variadic also */
+#ifdef TPARM_VARARGS
+# define tparm tiparm
+# define ti_parm ti_tiparm
+#endif
+
+/* Convert a termcap string into a terminfo string.
+ * The passed string is destroyed and the return string needs to be freed. */
+char * captoinfo(char *);
+
+/* POSIX says that term.h should also pull in our termcap definitions. */
+#include "terminfo_termcap.h"
+
+__END_DECLS
+#endif
Index: contrib/mg/terminfo_termcap.h
===================================================================
--- /dev/null
+++ contrib/mg/terminfo_termcap.h
@@ -0,0 +1,54 @@
+/* $NetBSD: termcap.h,v 1.2 2011/04/11 21:13:09 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TERMCAP_H_
+#define _TERMCAP_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/* Output functions.
+ * These are still valid for terminfo. */
+int putp(const char *);
+int tputs(const char *, int, int (*)(int));
+
+extern short ospeed;
+extern char PC;
+extern char *BC;
+extern char *UP;
+
+int tgetent(char *, const char *);
+char * tgetstr(const char *, char **);
+int tgetflag(const char *);
+int tgetnum(const char *);
+char * tgoto(const char *, int, int);
+
+__END_DECLS
+#endif
Index: contrib/mg/ti.c
===================================================================
--- /dev/null
+++ contrib/mg/ti.c
@@ -0,0 +1,126 @@
+/* $NetBSD: ti.c,v 1.3 2013/06/07 13:16:18 roy Exp $ */
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ *
+ * This id is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source id must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <string.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#ifndef __UNCONST
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+
+int
+ti_getflag(const TERMINAL *term, const char *id)
+{
+ ssize_t ind;
+ size_t i;
+ TERMUSERDEF *ud;
+
+ ind = _ti_flagindex(id);
+ if (ind != -1)
+ return term->flags[ind];
+ for (i = 0; i < term->_nuserdefs; i++) {
+ ud = &term->_userdefs[i];
+ if (ud->type == 'f' && strcmp(ud->id, id) == 0)
+ return ud->flag;
+ }
+ return ABSENT_BOOLEAN;
+}
+
+int
+tigetflag(const char *id)
+{
+
+ if (cur_term != NULL)
+ return ti_getflag(cur_term, id);
+ return ABSENT_BOOLEAN;
+}
+
+int
+ti_getnum(const TERMINAL *term, const char *id)
+{
+ ssize_t ind;
+ size_t i;
+ TERMUSERDEF *ud;
+
+ ind = _ti_numindex(id);
+ if (ind != -1) {
+ if (!VALID_NUMERIC(term->nums[ind]))
+ return ABSENT_NUMERIC;
+ return term->nums[ind];
+ }
+ for (i = 0; i < term->_nuserdefs; i++) {
+ ud = &term->_userdefs[i];
+ if (ud->type == 'n' && strcmp(ud->id, id) == 0) {
+ if (!VALID_NUMERIC(ud->num))
+ return ABSENT_NUMERIC;
+ return ud->num;
+ }
+ }
+ return CANCELLED_NUMERIC;
+}
+
+int
+tigetnum(const char *id)
+{
+
+ if (cur_term != NULL)
+ return ti_getnum(cur_term, id);
+ return CANCELLED_NUMERIC;
+}
+
+const char *
+ti_getstr(const TERMINAL *term, const char *id)
+{
+ ssize_t ind;
+ size_t i;
+ TERMUSERDEF *ud;
+
+ ind = _ti_strindex(id);
+ if (ind != -1)
+ return term->strs[ind];
+ for (i = 0; i < term->_nuserdefs; i++) {
+ ud = &term->_userdefs[i];
+ if (ud->type == 's' && strcmp(ud->id, id) == 0)
+ return ud->str;
+ }
+ return (const char *)CANCELLED_STRING;
+}
+
+char *
+tigetstr(const char *id)
+{
+
+ if (cur_term != NULL)
+ return __UNCONST(ti_getstr(cur_term, id));
+ return (char *)CANCELLED_STRING;
+}
Index: contrib/mg/tic.c
===================================================================
--- /dev/null
+++ contrib/mg/tic.c
@@ -0,0 +1,629 @@
+/* $NetBSD: tic.c,v 1.31 2017/10/02 21:53:55 joerg Exp $ */
+
+/*
+ * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <search.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#if defined(__linux__) || defined(__CYGWIN__)
+#include "util.h"
+#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#include "cdbw.h"
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#define HASH_SIZE 16384 /* 2012-06-01: 3600 entries */
+
+#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+
+#ifndef TAILQ_REMOVE_HEAD
+#define TAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->tqh_first = (head)->tqh_first->field.tqe_next) == \
+ NULL) \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+#endif
+
+typedef struct term {
+ TAILQ_ENTRY(term) next;
+ char *name;
+ TIC *tic;
+ uint32_t id;
+ struct term *base_term;
+} TERM;
+static TAILQ_HEAD(, term) terms = TAILQ_HEAD_INITIALIZER(terms);
+
+static int error_exit;
+static int Sflag;
+static size_t nterm, nalias;
+
+static void
+dowarn(const char *fmt, ...)
+{
+ va_list va;
+
+ error_exit = 1;
+ va_start(va, fmt);
+ vwarnx(fmt, va);
+ va_end(va);
+}
+
+static char *
+grow_tbuf(TBUF *tbuf, size_t len)
+{
+ char *buf;
+
+ buf = _ti_grow_tbuf(tbuf, len);
+ if (buf == NULL)
+ err(1, "_ti_grow_tbuf");
+ return buf;
+}
+
+/* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */
+static uint16_t
+le16dec(const void *buf)
+{
+ const uint8_t *p = (const uint8_t *)buf;
+
+ return ((p[1] << 8) | p[0]);
+}
+
+static void
+le16enc(void *buf, uint32_t u)
+{
+ uint8_t *p = (uint8_t *) buf;
+
+ p[0] = u & 0xff;
+ p[1] = ((unsigned)u >> 8) & 0xff;
+}
+
+void
+le32enc(void *buf, uint32_t u)
+{
+ uint8_t *p = (uint8_t *) buf;
+
+ p[0] = u & 0xff;
+ p[1] = (u >> 8) & 0xff;
+ p[2] = (u >> 16) & 0xff;
+ p[3] = (u >> 24) & 0xff;
+}
+
+static int
+save_term(struct cdbw *db, TERM *term)
+{
+ uint8_t *buf;
+ ssize_t len;
+ size_t slen = strlen(term->name) + 1;
+
+ if (term->base_term != NULL) {
+ len = (ssize_t)slen + 7;
+ buf = malloc(len);
+ buf[0] = 2;
+ le32enc(buf + 1, term->base_term->id);
+ le16enc(buf + 5, slen);
+ memcpy(buf + 7, term->name, slen);
+ if (cdbw_put(db, term->name, slen, buf, len))
+ err(1, "cdbw_put");
+ free(buf);
+ return 0;
+ }
+
+ len = _ti_flatten(&buf, term->tic);
+ if (len == -1)
+ return -1;
+
+ if (cdbw_put_data(db, buf, len, &term->id))
+ err(1, "cdbw_put_data");
+ if (cdbw_put_key(db, term->name, slen, term->id))
+ err(1, "cdbw_put_key");
+ free(buf);
+ return 0;
+}
+
+static TERM *
+find_term(const char *name)
+{
+ ENTRY elem, *elemp;
+
+ elem.key = __UNCONST(name);
+ elem.data = NULL;
+ elemp = hsearch(elem, FIND);
+ return elemp ? (TERM *)elemp->data : NULL;
+}
+
+static TERM *
+store_term(const char *name, TERM *base_term)
+{
+ TERM *term;
+ ENTRY elem;
+
+ term = calloc(1, sizeof(*term));
+ term->name = strdup(name);
+ TAILQ_INSERT_TAIL(&terms, term, next);
+ elem.key = strdup(name);
+ elem.data = term;
+ hsearch(elem, ENTER);
+
+ term->base_term = base_term;
+ if (base_term != NULL)
+ nalias++;
+ else
+ nterm++;
+
+ return term;
+}
+
+static int
+process_entry(TBUF *buf, int flags)
+{
+ char *p, *e, *alias;
+ TERM *term;
+ TIC *tic;
+
+ if (buf->bufpos == 0)
+ return 0;
+ /* Terminate the string */
+ buf->buf[buf->bufpos - 1] = '\0';
+ /* First rewind the buffer for new entries */
+ buf->bufpos = 0;
+
+ if (isspace((unsigned char)*buf->buf))
+ return 0;
+
+ tic = _ti_compile(buf->buf, flags);
+ if (tic == NULL)
+ return 0;
+
+ if (find_term(tic->name) != NULL) {
+ dowarn("%s: duplicate entry", tic->name);
+ _ti_freetic(tic);
+ return 0;
+ }
+ term = store_term(tic->name, NULL);
+ term->tic = tic;
+
+ /* Create aliased terms */
+ if (tic->alias != NULL) {
+ alias = p = strdup(tic->alias);
+ while (p != NULL && *p != '\0') {
+ e = strchr(p, '|');
+ if (e != NULL)
+ *e++ = '\0';
+ if (find_term(p) != NULL) {
+ dowarn("%s: has alias for already assigned"
+ " term %s", tic->name, p);
+ } else {
+ store_term(p, term);
+ }
+ p = e;
+ }
+ free(alias);
+ }
+
+ return 0;
+}
+
+static void
+merge(TIC *rtic, TIC *utic, int flags)
+{
+ char *cap, flag, *code, type, *str;
+ short ind, num;
+ size_t n;
+
+ cap = utic->flags.buf;
+ for (n = utic->flags.entries; n > 0; n--) {
+ ind = le16dec(cap);
+ cap += sizeof(uint16_t);
+ flag = *cap++;
+ if (VALID_BOOLEAN(flag) &&
+ _ti_find_cap(&rtic->flags, 'f', ind) == NULL)
+ {
+ _ti_grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1);
+ le16enc(rtic->flags.buf + rtic->flags.bufpos, ind);
+ rtic->flags.bufpos += sizeof(uint16_t);
+ rtic->flags.buf[rtic->flags.bufpos++] = flag;
+ rtic->flags.entries++;
+ }
+ }
+
+ cap = utic->nums.buf;
+ for (n = utic->nums.entries; n > 0; n--) {
+ ind = le16dec(cap);
+ cap += sizeof(uint16_t);
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (VALID_NUMERIC(num) &&
+ _ti_find_cap(&rtic->nums, 'n', ind) == NULL)
+ {
+ grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2);
+ le16enc(rtic->nums.buf + rtic->nums.bufpos, ind);
+ rtic->nums.bufpos += sizeof(uint16_t);
+ le16enc(rtic->nums.buf + rtic->nums.bufpos, num);
+ rtic->nums.bufpos += sizeof(uint16_t);
+ rtic->nums.entries++;
+ }
+ }
+
+ cap = utic->strs.buf;
+ for (n = utic->strs.entries; n > 0; n--) {
+ ind = le16dec(cap);
+ cap += sizeof(uint16_t);
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (num > 0 &&
+ _ti_find_cap(&rtic->strs, 's', ind) == NULL)
+ {
+ grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num);
+ le16enc(rtic->strs.buf + rtic->strs.bufpos, ind);
+ rtic->strs.bufpos += sizeof(uint16_t);
+ le16enc(rtic->strs.buf + rtic->strs.bufpos, num);
+ rtic->strs.bufpos += sizeof(uint16_t);
+ memcpy(rtic->strs.buf + rtic->strs.bufpos,
+ cap, num);
+ rtic->strs.bufpos += num;
+ rtic->strs.entries++;
+ }
+ cap += num;
+ }
+
+ cap = utic->extras.buf;
+ for (n = utic->extras.entries; n > 0; n--) {
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ code = cap;
+ cap += num;
+ type = *cap++;
+ flag = 0;
+ str = NULL;
+ switch (type) {
+ case 'f':
+ flag = *cap++;
+ if (!VALID_BOOLEAN(flag))
+ continue;
+ break;
+ case 'n':
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ if (!VALID_NUMERIC(num))
+ continue;
+ break;
+ case 's':
+ num = le16dec(cap);
+ cap += sizeof(uint16_t);
+ str = cap;
+ cap += num;
+ if (num == 0)
+ continue;
+ break;
+ }
+ _ti_store_extra(rtic, 0, code, type, flag, num, str, num,
+ flags);
+ }
+}
+
+static size_t
+merge_use(int flags)
+{
+ size_t skipped, merged, memn;
+ char *cap, *scap;
+ uint16_t num;
+ TIC *rtic, *utic;
+ TERM *term, *uterm;;
+
+ skipped = merged = 0;
+ TAILQ_FOREACH(term, &terms, next) {
+ if (term->base_term != NULL)
+ continue;
+ rtic = term->tic;
+ while ((cap = _ti_find_extra(&rtic->extras, "use")) != NULL) {
+ if (*cap++ != 's') {
+ dowarn("%s: use is not string", rtic->name);
+ break;
+ }
+ cap += sizeof(uint16_t);
+ if (strcmp(rtic->name, cap) == 0) {
+ dowarn("%s: uses itself", rtic->name);
+ goto remove;
+ }
+ uterm = find_term(cap);
+ if (uterm != NULL && uterm->base_term != NULL)
+ uterm = uterm->base_term;
+ if (uterm == NULL) {
+ dowarn("%s: no use record for %s",
+ rtic->name, cap);
+ goto remove;
+ }
+ utic = uterm->tic;
+ if (strcmp(utic->name, rtic->name) == 0) {
+ dowarn("%s: uses itself", rtic->name);
+ goto remove;
+ }
+ if (_ti_find_extra(&utic->extras, "use") != NULL) {
+ skipped++;
+ break;
+ }
+ cap = _ti_find_extra(&rtic->extras, "use");
+ merge(rtic, utic, flags);
+ remove:
+ /* The pointers may have changed, find the use again */
+ cap = _ti_find_extra(&rtic->extras, "use");
+ if (cap == NULL)
+ dowarn("%s: use no longer exists - impossible",
+ rtic->name);
+ else {
+ scap = cap - (4 + sizeof(uint16_t));
+ cap++;
+ num = le16dec(cap);
+ cap += sizeof(uint16_t) + num;
+ memn = rtic->extras.bufpos -
+ (cap - rtic->extras.buf);
+ memmove(scap, cap, memn);
+ rtic->extras.bufpos -= cap - scap;
+ cap = scap;
+ rtic->extras.entries--;
+ merged++;
+ }
+ }
+ }
+
+ if (merged == 0 && skipped != 0)
+ dowarn("circular use detected");
+ return merged;
+}
+
+static int
+print_dump(int argc, char **argv)
+{
+ TERM *term;
+ uint8_t *buf;
+ int i, n;
+ size_t j, col;
+ ssize_t len;
+
+ printf("struct compiled_term {\n");
+ printf("\tconst char *name;\n");
+ printf("\tconst char *cap;\n");
+ printf("\tsize_t caplen;\n");
+ printf("};\n\n");
+
+ printf("const struct compiled_term compiled_terms[] = {\n");
+
+ n = 0;
+ for (i = 0; i < argc; i++) {
+ term = find_term(argv[i]);
+ if (term == NULL) {
+ warnx("%s: no description for terminal", argv[i]);
+ continue;
+ }
+ if (term->base_term != NULL) {
+ warnx("%s: cannot dump alias", argv[i]);
+ continue;
+ }
+ /* Don't compile the aliases in, save space */
+ free(term->tic->alias);
+ term->tic->alias = NULL;
+ len = _ti_flatten(&buf, term->tic);
+ if (len == 0 || len == -1)
+ continue;
+
+ printf("\t{\n");
+ printf("\t\t\"%s\",\n", argv[i]);
+ n++;
+ for (j = 0, col = 0; j < (size_t)len; j++) {
+ if (col == 0) {
+ printf("\t\t\"");
+ col = 16;
+ }
+
+ col += printf("\\%03o", (uint8_t)buf[j]);
+ if (col > 75) {
+ printf("\"%s\n",
+ j + 1 == (size_t)len ? "," : "");
+ col = 0;
+ }
+ }
+ if (col != 0)
+ printf("\",\n");
+ printf("\t\t%zu\n", len);
+ printf("\t}");
+ if (i + 1 < argc)
+ printf(",");
+ printf("\n");
+ free(buf);
+ }
+ printf("};\n");
+
+ return n;
+}
+
+static void
+write_database(const char *dbname)
+{
+ struct cdbw *db;
+ char *tmp_dbname;
+ TERM *term;
+ int fd;
+
+ db = cdbw_open();
+ if (db == NULL)
+ err(1, "cdbw_open failed");
+ /* Save the terms */
+ TAILQ_FOREACH(term, &terms, next)
+ save_term(db, term);
+
+ asprintf(&tmp_dbname, "%s.XXXXXX", dbname);
+ fd = mkstemp(tmp_dbname);
+ if (fd == -1)
+ err(1, "creating temporary database %s failed", tmp_dbname);
+ if (cdbw_output(db, fd, "NetBSD terminfo", cdbw_stable_seeder))
+ err(1, "writing temporary database %s failed", tmp_dbname);
+ if (fchmod(fd, DEFFILEMODE))
+ err(1, "fchmod failed");
+ if (close(fd))
+ err(1, "writing temporary database %s failed", tmp_dbname);
+ if (rename(tmp_dbname, dbname))
+ err(1, "renaming %s to %s failed", tmp_dbname, dbname);
+ free(tmp_dbname);
+ cdbw_close(db);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, cflag, sflag, flags;
+ char *source, *dbname, *buf, *ofile;
+ FILE *f;
+ size_t buflen;
+ ssize_t len;
+ TBUF tbuf;
+ struct term *term;
+
+ cflag = sflag = 0;
+ ofile = NULL;
+ flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING;
+ while ((ch = getopt(argc, argv, "Saco:sx")) != -1)
+ switch (ch) {
+ case 'S':
+ Sflag = 1;
+ /* We still compile aliases so that use= works.
+ * However, it's removed before we flatten to save space. */
+ flags &= ~TIC_DESCRIPTION;
+ break;
+ case 'a':
+ flags |= TIC_COMMENT;
+ break;
+ case 'c':
+ cflag = 1;
+ break;
+ case 'o':
+ ofile = optarg;
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ case 'x':
+ flags |= TIC_EXTRA;
+ break;
+ case '?': /* FALLTHROUGH */
+ default:
+ fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n",
+ getprogname());
+ return EXIT_FAILURE;
+ }
+
+ if (optind == argc)
+ errx(1, "No source file given");
+ source = argv[optind++];
+ f = fopen(source, "r");
+ if (f == NULL)
+ err(1, "fopen: %s", source);
+
+ hcreate(HASH_SIZE);
+
+ buf = tbuf.buf = NULL;
+ buflen = tbuf.buflen = tbuf.bufpos = 0;
+ while ((len = getline(&buf, &buflen, f)) != -1) {
+ /* Skip comments */
+ if (*buf == '#')
+ continue;
+ if (buf[len - 1] != '\n') {
+ process_entry(&tbuf, flags);
+ dowarn("last line is not a comment"
+ " and does not end with a newline");
+ continue;
+ }
+ /*
+ * If the first char is space not a space then we have a
+ * new entry, so process it.
+ */
+ if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0)
+ process_entry(&tbuf, flags);
+
+ /* Grow the buffer if needed */
+ grow_tbuf(&tbuf, len);
+ /* Append the string */
+ memcpy(tbuf.buf + tbuf.bufpos, buf, len);
+ tbuf.bufpos += len;
+ }
+ free(buf);
+ /* Process the last entry if not done already */
+ process_entry(&tbuf, flags);
+ free(tbuf.buf);
+
+ /* Merge use entries until we have merged all we can */
+ while (merge_use(flags) != 0)
+ ;
+
+ if (Sflag) {
+ print_dump(argc - optind, argv + optind);
+ return error_exit;
+ }
+
+ if (cflag)
+ return error_exit;
+
+ if (ofile == NULL)
+ asprintf(&dbname, "%s.cdb", source);
+ else
+ dbname = ofile;
+ write_database(dbname);
+
+ if (sflag != 0)
+ fprintf(stderr, "%zu entries and %zu aliases written to %s\n",
+ nterm, nalias, dbname);
+
+ if (ofile == NULL)
+ free(dbname);
+ while ((term = TAILQ_FIRST(&terms)) != NULL) {
+ TAILQ_REMOVE_HEAD(&terms, next);
+ _ti_freetic(term->tic);
+ free(term->name);
+ free(term);
+ }
+
+ return EXIT_SUCCESS;
+}
Index: contrib/mg/tparm.c
===================================================================
--- /dev/null
+++ contrib/mg/tparm.c
@@ -0,0 +1,620 @@
+/* $NetBSD: tparm.c,v 1.17 2017/05/04 09:42:23 roy Exp $ */
+
+/*
+ * Copyright (c) 2009, 2011, 2013 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#define LONG_STR_MAX ((CHAR_BIT * sizeof(long)) / 3)
+#define BUFINC 128 /* Size to increament the terminal buffer by */
+
+#define VA_LONG_LONG 1
+#define VA_CHAR_INT 2
+//#define VA_CHAR_LONG 3 /* No need for this yet */
+
+static TERMINAL *dumbterm; /* For non thread safe functions */
+
+typedef struct {
+ long nums[20];
+ char *strings[20];
+ size_t offset;
+} TPSTACK;
+
+typedef struct {
+ long num;
+ char *string;
+} TPVAR;
+
+static int
+push(long num, char *string, TPSTACK *stack)
+{
+ if (stack->offset >= sizeof(stack->nums)) {
+ errno = E2BIG;
+ return -1;
+ }
+ stack->nums[stack->offset] = num;
+ stack->strings[stack->offset] = string;
+ stack->offset++;
+ return 0;
+}
+
+static int
+pop(long *num, char **string, TPSTACK *stack)
+{
+ if (stack->offset == 0) {
+ if (num)
+ *num = 0;
+ if (string)
+ *string = NULL;
+ errno = E2BIG;
+ return -1;
+ }
+ stack->offset--;
+ if (num)
+ *num = stack->nums[stack->offset];
+ if (string)
+ *string = stack->strings[stack->offset];
+ return 0;
+}
+
+static char *
+checkbuf(TERMINAL *term, size_t len)
+{
+ char *buf;
+
+ if (term->_bufpos + len >= term->_buflen) {
+ len = term->_buflen + MAX(len, BUFINC);
+ buf = realloc(term->_buf, len);
+ if (buf == NULL)
+ return NULL;
+ term->_buf = buf;
+ term->_buflen = len;
+ }
+ return term->_buf;
+}
+
+static size_t
+ochar(TERMINAL *term, int c)
+{
+ if (c == 0)
+ c = 0200;
+ /* Check we have space and a terminator */
+ if (checkbuf(term, 2) == NULL)
+ return 0;
+ term->_buf[term->_bufpos++] = (char)c;
+ return 1;
+}
+
+static size_t
+onum(TERMINAL *term, const char *fmt, int num, size_t len)
+{
+ int l;
+ size_t r;
+
+ if (len < LONG_STR_MAX)
+ len = LONG_STR_MAX;
+ if (checkbuf(term, len + 2) == NULL)
+ return 0;
+ l = snprintf(term->_buf + term->_bufpos, len + 2, fmt, num);
+ if (l == -1)
+ return 0;
+ r = (size_t)l;
+ term->_bufpos += r;
+ return r;
+}
+
+/*
+ Make a pass through the string so we can work out
+ which parameters are ints and which are char *.
+ Basically we only use char * if %p[1-9] is followed by %l or %s.
+*/
+int
+_ti_parm_analyse(const char *str, int *piss, int piss_len)
+{
+ int nparm, lpop;
+ char c;
+
+ nparm = 0;
+ lpop = -1;
+ while ((c = *str++) != '\0') {
+ if (c != '%')
+ continue;
+ c = *str++;
+ switch (c) {
+ case 'l': /* FALLTHROUGH */
+ case 's':
+ if (lpop > 0) {
+ if (lpop <= piss_len)
+ piss[lpop - 1] = 1;
+ else if (piss)
+ errno = E2BIG;
+ }
+ break;
+ case 'p':
+ c = *str++;
+ if (c < '1' || c > '9') {
+ errno = EINVAL;
+ continue;
+ } else {
+ lpop = c - '0';
+ if (lpop > nparm)
+ nparm = lpop;
+ }
+ break;
+ default:
+ lpop = -1;
+ }
+ }
+
+ return nparm;
+}
+
+static char *
+_ti_tiparm(TERMINAL *term, const char *str, int va_type, va_list parms)
+{
+ char c, fmt[64], *fp, *ostr;
+ long val, val2;
+ long dnums[26]; /* dynamic variables a-z, not preserved */
+ size_t l, max, width, precision, olen;
+ TPSTACK stack;
+ TPVAR params[TPARM_MAX];
+ unsigned int done, dot, minus;
+ int piss[TPARM_MAX]; /* Parameter IS String - piss ;) */
+
+ if (str == NULL)
+ return NULL;
+
+ /*
+ If not passed a terminal, malloc a dummy one.
+ This means we can preserve buffers and variables per terminal and
+ still work with non thread safe functions (which sadly are still the
+ norm and standard).
+ */
+ if (term == NULL) {
+ if (dumbterm == NULL) {
+ dumbterm = malloc(sizeof(*dumbterm));
+ if (dumbterm == NULL)
+ return NULL;
+ dumbterm->_buflen = 0;
+ }
+ term = dumbterm;
+ }
+
+ term->_bufpos = 0;
+ /* Ensure we have an initial buffer */
+ if (term->_buflen == 0) {
+ term->_buf = malloc(BUFINC);
+ if (term->_buf == NULL)
+ return NULL;
+ term->_buflen = BUFINC;
+ }
+
+ memset(&piss, 0, sizeof(piss));
+ max = (size_t)_ti_parm_analyse(str, piss, TPARM_MAX);
+
+ /* Put our parameters into variables */
+ memset(&params, 0, sizeof(params));
+ for (l = 0; l < max; l++) {
+ if (piss[l]) {
+ if (va_type == VA_LONG_LONG) {
+ /* This only works if char * fits into a long
+ * on this platform. */
+ if (sizeof(char *) <= sizeof(long)/*CONSTCOND*/)
+ params[l].string =
+ (char *)va_arg(parms, long);
+ else {
+ errno = ENOTSUP;
+ return NULL;
+ }
+ } else
+ params[l].string = va_arg(parms, char *);
+ } else {
+ if (va_type == VA_CHAR_INT)
+ params[l].num = (long)va_arg(parms, int);
+ else
+ params[l].num = va_arg(parms, long);
+ }
+ }
+
+ memset(&stack, 0, sizeof(stack));
+ while ((c = *str++) != '\0') {
+ if (c != '%' || (c = *str++) == '%') {
+ if (c == '\0')
+ break;
+ if (ochar(term, c) == 0)
+ return NULL;
+ continue;
+ }
+
+ /* Handle formatting. */
+ fp = fmt;
+ *fp++ = '%';
+ done = dot = minus = width = precision = 0;
+ val = 0;
+ while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) {
+ switch (c) {
+ case 'c': /* FALLTHROUGH */
+ case 's':
+ *fp++ = c;
+ done = 1;
+ break;
+ case 'd': /* FALLTHROUGH */
+ case 'o': /* FALLTHROUGH */
+ case 'x': /* FALLTHROUGH */
+ case 'X': /* FALLTHROUGH */
+ *fp++ = 'l';
+ *fp++ = c;
+ done = 1;
+ break;
+ case '#': /* FALLTHROUGH */
+ case ' ':
+ *fp++ = c;
+ break;
+ case '.':
+ *fp++ = c;
+ if (dot == 0) {
+ dot = 1;
+ width = (size_t)val;
+ } else
+ done = 2;
+ val = 0;
+ break;
+ case ':':
+ minus = 1;
+ break;
+ case '-':
+ if (minus)
+ *fp++ = c;
+ else
+ done = 1;
+ break;
+ default:
+ if (isdigit((unsigned char)c)) {
+ val = (val * 10) + (c - '0');
+ if (val > 10000)
+ done = 2;
+ else
+ *fp++ = c;
+ } else
+ done = 1;
+ }
+ if (done == 0)
+ c = *str++;
+ }
+ if (done == 2) {
+ /* Found an error in the format */
+ fp = fmt + 1;
+ *fp = *str;
+ olen = 0;
+ } else {
+ if (dot == 0)
+ width = (size_t)val;
+ else
+ precision = (size_t)val;
+ olen = MAX(width, precision);
+ }
+ *fp++ = '\0';
+
+ /* Handle commands */
+ switch (c) {
+ case 'c':
+ pop(&val, NULL, &stack);
+ if (ochar(term, (unsigned char)val) == 0)
+ return NULL;
+ break;
+ case 's':
+ pop(NULL, &ostr, &stack);
+ if (ostr != NULL) {
+ int r;
+
+ l = strlen(ostr);
+ if (l < (size_t)olen)
+ l = olen;
+ if (checkbuf(term, (size_t)(l + 1)) == NULL)
+ return NULL;
+ r = snprintf(term->_buf + term->_bufpos, l + 1,
+ fmt, ostr);
+ if (r != -1)
+ term->_bufpos += (size_t)r;
+ }
+ break;
+ case 'l':
+ pop(NULL, &ostr, &stack);
+ if (ostr == NULL)
+ l = 0;
+ else
+ l = strlen(ostr);
+#ifdef NCURSES_COMPAT_57
+ if (onum(term, "%ld", (long)l, 0) == 0)
+ return NULL;
+#else
+ push((long)l, NULL, &stack);
+#endif
+ break;
+ case 'd': /* FALLTHROUGH */
+ case 'o': /* FALLTHROUGH */
+ case 'x': /* FALLTHROUGH */
+ case 'X':
+ pop(&val, NULL, &stack);
+ if (onum(term, fmt, (int)val, olen) == 0)
+ return NULL;
+ break;
+ case 'p':
+ if (*str < '1' || *str > '9')
+ break;
+ l = (size_t)(*str++ - '1');
+ if (push(params[l].num, params[l].string, &stack))
+ return NULL;
+ break;
+ case 'P':
+ pop(&val, NULL, &stack);
+ if (*str >= 'a' && *str <= 'z')
+ dnums[*str - 'a'] = val;
+ else if (*str >= 'A' && *str <= 'Z')
+ term->_snums[*str - 'A'] = val;
+ break;
+ case 'g':
+ if (*str >= 'a' && *str <= 'z') {
+ if (push(dnums[*str - 'a'], NULL, &stack))
+ return NULL;
+ } else if (*str >= 'A' && *str <= 'Z') {
+ if (push(term->_snums[*str - 'A'],
+ NULL, &stack))
+ return NULL;
+ }
+ break;
+ case 'i':
+ if (piss[0] == 0)
+ params[0].num++;
+ if (piss[1] == 0)
+ params[1].num++;
+ break;
+ case '\'':
+ if (push((long)(unsigned char)*str++, NULL, &stack))
+ return NULL;
+ while (*str != '\0' && *str != '\'')
+ str++;
+ if (*str == '\'')
+ str++;
+ break;
+ case '{':
+ val = 0;
+ for (; isdigit((unsigned char)*str); str++)
+ val = (val * 10) + (*str - '0');
+ if (push(val, NULL, &stack))
+ return NULL;
+ while (*str != '\0' && *str != '}')
+ str++;
+ if (*str == '}')
+ str++;
+ break;
+ case '+': /* FALLTHROUGH */
+ case '-': /* FALLTHROUGH */
+ case '*': /* FALLTHROUGH */
+ case '/': /* FALLTHROUGH */
+ case 'm': /* FALLTHROUGH */
+ case 'A': /* FALLTHROUGH */
+ case 'O': /* FALLTHROUGH */
+ case '&': /* FALLTHROUGH */
+ case '|': /* FALLTHROUGH */
+ case '^': /* FALLTHROUGH */
+ case '=': /* FALLTHROUGH */
+ case '<': /* FALLTHROUGH */
+ case '>':
+ pop(&val, NULL, &stack);
+ pop(&val2, NULL, &stack);
+ switch (c) {
+ case '+':
+ val = val + val2;
+ break;
+ case '-':
+ val = val2 - val;
+ break;
+ case '*':
+ val = val * val2;
+ break;
+ case '/':
+ val = val ? val2 / val : 0;
+ break;
+ case 'm':
+ val = val ? val2 % val : 0;
+ break;
+ case 'A':
+ val = val && val2;
+ break;
+ case 'O':
+ val = val || val2;
+ break;
+ case '&':
+ val = val & val2;
+ break;
+ case '|':
+ val = val | val2;
+ break;
+ case '^':
+ val = val ^ val2;
+ break;
+ case '=':
+ val = val == val2;
+ break;
+ case '<':
+ val = val2 < val;
+ break;
+ case '>':
+ val = val2 > val;
+ break;
+ }
+ if (push(val, NULL, &stack))
+ return NULL;
+ break;
+ case '!':
+ case '~':
+ pop(&val, NULL, &stack);
+ switch (c) {
+ case '!':
+ val = !val;
+ break;
+ case '~':
+ val = ~val;
+ break;
+ }
+ if (push(val, NULL, &stack))
+ return NULL;
+ break;
+ case '?': /* if */
+ break;
+ case 't': /* then */
+ pop(&val, NULL, &stack);
+ if (val == 0) {
+ l = 0;
+ for (; *str != '\0'; str++) {
+ if (*str != '%')
+ continue;
+ str++;
+ if (*str == '?')
+ l++;
+ else if (*str == ';') {
+ if (l > 0)
+ l--;
+ else {
+ str++;
+ break;
+ }
+ } else if (*str == 'e' && l == 0) {
+ str++;
+ break;
+ }
+ }
+ }
+ break;
+ case 'e': /* else */
+ l = 0;
+ for (; *str != '\0'; str++) {
+ if (*str != '%')
+ continue;
+ str++;
+ if (*str == '?')
+ l++;
+ else if (*str == ';') {
+ if (l > 0)
+ l--;
+ else {
+ str++;
+ break;
+ }
+ }
+ }
+ break;
+ case ';': /* fi */
+ break;
+ }
+ }
+ term->_buf[term->_bufpos] = '\0';
+ return term->_buf;
+}
+
+char *
+ti_tiparm(TERMINAL *term, const char *str, ...)
+{
+ va_list va;
+ char *ret;
+
+ va_start(va, str);
+ ret = _ti_tiparm(term, str, VA_CHAR_INT, va);
+ va_end(va);
+ return ret;
+}
+
+char *
+tiparm(const char *str, ...)
+{
+ va_list va;
+ char *ret;
+
+ va_start(va, str);
+ ret = _ti_tiparm(NULL, str, VA_CHAR_INT, va);
+ va_end(va);
+ return ret;
+}
+
+#ifdef VA_CHAR_LONG
+char *
+ti_tlparm(TERMINAL *term, const char *str, ...)
+{
+ va_list va;
+ char *ret;
+
+ va_start(va, str);
+ ret = _ti_tiparm(term, str, VA_CHAR_LONG, va);
+ va_end(va);
+ return ret;
+}
+
+char *
+tlparm(const char *str, ...)
+{
+ va_list va;
+ char *ret;
+
+ va_start(va, str);
+ ret = _ti_tiparm(NULL, str, VA_CHAR_LONG, va);
+ va_end(va);
+ return ret;
+}
+#endif
+
+static char *
+_tparm(const char *str, ...)
+{
+ va_list va;
+ char *ret;
+
+ va_start(va, str);
+ ret = _ti_tiparm(NULL, str, VA_LONG_LONG, va);
+ va_end(va);
+ return ret;
+}
+
+char *
+tparm(const char *str,
+ long p1, long p2, long p3, long p4, long p5,
+ long p6, long p7, long p8, long p9)
+{
+
+ return _tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9);
+}
Index: contrib/mg/tputs.c
===================================================================
--- /dev/null
+++ contrib/mg/tputs.c
@@ -0,0 +1,173 @@
+/* $NetBSD: tputs.c,v 1.5 2019/10/03 18:02:05 christos Exp $ */
+
+/*
+ * Copyright (c) 2009 The NetBSD Foundation, Inc.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Roy Marples.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "term_private.h"
+#include "terminfo_term.h"
+
+#ifndef __arraycount
+#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
+#endif
+
+/*
+ * The following array gives the number of tens of milliseconds per
+ * character for each speed as returned by gtty. Thus since 300
+ * baud returns a 7, there are 33.3 milliseconds per char at 300 baud.
+ */
+static const short tmspc10[] = {
+ 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5
+};
+
+short ospeed;
+char PC;
+
+static int
+_ti_calcdelay(const char **str, int affcnt, int *mand)
+{
+ int i;
+
+ i = 0;
+ /* Convert the delay */
+ while (isdigit(*(const unsigned char *)*str))
+ i = i * 10 + *(*str)++ - '0';
+ i *= 10;
+ if (*(*str) == '.') {
+ (*str)++;
+ if (isdigit(*(const unsigned char *)*str))
+ i += *(*str) - '0';
+ while (isdigit(*(const unsigned char *)*str))
+ (*str)++;
+ }
+ if (*(*str) == '*') {
+ (*str)++;
+ i *= affcnt;
+ } else if (*(*str) == '/') {
+ (*str)++;
+ if (mand != NULL)
+ *mand = 1;
+ }
+ return i;
+}
+
+static void
+_ti_outputdelay(int delay, short os, char pc,
+ int (*outc)(int, void *), void *args)
+{
+ int mspc10;
+
+ if (delay < 1 || os < 1 || (size_t)os >= __arraycount(tmspc10))
+ return;
+
+ mspc10 = tmspc10[os];
+ delay += mspc10 / 2;
+ for (delay /= mspc10; delay > 0; delay--)
+ outc(pc, args);
+}
+
+static int
+_ti_puts(int dodelay, short os, char pc,
+ const char *str, int affcnt, int (*outc)(int, void *), void *args)
+{
+ int taildelay, delay, mand;
+
+ if (str == NULL)
+ return OK;
+
+ taildelay = _ti_calcdelay(&str, affcnt, NULL);
+
+ /* Output the string with embedded delays */
+ for (; *str != '\0'; str++) {
+ if (str[0] != '$' ||
+ str[1] != '<' ||
+ !(isdigit((const unsigned char)str[2]) || str[2] == '.') ||
+ strchr(str + 3, '>') == NULL)
+ {
+ outc(*str, args);
+ } else {
+ str += 2;
+ mand = 0;
+ delay = _ti_calcdelay(&str, affcnt, &mand);
+ if (dodelay != 0 || mand != 0)
+ _ti_outputdelay(delay, os, pc, outc, args);
+ }
+ }
+
+ /* Delay if needed */
+ if (dodelay)
+ _ti_outputdelay(taildelay, os, pc, outc, args);
+
+ return OK;
+}
+
+int
+ti_puts(const TERMINAL *term, const char *str, int affcnt,
+ int (*outc)(int, void *), void *args)
+{
+ int dodelay;
+ char pc;
+
+ dodelay = (str == t_bell(term) ||
+ str == t_flash_screen(term) ||
+ (t_xon_xoff(term) == 0 && t_padding_baud_rate(term) != 0));
+
+ if (t_pad_char(term) == NULL)
+ pc = '\0';
+ else
+ pc = *t_pad_char(term);
+ return _ti_puts(dodelay, term->_ospeed, pc,
+ str, affcnt, outc, args);
+}
+
+int
+ti_putp(const TERMINAL *term, const char *str)
+{
+
+ return ti_puts(term, str, 1,
+ (int (*)(int, void *))(void *)putchar, NULL);
+}
+
+int
+tputs(const char *str, int affcnt, int (*outc)(int))
+{
+
+ return _ti_puts(1, ospeed, PC, str, affcnt,
+ (int (*)(int, void *))(void *)outc, NULL);
+}
+
+int
+putp(const char *str)
+{
+
+ return tputs(str, 1, putchar);
+}
Index: contrib/mg/tree.h
===================================================================
--- /dev/null
+++ contrib/mg/tree.h
@@ -0,0 +1,748 @@
+/* $OpenBSD: tree.h,v 1.14 2015/05/25 03:07:49 deraadt Exp $ */
+/*
+ * Copyright 2002 Niels Provos <provos@citi.umich.edu>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TREE_H_
+#define _SYS_TREE_H_
+
+/*
+ * This file defines data structures for different types of trees:
+ * splay trees and red-black trees.
+ *
+ * A splay tree is a self-organizing data structure. Every operation
+ * on the tree causes a splay to happen. The splay moves the requested
+ * node to the root of the tree and partly rebalances it.
+ *
+ * This has the benefit that request locality causes faster lookups as
+ * the requested nodes move to the top of the tree. On the other hand,
+ * every lookup causes memory writes.
+ *
+ * The Balance Theorem bounds the total access time for m operations
+ * and n inserts on an initially empty tree as O((m + n)lg n). The
+ * amortized cost for a sequence of m accesses to a splay tree is O(lg n);
+ *
+ * A red-black tree is a binary search tree with the node color as an
+ * extra attribute. It fulfills a set of conditions:
+ * - every search path from the root to a leaf consists of the
+ * same number of black nodes,
+ * - each red node (except for the root) has a black parent,
+ * - each leaf node is black.
+ *
+ * Every operation on a red-black tree is bounded as O(lg n).
+ * The maximum height of a red-black tree is 2lg (n+1).
+ */
+
+#define SPLAY_HEAD(name, type) \
+struct name { \
+ struct type *sph_root; /* root of the tree */ \
+}
+
+#define SPLAY_INITIALIZER(root) \
+ { NULL }
+
+#define SPLAY_INIT(root) do { \
+ (root)->sph_root = NULL; \
+} while (0)
+
+#define SPLAY_ENTRY(type) \
+struct { \
+ struct type *spe_left; /* left element */ \
+ struct type *spe_right; /* right element */ \
+}
+
+#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
+#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
+#define SPLAY_ROOT(head) (head)->sph_root
+#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
+
+/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
+#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ (head)->sph_root = tmp; \
+} while (0)
+
+#define SPLAY_LINKLEFT(head, tmp, field) do { \
+ SPLAY_LEFT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_LINKRIGHT(head, tmp, field) do { \
+ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
+ tmp = (head)->sph_root; \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
+} while (0)
+
+#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
+ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
+ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
+ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+
+#define SPLAY_PROTOTYPE(name, type, field, cmp) \
+void name##_SPLAY(struct name *, struct type *); \
+void name##_SPLAY_MINMAX(struct name *, int); \
+struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
+struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
+ \
+/* Finds the node with the same key as elm */ \
+static __inline struct type * \
+name##_SPLAY_FIND(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) \
+ return(NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) \
+ return (head->sph_root); \
+ return (NULL); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_NEXT(struct name *head, struct type *elm) \
+{ \
+ name##_SPLAY(head, elm); \
+ if (SPLAY_RIGHT(elm, field) != NULL) { \
+ elm = SPLAY_RIGHT(elm, field); \
+ while (SPLAY_LEFT(elm, field) != NULL) { \
+ elm = SPLAY_LEFT(elm, field); \
+ } \
+ } else \
+ elm = NULL; \
+ return (elm); \
+} \
+ \
+static __inline struct type * \
+name##_SPLAY_MIN_MAX(struct name *head, int val) \
+{ \
+ name##_SPLAY_MINMAX(head, val); \
+ return (SPLAY_ROOT(head)); \
+}
+
+/* Main splay operation.
+ * Moves node close to the key of elm to top
+ */
+#define SPLAY_GENERATE(name, type, field, cmp) \
+struct type * \
+name##_SPLAY_INSERT(struct name *head, struct type *elm) \
+{ \
+ if (SPLAY_EMPTY(head)) { \
+ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
+ } else { \
+ int __comp; \
+ name##_SPLAY(head, elm); \
+ __comp = (cmp)(elm, (head)->sph_root); \
+ if(__comp < 0) { \
+ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
+ SPLAY_RIGHT(elm, field) = (head)->sph_root; \
+ SPLAY_LEFT((head)->sph_root, field) = NULL; \
+ } else if (__comp > 0) { \
+ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
+ SPLAY_LEFT(elm, field) = (head)->sph_root; \
+ SPLAY_RIGHT((head)->sph_root, field) = NULL; \
+ } else \
+ return ((head)->sph_root); \
+ } \
+ (head)->sph_root = (elm); \
+ return (NULL); \
+} \
+ \
+struct type * \
+name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *__tmp; \
+ if (SPLAY_EMPTY(head)) \
+ return (NULL); \
+ name##_SPLAY(head, elm); \
+ if ((cmp)(elm, (head)->sph_root) == 0) { \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
+ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
+ } else { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
+ name##_SPLAY(head, elm); \
+ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
+ } \
+ return (elm); \
+ } \
+ return (NULL); \
+} \
+ \
+void \
+name##_SPLAY(struct name *head, struct type *elm) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+ int __comp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while ((__comp = (cmp)(elm, (head)->sph_root))) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if ((cmp)(elm, __tmp) > 0){ \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+} \
+ \
+/* Splay with either the minimum or the maximum element \
+ * Used to find minimum or maximum element in tree. \
+ */ \
+void name##_SPLAY_MINMAX(struct name *head, int __comp) \
+{ \
+ struct type __node, *__left, *__right, *__tmp; \
+\
+ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
+ __left = __right = &__node; \
+\
+ while (1) { \
+ if (__comp < 0) { \
+ __tmp = SPLAY_LEFT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp < 0){ \
+ SPLAY_ROTATE_RIGHT(head, __tmp, field); \
+ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKLEFT(head, __right, field); \
+ } else if (__comp > 0) { \
+ __tmp = SPLAY_RIGHT((head)->sph_root, field); \
+ if (__tmp == NULL) \
+ break; \
+ if (__comp > 0) { \
+ SPLAY_ROTATE_LEFT(head, __tmp, field); \
+ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
+ break; \
+ } \
+ SPLAY_LINKRIGHT(head, __left, field); \
+ } \
+ } \
+ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
+}
+
+#define SPLAY_NEGINF -1
+#define SPLAY_INF 1
+
+#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
+#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
+#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
+#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
+#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
+#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
+ : name##_SPLAY_MIN_MAX(x, SPLAY_INF))
+
+#define SPLAY_FOREACH(x, name, head) \
+ for ((x) = SPLAY_MIN(name, head); \
+ (x) != NULL; \
+ (x) = SPLAY_NEXT(name, head, x))
+
+/* Macros that define a red-black tree */
+#define RB_HEAD(name, type) \
+struct name { \
+ struct type *rbh_root; /* root of the tree */ \
+}
+
+#define RB_INITIALIZER(root) \
+ { NULL }
+
+#define RB_INIT(root) do { \
+ (root)->rbh_root = NULL; \
+} while (0)
+
+#define RB_BLACK 0
+#define RB_RED 1
+#define RB_ENTRY(type) \
+struct { \
+ struct type *rbe_left; /* left element */ \
+ struct type *rbe_right; /* right element */ \
+ struct type *rbe_parent; /* parent element */ \
+ int rbe_color; /* node color */ \
+}
+
+#define RB_LEFT(elm, field) (elm)->field.rbe_left
+#define RB_RIGHT(elm, field) (elm)->field.rbe_right
+#define RB_PARENT(elm, field) (elm)->field.rbe_parent
+#define RB_COLOR(elm, field) (elm)->field.rbe_color
+#define RB_ROOT(head) (head)->rbh_root
+#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
+
+#define RB_SET(elm, parent, field) do { \
+ RB_PARENT(elm, field) = parent; \
+ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
+ RB_COLOR(elm, field) = RB_RED; \
+} while (0)
+
+#define RB_SET_BLACKRED(black, red, field) do { \
+ RB_COLOR(black, field) = RB_BLACK; \
+ RB_COLOR(red, field) = RB_RED; \
+} while (0)
+
+#ifndef RB_AUGMENT
+#define RB_AUGMENT(x) do {} while (0)
+#endif
+
+#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
+ (tmp) = RB_RIGHT(elm, field); \
+ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \
+ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_LEFT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
+ (tmp) = RB_LEFT(elm, field); \
+ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \
+ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
+ } \
+ RB_AUGMENT(elm); \
+ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \
+ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
+ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
+ else \
+ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
+ } else \
+ (head)->rbh_root = (tmp); \
+ RB_RIGHT(tmp, field) = (elm); \
+ RB_PARENT(elm, field) = (tmp); \
+ RB_AUGMENT(tmp); \
+ if ((RB_PARENT(tmp, field))) \
+ RB_AUGMENT(RB_PARENT(tmp, field)); \
+} while (0)
+
+/* Generates prototypes and inline functions */
+#define RB_PROTOTYPE(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
+#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
+ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
+attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
+attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
+attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
+attr struct type *name##_RB_INSERT(struct name *, struct type *); \
+attr struct type *name##_RB_FIND(struct name *, struct type *); \
+attr struct type *name##_RB_NFIND(struct name *, struct type *); \
+attr struct type *name##_RB_NEXT(struct type *); \
+attr struct type *name##_RB_PREV(struct type *); \
+attr struct type *name##_RB_MINMAX(struct name *, int); \
+ \
+
+/* Main rb operation.
+ * Moves node close to the key of elm to top
+ */
+#define RB_GENERATE(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp,)
+#define RB_GENERATE_STATIC(name, type, field, cmp) \
+ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static)
+#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
+attr void \
+name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
+{ \
+ struct type *parent, *gparent, *tmp; \
+ while ((parent = RB_PARENT(elm, field)) && \
+ RB_COLOR(parent, field) == RB_RED) { \
+ gparent = RB_PARENT(parent, field); \
+ if (parent == RB_LEFT(gparent, field)) { \
+ tmp = RB_RIGHT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_RIGHT(parent, field) == elm) { \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_RIGHT(head, gparent, tmp, field); \
+ } else { \
+ tmp = RB_LEFT(gparent, field); \
+ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
+ RB_COLOR(tmp, field) = RB_BLACK; \
+ RB_SET_BLACKRED(parent, gparent, field);\
+ elm = gparent; \
+ continue; \
+ } \
+ if (RB_LEFT(parent, field) == elm) { \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = parent; \
+ parent = elm; \
+ elm = tmp; \
+ } \
+ RB_SET_BLACKRED(parent, gparent, field); \
+ RB_ROTATE_LEFT(head, gparent, tmp, field); \
+ } \
+ } \
+ RB_COLOR(head->rbh_root, field) = RB_BLACK; \
+} \
+ \
+attr void \
+name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
+{ \
+ struct type *tmp; \
+ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
+ elm != RB_ROOT(head)) { \
+ if (RB_LEFT(parent, field) == elm) { \
+ tmp = RB_RIGHT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
+ struct type *oleft; \
+ if ((oleft = RB_LEFT(tmp, field)))\
+ RB_COLOR(oleft, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_RIGHT(head, tmp, oleft, field);\
+ tmp = RB_RIGHT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_RIGHT(tmp, field)) \
+ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_LEFT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } else { \
+ tmp = RB_LEFT(parent, field); \
+ if (RB_COLOR(tmp, field) == RB_RED) { \
+ RB_SET_BLACKRED(tmp, parent, field); \
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ if ((RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
+ (RB_RIGHT(tmp, field) == NULL || \
+ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
+ RB_COLOR(tmp, field) = RB_RED; \
+ elm = parent; \
+ parent = RB_PARENT(elm, field); \
+ } else { \
+ if (RB_LEFT(tmp, field) == NULL || \
+ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
+ struct type *oright; \
+ if ((oright = RB_RIGHT(tmp, field)))\
+ RB_COLOR(oright, field) = RB_BLACK;\
+ RB_COLOR(tmp, field) = RB_RED; \
+ RB_ROTATE_LEFT(head, tmp, oright, field);\
+ tmp = RB_LEFT(parent, field); \
+ } \
+ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
+ RB_COLOR(parent, field) = RB_BLACK; \
+ if (RB_LEFT(tmp, field)) \
+ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
+ RB_ROTATE_RIGHT(head, parent, tmp, field);\
+ elm = RB_ROOT(head); \
+ break; \
+ } \
+ } \
+ } \
+ if (elm) \
+ RB_COLOR(elm, field) = RB_BLACK; \
+} \
+ \
+attr struct type * \
+name##_RB_REMOVE(struct name *head, struct type *elm) \
+{ \
+ struct type *child, *parent, *old = elm; \
+ int color; \
+ if (RB_LEFT(elm, field) == NULL) \
+ child = RB_RIGHT(elm, field); \
+ else if (RB_RIGHT(elm, field) == NULL) \
+ child = RB_LEFT(elm, field); \
+ else { \
+ struct type *left; \
+ elm = RB_RIGHT(elm, field); \
+ while ((left = RB_LEFT(elm, field))) \
+ elm = left; \
+ child = RB_RIGHT(elm, field); \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+ if (RB_PARENT(elm, field) == old) \
+ parent = elm; \
+ (elm)->field = (old)->field; \
+ if (RB_PARENT(old, field)) { \
+ if (RB_LEFT(RB_PARENT(old, field), field) == old)\
+ RB_LEFT(RB_PARENT(old, field), field) = elm;\
+ else \
+ RB_RIGHT(RB_PARENT(old, field), field) = elm;\
+ RB_AUGMENT(RB_PARENT(old, field)); \
+ } else \
+ RB_ROOT(head) = elm; \
+ RB_PARENT(RB_LEFT(old, field), field) = elm; \
+ if (RB_RIGHT(old, field)) \
+ RB_PARENT(RB_RIGHT(old, field), field) = elm; \
+ if (parent) { \
+ left = parent; \
+ do { \
+ RB_AUGMENT(left); \
+ } while ((left = RB_PARENT(left, field))); \
+ } \
+ goto color; \
+ } \
+ parent = RB_PARENT(elm, field); \
+ color = RB_COLOR(elm, field); \
+ if (child) \
+ RB_PARENT(child, field) = parent; \
+ if (parent) { \
+ if (RB_LEFT(parent, field) == elm) \
+ RB_LEFT(parent, field) = child; \
+ else \
+ RB_RIGHT(parent, field) = child; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = child; \
+color: \
+ if (color == RB_BLACK) \
+ name##_RB_REMOVE_COLOR(head, parent, child); \
+ return (old); \
+} \
+ \
+/* Inserts a node into the RB tree */ \
+attr struct type * \
+name##_RB_INSERT(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp; \
+ struct type *parent = NULL; \
+ int comp = 0; \
+ tmp = RB_ROOT(head); \
+ while (tmp) { \
+ parent = tmp; \
+ comp = (cmp)(elm, parent); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ RB_SET(elm, parent, field); \
+ if (parent != NULL) { \
+ if (comp < 0) \
+ RB_LEFT(parent, field) = elm; \
+ else \
+ RB_RIGHT(parent, field) = elm; \
+ RB_AUGMENT(parent); \
+ } else \
+ RB_ROOT(head) = elm; \
+ name##_RB_INSERT_COLOR(head, elm); \
+ return (NULL); \
+} \
+ \
+/* Finds the node with the same key as elm */ \
+attr struct type * \
+name##_RB_FIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (NULL); \
+} \
+ \
+/* Finds the first node greater than or equal to the search key */ \
+attr struct type * \
+name##_RB_NFIND(struct name *head, struct type *elm) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *res = NULL; \
+ int comp; \
+ while (tmp) { \
+ comp = cmp(elm, tmp); \
+ if (comp < 0) { \
+ res = tmp; \
+ tmp = RB_LEFT(tmp, field); \
+ } \
+ else if (comp > 0) \
+ tmp = RB_RIGHT(tmp, field); \
+ else \
+ return (tmp); \
+ } \
+ return (res); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_NEXT(struct type *elm) \
+{ \
+ if (RB_RIGHT(elm, field)) { \
+ elm = RB_RIGHT(elm, field); \
+ while (RB_LEFT(elm, field)) \
+ elm = RB_LEFT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+/* ARGSUSED */ \
+attr struct type * \
+name##_RB_PREV(struct type *elm) \
+{ \
+ if (RB_LEFT(elm, field)) { \
+ elm = RB_LEFT(elm, field); \
+ while (RB_RIGHT(elm, field)) \
+ elm = RB_RIGHT(elm, field); \
+ } else { \
+ if (RB_PARENT(elm, field) && \
+ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
+ elm = RB_PARENT(elm, field); \
+ else { \
+ while (RB_PARENT(elm, field) && \
+ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\
+ elm = RB_PARENT(elm, field); \
+ elm = RB_PARENT(elm, field); \
+ } \
+ } \
+ return (elm); \
+} \
+ \
+attr struct type * \
+name##_RB_MINMAX(struct name *head, int val) \
+{ \
+ struct type *tmp = RB_ROOT(head); \
+ struct type *parent = NULL; \
+ while (tmp) { \
+ parent = tmp; \
+ if (val < 0) \
+ tmp = RB_LEFT(tmp, field); \
+ else \
+ tmp = RB_RIGHT(tmp, field); \
+ } \
+ return (parent); \
+}
+
+#define RB_NEGINF -1
+#define RB_INF 1
+
+#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
+#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
+#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
+#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
+#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
+#define RB_PREV(name, x, y) name##_RB_PREV(y)
+#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
+#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
+
+#define RB_FOREACH(x, name, head) \
+ for ((x) = RB_MIN(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_NEXT(x))
+
+#define RB_FOREACH_SAFE(x, name, head, y) \
+ for ((x) = RB_MIN(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \
+ (x) = (y))
+
+#define RB_FOREACH_REVERSE(x, name, head) \
+ for ((x) = RB_MAX(name, head); \
+ (x) != NULL; \
+ (x) = name##_RB_PREV(x))
+
+#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
+ for ((x) = RB_MAX(name, head); \
+ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \
+ (x) = (y))
+
+#endif /* _SYS_TREE_H_ */
Index: contrib/mg/tty.c
===================================================================
--- /dev/null
+++ contrib/mg/tty.c
@@ -0,0 +1,459 @@
+/* $OpenBSD: tty.c,v 1.39 2021/03/20 09:00:49 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Terminfo display driver
+ *
+ * Terminfo is a terminal information database and routines to describe
+ * terminals on most modern UNIX systems. Many other systems have adopted
+ * this as a reasonable way to allow for widely varying and ever changing
+ * varieties of terminal types. This should be used where practical.
+ */
+/*
+ * Known problems: If you have a terminal with no clear to end of screen and
+ * memory of lines below the ones visible on the screen, display will be
+ * wrong in some cases. I doubt that any such terminal was ever made, but I
+ * thought everyone with delete line would have clear to end of screen too...
+ *
+ * Code for terminals without clear to end of screen and/or clear to end of line
+ * has not been extensively tested.
+ *
+ * Cost calculations are very rough. Costs of insert/delete line may be far
+ * from the truth. This is accentuated by display.c not knowing about
+ * multi-line insert/delete.
+ *
+ * Using scrolling region vs insert/delete line should probably be based on cost
+ * rather than the assumption that scrolling region operations look better.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <stdio.h>
+#include <term.h>
+#include <unistd.h>
+
+#include "def.h"
+
+static int charcost(const char *);
+
+static int cci;
+static int insdel; /* Do we have both insert & delete line? */
+static char *scroll_fwd; /* How to scroll forward. */
+
+static void winchhandler(int);
+
+volatile sig_atomic_t winch_flag;
+int tceeol;
+int tcinsl;
+int tcdell;
+
+/* ARGSUSED */
+static void
+winchhandler(int sig)
+{
+ winch_flag = 1;
+}
+
+/*
+ * Initialize the terminal when the editor
+ * gets started up.
+ */
+void
+ttinit(void)
+{
+ char *tty;
+ int errret;
+
+ if (batch == 1)
+ tty = "pty";
+ else
+ tty = NULL;
+
+ if (setupterm(tty, STDOUT_FILENO, &errret))
+ panic("Terminal setup failed");
+
+ signal(SIGWINCH, winchhandler);
+ signal(SIGCONT, winchhandler);
+ siginterrupt(SIGWINCH, 1);
+
+ scroll_fwd = scroll_forward;
+ if (scroll_fwd == NULL || *scroll_fwd == '\0') {
+ /* this is what GNU Emacs does */
+ scroll_fwd = parm_down_cursor;
+ if (scroll_fwd == NULL || *scroll_fwd == '\0')
+ scroll_fwd = curbp->b_nlchr;
+ }
+
+ if (cursor_address == NULL || cursor_up == NULL)
+ panic("This terminal is too stupid to run mg");
+
+ /* set nrow & ncol */
+ ttresize();
+
+ if (!clr_eol)
+ tceeol = ncol;
+ else
+ tceeol = charcost(clr_eol);
+
+ /* Estimate cost of inserting a line */
+ if (change_scroll_region && scroll_reverse)
+ tcinsl = charcost(change_scroll_region) * 2 +
+ charcost(scroll_reverse);
+ else if (parm_insert_line)
+ tcinsl = charcost(parm_insert_line);
+ else if (insert_line)
+ tcinsl = charcost(insert_line);
+ else
+ /* make this cost high enough */
+ tcinsl = nrow * ncol;
+
+ /* Estimate cost of deleting a line */
+ if (change_scroll_region)
+ tcdell = charcost(change_scroll_region) * 2 +
+ charcost(scroll_fwd);
+ else if (parm_delete_line)
+ tcdell = charcost(parm_delete_line);
+ else if (delete_line)
+ tcdell = charcost(delete_line);
+ else
+ /* make this cost high enough */
+ tcdell = nrow * ncol;
+
+ /* Flag to indicate that we can both insert and delete lines */
+ insdel = (insert_line || parm_insert_line) &&
+ (delete_line || parm_delete_line);
+
+ if (enter_ca_mode)
+ /* enter application mode */
+ putpad(enter_ca_mode, 1);
+
+ ttresize();
+}
+
+/*
+ * Re-initialize the terminal when the editor is resumed.
+ * The keypad_xmit doesn't really belong here but...
+ */
+void
+ttreinit(void)
+{
+ /* check if file was modified while we were gone */
+ if (fchecktime(curbp) != TRUE) {
+ curbp->b_flag |= BFDIRTY;
+ }
+
+ if (enter_ca_mode)
+ /* enter application mode */
+ putpad(enter_ca_mode, 1);
+
+ if (keypad_xmit)
+ /* turn on keypad */
+ putpad(keypad_xmit, 1);
+
+ ttresize();
+}
+
+/*
+ * Clean up the terminal, in anticipation of a return to the command
+ * interpreter. This is a no-op on the ANSI display. On the SCALD display,
+ * it sets the window back to half screen scrolling. Perhaps it should
+ * query the display for the increment, and put it back to what it was.
+ */
+void
+tttidy(void)
+{
+ ttykeymaptidy();
+
+ /* set the term back to normal mode */
+ if (exit_ca_mode)
+ putpad(exit_ca_mode, 1);
+}
+
+/*
+ * Move the cursor to the specified origin 0 row and column position. Try to
+ * optimize out extra moves; redisplay may have left the cursor in the right
+ * location last time!
+ */
+void
+ttmove(int row, int col)
+{
+ if (ttrow != row || ttcol != col) {
+ putpad(tgoto(cursor_address, col, row), 1);
+ ttrow = row;
+ ttcol = col;
+ }
+}
+
+/*
+ * Erase to end of line.
+ */
+void
+tteeol(void)
+{
+ int i;
+
+ if (clr_eol)
+ putpad(clr_eol, 1);
+ else {
+ i = ncol - ttcol;
+ while (i--)
+ ttputc(' ');
+ ttrow = ttcol = HUGE;
+ }
+}
+
+/*
+ * Erase to end of page.
+ */
+void
+tteeop(void)
+{
+ int line;
+
+ if (clr_eos)
+ putpad(clr_eos, nrow - ttrow);
+ else {
+ putpad(clr_eol, 1);
+ if (insdel)
+ ttdell(ttrow + 1, lines, lines - ttrow - 1);
+ else {
+ /* do it by hand */
+ for (line = ttrow + 1; line <= lines; ++line) {
+ ttmove(line, 0);
+ tteeol();
+ }
+ }
+ ttrow = ttcol = HUGE;
+ }
+}
+
+/*
+ * Make a noise.
+ */
+void
+ttbeep(void)
+{
+ putpad(bell, 1);
+ ttflush();
+}
+
+/*
+ * Insert nchunk blank line(s) onto the screen, scrolling the last line on
+ * the screen off the bottom. Use the scrolling region if possible for a
+ * smoother display. If there is no scrolling region, use a set of insert
+ * and delete line sequences.
+ */
+void
+ttinsl(int row, int bot, int nchunk)
+{
+ int i, nl;
+
+ /* One line special cases */
+ if (row == bot) {
+ ttmove(row, 0);
+ tteeol();
+ return;
+ }
+ /* Use scroll region and back index */
+ if (change_scroll_region && scroll_reverse) {
+ nl = bot - row;
+ ttwindow(row, bot);
+ ttmove(row, 0);
+ while (nchunk--)
+ putpad(scroll_reverse, nl);
+ ttnowindow();
+ return;
+ /* else use insert/delete line */
+ } else if (insdel) {
+ ttmove(1 + bot - nchunk, 0);
+ nl = nrow - ttrow;
+ if (parm_delete_line)
+ putpad(tgoto(parm_delete_line, 0, nchunk), nl);
+ else
+ /* For all lines in the chunk */
+ for (i = 0; i < nchunk; i++)
+ putpad(delete_line, nl);
+ ttmove(row, 0);
+
+ /* ttmove() changes ttrow */
+ nl = nrow - ttrow;
+
+ if (parm_insert_line)
+ putpad(tgoto(parm_insert_line, 0, nchunk), nl);
+ else
+ /* For all lines in the chunk */
+ for (i = 0; i < nchunk; i++)
+ putpad(insert_line, nl);
+ ttrow = HUGE;
+ ttcol = HUGE;
+ } else
+ panic("ttinsl: Can't insert/delete line");
+}
+
+/*
+ * Delete nchunk line(s) from "row", replacing the bottom line on the
+ * screen with a blank line. Unless we're using the scrolling region,
+ * this is done with crafty sequences of insert and delete lines. The
+ * presence of the echo area makes a boundary condition go away.
+ */
+void
+ttdell(int row, int bot, int nchunk)
+{
+ int i, nl;
+
+ /* One line special cases */
+ if (row == bot) {
+ ttmove(row, 0);
+ tteeol();
+ return;
+ }
+ /* scrolling region */
+ if (change_scroll_region) {
+ nl = bot - row;
+ ttwindow(row, bot);
+ ttmove(bot, 0);
+ while (nchunk--)
+ putpad(scroll_fwd, nl);
+ ttnowindow();
+ /* else use insert/delete line */
+ } else if (insdel) {
+ ttmove(row, 0);
+ nl = nrow - ttrow;
+ if (parm_delete_line)
+ putpad(tgoto(parm_delete_line, 0, nchunk), nl);
+ else
+ /* For all lines in the chunk */
+ for (i = 0; i < nchunk; i++)
+ putpad(delete_line, nl);
+ ttmove(1 + bot - nchunk, 0);
+
+ /* ttmove() changes ttrow */
+ nl = nrow - ttrow;
+
+ if (parm_insert_line)
+ putpad(tgoto(parm_insert_line, 0, nchunk), nl);
+ else
+ /* For all lines in the chunk */
+ for (i = 0; i < nchunk; i++)
+ putpad(insert_line, nl);
+ ttrow = HUGE;
+ ttcol = HUGE;
+ } else
+ panic("ttdell: Can't insert/delete line");
+}
+
+/*
+ * This routine sets the scrolling window on the display to go from line
+ * "top" to line "bot" (origin 0, inclusive). The caller checks for the
+ * pathological 1-line scroll window which doesn't work right and avoids
+ * it. The "ttrow" and "ttcol" variables are set to a crazy value to
+ * ensure that the next call to "ttmove" does not turn into a no-op (the
+ * window adjustment moves the cursor).
+ */
+void
+ttwindow(int top, int bot)
+{
+ if (change_scroll_region && (tttop != top || ttbot != bot)) {
+ putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
+ ttrow = HUGE; /* Unknown. */
+ ttcol = HUGE;
+ tttop = top; /* Remember region. */
+ ttbot = bot;
+ }
+}
+
+/*
+ * Switch to full screen scroll. This is used by "spawn.c" just before it
+ * suspends the editor and by "display.c" when it is getting ready to
+ * exit. This function does a full screen scroll by telling the terminal
+ * to set a scrolling region that is lines or nrow rows high, whichever is
+ * larger. This behavior seems to work right on systems where you can set
+ * your terminal size.
+ */
+void
+ttnowindow(void)
+{
+ if (change_scroll_region) {
+ putpad(tgoto(change_scroll_region,
+ (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
+ ttrow = HUGE; /* Unknown. */
+ ttcol = HUGE;
+ tttop = HUGE; /* No scroll region. */
+ ttbot = HUGE;
+ }
+}
+
+/*
+ * Set the current writing color to the specified color. Watch for color
+ * changes that are not going to do anything (the color is already right)
+ * and don't send anything to the display. The rainbow version does this
+ * in putline.s on a line by line basis, so don't bother sending out the
+ * color shift.
+ */
+void
+ttcolor(int color)
+{
+ if (color != tthue) {
+ if (color == CTEXT)
+ /* normal video */
+ putpad(exit_standout_mode, 1);
+ else if (color == CMODE)
+ /* reverse video */
+ putpad(enter_standout_mode, 1);
+ /* save the color */
+ tthue = color;
+ }
+}
+
+/*
+ * This routine is called by the "refresh the screen" command to try
+ * to resize the display. Look in "window.c" to see how
+ * the caller deals with a change.
+ *
+ * We use `newrow' and `newcol' so vtresize() know the difference between the
+ * new and old settings.
+ */
+void
+ttresize(void)
+{
+ int newrow = 0, newcol = 0;
+
+ struct winsize winsize;
+
+ if (ioctl(0, TIOCGWINSZ, &winsize) == 0) {
+ newrow = winsize.ws_row;
+ newcol = winsize.ws_col;
+ }
+ if ((newrow <= 0 || newcol <= 0) &&
+ ((newrow = lines) <= 0 || (newcol = columns) <= 0)) {
+ newrow = 24;
+ newcol = 80;
+ }
+ if (vtresize(1, newrow, newcol) != TRUE)
+ panic("vtresize failed");
+}
+
+/*
+ * fake char output for charcost()
+ */
+/* ARGSUSED */
+static int
+fakec(int c)
+{
+ cci++;
+ return (0);
+}
+
+/* calculate the cost of doing string s */
+static int
+charcost(const char *s)
+{
+ cci = 0;
+
+ tputs(s, nrow, fakec);
+ return (cci);
+}
Index: contrib/mg/ttyio.c
===================================================================
--- /dev/null
+++ contrib/mg/ttyio.c
@@ -0,0 +1,228 @@
+/* $OpenBSD: ttyio.c,v 1.40 2021/03/20 09:00:49 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * POSIX terminal I/O.
+ *
+ * The functions in this file negotiate with the operating system for
+ * keyboard characters, and write characters to the display in a barely
+ * buffered fashion.
+ */
+
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <term.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "def.h"
+
+#define NOBUF 512 /* Output buffer size. */
+
+int ttstarted;
+char obuf[NOBUF]; /* Output buffer. */
+size_t nobuf; /* Buffer count. */
+struct termios oldtty; /* POSIX tty settings. */
+struct termios newtty;
+int nrow; /* Terminal size, rows. */
+int ncol; /* Terminal size, columns. */
+
+/*
+ * This function gets called once, to set up the terminal.
+ * On systems w/o TCSASOFT we turn off off flow control,
+ * which isn't really the right thing to do.
+ */
+void
+ttopen(void)
+{
+ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
+ panic("standard input and output must be a terminal");
+
+ if (ttraw() == FALSE)
+ panic("aborting due to terminal initialize failure");
+}
+
+/*
+ * This function sets the terminal to RAW mode, as defined for the current
+ * shell. This is called both by ttopen() above and by spawncli() to
+ * get the current terminal settings and then change them to what
+ * mg expects. Thus, tty changes done while spawncli() is in effect
+ * will be reflected in mg.
+ */
+int
+ttraw(void)
+{
+ if (tcgetattr(0, &oldtty) == -1) {
+ dobeep();
+ ewprintf("ttopen can't get terminal attributes");
+ return (FALSE);
+ }
+ (void)memcpy(&newtty, &oldtty, sizeof(newtty));
+ /* Set terminal to 'raw' mode and ignore a 'break' */
+ newtty.c_cc[VMIN] = 1;
+ newtty.c_cc[VTIME] = 0;
+ newtty.c_iflag |= IGNBRK;
+ newtty.c_iflag &= ~(BRKINT | PARMRK | INLCR | IGNCR | ICRNL | IXON);
+ newtty.c_oflag &= ~OPOST;
+ newtty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+
+ if (tcsetattr(0, TCSASOFT | TCSADRAIN, &newtty) == -1) {
+ dobeep();
+ ewprintf("ttopen can't tcsetattr");
+ return (FALSE);
+ }
+ ttstarted = 1;
+
+ return (TRUE);
+}
+
+/*
+ * This function gets called just before we go back home to the shell.
+ * Put all of the terminal parameters back.
+ * Under UN*X this just calls ttcooked(), but the ttclose() hook is in
+ * because vttidy() in display.c expects it for portability reasons.
+ */
+void
+ttclose(void)
+{
+ if (ttstarted) {
+ if (ttcooked() == FALSE)
+ panic(""); /* ttcooked() already printf'd */
+ ttstarted = 0;
+ }
+}
+
+/*
+ * This function restores all terminal settings to their default values,
+ * in anticipation of exiting or suspending the editor.
+ */
+int
+ttcooked(void)
+{
+ ttflush();
+ if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) == -1) {
+ dobeep();
+ ewprintf("ttclose can't tcsetattr");
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/*
+ * Write character to the display. Characters are buffered up,
+ * to make things a little bit more efficient.
+ */
+int
+ttputc(int c)
+{
+ if (nobuf >= NOBUF)
+ ttflush();
+ obuf[nobuf++] = c;
+ return (c);
+}
+
+/*
+ * Flush output.
+ */
+void
+ttflush(void)
+{
+ ssize_t written;
+ char *buf = obuf;
+
+ if (nobuf == 0 || batch == 1)
+ return;
+
+ while ((written = write(fileno(stdout), buf, nobuf)) != nobuf) {
+ if (written == -1) {
+ if (errno == EINTR)
+ continue;
+ panic("ttflush write failed");
+ }
+ buf += written;
+ nobuf -= written;
+ }
+ nobuf = 0;
+}
+
+/*
+ * Read character from terminal. All 8 bits are returned, so that you
+ * can use a multi-national terminal.
+ */
+int
+ttgetc(void)
+{
+ char c;
+ ssize_t ret;
+
+ do {
+ ret = read(STDIN_FILENO, &c, 1);
+ if (ret == -1 && errno == EINTR) {
+ if (winch_flag) {
+ redraw(0, 0);
+ winch_flag = 0;
+ }
+ } else if (ret == -1 && errno == EIO)
+ panic("lost stdin");
+ else if (ret == 1)
+ break;
+ } while (1);
+ return ((int) c) & 0xFF;
+}
+
+/*
+ * Returns TRUE if there are characters waiting to be read.
+ */
+int
+charswaiting(void)
+{
+ int x;
+
+ return ((ioctl(0, FIONREAD, &x) == -1) ? 0 : x);
+}
+
+/*
+ * panic - just exit, as quickly as we can.
+ */
+void
+panic(char *s)
+{
+ static int panicking = 0;
+
+ if (panicking)
+ return;
+ else
+ panicking = 1;
+ ttclose();
+ (void) fputs("panic: ", stderr);
+ (void) fputs(s, stderr);
+ (void) fputc('\n', stderr); /* Use '\n' as no buffers now. */
+ exit(1);
+}
+
+/*
+ * This function returns FALSE if any characters have showed up on the
+ * tty before 'msec' milliseconds.
+ */
+int
+ttwait(int msec)
+{
+ struct pollfd pfd[1];
+
+ pfd[0].fd = 0;
+ pfd[0].events = POLLIN;
+
+ if ((poll(pfd, 1, msec)) == 0)
+ return (TRUE);
+ return (FALSE);
+}
Index: contrib/mg/ttykbd.c
===================================================================
--- /dev/null
+++ contrib/mg/ttykbd.c
@@ -0,0 +1,80 @@
+/* $OpenBSD: ttykbd.c,v 1.20 2021/02/23 08:10:51 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Name: MG 2a
+ * Terminfo keyboard driver using key files
+ * Created: 22-Nov-1987 Mic Kaczmarczik (mic@emx.cc.utexas.edu)
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <term.h>
+
+#include "def.h"
+#include "kbd.h"
+
+/*
+ * Get keyboard character. Very simple if you use keymaps and keys files.
+ */
+
+char *keystrings[] = {NULL};
+
+/*
+ * Turn on function keys using keypad_xmit, then load a keys file, if
+ * available. The keys file is located in the same manner as the startup
+ * file is, depending on what startupfile() does on your system.
+ */
+void
+ttykeymapinit(void)
+{
+ char *cp;
+
+ /* Bind keypad function keys. */
+ if (key_left)
+ dobindkey(fundamental_map, "backward-char", key_left);
+ if (key_right)
+ dobindkey(fundamental_map, "forward-char", key_right);
+ if (key_up)
+ dobindkey(fundamental_map, "previous-line", key_up);
+ if (key_down)
+ dobindkey(fundamental_map, "next-line", key_down);
+ if (key_beg)
+ dobindkey(fundamental_map, "beginning-of-line", key_beg);
+ else if (key_home)
+ dobindkey(fundamental_map, "beginning-of-line", key_home);
+ if (key_end)
+ dobindkey(fundamental_map, "end-of-line", key_end);
+ if (key_npage)
+ dobindkey(fundamental_map, "scroll-up", key_npage);
+ if (key_ppage)
+ dobindkey(fundamental_map, "scroll-down", key_ppage);
+ if (key_ic)
+ dobindkey(fundamental_map, "overwrite-mode", key_ic);
+ if (key_dc)
+ dobindkey(fundamental_map, "delete-char", key_dc);
+
+ if ((cp = getenv("TERM"))) {
+ if (((cp = startupfile(cp, NULL)) != NULL) &&
+ (load(cp) != TRUE))
+ ewprintf("Error reading key initialization file");
+ }
+ if (keypad_xmit)
+ /* turn on keypad */
+ putpad(keypad_xmit, 1);
+}
+
+/*
+ * Clean up the keyboard -- called by tttidy()
+ */
+void
+ttykeymaptidy(void)
+{
+ if (keypad_local)
+ /* turn off keypad */
+ putpad(keypad_local, 1);
+}
+
Index: contrib/mg/tutorial
===================================================================
--- /dev/null
+++ contrib/mg/tutorial
@@ -0,0 +1,356 @@
+The mg Tutorial
+---------------
+
+The mg editor is a public domain editor intended to loosely resemble GNU Emacs,
+while still retaining fast speed and a small memory footprint.
+
+Most mg commands involve using the Control (sometimes labelled "Ctrl") or the
+Meta (sometimes labelled "Alt") key. We will use the following conventions in
+this tutorial:
+
+ C-<chr> means hold down the Control key while typing the character <chr>.
+ M-<chr> means hold down the Meta key while typing the character <chr>.
+
+If you don't have a Meta key, you can use Esc instead. Press and release the
+Esc key and type <chr>. This is equivalent to M-<chr>.
+
+The first thing to learn is how to move up and down a document. To move your
+cursor down a line, use the down-arrow cursor key or C-n (Control and n).
+
+>> Now type C-n multiple times and move your cursor past this line.
+
+Congratulations. You have now learned how to move your cursor down. To move
+your cursor up one line, you can use the up-arrow cursor key or C-p (Control
+and p).
+
+>> Try using C-p and C-n to move up and down and then move past this line.
+
+The next commands to learn are how to move your cursor left and right. To do
+this, you can use the left-arrow and right-arrow cursor keys. Alternatively,
+you can use C-b and C-f to do this.
+
+>> Practise using the arrow keys or C-b and C-f on this line.
+
+To make it easy to remember these commands, it helps to remember by letter:
+P - Previous line, N - Next line, B - Backwards and F - Forward.
+
+Now that you've learned how to move single characters at a time, next we learn
+how to move one word at a time. To do this, you can use M-f (Meta and f) or
+M-b (Meta and b) to move forwards and backwards, one word at a time.
+
+>> Try moving one word at a time by using M-f and M-b on this line.
+
+Notice how the Ctrl and Meta key combinations perform related functions. C-f
+moves one letter forward, whereas M-f moves one word forward. There are many
+key combinations in mg, where C-<chr> will perform one function and M-<chr>
+will perform a similar related function.
+
+Most probably by now you have moved passed the bottom of the text which was
+initially shown to you when you opened this document. mg redraws your terminal
+screen so that the cursor is in the middle. This is a feature of mg, which
+allows you to see the lines before and after the current cursor position. The
+same effect can be achieved manually.
+
+>> Now move the cursor down to this line and then type C-l (that's Control and
+ lowercase L).
+
+Note that C-l refreshes the screen and centers it on the line you typed it on.
+
+To move to the beginning or end of a line, you can use the Home and End keys,
+or you can use C-a and C-e to move to the beginning and end.
+
+>> Use C-a and C-e to move to the beginning and end of this line.
+
+The next commands we will learn is how to move up and down, one page at a time.
+To do this, you can use the Page Up (sometimes labelled PgUp) and Page Down
+(sometimes labelled PgDn) keys. You can also use C-v and M-v to do this. C-v
+moves the cursor down one page and M-v moves it up one page.
+
+>> Try using M-v and C-v to move up and down, one page at a time.
+
+The final two motion commands we will learn are M-< (Meta-Less than) and
+M-> (Meta-Greater than) which move you to the beginning and end of a file,
+respectively. You may not want to try that now as you will probably lose your
+place in this tutorial. Note that on most terminals, < is above the , key, so
+you'll need to press the Shift key to type <.
+
+Movement Summary
+-----------------
+
+The following is a summary of the movement commands we've learned so far:
+
+ C-f Move forward one character (can also use right arrow key).
+ C-b Move backward one character (can also use left arrow key).
+ C-p Move up one line (can also use up arrow key).
+ C-n Move down one line (can also use down arrow key).
+ M-f Move forward one word.
+ M-b Move backward one word.
+ C-a Move to beginning of line (can also use Home key).
+ C-e Move to end of line (can also use End key).
+ C-v Move forward one page (can also use PgDn/Page Down key).
+ M-v Move backward one page (can also use PgUp/Page Up key).
+ M-< Move to beginning of file.
+ M-> Move to end of file.
+
+Now that you've mastered the basics of moving around in mg, you can cause mg
+to execute these commands multiple times. The way to do this is to type C-u
+followed by some digits followed by a movement command.
+
+>> Type C-u 5 C-f to move forward 5 characters.
+
+In general, C-u allows you to execute any command multiple times, not just
+cursor motion commands. The only exception to this rule are C-v and M-v.
+When using these two commands with an argument, they move the cursor by that
+many lines instead of pages.
+
+Cancelling mg Commands
+----------------------
+
+If you have started typing out a command that you didn't mean to finish, you
+can use the C-g command to cancel the command immediately.
+
+>> For example, type C-u 50 and then type C-g to cancel the C-u command.
+>> Type Esc and then C-g to cancel the Esc key.
+
+In general, you can use C-g to stop any mg commands. You may type it multiple
+times if you wish. You should see the word "Quit" appear in the bottom of the
+screen when you type C-g indicating that a command was cancelled.
+
+In general, when in doubt, use C-g to get out of trouble.
+
+Inserting/Deleting Text
+-----------------------
+
+To insert text anywhere, simply move your cursor to the appropriate position
+and begin typing. To delete characters, use the backspace key. If you use
+M-<backspace> (Meta and backspace key), you will delete one word instead
+of one character at a time.
+
+To delete characters to the right of the cursor, you can use C-d to delete
+characters to the right of the current position. If you use M-d instead of
+C-d, you can delete one word at a time instead of one character at a time.
+
+>> Try inserting and deleting characters and words on this line.
+
+Note that if you type too many characters on a single line, the line will
+scroll off the screen and you will see a $ on the line to indicate that the
+line is too long to fit on the screen at one time.
+
+To delete a line at a time, you can use C-k to kill the line from the current
+cursor position to the end of the line. You can type C-k multiple times to
+kill many lines.
+
+You can issue insert or delete commands multiple times using C-u. For example,
+C-u 10 e will type out eeeeeeeeee, C-u 4 M-d will delete four words to the
+right of the cursor and so on.
+
+To undo any operation, you can use C-_ (that's control-underscore).
+
+Now if you kill something that you didn't mean to, you can yank it back from
+the dead by using C-y. In general, when you kill something bigger than a single
+character, mg saves it in a buffer somewhere and you can restore it by using
+C-y. This is useful for moving text around. You can kill text in one place,
+move your cursor to the new location and then use C-y to paste it there.
+
+Search for Text
+---------------
+
+To search for text, type C-s followed by the text you wish to search for. Note
+that as you start typing the characters, mg automatically searches as you type
+the characters.
+
+To continue searching the text you're looking for, type C-s to find the next
+instance. To search in reverse, type C-r instead of C-s. If you type C-s or
+C-r twice, it will simply search for the last text that you searched for.
+
+To stop searching for text, simply use the cursor keys (or C-f, C-b etc.) or
+C-g to stop the search operation.
+
+>> Use C-s foo to search for "foo" in the text. You can use C-s again to
+ find other instances of foo in the file.
+
+Note that if a word cannot be found, it will say Failing I-search: at the
+bottom of the screen. Typing C-s again will wrap the search around from the
+top of the file and begin searching from there.
+
+Replace Text
+------------
+
+To replace text, use M-%. You will be prompted for the text to search for and
+the text to replace it with. You will then be taken to the first instance of
+text from the current position. At this point you can do one of the following:
+
+ y - Replace the text at this instance and search for more items.
+ n - Skip this instance and search for more items.
+ . or Enter - Stop replacing text (you can also use C-g).
+ ! - Replace all the instances without prompting at each one.
+
+>> Try replacing "frobnitz" with "zutwalt" on this line.
+
+Cut/Copy/Paste Text
+-------------------
+
+As explained above, you can cut regions using C-k to kill multiple lines. To
+paste the text that you just cut, simply move your cursor to the point and
+then type C-y to restore the text. You may type C-y multiple times to restore
+the text. Hence, to copy text, you can use C-k to kill all the lines, use C-y
+to restore it immediately, then move to the region you want to copy it to and
+then type C-y again to restore the last cut text block again.
+
+Another way to cut or copy chunks of text is to first position your cursor at
+the starting point of the chunk of text. Then type C-<space> to mark this as
+the starting point to cut or copy. Then move the cursor to the end point of the
+text chunk that you wish to manipulate. Then type C-w to cut the region, or
+M-w to copy the region. If you wish to cancel marking a block of text, simply
+type C-g to cancel the operation.
+
+To paste the region that you've cut or copied above, simply move your cursor
+to the desired location and then type C-y to paste it.
+
+Status Line
+-----------
+
+At the bottom of your screen is a reverse highlighted line. This is the status
+line and lets you know some useful information about the file you're editing.
+
+On the status line, you should see "Mg: tutorial". This lets you know that
+you're editing a file named "tutorial". If you've edited this file and not
+saved it, it should have a "**" to the left of those words. If this file is
+read-only, you should see a "%%" to the left of those words.
+
+To the right of the status line, you should see L followed by digits
+and, if column-number-mode is enabled, C followed by some more digits.
+Type M-x column-number-mode Enter to enable it if it is disabled (the
+default). These indicate the line number and column number of the file
+that your cursor is currently on. If you move the cursor around, you
+should see the line and column number change.
+
+In the middle of the screen, you should see the word "(fundamental)" which
+indicates that the current editing mode is "fundamental-mode". The mg editor
+also supports a c-mode that is more suited to editing C code. There are also
+some other useful editing modes for different situations. See the man page
+for mg(1) to learn about the various editing modes.
+
+Opening and Saving Files
+------------------------
+
+To open a file, you can use C-x C-f. You will then be prompted for a file name.
+If you type a file name that doesn't already exist, a new file will be opened
+for you. If the file name already exists, then it will be opened for you and
+you can begin editing it. Note that you do not need to type the whole file
+name for an existing file. You can type part of the file name and then press
+the TAB key. If there is only file name that matches, mg will fill in the rest
+of the file name for you. If there are multiple files, mg will display that
+the choice is ambiguous. If you type the TAB key again, mg will show you all
+the available choices for file names.
+
+NOTE: If you type C-x f instead of C-x C-f, you can use C-g to cancel the
+Set-Fill-Column command. You can also use C-g to cancel the C-x C-f command
+if you don't wish to open a new file.
+
+To save the file once you've edited it, use C-x C-s to save the file. When
+mg is done saving the file, you should see the words "Wrote /path/to/file"
+in the bottom of your screen. In general, it is a good idea to save quite
+often. When you save a file, mg saves a backup of the file with a tilde (~)
+character at the end.
+
+If you decide to open a directory instead of a file, mg will transistion into a
+mode called dired. Dired fills a buffer with the contents of the selected
+directory, one file or sub-directory's details per line. Some basic file
+management functions can be performed on the files and sub-directories in the
+buffer. For example, with the cursor over a specific file:
+
+ Pressing c will give you the opportunity to copy the file.
+ Pressing d will mark the file for deletion.
+ Pressing x will unlink files previously marked for deletion.
+ Pressing Return will open the highlighted file into it's own buffer for
+ editing.
+
+There are more dired commands, see the man page for further information.
+
+Working with Buffers and Windows
+--------------------------------
+
+Once a file is loaded into mg, it is often referred to as a buffer.
+
+The mg editor is capable of editing multiple buffers at the same time. When you
+open a second file with C-x C-f, the first buffer is still being edited by mg.
+
+Both buffers can be viewed simultaneously because mg can support several windows
+at the same time, each one displaying different text. To split a screen into two
+horizontal windows use C-x 2. To return to one window, use C-x 1 to close the
+other windows and only keep the current window.
+
+>> Use C-x 2 to split the screen into two windows.
+
+>> Use C-x o to move from one window to the other. You can scroll up and down
+ in each window using the cursor keys or C-n and C-p keys.
+
+>> Use C-x 1 to restore back to one window.
+
+You can list all the buffers that are opened by mg by typing C-x C-b. The
+screen should divide into two and the top window will list the buffers that
+are currently open. Use C-x o to switch to the top window, then use the arrow
+keys to move to the buffer you wish to switch to, and then type the Enter key to
+select that buffer. Then use C-x 1 to switch back to only one window.
+
+You may also move back to the last opened buffer by using C-x b to toggle back
+and forth between two buffers. Note the difference between C-x b and C-x C-b.
+
+>> Use C-x C-f to open a new file
+>> Use C-x b to switch back and forth between that buffer and this one.
+
+To kill any buffer, use C-x k. You will be prompted for the buffer to kill.
+By default, the current buffer is selected as the one to kill. You may also
+type another buffer name or use C-g to cancel the operation.
+
+Extended Commands
+-----------------
+
+The mg editor has several extended commands, more than what can be covered
+by the Control and Meta keys. The mg editor gets around this by using what is
+called the X (eXtend) command. There are two forms of this:
+
+ C-x Character eXtension. Followed by one character.
+ M-x Named character eXtension. Followed by a long command.
+
+You've already seen C-x C-f and C-x C-s to open and save a file. There are
+other longer commands. For instance, you can also open a file by typing
+M-x find-file Enter. When you type a command using M-x, mg prompts you for
+the command at the bottom of the screen. You can type out the whole command
+if you wish, or you can type out part of the command and then use the TAB key
+for autocompleting the command.
+
+For instance, to replace text, you can type M-x repl TAB enter to execute
+the replace-text command. To cancel this command, type C-g.
+
+To see a list of all available mg(1) commands, consult the man page.
+
+Exiting mg
+----------
+
+To exit mg temporarily and return to the shell, use C-z. This will take you
+back to the command shell. To return back to mg, type fg in the shell and you
+will be returned to your mg session.
+
+To exit mg permanently, type C-x C-c. If you have any unsaved buffers, you
+will be asked if you wish to save them or not.
+
+Conclusion
+----------
+
+This tutorial is meant to get new users up and running with mg. There is more
+information available via the mg(1) man page. If you have any suggestions for
+improvement, please don't hesitate to drop a message or (better still) submit
+a diff to tech@openbsd.org.
+
+Author Info
+-----------
+
+Original Author of this document: Mayukh Bose,
+Date last updated: 2018-05-27
+
+Copyright
+---------
+
+None. This document is in the public domain.
Index: contrib/mg/undo.c
===================================================================
--- /dev/null
+++ contrib/mg/undo.c
@@ -0,0 +1,602 @@
+/* $OpenBSD: undo.c,v 1.58 2016/09/05 08:10:58 lum Exp $ */
+/*
+ * This file is in the public domain
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+#include "kbd.h"
+
+#define MAX_FREE_RECORDS 32
+
+/*
+ * Local variables
+ */
+static struct undoq undo_free;
+static int undo_free_num;
+static int boundary_flag = TRUE;
+static int undo_enable_flag = TRUE;
+
+/*
+ * Local functions
+ */
+static int find_dot(struct line *, int);
+static int find_lo(int, struct line **, int *, int *);
+static struct undo_rec *new_undo_record(void);
+static int drop_oldest_undo_record(void);
+
+/*
+ * find_dot, find_lo()
+ *
+ * Find an absolute dot in the buffer from a line/offset pair, and vice-versa.
+ *
+ * Since lines can be deleted while they are referenced by undo record, we
+ * need to have an absolute dot to have something reliable.
+ */
+static int
+find_dot(struct line *lp, int off)
+{
+ int count = 0;
+ struct line *p;
+
+ for (p = curbp->b_headp; p != lp; p = lforw(p)) {
+ if (count != 0) {
+ if (p == curbp->b_headp) {
+ dobeep();
+ ewprintf("Error: Undo stuff called with a"
+ "nonexistent line");
+ return (FALSE);
+ }
+ }
+ count += llength(p) + 1;
+ }
+ count += off;
+
+ return (count);
+}
+
+static int
+find_lo(int pos, struct line **olp, int *offset, int *lnum)
+{
+ struct line *p;
+ int lineno;
+
+ p = curbp->b_headp;
+ lineno = 0;
+ while (pos > llength(p)) {
+ pos -= llength(p) + 1;
+ if ((p = lforw(p)) == curbp->b_headp) {
+ *olp = NULL;
+ *offset = 0;
+ return (FALSE);
+ }
+ lineno++;
+ }
+ *olp = p;
+ *offset = pos;
+ *lnum = lineno;
+
+ return (TRUE);
+}
+
+static struct undo_rec *
+new_undo_record(void)
+{
+ struct undo_rec *rec;
+
+ rec = TAILQ_FIRST(&undo_free);
+ if (rec != NULL) {
+ /* Remove it from the free-list */
+ TAILQ_REMOVE(&undo_free, rec, next);
+ undo_free_num--;
+ } else {
+ if ((rec = malloc(sizeof(*rec))) == NULL)
+ panic("Out of memory in undo code (record)");
+ }
+ memset(rec, 0, sizeof(struct undo_rec));
+
+ return (rec);
+}
+
+void
+free_undo_record(struct undo_rec *rec)
+{
+ static int initialised = 0;
+
+ /*
+ * On the first run, do initialisation of the free list.
+ */
+ if (initialised == 0) {
+ TAILQ_INIT(&undo_free);
+ initialised = 1;
+ }
+ free(rec->content);
+ rec->content = NULL;
+ if (undo_free_num >= MAX_FREE_RECORDS) {
+ free(rec);
+ return;
+ }
+ undo_free_num++;
+
+ TAILQ_INSERT_HEAD(&undo_free, rec, next);
+}
+
+/*
+ * Drop the oldest undo record in our list. Return 1 if we could remove it,
+ * 0 if the undo list was empty.
+ */
+static int
+drop_oldest_undo_record(void)
+{
+ struct undo_rec *rec;
+
+ rec = TAILQ_LAST(&curbp->b_undo, undoq);
+ if (rec != NULL) {
+ undo_free_num--;
+ TAILQ_REMOVE(&curbp->b_undo, rec, next);
+ free_undo_record(rec);
+ return (1);
+ }
+ return (0);
+}
+
+static int
+lastrectype(void)
+{
+ struct undo_rec *rec;
+
+ if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL)
+ return (rec->type);
+ return (0);
+}
+
+/*
+ * Returns TRUE if undo is enabled, FALSE otherwise.
+ */
+int
+undo_enabled(void)
+{
+ return (undo_enable_flag);
+}
+
+/*
+ * undo_enable: toggle undo_enable.
+ * Returns the previous value of the flag.
+ */
+int
+undo_enable(int f, int n)
+{
+ int pon = undo_enable_flag;
+
+ if (f & (FFARG | FFRAND))
+ undo_enable_flag = n > 0;
+ else
+ undo_enable_flag = !undo_enable_flag;
+
+ if (!(f & FFRAND))
+ ewprintf("Undo %sabled", undo_enable_flag ? "en" : "dis");
+
+ return (pon);
+}
+
+/*
+ * If undo is enabled, then:
+ * Toggle undo boundary recording.
+ * If called with an argument, (n > 0) => enable. Otherwise disable.
+ * In either case, add an undo boundary
+ * If undo is disabled, this function has no effect.
+ */
+int
+undo_boundary_enable(int f, int n)
+{
+ int bon = boundary_flag;
+
+ if (!undo_enable_flag)
+ return (FALSE);
+
+ undo_add_boundary(FFRAND, 1);
+
+ if (f & (FFARG | FFRAND))
+ boundary_flag = n > 0;
+ else
+ boundary_flag = !boundary_flag;
+
+ if (!(f & FFRAND))
+ ewprintf("Undo boundaries %sabled",
+ boundary_flag ? "en" : "dis");
+
+ return (bon);
+}
+
+/*
+ * Record an undo boundary, unless boundary_flag == FALSE.
+ * Does nothing if previous undo entry is already a boundary or 'modified' flag.
+ */
+int
+undo_add_boundary(int f, int n)
+{
+ struct undo_rec *rec;
+ int last;
+
+ if (boundary_flag == FALSE)
+ return (FALSE);
+
+ last = lastrectype();
+ if (last == BOUNDARY || last == MODIFIED)
+ return (TRUE);
+
+ rec = new_undo_record();
+ rec->type = BOUNDARY;
+
+ TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
+
+ return (TRUE);
+}
+
+/*
+ * Record an undo "modified" boundary
+ */
+void
+undo_add_modified(void)
+{
+ struct undo_rec *rec, *trec;
+
+ TAILQ_FOREACH_SAFE(rec, &curbp->b_undo, next, trec)
+ if (rec->type == MODIFIED) {
+ TAILQ_REMOVE(&curbp->b_undo, rec, next);
+ free_undo_record(rec);
+ }
+
+ rec = new_undo_record();
+ rec->type = MODIFIED;
+
+ TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
+
+ return;
+}
+
+int
+undo_add_insert(struct line *lp, int offset, int size)
+{
+ struct region reg;
+ struct undo_rec *rec;
+ int pos;
+
+ if (!undo_enable_flag)
+ return (TRUE);
+
+ memset(&reg, 0, sizeof(reg));
+ reg.r_linep = lp;
+ reg.r_offset = offset;
+ reg.r_size = size;
+
+ pos = find_dot(lp, offset);
+
+ /*
+ * We try to reuse the last undo record to `compress' things.
+ */
+ rec = TAILQ_FIRST(&curbp->b_undo);
+ if (rec != NULL && rec->type == INSERT) {
+ if (rec->pos + rec->region.r_size == pos) {
+ rec->region.r_size += reg.r_size;
+ return (TRUE);
+ }
+ }
+
+ /*
+ * We couldn't reuse the last undo record, so prepare a new one.
+ */
+ rec = new_undo_record();
+ rec->pos = pos;
+ rec->type = INSERT;
+ memmove(&rec->region, &reg, sizeof(struct region));
+ rec->content = NULL;
+
+ undo_add_boundary(FFRAND, 1);
+
+ TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
+
+ return (TRUE);
+}
+
+/*
+ * This of course must be done _before_ the actual deletion is done.
+ */
+int
+undo_add_delete(struct line *lp, int offset, int size, int isreg)
+{
+ struct region reg;
+ struct undo_rec *rec;
+ int pos;
+
+ if (!undo_enable_flag)
+ return (TRUE);
+
+ memset(&reg, 0, sizeof(reg));
+ reg.r_linep = lp;
+ reg.r_offset = offset;
+ reg.r_size = size;
+
+ pos = find_dot(lp, offset);
+
+ if (offset == llength(lp)) /* if it's a newline... */
+ undo_add_boundary(FFRAND, 1);
+ else if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL) {
+ /*
+ * Separate this command from the previous one if we're not
+ * just before the previous record...
+ */
+ if (!isreg && rec->type == DELETE) {
+ if (rec->pos - rec->region.r_size != pos)
+ undo_add_boundary(FFRAND, 1);
+ }
+ }
+ rec = new_undo_record();
+ rec->pos = pos;
+ if (isreg)
+ rec->type = DELREG;
+ else
+ rec->type = DELETE;
+ memmove(&rec->region, &reg, sizeof(struct region));
+ do {
+ rec->content = malloc(reg.r_size + 1);
+ } while ((rec->content == NULL) && drop_oldest_undo_record());
+
+ if (rec->content == NULL)
+ panic("Out of memory");
+
+ region_get_data(&reg, rec->content, reg.r_size);
+
+ if (isreg || lastrectype() != DELETE)
+ undo_add_boundary(FFRAND, 1);
+
+ TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next);
+
+ return (TRUE);
+}
+
+/*
+ * This of course must be called before the change takes place.
+ */
+int
+undo_add_change(struct line *lp, int offset, int size)
+{
+ if (!undo_enable_flag)
+ return (TRUE);
+ undo_add_boundary(FFRAND, 1);
+ boundary_flag = FALSE;
+ undo_add_delete(lp, offset, size, 0);
+ undo_add_insert(lp, offset, size);
+ boundary_flag = TRUE;
+ undo_add_boundary(FFRAND, 1);
+
+ return (TRUE);
+}
+
+/*
+ * Show the undo records for the current buffer in a new buffer.
+ */
+/* ARGSUSED */
+int
+undo_dump(int f, int n)
+{
+ struct undo_rec *rec;
+ struct buffer *bp;
+ struct mgwin *wp;
+ char buf[4096], tmp[1024];
+ int num;
+
+ /*
+ * Prepare the buffer for insertion.
+ */
+ if ((bp = bfind("*undo*", TRUE)) == NULL)
+ return (FALSE);
+ bp->b_flag |= BFREADONLY;
+ bclear(bp);
+ if ((wp = popbuf(bp, WNONE)) == NULL)
+ return (FALSE);
+
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == bp) {
+ wp->w_dotp = bp->b_headp;
+ wp->w_doto = 0;
+ }
+ }
+
+ num = 0;
+ TAILQ_FOREACH(rec, &curbp->b_undo, next) {
+ num++;
+ snprintf(buf, sizeof(buf),
+ "%d:\t %s at %d ", num,
+ (rec->type == DELETE) ? "DELETE":
+ (rec->type == DELREG) ? "DELREGION":
+ (rec->type == INSERT) ? "INSERT":
+ (rec->type == BOUNDARY) ? "----" :
+ (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN",
+ rec->pos);
+
+ if (rec->content) {
+ (void)strlcat(buf, "\"", sizeof(buf));
+ snprintf(tmp, sizeof(tmp), "%.*s", rec->region.r_size,
+ rec->content);
+ (void)strlcat(buf, tmp, sizeof(buf));
+ (void)strlcat(buf, "\"", sizeof(buf));
+ }
+ snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size);
+ if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) {
+ dobeep();
+ ewprintf("Undo record too large. Aborted.");
+ return (FALSE);
+ }
+ addlinef(bp, "%s", buf);
+ }
+ for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
+ if (wp->w_bufp == bp) {
+ wp->w_dotline = num+1;
+ wp->w_rflag |= WFFULL;
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * After the user did action1, then action2, then action3:
+ *
+ * [action3] <--- Undoptr
+ * [action2]
+ * [action1]
+ * ------
+ * [undo]
+ *
+ * After undo:
+ *
+ * [undo of action3]
+ * [action2] <--- Undoptr
+ * [action1]
+ * ------
+ * [undo]
+ *
+ * After another undo:
+ *
+ *
+ * [undo of action2]
+ * [undo of action3]
+ * [action1] <--- Undoptr
+ * ------
+ * [undo]
+ *
+ * Note that the "undo of actionX" have no special meaning. Only when
+ * we undo a deletion, the insertion will be recorded just as if it
+ * was typed on the keyboard. Resulting in the inverse operation being
+ * saved in the list.
+ *
+ * If undoptr reaches the bottom of the list, or if we moved between
+ * two undo actions, we make it point back at the topmost record. This is
+ * how we handle redoing.
+ */
+/* ARGSUSED */
+int
+undo(int f, int n)
+{
+ struct undo_rec *ptr, *nptr;
+ int done, rval;
+ struct line *lp;
+ int offset, save;
+ static int nulled = FALSE;
+ int lineno;
+
+ if (n < 0)
+ return (FALSE);
+
+ ptr = curbp->b_undoptr;
+
+ /* first invocation, make ptr point back to the top of the list */
+ if ((ptr == NULL && nulled == TRUE) || rptcount == 0) {
+ ptr = TAILQ_FIRST(&curbp->b_undo);
+ nulled = TRUE;
+ }
+
+ rval = TRUE;
+ while (n--) {
+ /* if we have a spurious boundary, free it and move on.... */
+ while (ptr && ptr->type == BOUNDARY) {
+ nptr = TAILQ_NEXT(ptr, next);
+ TAILQ_REMOVE(&curbp->b_undo, ptr, next);
+ free_undo_record(ptr);
+ ptr = nptr;
+ }
+ /*
+ * Ptr is NULL, but on the next run, it will point to the
+ * top again, redoing all stuff done in the buffer since
+ * its creation.
+ */
+ if (ptr == NULL) {
+ dobeep();
+ ewprintf("No further undo information");
+ rval = FALSE;
+ nulled = TRUE;
+ break;
+ }
+ nulled = FALSE;
+
+ /*
+ * Loop while we don't get a boundary specifying we've
+ * finished the current action...
+ */
+
+ undo_add_boundary(FFRAND, 1);
+
+ save = boundary_flag;
+ boundary_flag = FALSE;
+
+ done = 0;
+ do {
+ /*
+ * Move to where this has to apply
+ *
+ * Boundaries (and the modified flag) are put as
+ * position 0 (to save lookup time in find_dot)
+ * so we must not move there...
+ */
+ if (ptr->type != BOUNDARY && ptr->type != MODIFIED) {
+ if (find_lo(ptr->pos, &lp,
+ &offset, &lineno) == FALSE) {
+ dobeep();
+ ewprintf("Internal error in Undo!");
+ rval = FALSE;
+ break;
+ }
+ curwp->w_dotp = lp;
+ curwp->w_doto = offset;
+ curwp->w_markline = curwp->w_dotline;
+ curwp->w_dotline = lineno;
+ }
+
+ /*
+ * Do operation^-1
+ */
+ switch (ptr->type) {
+ case INSERT:
+ ldelete(ptr->region.r_size, KNONE);
+ break;
+ case DELETE:
+ lp = curwp->w_dotp;
+ offset = curwp->w_doto;
+ region_put_data(ptr->content,
+ ptr->region.r_size);
+ curwp->w_dotp = lp;
+ curwp->w_doto = offset;
+ break;
+ case DELREG:
+ region_put_data(ptr->content,
+ ptr->region.r_size);
+ break;
+ case BOUNDARY:
+ done = 1;
+ break;
+ case MODIFIED:
+ curbp->b_flag &= ~BFCHG;
+ break;
+ default:
+ break;
+ }
+
+ /* And move to next record */
+ ptr = TAILQ_NEXT(ptr, next);
+ } while (ptr != NULL && !done);
+
+ boundary_flag = save;
+ undo_add_boundary(FFRAND, 1);
+
+ ewprintf("Undo!");
+ }
+
+ curbp->b_undoptr = ptr;
+
+ return (rval);
+}
Index: contrib/mg/util.h
===================================================================
--- /dev/null
+++ contrib/mg/util.h
@@ -0,0 +1,121 @@
+/* $OpenBSD: util.h,v 1.34 2013/06/03 21:07:02 tedu Exp $ */
+/* $NetBSD: util.h,v 1.2 1996/05/16 07:00:22 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1995
+ * The Regents of the University of California. All rights reserved.
+ * Portions Copyright (c) 1996, Jason Downs. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+/*
+ * fparseln() specific operation flags.
+ */
+#define FPARSELN_UNESCESC 0x01
+#define FPARSELN_UNESCCONT 0x02
+#define FPARSELN_UNESCCOMM 0x04
+#define FPARSELN_UNESCREST 0x08
+#define FPARSELN_UNESCALL 0x0f
+
+/*
+ * opendev() specific operation flags.
+ */
+#define OPENDEV_PART 0x01 /* Try to open the raw partition. */
+#define OPENDEV_BLCK 0x04 /* Open block, not character device. */
+
+/*
+ * uucplock(3) specific flags.
+ */
+#define UU_LOCK_INUSE (1)
+#define UU_LOCK_OK (0)
+#define UU_LOCK_OPEN_ERR (-1)
+#define UU_LOCK_READ_ERR (-2)
+#define UU_LOCK_CREAT_ERR (-3)
+#define UU_LOCK_WRITE_ERR (-4)
+#define UU_LOCK_LINK_ERR (-5)
+#define UU_LOCK_TRY_ERR (-6)
+#define UU_LOCK_OWNER_ERR (-7)
+
+/*
+ * fmt_scaled(3) specific flags.
+ */
+#define FMT_SCALED_STRSIZE 7 /* minus sign, 4 digits, suffix, null byte */
+
+/*
+ * stub struct definitions.
+ */
+struct __sFILE;
+struct login_cap;
+struct passwd;
+struct termios;
+struct utmp;
+struct winsize;
+
+char *fparseln(FILE *, size_t *, size_t *, const char[3], int);
+void login(struct utmp *);
+int login_tty(int);
+int logout(const char *);
+void logwtmp(const char *, const char *, const char *);
+int opendev(const char *, int, int, char **);
+int pidfile(const char *);
+void pw_setdir(const char *);
+char *pw_file(const char *);
+int pw_lock(int);
+int pw_mkdb(char *, int);
+int pw_abort(void);
+void pw_init(void);
+void pw_edit(int, const char *);
+void pw_prompt(void);
+void pw_copy(int, int, const struct passwd *, const struct passwd *);
+int pw_scan(char *, struct passwd *, int *);
+void pw_error(const char *, int, int);
+int openpty(int *, int *, char *, struct termios *, struct winsize *);
+int opendisk(const char *, int, char *, size_t, int);
+pid_t forkpty(int *, char *, struct termios *, struct winsize *);
+int getmaxpartitions(void);
+int getrawpartition(void);
+void login_fbtab(const char *, uid_t, gid_t);
+int login_check_expire(struct __sFILE *, struct passwd *, char *, int);
+char *readlabelfs(char *, int);
+const char *uu_lockerr(int);
+int uu_lock(const char *);
+int uu_lock_txfr(const char *, pid_t);
+int uu_unlock(const char *);
+int fmt_scaled(long long, char *);
+int scan_scaled(char *, long long *);
+int isduid(const char *, int);
+int pkcs5_pbkdf2(const char *, size_t, const uint8_t *, size_t,
+ uint8_t *, size_t, unsigned int);
+int bcrypt_pbkdf(const char *, size_t, const uint8_t *, size_t,
+ uint8_t *, size_t, unsigned int);
+
+#endif /* !_UTIL_H_ */
Index: contrib/mg/util.c
===================================================================
--- /dev/null
+++ contrib/mg/util.c
@@ -0,0 +1,529 @@
+/* $OpenBSD: util.c,v 1.43 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Assorted commands.
+ * This file contains the command processors for a large assortment of
+ * unrelated commands. The only thing they have in common is that they
+ * are all command processors.
+ */
+
+#include <sys/queue.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "def.h"
+
+/*
+ * Display a bunch of useful information about the current location of dot.
+ * The character under the cursor (in octal), the current line, row, and
+ * column, and approximate position of the cursor in the file (as a
+ * percentage) is displayed.
+ * Also included at the moment are some values in parenthesis for debugging
+ * explicit newline inclusion into the buffer.
+ * The column position assumes an infinite
+ * position display; it does not truncate just because the screen does.
+ * This is normally bound to "C-x =".
+ */
+/* ARGSUSED */
+int
+showcpos(int f, int n)
+{
+ struct line *clp;
+ char *msg;
+ long nchar, cchar;
+ int nline, row;
+ int cline, cbyte; /* Current line/char/byte */
+ int ratio;
+
+ /* collect the data */
+ clp = bfirstlp(curbp);
+ msg = "Char:";
+ cchar = 0;
+ cline = 0;
+ cbyte = 0;
+ nchar = 0;
+ nline = 0;
+ for (;;) {
+ /* count lines and display total as (raw) 'lines' and
+ compare with b_lines */
+ ++nline;
+ if (clp == curwp->w_dotp) {
+ /* obtain (raw) dot line # and compare with w_dotline */
+ cline = nline;
+ cchar = nchar + curwp->w_doto;
+ if (curwp->w_doto == llength(clp))
+ /* fake a \n at end of line */
+ cbyte = *curbp->b_nlchr;
+ else
+ cbyte = lgetc(clp, curwp->w_doto);
+ }
+ /* include # of chars in this line for point-thru-buff ratio */
+ nchar += llength(clp);
+ clp = lforw(clp);
+ if (clp == curbp->b_headp) {
+ if (cbyte == *curbp->b_nlchr &&
+ cline == curbp->b_lines) {
+ /* swap faked \n for EOB msg */
+ cbyte = EOF;
+ msg = "(EOB)";
+ }
+ break;
+ }
+ /* count the implied newline */
+ nchar++;
+ }
+ /* determine row # within current window */
+ row = curwp->w_toprow + 1;
+ clp = curwp->w_linep;
+ while (clp != curbp->b_headp && clp != curwp->w_dotp) {
+ ++row;
+ clp = lforw(clp);
+ }
+ ratio = nchar ? (100L * cchar) / nchar : 100;
+ ewprintf("%s %c (0%o) point=%ld(%d%%) line=%d row=%d col=%d" \
+ " (blines=%d rlines=%d l_size=%d)", msg,
+ cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp),
+ curbp->b_lines, nline, clp->l_size);
+ return (TRUE);
+}
+
+int
+getcolpos(struct mgwin *wp)
+{
+ int col, i, c;
+ char tmp[5];
+
+ /* determine column */
+ col = 0;
+
+ for (i = 0; i < wp->w_doto; ++i) {
+ c = lgetc(wp->w_dotp, i);
+ if (c == '\t'
+#ifdef NOTAB
+ && !(wp->w_bufp->b_flag & BFNOTAB)
+#endif /* NOTAB */
+ ) {
+ col |= 0x07;
+ col++;
+ } else if (ISCTRL(c) != FALSE)
+ col += 2;
+ else if (isprint(c)) {
+ col++;
+ } else {
+ col += snprintf(tmp, sizeof(tmp), "\\%o", c);
+ }
+
+ }
+ return (col);
+}
+
+/*
+ * Twiddle the two characters in front of and under dot, then move forward
+ * one character. Treat new-line characters the same as any other.
+ * Normally bound to "C-t". This always works within a line, so "WFEDIT"
+ * is good enough.
+ */
+/* ARGSUSED */
+int
+twiddle(int f, int n)
+{
+ struct line *dotp;
+ int doto, cr;
+
+ if (n == 0)
+ return (TRUE);
+
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+
+ /* Don't twiddle if the dot is on the first char of buffer */
+ if (doto == 0 && lback(dotp) == curbp->b_headp) {
+ dobeep();
+ ewprintf("Beginning of buffer");
+ return(FALSE);
+ }
+ /* Don't twiddle if the dot is on the last char of buffer */
+ if (doto == llength(dotp) && lforw(dotp) == curbp->b_headp) {
+ dobeep();
+ return(FALSE);
+ }
+ undo_boundary_enable(FFRAND, 0);
+ if (doto == 0 && doto == llength(dotp)) { /* only '\n' on this line */
+ (void)forwline(FFRAND, 1);
+ curwp->w_doto = 0;
+ } else {
+ if (doto == 0) { /* 1st twiddle is on 1st character of a line */
+ cr = lgetc(dotp, doto);
+ (void)backdel(FFRAND, 1);
+ (void)forwchar(FFRAND, 1);
+ lnewline();
+ linsert(1, cr);
+ (void)backdel(FFRAND, 1);
+ } else { /* twiddle is elsewhere in line */
+ cr = lgetc(dotp, doto - 1);
+ (void)backdel(FFRAND, 1);
+ (void)forwchar(FFRAND, 1);
+ linsert(1, cr);
+ }
+ }
+ undo_boundary_enable(FFRAND, 1);
+ lchange(WFEDIT);
+ return (TRUE);
+}
+
+/*
+ * Open up some blank space. The basic plan is to insert a bunch of
+ * newlines, and then back up over them. Everything is done by the
+ * subcommand processors. They even handle the looping. Normally this
+ * is bound to "C-o".
+ */
+/* ARGSUSED */
+int
+openline(int f, int n)
+{
+ int i, s;
+
+ if (n < 0)
+ return (FALSE);
+ if (n == 0)
+ return (TRUE);
+
+ /* insert newlines */
+ undo_boundary_enable(FFRAND, 0);
+ i = n;
+ do {
+ s = lnewline();
+ } while (s == TRUE && --i);
+
+ /* then go back up overtop of them all */
+ if (s == TRUE)
+ s = backchar(f | FFRAND, n);
+ undo_boundary_enable(FFRAND, 1);
+ return (s);
+}
+
+/*
+ * Insert a newline.
+ */
+/* ARGSUSED */
+int
+enewline(int f, int n)
+{
+ int s;
+
+ if (n < 0)
+ return (FALSE);
+
+ while (n--) {
+ if ((s = lnewline()) != TRUE)
+ return (s);
+ }
+ return (TRUE);
+}
+
+/*
+ * Delete blank lines around dot. What this command does depends if dot is
+ * sitting on a blank line. If dot is sitting on a blank line, this command
+ * deletes all the blank lines above and below the current line. If it is
+ * sitting on a non blank line then it deletes all of the blank lines after
+ * the line. Normally this command is bound to "C-x C-o". Any argument is
+ * ignored.
+ */
+/* ARGSUSED */
+int
+deblank(int f, int n)
+{
+ struct line *lp1, *lp2;
+ RSIZE nld;
+
+ lp1 = curwp->w_dotp;
+ while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_headp)
+ lp1 = lp2;
+ lp2 = lp1;
+ nld = (RSIZE)0;
+ while ((lp2 = lforw(lp2)) != curbp->b_headp && llength(lp2) == 0)
+ ++nld;
+ if (nld == 0)
+ return (TRUE);
+ curwp->w_dotp = lforw(lp1);
+ curwp->w_doto = 0;
+ return (ldelete((RSIZE)nld, KNONE));
+}
+
+/*
+ * Delete any whitespace around dot, then insert a space.
+ */
+int
+justone(int f, int n)
+{
+ undo_boundary_enable(FFRAND, 0);
+ (void)delwhite(f, n);
+ linsert(1, ' ');
+ undo_boundary_enable(FFRAND, 1);
+ return (TRUE);
+}
+
+/*
+ * Delete any whitespace around dot.
+ */
+/* ARGSUSED */
+int
+delwhite(int f, int n)
+{
+ int col, s;
+
+ col = curwp->w_doto;
+
+ while (col < llength(curwp->w_dotp) &&
+ (isspace(lgetc(curwp->w_dotp, col))))
+ ++col;
+ do {
+ if (curwp->w_doto == 0) {
+ s = FALSE;
+ break;
+ }
+ if ((s = backchar(FFRAND, 1)) != TRUE)
+ break;
+ } while (isspace(lgetc(curwp->w_dotp, curwp->w_doto)));
+
+ if (s == TRUE)
+ (void)forwchar(FFRAND, 1);
+ (void)ldelete((RSIZE)(col - curwp->w_doto), KNONE);
+ return (TRUE);
+}
+
+/*
+ * Delete any leading whitespace on the current line
+ */
+int
+delleadwhite(int f, int n)
+{
+ int soff, ls;
+ struct line *slp;
+
+ /* Save current position */
+ slp = curwp->w_dotp;
+ soff = curwp->w_doto;
+
+ for (ls = 0; ls < llength(slp); ls++)
+ if (!isspace(lgetc(slp, ls)))
+ break;
+ gotobol(FFRAND, 1);
+ forwdel(FFRAND, ls);
+ soff -= ls;
+ if (soff < 0)
+ soff = 0;
+ forwchar(FFRAND, soff);
+
+ return (TRUE);
+}
+
+/*
+ * Delete any trailing whitespace on the current line
+ */
+int
+deltrailwhite(int f, int n)
+{
+ int soff;
+
+ /* Save current position */
+ soff = curwp->w_doto;
+
+ gotoeol(FFRAND, 1);
+ delwhite(FFRAND, 1);
+
+ /* restore original position, if possible */
+ if (soff < curwp->w_doto)
+ curwp->w_doto = soff;
+
+ return (TRUE);
+}
+
+
+
+/*
+ * Insert a newline, then enough tabs and spaces to duplicate the indentation
+ * of the previous line. Assumes tabs are every eight characters. Quite
+ * simple. Figure out the indentation of the current line. Insert a newline
+ * by calling the standard routine. Insert the indentation by inserting the
+ * right number of tabs and spaces. Return TRUE if all ok. Return FALSE if
+ * one of the subcommands failed. Normally bound to "C-m".
+ */
+/* ARGSUSED */
+int
+lfindent(int f, int n)
+{
+ int c, i, nicol;
+ int s = TRUE;
+
+ if (n < 0)
+ return (FALSE);
+
+ undo_boundary_enable(FFRAND, 0);
+ while (n--) {
+ nicol = 0;
+ for (i = 0; i < llength(curwp->w_dotp); ++i) {
+ c = lgetc(curwp->w_dotp, i);
+ if (c != ' ' && c != '\t')
+ break;
+ if (c == '\t')
+ nicol |= 0x07;
+ ++nicol;
+ }
+ if (lnewline() == FALSE || ((
+#ifdef NOTAB
+ curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : (
+#endif /* NOTAB */
+ ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) ||
+ ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) {
+ s = FALSE;
+ break;
+ }
+ }
+ undo_boundary_enable(FFRAND, 1);
+ return (s);
+}
+
+/*
+ * Indent the current line. Delete existing leading whitespace,
+ * and use tabs/spaces to achieve correct indentation. Try
+ * to leave dot where it started.
+ */
+int
+indent(int f, int n)
+{
+ int soff, i;
+
+ if (n < 0)
+ return (FALSE);
+
+ delleadwhite(FFRAND, 1);
+
+ /* If not invoked with a numerical argument, done */
+ if (!(f & FFARG))
+ return (TRUE);
+
+ /* insert appropriate whitespace */
+ soff = curwp->w_doto;
+ (void)gotobol(FFRAND, 1);
+ if (
+#ifdef NOTAB
+ (curbp->b_flag & BFNOTAB) ? linsert(n, ' ') == FALSE :
+#endif /* NOTAB */
+ (((i = n / 8) != 0 && linsert(i, '\t') == FALSE) ||
+ ((i = n % 8) != 0 && linsert(i, ' ') == FALSE)))
+ return (FALSE);
+
+ forwchar(FFRAND, soff);
+
+ return (TRUE);
+}
+
+
+/*
+ * Delete forward. This is real easy, because the basic delete routine does
+ * all of the work. Watches for negative arguments, and does the right thing.
+ * If any argument is present, it kills rather than deletes, to prevent loss
+ * of text if typed with a big argument. Normally bound to "C-d".
+ */
+/* ARGSUSED */
+int
+forwdel(int f, int n)
+{
+ if (n < 0)
+ return (backdel(f | FFRAND, -n));
+
+ /* really a kill */
+ if (f & FFARG) {
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ }
+
+ return (ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE));
+}
+
+/*
+ * Delete backwards. This is quite easy too, because it's all done with
+ * other functions. Just move the cursor back, and delete forwards. Like
+ * delete forward, this actually does a kill if presented with an argument.
+ */
+/* ARGSUSED */
+int
+backdel(int f, int n)
+{
+ int s;
+
+ if (n < 0)
+ return (forwdel(f | FFRAND, -n));
+
+ /* really a kill */
+ if (f & FFARG) {
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ }
+ if ((s = backchar(f | FFRAND, n)) == TRUE)
+ s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE);
+
+ return (s);
+}
+
+#ifdef NOTAB
+/* ARGSUSED */
+int
+space_to_tabstop(int f, int n)
+{
+ if (n < 0)
+ return (FALSE);
+ if (n == 0)
+ return (TRUE);
+ return (linsert((n << 3) - (curwp->w_doto & 7), ' '));
+}
+#endif /* NOTAB */
+
+/*
+ * Move the dot to the first non-whitespace character of the current line.
+ */
+int
+backtoindent(int f, int n)
+{
+ gotobol(FFRAND, 1);
+ while (curwp->w_doto < llength(curwp->w_dotp) &&
+ (isspace(lgetc(curwp->w_dotp, curwp->w_doto))))
+ ++curwp->w_doto;
+ return (TRUE);
+}
+
+/*
+ * Join the current line to the previous, or with arg, the next line
+ * to the current one. If the former line is not empty, leave exactly
+ * one space at the joint. Otherwise, leave no whitespace.
+ */
+int
+joinline(int f, int n)
+{
+ int doto;
+
+ undo_boundary_enable(FFRAND, 0);
+ if (f & FFARG) {
+ gotoeol(FFRAND, 1);
+ forwdel(FFRAND, 1);
+ } else {
+ gotobol(FFRAND, 1);
+ backdel(FFRAND, 1);
+ }
+
+ delwhite(FFRAND, 1);
+
+ if ((doto = curwp->w_doto) > 0) {
+ linsert(1, ' ');
+ curwp->w_doto = doto;
+ }
+ undo_boundary_enable(FFRAND, 1);
+
+ return (TRUE);
+}
Index: contrib/mg/version.c
===================================================================
--- /dev/null
+++ contrib/mg/version.c
@@ -0,0 +1,28 @@
+/* $OpenBSD: version.c,v 1.10 2015/03/19 21:22:15 bcallah Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * This file contains the string that gets written
+ * out by the emacs-version command.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+
+#include "def.h"
+
+const char version[] = "Mg 2a portable 7.0";
+
+/*
+ * Display the version. All this does
+ * is copy the version string onto the echo line.
+ */
+/* ARGSUSED */
+int
+showversion(int f, int n)
+{
+ ewprintf("%s", version);
+ return (TRUE);
+}
Index: contrib/mg/window.c
===================================================================
--- /dev/null
+++ contrib/mg/window.c
@@ -0,0 +1,440 @@
+/* $OpenBSD: window.c,v 1.36 2015/11/18 18:21:06 jasper Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Window handling.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "def.h"
+
+struct mgwin *
+new_window(struct buffer *bp)
+{
+ struct mgwin *wp;
+
+ wp = calloc(1, sizeof(struct mgwin));
+ if (wp == NULL)
+ return (NULL);
+
+ wp->w_bufp = bp;
+ wp->w_dotp = NULL;
+ wp->w_doto = 0;
+ wp->w_markp = NULL;
+ wp->w_marko = 0;
+ wp->w_rflag = 0;
+ wp->w_frame = 0;
+ wp->w_wrapline = NULL;
+ wp->w_dotline = wp->w_markline = 1;
+ if (bp)
+ bp->b_nwnd++;
+ return (wp);
+}
+
+/*
+ * Reposition dot in the current window to line "n". If the argument is
+ * positive, it is that line. If it is negative it is that line from the
+ * bottom. If it is 0 the window is centered (this is what the standard
+ * redisplay code does).
+ */
+/* ARGSUSED */
+int
+reposition(int f, int n)
+{
+ curwp->w_frame = (f & FFARG) ? (n >= 0 ? n + 1 : n) : 0;
+ curwp->w_rflag |= WFFRAME;
+ sgarbf = TRUE;
+ return (TRUE);
+}
+
+/*
+ * Refresh the display. A call is made to the "ttresize" entry in the
+ * terminal handler, which tries to reset "nrow" and "ncol". They will,
+ * however, never be set outside of the NROW or NCOL range. If the display
+ * changed size, arrange that everything is redone, then call "update" to
+ * fix the display. We do this so the new size can be displayed. In the
+ * normal case the call to "update" in "main.c" refreshes the screen, and
+ * all of the windows need not be recomputed. This call includes a
+ * 'force' parameter to ensure that the redraw is done, even after a
+ * a suspend/continue (where the window size parameters will already
+ * be updated). Note that when you get to the "display unusable"
+ * message, the screen will be messed up. If you make the window bigger
+ * again, and send another command, everything will get fixed!
+ */
+int
+redraw(int f, int n)
+{
+ return (do_redraw(f, n, FALSE));
+}
+
+/* ARGSUSED */
+int
+do_redraw(int f, int n, int force)
+{
+ struct mgwin *wp;
+ int oldnrow, oldncol;
+
+ oldnrow = nrow;
+ oldncol = ncol;
+ ttresize();
+ if (nrow != oldnrow || ncol != oldncol || force) {
+
+ /* find last */
+ wp = wheadp;
+ while (wp->w_wndp != NULL)
+ wp = wp->w_wndp;
+
+ /* check if too small */
+ if (nrow < wp->w_toprow + 3) {
+ dobeep();
+ ewprintf("Display unusable");
+ return (FALSE);
+ }
+ wp->w_ntrows = nrow - wp->w_toprow - 2;
+ sgarbf = TRUE;
+ update(CMODE);
+ } else
+ sgarbf = TRUE;
+ return (TRUE);
+}
+
+/*
+ * The command to make the next window (next => down the screen) the current
+ * window. There are no real errors, although the command does nothing if
+ * there is only 1 window on the screen.
+ */
+/* ARGSUSED */
+int
+nextwind(int f, int n)
+{
+ struct mgwin *wp;
+
+ if ((wp = curwp->w_wndp) == NULL)
+ wp = wheadp;
+ curwp = wp;
+ curbp = wp->w_bufp;
+ return (TRUE);
+}
+
+/* not in GNU Emacs */
+/*
+ * This command makes the previous window (previous => up the screen) the
+ * current window. There are no errors, although the command does not do
+ * a lot if there is only 1 window.
+ */
+/* ARGSUSED */
+int
+prevwind(int f, int n)
+{
+ struct mgwin *wp1, *wp2;
+
+ wp1 = wheadp;
+ wp2 = curwp;
+ if (wp1 == wp2)
+ wp2 = NULL;
+ while (wp1->w_wndp != wp2)
+ wp1 = wp1->w_wndp;
+ curwp = wp1;
+ curbp = wp1->w_bufp;
+ return (TRUE);
+}
+
+/*
+ * This command makes the current window the only window on the screen. Try
+ * to set the framing so that "." does not have to move on the display. Some
+ * care has to be taken to keep the values of dot and mark in the buffer
+ * structures right if the destruction of a window makes a buffer become
+ * undisplayed.
+ */
+/* ARGSUSED */
+int
+onlywind(int f, int n)
+{
+ struct mgwin *wp;
+ struct line *lp;
+ int i;
+
+ while (wheadp != curwp) {
+ wp = wheadp;
+ wheadp = wp->w_wndp;
+ if (--wp->w_bufp->b_nwnd == 0) {
+ wp->w_bufp->b_dotp = wp->w_dotp;
+ wp->w_bufp->b_doto = wp->w_doto;
+ wp->w_bufp->b_markp = wp->w_markp;
+ wp->w_bufp->b_marko = wp->w_marko;
+ wp->w_bufp->b_dotline = wp->w_dotline;
+ wp->w_bufp->b_markline = wp->w_markline;
+ }
+ free(wp);
+ }
+ while (curwp->w_wndp != NULL) {
+ wp = curwp->w_wndp;
+ curwp->w_wndp = wp->w_wndp;
+ if (--wp->w_bufp->b_nwnd == 0) {
+ wp->w_bufp->b_dotp = wp->w_dotp;
+ wp->w_bufp->b_doto = wp->w_doto;
+ wp->w_bufp->b_markp = wp->w_markp;
+ wp->w_bufp->b_marko = wp->w_marko;
+ wp->w_bufp->b_dotline = wp->w_dotline;
+ wp->w_bufp->b_markline = wp->w_markline;
+ }
+ free(wp);
+ }
+ lp = curwp->w_linep;
+ i = curwp->w_toprow;
+ while (i != 0 && lback(lp) != curbp->b_headp) {
+ --i;
+ lp = lback(lp);
+ }
+ curwp->w_toprow = 0;
+
+ /* 2 = mode, echo */
+ curwp->w_ntrows = nrow - 2;
+ curwp->w_linep = lp;
+ curwp->w_rflag |= WFMODE | WFFULL;
+ return (TRUE);
+}
+
+/*
+ * Split the current window. A window smaller than 3 lines cannot be split.
+ * The only other error that is possible is a "malloc" failure allocating the
+ * structure for the new window.
+ * If called with a FFOTHARG, flags on the new window are set to 'n'.
+ */
+/* ARGSUSED */
+int
+splitwind(int f, int n)
+{
+ struct mgwin *wp, *wp1, *wp2;
+ struct line *lp;
+ int ntru, ntrd, ntrl;
+
+ if (curwp->w_ntrows < 3) {
+ dobeep();
+ ewprintf("Cannot split a %d line window", curwp->w_ntrows);
+ return (FALSE);
+ }
+ wp = new_window(curbp);
+ if (wp == NULL) {
+ dobeep();
+ ewprintf("Unable to create a window");
+ return (FALSE);
+ }
+
+ /* use the current dot and mark */
+ wp->w_dotp = curwp->w_dotp;
+ wp->w_doto = curwp->w_doto;
+ wp->w_markp = curwp->w_markp;
+ wp->w_marko = curwp->w_marko;
+ wp->w_dotline = curwp->w_dotline;
+ wp->w_markline = curwp->w_markline;
+
+ /* figure out which half of the screen we're in */
+ ntru = (curwp->w_ntrows - 1) / 2; /* Upper size */
+ ntrl = (curwp->w_ntrows - 1) - ntru; /* Lower size */
+
+ for (lp = curwp->w_linep, ntrd = 0; lp != curwp->w_dotp;
+ lp = lforw(lp))
+ ntrd++;
+
+ lp = curwp->w_linep;
+
+ /* old is upper window */
+ if (ntrd <= ntru) {
+ /* hit mode line */
+ if (ntrd == ntru)
+ lp = lforw(lp);
+ curwp->w_ntrows = ntru;
+ wp->w_wndp = curwp->w_wndp;
+ curwp->w_wndp = wp;
+ wp->w_toprow = curwp->w_toprow + ntru + 1;
+ wp->w_ntrows = ntrl;
+ /* old is lower window */
+ } else {
+ wp1 = NULL;
+ wp2 = wheadp;
+ while (wp2 != curwp) {
+ wp1 = wp2;
+ wp2 = wp2->w_wndp;
+ }
+ if (wp1 == NULL)
+ wheadp = wp;
+ else
+ wp1->w_wndp = wp;
+ wp->w_wndp = curwp;
+ wp->w_toprow = curwp->w_toprow;
+ wp->w_ntrows = ntru;
+
+ /* mode line */
+ ++ntru;
+ curwp->w_toprow += ntru;
+ curwp->w_ntrows = ntrl;
+ while (ntru--)
+ lp = lforw(lp);
+ }
+
+ /* adjust the top lines if necessary */
+ curwp->w_linep = lp;
+ wp->w_linep = lp;
+
+ curwp->w_rflag |= WFMODE | WFFULL;
+ wp->w_rflag |= WFMODE | WFFULL;
+ /* if FFOTHARG, set flags) */
+ if (f & FFOTHARG)
+ wp->w_flag = n;
+
+ return (TRUE);
+}
+
+/*
+ * Enlarge the current window. Find the window that loses space. Make sure
+ * it is big enough. If so, hack the window descriptions, and ask redisplay
+ * to do all the hard work. You don't just set "force reframe" because dot
+ * would move.
+ */
+/* ARGSUSED */
+int
+enlargewind(int f, int n)
+{
+ struct mgwin *adjwp;
+ struct line *lp;
+ int i;
+
+ if (n < 0)
+ return (shrinkwind(f, -n));
+ if (wheadp->w_wndp == NULL) {
+ dobeep();
+ ewprintf("Only one window");
+ return (FALSE);
+ }
+ if ((adjwp = curwp->w_wndp) == NULL) {
+ adjwp = wheadp;
+ while (adjwp->w_wndp != curwp)
+ adjwp = adjwp->w_wndp;
+ }
+ if (adjwp->w_ntrows <= n) {
+ dobeep();
+ ewprintf("Impossible change");
+ return (FALSE);
+ }
+
+ /* shrink below */
+ if (curwp->w_wndp == adjwp) {
+ lp = adjwp->w_linep;
+ for (i = 0; i < n && lp != adjwp->w_bufp->b_headp; ++i)
+ lp = lforw(lp);
+ adjwp->w_linep = lp;
+ adjwp->w_toprow += n;
+ /* shrink above */
+ } else {
+ lp = curwp->w_linep;
+ for (i = 0; i < n && lback(lp) != curbp->b_headp; ++i)
+ lp = lback(lp);
+ curwp->w_linep = lp;
+ curwp->w_toprow -= n;
+ }
+ curwp->w_ntrows += n;
+ adjwp->w_ntrows -= n;
+ curwp->w_rflag |= WFMODE | WFFULL;
+ adjwp->w_rflag |= WFMODE | WFFULL;
+ return (TRUE);
+}
+
+/*
+ * Shrink the current window. Find the window that gains space. Hack at the
+ * window descriptions. Ask the redisplay to do all the hard work.
+ */
+int
+shrinkwind(int f, int n)
+{
+ struct mgwin *adjwp;
+ struct line *lp;
+ int i;
+
+ if (n < 0)
+ return (enlargewind(f, -n));
+ if (wheadp->w_wndp == NULL) {
+ dobeep();
+ ewprintf("Only one window");
+ return (FALSE);
+ }
+ /*
+ * Bit of flakiness - FFRAND means it was an internal call, and
+ * to be trusted implicitly about sizes.
+ */
+ if (!(f & FFRAND) && curwp->w_ntrows <= n) {
+ dobeep();
+ ewprintf("Impossible change");
+ return (FALSE);
+ }
+ if ((adjwp = curwp->w_wndp) == NULL) {
+ adjwp = wheadp;
+ while (adjwp->w_wndp != curwp)
+ adjwp = adjwp->w_wndp;
+ }
+
+ /* grow below */
+ if (curwp->w_wndp == adjwp) {
+ lp = adjwp->w_linep;
+ for (i = 0; i < n && lback(lp) != adjwp->w_bufp->b_headp; ++i)
+ lp = lback(lp);
+ adjwp->w_linep = lp;
+ adjwp->w_toprow -= n;
+ /* grow above */
+ } else {
+ lp = curwp->w_linep;
+ for (i = 0; i < n && lp != curbp->b_headp; ++i)
+ lp = lforw(lp);
+ curwp->w_linep = lp;
+ curwp->w_toprow += n;
+ }
+ curwp->w_ntrows -= n;
+ adjwp->w_ntrows += n;
+ curwp->w_rflag |= WFMODE | WFFULL;
+ adjwp->w_rflag |= WFMODE | WFFULL;
+ return (TRUE);
+}
+
+/*
+ * Delete current window. Call shrink-window to do the screen updating, then
+ * throw away the window.
+ */
+/* ARGSUSED */
+int
+delwind(int f, int n)
+{
+ struct mgwin *wp, *nwp;
+
+ wp = curwp; /* Cheap... */
+
+ /* shrinkwind returning false means only one window... */
+ if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE)
+ return (FALSE);
+ if (--wp->w_bufp->b_nwnd == 0) {
+ wp->w_bufp->b_dotp = wp->w_dotp;
+ wp->w_bufp->b_doto = wp->w_doto;
+ wp->w_bufp->b_markp = wp->w_markp;
+ wp->w_bufp->b_marko = wp->w_marko;
+ wp->w_bufp->b_dotline = wp->w_dotline;
+ wp->w_bufp->b_markline = wp->w_markline;
+ }
+
+ /* since shrinkwind did't crap out, we know we have a second window */
+ if (wp == wheadp)
+ wheadp = curwp = wp->w_wndp;
+ else if ((curwp = wp->w_wndp) == NULL)
+ curwp = wheadp;
+ curbp = curwp->w_bufp;
+ for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp)
+ if (nwp->w_wndp == wp) {
+ nwp->w_wndp = wp->w_wndp;
+ break;
+ }
+ free(wp);
+ return (TRUE);
+}
Index: contrib/mg/word.c
===================================================================
--- /dev/null
+++ contrib/mg/word.c
@@ -0,0 +1,514 @@
+/* $OpenBSD: word.c,v 1.19 2015/12/30 20:51:51 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * Word mode commands.
+ * The routines in this file implement commands that work word at a time.
+ * There are all sorts of word mode commands.
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+
+RSIZE countfword(void);
+int grabword(char **);
+
+/*
+ * Move the cursor backward by "n" words. All of the details of motion are
+ * performed by the "backchar" and "forwchar" routines.
+ */
+/* ARGSUSED */
+int
+backword(int f, int n)
+{
+ if (n < 0)
+ return (forwword(f | FFRAND, -n));
+ if (backchar(FFRAND, 1) == FALSE)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (backchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ while (inword() != FALSE) {
+ if (backchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ }
+ return (forwchar(FFRAND, 1));
+}
+
+/*
+ * Move the cursor forward by the specified number of words. All of the
+ * motion is done by "forwchar".
+ */
+/* ARGSUSED */
+int
+forwword(int f, int n)
+{
+ if (n < 0)
+ return (backword(f | FFRAND, -n));
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ while (inword() != FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Transpose 2 words.
+ * The function below is artifically restricted to only a maximum of 1 iteration
+ * at the moment because the 'undo' functionality within mg needs amended for
+ * multiple movements of point, backwards and forwards.
+ */
+int
+transposeword(int f, int n)
+{
+ struct line *tmp1_w_dotp = NULL;
+ struct line *tmp2_w_dotp = NULL;
+ int tmp2_w_doto = 0;
+ int tmp1_w_dotline = 0;
+ int tmp2_w_dotline = 0;
+ int tmp1_w_doto;
+ int i; /* start-of-line space counter */
+ int ret, s;
+ int newline;
+ int leave = 0;
+ int tmp_len;
+ char *word1 = NULL;
+ char *word2 = NULL;
+ char *chr;
+
+ if (n == 0)
+ return (TRUE);
+
+ n = 1; /* remove this line to allow muliple-iterations */
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+ undo_boundary_enable(FFRAND, 0);
+
+ /* go backwards to find the start of a word to transpose. */
+ (void)backword(FFRAND, 1);
+ ret = grabword(&word1);
+ if (ret == ABORT) {
+ ewprintf("No word to the left to tranpose.");
+ return (FALSE);
+ }
+ if (ret < 0) {
+ dobeep();
+ ewprintf("Error copying word: %s", strerror(ret));
+ free(word1);
+ return (FALSE);
+ }
+
+ while (n-- > 0) {
+ i = 0;
+ newline = 0;
+
+ tmp1_w_doto = curwp->w_doto;
+ tmp1_w_dotline = curwp->w_dotline;
+ tmp1_w_dotp = curwp->w_dotp;
+
+ /* go forward and find next word. */
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE) {
+ leave = 1;
+ if (tmp1_w_dotline < curwp->w_dotline)
+ curwp->w_dotline--;
+ ewprintf("Don't have two things to transpose");
+ break;
+ }
+ if (curwp->w_doto == 0) {
+ newline = 1;
+ i = 0;
+ } else if (newline)
+ i++;
+ }
+ if (leave) {
+ tmp2_w_doto = tmp1_w_doto;
+ tmp2_w_dotline = tmp1_w_dotline;
+ tmp2_w_dotp = tmp1_w_dotp;
+ break;
+ }
+ tmp2_w_doto = curwp->w_doto;
+ tmp2_w_dotline = curwp->w_dotline;
+ tmp2_w_dotp = curwp->w_dotp;
+
+ ret = grabword(&word2);
+ if (ret < 0 || ret == ABORT) {
+ dobeep();
+ ewprintf("Error copying word: %s", strerror(ret));
+ free(word1);
+ return (FALSE);
+ }
+ tmp_len = strlen(word2);
+ tmp2_w_doto += tmp_len;
+
+ curwp->w_doto = tmp1_w_doto;
+ curwp->w_dotline = tmp1_w_dotline;
+ curwp->w_dotp = tmp1_w_dotp;
+
+ /* insert shuffled along word */
+ for (chr = word2; *chr != '\0'; ++chr)
+ linsert(1, *chr);
+
+ if (newline)
+ tmp2_w_doto = i;
+
+ curwp->w_doto = tmp2_w_doto;
+ curwp->w_dotline = tmp2_w_dotline;
+ curwp->w_dotp = tmp2_w_dotp;
+
+ word2 = NULL;
+ }
+ curwp->w_doto = tmp2_w_doto;
+ curwp->w_dotline = tmp2_w_dotline;
+ curwp->w_dotp = tmp2_w_dotp;
+
+ /* insert very first word in its new position */
+ for (chr = word1; *chr != '\0'; ++chr)
+ linsert(1, *chr);
+
+ if (leave)
+ (void)backword(FFRAND, 1);
+
+ free(word1);
+ free(word2);
+
+ undo_boundary_enable(FFRAND, 1);
+
+ return (TRUE);
+}
+
+/*
+ * copy and delete word.
+*/
+int
+grabword(char **word)
+{
+ int c;
+
+ while (inword() == TRUE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+ if (*word == NULL) {
+ if (asprintf(word, "%c", c) == -1)
+ return (errno);
+ } else {
+ if (asprintf(word, "%s%c", *word, c) == -1)
+ return (errno);
+ }
+ (void)forwdel(FFRAND, 1);
+ }
+ if (*word == NULL)
+ return (ABORT);
+ return (TRUE);
+}
+
+/*
+ * Move the cursor forward by the specified number of words. As you move,
+ * convert any characters to upper case.
+ */
+/* ARGSUSED */
+int
+upperword(int f, int n)
+{
+ int c, s;
+ RSIZE size;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ size = countfword();
+ undo_add_change(curwp->w_dotp, curwp->w_doto, size);
+
+ while (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+ if (ISLOWER(c) != FALSE) {
+ c = TOUPPER(c);
+ lputc(curwp->w_dotp, curwp->w_doto, c);
+ lchange(WFFULL);
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Move the cursor forward by the specified number of words. As you move
+ * convert characters to lower case.
+ */
+/* ARGSUSED */
+int
+lowerword(int f, int n)
+{
+ int c, s;
+ RSIZE size;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ size = countfword();
+ undo_add_change(curwp->w_dotp, curwp->w_doto, size);
+
+ while (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+ if (ISUPPER(c) != FALSE) {
+ c = TOLOWER(c);
+ lputc(curwp->w_dotp, curwp->w_doto, c);
+ lchange(WFFULL);
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Move the cursor forward by the specified number of words. As you move
+ * convert the first character of the word to upper case, and subsequent
+ * characters to lower case. Error if you try to move past the end of the
+ * buffer.
+ */
+/* ARGSUSED */
+int
+capword(int f, int n)
+{
+ int c, s;
+ RSIZE size;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+
+ if (n < 0)
+ return (FALSE);
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ size = countfword();
+ undo_add_change(curwp->w_dotp, curwp->w_doto, size);
+
+ if (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+ if (ISLOWER(c) != FALSE) {
+ c = TOUPPER(c);
+ lputc(curwp->w_dotp, curwp->w_doto, c);
+ lchange(WFFULL);
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ while (inword() != FALSE) {
+ c = lgetc(curwp->w_dotp, curwp->w_doto);
+ if (ISUPPER(c) != FALSE) {
+ c = TOLOWER(c);
+ lputc(curwp->w_dotp, curwp->w_doto, c);
+ lchange(WFFULL);
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (TRUE);
+ }
+ }
+ }
+ return (TRUE);
+}
+
+/*
+ * Count characters in word, from current position
+ */
+RSIZE
+countfword()
+{
+ RSIZE size;
+ struct line *dotp;
+ int doto;
+
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ size = 0;
+
+ while (inword() != FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ /* hit the end of the buffer */
+ goto out;
+ ++size;
+ }
+out:
+ curwp->w_dotp = dotp;
+ curwp->w_doto = doto;
+ return (size);
+}
+
+
+/*
+ * Kill forward by "n" words.
+ */
+/* ARGSUSED */
+int
+delfword(int f, int n)
+{
+ RSIZE size;
+ struct line *dotp;
+ int doto;
+ int s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+ if (n < 0)
+ return (FALSE);
+
+ /* purge kill buffer */
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+
+ thisflag |= CFKILL;
+ dotp = curwp->w_dotp;
+ doto = curwp->w_doto;
+ size = 0;
+
+ while (n--) {
+ while (inword() == FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ /* hit the end of the buffer */
+ goto out;
+ ++size;
+ }
+ while (inword() != FALSE) {
+ if (forwchar(FFRAND, 1) == FALSE)
+ /* hit the end of the buffer */
+ goto out;
+ ++size;
+ }
+ }
+out:
+ curwp->w_dotp = dotp;
+ curwp->w_doto = doto;
+ return (ldelete(size, KFORW));
+}
+
+/*
+ * Kill backwards by "n" words. The rules for success and failure are now
+ * different, to prevent strange behavior at the start of the buffer. The
+ * command only fails if something goes wrong with the actual delete of the
+ * characters. It is successful even if no characters are deleted, or if you
+ * say delete 5 words, and there are only 4 words left. I considered making
+ * the first call to "backchar" special, but decided that that would just be
+ * weird. Normally this is bound to "M-Rubout" and to "M-Backspace".
+ */
+/* ARGSUSED */
+int
+delbword(int f, int n)
+{
+ RSIZE size;
+ int s;
+
+ if ((s = checkdirty(curbp)) != TRUE)
+ return (s);
+ if (curbp->b_flag & BFREADONLY) {
+ dobeep();
+ ewprintf("Buffer is read-only");
+ return (FALSE);
+ }
+
+ if (n < 0)
+ return (FALSE);
+
+ /* purge kill buffer */
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ if (backchar(FFRAND, 1) == FALSE)
+ /* hit buffer start */
+ return (TRUE);
+
+ /* one deleted */
+ size = 1;
+ while (n--) {
+ while (inword() == FALSE) {
+ if (backchar(FFRAND, 1) == FALSE)
+ /* hit buffer start */
+ goto out;
+ ++size;
+ }
+ while (inword() != FALSE) {
+ if (backchar(FFRAND, 1) == FALSE)
+ /* hit buffer start */
+ goto out;
+ ++size;
+ }
+ }
+ if (forwchar(FFRAND, 1) == FALSE)
+ return (FALSE);
+
+ /* undo assumed delete */
+ --size;
+out:
+ return (ldelete(size, KBACK));
+}
+
+/*
+ * Return TRUE if the character at dot is a character that is considered to be
+ * part of a word. The word character list is hard coded. Should be settable.
+ */
+int
+inword(void)
+{
+ /* can't use lgetc in ISWORD due to bug in OSK cpp */
+ return (curwp->w_doto != llength(curwp->w_dotp) &&
+ ISWORD(curwp->w_dotp->l_text[curwp->w_doto]));
+}
Index: contrib/mg/yank.c
===================================================================
--- /dev/null
+++ contrib/mg/yank.c
@@ -0,0 +1,267 @@
+/* $OpenBSD: yank.c,v 1.15 2021/03/01 10:51:14 lum Exp $ */
+
+/* This file is in the public domain. */
+
+/*
+ * kill ring functions
+ */
+
+#include <sys/queue.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "def.h"
+
+#define KBLOCK 8192 /* Kill grow. */
+
+static char *kbufp = NULL; /* Kill buffer data. */
+static RSIZE kused = 0; /* # of bytes used in KB. */
+static RSIZE ksize = 0; /* # of bytes allocated in KB. */
+static RSIZE kstart = 0; /* # of first used byte in KB. */
+
+static int kgrow(int);
+
+/*
+ * Delete all of the text saved in the kill buffer. Called by commands when
+ * a new kill context is created. The kill buffer array is released, just in
+ * case the buffer has grown to an immense size. No errors.
+ */
+void
+kdelete(void)
+{
+ if (kbufp != NULL) {
+ free(kbufp);
+ kbufp = NULL;
+ kstart = kused = ksize = 0;
+ }
+}
+
+/*
+ * Insert a character to the kill buffer, enlarging the buffer if there
+ * isn't any room. Always grow the buffer in chunks, on the assumption
+ * that if you put something in the kill buffer you are going to put more
+ * stuff there too later. Return TRUE if all is well, and FALSE on errors.
+ * Print a message on errors. Dir says whether to put it at back or front.
+ * This call is ignored if KNONE is set.
+ */
+int
+kinsert(int c, int dir)
+{
+ if (dir == KNONE)
+ return (TRUE);
+ if (kused == ksize && dir == KFORW && kgrow(dir) == FALSE)
+ return (FALSE);
+ if (kstart == 0 && dir == KBACK && kgrow(dir) == FALSE)
+ return (FALSE);
+ if (dir == KFORW)
+ kbufp[kused++] = c;
+ else if (dir == KBACK)
+ kbufp[--kstart] = c;
+ else
+ panic("broken kinsert call"); /* Oh shit! */
+ return (TRUE);
+}
+
+/*
+ * kgrow - just get more kill buffer for the callee. If dir = KBACK
+ * we are trying to get space at the beginning of the kill buffer.
+ */
+static int
+kgrow(int dir)
+{
+ int nstart;
+ char *nbufp;
+
+ if ((unsigned)(ksize + KBLOCK) <= (unsigned)ksize) {
+ /* probably 16 bit unsigned */
+ dobeep();
+ ewprintf("Kill buffer size at maximum");
+ return (FALSE);
+ }
+ if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) {
+ dobeep();
+ ewprintf("Can't get %ld bytes", (long)(ksize + KBLOCK));
+ return (FALSE);
+ }
+ nstart = (dir == KBACK) ? (kstart + KBLOCK) : (KBLOCK / 4);
+ bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int)(kused - kstart));
+ free(kbufp);
+ kbufp = nbufp;
+ ksize += KBLOCK;
+ kused = kused - kstart + nstart;
+ kstart = nstart;
+ return (TRUE);
+}
+
+/*
+ * This function gets characters from the kill buffer. If the character
+ * index "n" is off the end, it returns "-1". This lets the caller just
+ * scan along until it gets a "-1" back.
+ */
+int
+kremove(int n)
+{
+ if (n < 0 || n + kstart >= kused)
+ return (-1);
+ return (CHARMASK(kbufp[n + kstart]));
+}
+
+/*
+ * Copy a string into the kill buffer. kflag gives direction.
+ * if KNONE, do nothing.
+ */
+int
+kchunk(char *cp1, RSIZE chunk, int kflag)
+{
+ /*
+ * HACK - doesn't matter, and fixes back-over-nl bug for empty
+ * kill buffers.
+ */
+ if (kused == kstart)
+ kflag = KFORW;
+
+ if (kflag & KFORW) {
+ while (ksize - kused < chunk)
+ if (kgrow(kflag) == FALSE)
+ return (FALSE);
+ bcopy(cp1, &(kbufp[kused]), (int)chunk);
+ kused += chunk;
+ } else if (kflag & KBACK) {
+ while (kstart < chunk)
+ if (kgrow(kflag) == FALSE)
+ return (FALSE);
+ bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk);
+ kstart -= chunk;
+ }
+
+ return (TRUE);
+}
+
+/*
+ * Kill line. If called without an argument, it kills from dot to the end
+ * of the line, unless it is at the end of the line, when it kills the
+ * newline. If called with an argument of 0, it kills from the start of the
+ * line to dot. If called with a positive argument, it kills from dot
+ * forward over that number of newlines. If called with a negative argument
+ * it kills any text before dot on the current line, then it kills back
+ * abs(arg) lines.
+ */
+/* ARGSUSED */
+int
+killline(int f, int n)
+{
+ struct line *nextp;
+ RSIZE chunk;
+ int i, c;
+
+ /* clear kill buffer if last wasn't a kill */
+ if ((lastflag & CFKILL) == 0)
+ kdelete();
+ thisflag |= CFKILL;
+ if (!(f & FFARG)) {
+ for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i)
+ if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t')
+ break;
+ if (i == llength(curwp->w_dotp))
+ chunk = llength(curwp->w_dotp) - curwp->w_doto + 1;
+ else {
+ chunk = llength(curwp->w_dotp) - curwp->w_doto;
+ if (chunk == 0)
+ chunk = 1;
+ }
+ } else if (n > 0) {
+ chunk = llength(curwp->w_dotp) - curwp->w_doto;
+ nextp = lforw(curwp->w_dotp);
+ if (nextp != curbp->b_headp)
+ chunk++; /* newline */
+ if (nextp == curbp->b_headp)
+ goto done; /* EOL */
+ i = n;
+ while (--i) {
+ chunk += llength(nextp);
+ nextp = lforw(nextp);
+ if (nextp != curbp->b_headp)
+ chunk++; /* newline */
+ if (nextp == curbp->b_headp)
+ break; /* EOL */
+ }
+ } else {
+ /* n <= 0 */
+ chunk = curwp->w_doto;
+ curwp->w_doto = 0;
+ i = n;
+ while (i++) {
+ if (lforw(curwp->w_dotp))
+ chunk++;
+ curwp->w_dotp = lback(curwp->w_dotp);
+ curwp->w_rflag |= WFMOVE;
+ chunk += llength(curwp->w_dotp);
+ }
+ }
+ /*
+ * KFORW here is a bug. Should be KBACK/KFORW, but we need to
+ * rewrite the ldelete code (later)?
+ */
+done:
+ if (chunk)
+ return (ldelete(chunk, KFORW));
+ return (TRUE);
+}
+
+/*
+ * Yank text back from the kill buffer. This is really easy. All of the work
+ * is done by the standard insert routines. All you do is run the loop, and
+ * check for errors. The blank lines are inserted with a call to "newline"
+ * instead of a call to "lnewline" so that the magic stuff that happens when
+ * you type a carriage return also happens when a carriage return is yanked
+ * back from the kill buffer. An attempt has been made to fix the cosmetic
+ * bug associated with a yank when dot is on the top line of the window
+ * (nothing moves, because all of the new text landed off screen).
+ */
+/* ARGSUSED */
+int
+yank(int f, int n)
+{
+ struct line *lp;
+ int c, i, nline;
+
+ if (n < 0)
+ return (FALSE);
+
+ /* newline counting */
+ nline = 0;
+
+ undo_boundary_enable(FFRAND, 0);
+ while (n--) {
+ /* mark around last yank */
+ isetmark();
+ i = 0;
+ while ((c = kremove(i)) >= 0) {
+ if (c == *curbp->b_nlchr) {
+ if (enewline(FFRAND, 1) == FALSE)
+ return (FALSE);
+ ++nline;
+ } else {
+ if (linsert(1, c) == FALSE)
+ return (FALSE);
+ }
+ ++i;
+ }
+ }
+ /* cosmetic adjustment */
+ lp = curwp->w_linep;
+
+ /* if offscreen insert */
+ if (curwp->w_dotp == lp) {
+ while (nline-- && lback(lp) != curbp->b_headp)
+ lp = lback(lp);
+ /* adjust framing */
+ curwp->w_linep = lp;
+ curwp->w_rflag |= WFFULL;
+ }
+ undo_boundary_enable(FFRAND, 1);
+ return (TRUE);
+}
+
Index: release/packages/Makefile.package
===================================================================
--- release/packages/Makefile.package
+++ release/packages/Makefile.package
@@ -79,6 +79,8 @@
kernel_DESC= FreeBSD Kernel
manuals_COMMENT= Manual Pages
manuals_DESC= Manual Pages
+mg_COMMENT= Small, fast Emacs-like editor
+mg_DESC= Small, fast Emacs-like editor
mlx-tools_COMMENT= Mellanox Utilities
mlx-tools_DESC= Mellanox Utilities
mtree_COMMENT= MTREE Files
Index: usr.bin/Makefile
===================================================================
--- usr.bin/Makefile
+++ usr.bin/Makefile
@@ -89,6 +89,7 @@
m4 \
mandoc \
mesg \
+ mg \
minigzip \
ministat \
mkdep \
Index: usr.bin/mg/Makefile
===================================================================
--- /dev/null
+++ usr.bin/mg/Makefile
@@ -0,0 +1,64 @@
+PACKAGE= mg
+PROG= mg
+SRCDIR= ${SRCTOP}/contrib/mg
+
+.PATH: ${SRCDIR}
+
+CFLAGS+= -DREGEX -w -D__dead=__dead2 -DLOGIN_NAME_MAX=MAXLOGNAME
+CFLAGS+= -I${.CURDIR} -I${SRCDIR}
+CWARNFLAGS+= -w
+
+LIBADD= util ncursesw
+
+SRCS= autoexec.c \
+ basic.c \
+ bell.c \
+ buffer.c \
+ cinfo.c \
+ dir.c \
+ display.c \
+ echo.c \
+ extend.c \
+ file.c \
+ fileio.c \
+ funmap.c \
+ help.c \
+ kbd.c \
+ keymap.c \
+ line.c \
+ macro.c \
+ main.c \
+ match.c \
+ modes.c \
+ paragraph.c \
+ re_search.c \
+ region.c \
+ search.c \
+ spawn.c \
+ tty.c \
+ ttyio.c \
+ ttykbd.c \
+ undo.c \
+ util.c \
+ version.c \
+ window.c \
+ word.c \
+ yank.c \
+ cmode.c \
+ cscope.c \
+ dired.c \
+ grep.c \
+ tags.c \
+ fparseln.c \
+ fstatat.c \
+ futimens.c \
+ getline.c \
+ reallocarray.c \
+ strlcat.c \
+ strlcpy.c \
+ strndup.c \
+ strtonum.c \
+ interpreter.c \
+ extensions.c
+
+.include <bsd.prog.mk>
Index: usr.bin/mg/config.h
===================================================================
--- /dev/null
+++ usr.bin/mg/config.h
@@ -0,0 +1,14 @@
+/* This file is based on the file generated by configure. */
+
+#include "common.h"
+#include "freebsd.h"
+
+#define HAVE_FPARSELN
+#define HAVE_FSTATAT
+#define HAVE_FUTIMENS
+#define HAVE_GETLINE
+#define HAVE_REALLOCARRAY
+#define HAVE_STRLCAT
+#define HAVE_STRLCPY
+#define HAVE_STRNDUP
+#define HAVE_STRTONUM

File Metadata

Mime Type
text/plain
Expires
Sat, Feb 8, 12:45 PM (20 h, 41 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b6/f7/7d27ef57fc04f4c717c8e9b0337f
Default Alt Text
D34411.diff (997 KB)

Event Timeline